playwright-cucumber-ts-steps 1.1.6 → 1.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +10 -15
  2. package/dist/backend/actions/click.d.ts +2 -0
  3. package/dist/backend/actions/click.d.ts.map +1 -0
  4. package/dist/backend/actions/click.js +179 -0
  5. package/dist/backend/actions/find.d.ts +2 -0
  6. package/dist/backend/actions/find.d.ts.map +1 -0
  7. package/dist/backend/actions/find.js +291 -0
  8. package/dist/backend/actions/form.d.ts +2 -0
  9. package/dist/backend/actions/form.d.ts.map +1 -0
  10. package/dist/backend/actions/form.js +185 -0
  11. package/dist/backend/actions/formTable.js +1 -1
  12. package/dist/backend/actions/frames.d.ts +2 -0
  13. package/dist/backend/actions/frames.d.ts.map +1 -0
  14. package/dist/backend/actions/frames.js +60 -0
  15. package/dist/backend/actions/index.d.ts +10 -0
  16. package/dist/backend/actions/index.d.ts.map +1 -1
  17. package/dist/backend/actions/index.js +10 -0
  18. package/dist/backend/actions/inputs.d.ts +2 -0
  19. package/dist/backend/actions/inputs.d.ts.map +1 -0
  20. package/dist/backend/actions/inputs.js +177 -0
  21. package/dist/backend/actions/keyboard.d.ts +2 -0
  22. package/dist/backend/actions/keyboard.d.ts.map +1 -0
  23. package/dist/backend/actions/keyboard.js +62 -0
  24. package/dist/backend/actions/misc.d.ts +2 -0
  25. package/dist/backend/actions/misc.d.ts.map +1 -0
  26. package/dist/backend/actions/misc.js +144 -0
  27. package/dist/backend/actions/mobile.d.ts +2 -0
  28. package/dist/backend/actions/mobile.d.ts.map +1 -0
  29. package/dist/backend/actions/mobile.js +87 -0
  30. package/dist/backend/actions/mouse.d.ts +2 -0
  31. package/dist/backend/actions/mouse.d.ts.map +1 -0
  32. package/dist/backend/actions/mouse.js +105 -0
  33. package/dist/backend/actions/navigation.js +31 -7
  34. package/dist/backend/actions/waits.d.ts +2 -0
  35. package/dist/backend/actions/waits.d.ts.map +1 -0
  36. package/dist/backend/actions/waits.js +51 -0
  37. package/dist/backend/api/index.d.ts +1 -0
  38. package/dist/backend/api/index.d.ts.map +1 -1
  39. package/dist/backend/api/index.js +1 -0
  40. package/dist/backend/api/network.d.ts +2 -0
  41. package/dist/backend/api/network.d.ts.map +1 -0
  42. package/dist/backend/api/network.js +145 -0
  43. package/dist/backend/assertions/pageState.js +25 -14
  44. package/dist/backend/assertions/visibility.js +116 -12
  45. package/dist/backend/utils/state.d.ts +18 -0
  46. package/dist/backend/utils/state.d.ts.map +1 -0
  47. package/dist/backend/utils/state.js +84 -0
  48. package/dist/core/registry.d.ts +14 -14
  49. package/dist/core/registry.d.ts.map +1 -1
  50. package/dist/core/registry.js +13 -4
  51. package/dist/core/runner.d.ts.map +1 -1
  52. package/dist/core/runner.js +91 -37
  53. package/package.json +1 -1
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const registry_1 = require("../../core/registry");
4
+ const state_1 = require("../utils/state");
5
+ // ==================================================
6
+ // 1. TIMING & WAITS
7
+ // ==================================================
8
+ /**
9
+ * Pauses execution for a set amount of time.
10
+ * Pattern: When I wait for 1000 milliseconds
11
+ */
12
+ (0, registry_1.Step)("I wait for {int} milliseconds", async (page, ms) => {
13
+ await page.waitForTimeout(ms);
14
+ console.log(`⏳ Waited for ${ms}ms`);
15
+ });
16
+ /**
17
+ * Pauses execution for a set amount of seconds.
18
+ * Pattern: When I wait for 5 seconds
19
+ */
20
+ (0, registry_1.Step)("I wait for {int} seconds", async (page, seconds) => {
21
+ await page.waitForTimeout(seconds * 1000);
22
+ console.log(`⏳ Waited for ${seconds}s`);
23
+ });
24
+ // ==================================================
25
+ // 2. DEBUGGING
26
+ // ==================================================
27
+ /**
28
+ * Pauses the test execution and opens the Playwright Inspector.
29
+ * Useful for manual debugging during a run.
30
+ * Pattern: When I pause
31
+ */
32
+ (0, registry_1.Step)("I pause", async (page) => {
33
+ console.log("⏸️ Pausing test execution...");
34
+ await page.pause();
35
+ });
36
+ /**
37
+ * Alias for pause.
38
+ * Pattern: When I debug
39
+ */
40
+ (0, registry_1.Step)("I debug", async (page) => {
41
+ console.log("🐞 Debugging...");
42
+ await page.pause();
43
+ });
44
+ /**
45
+ * Prints a message to the console logs.
46
+ * Pattern: When I log "Hello World"
47
+ */
48
+ (0, registry_1.Step)("I log {string}", async (page, message) => {
49
+ console.log(`📝 LOG: ${message}`);
50
+ });
51
+ // ==================================================
52
+ // 3. FOCUS & BLUR
53
+ // ==================================================
54
+ /**
55
+ * Focuses on the currently stored element.
56
+ * Pattern: When I focus
57
+ */
58
+ (0, registry_1.Step)("I focus", async (page) => {
59
+ const element = (0, state_1.getActiveElement)(page);
60
+ await element.focus();
61
+ console.log("👀 Focused on stored element");
62
+ });
63
+ /**
64
+ * Blurs (un-focuses) the currently stored element.
65
+ * Pattern: When I blur
66
+ */
67
+ (0, registry_1.Step)("I blur", async (page) => {
68
+ const element = (0, state_1.getActiveElement)(page);
69
+ // Playwright doesn't have a direct .blur(), so we use JS evaluation
70
+ await element.evaluate((el) => {
71
+ if (el instanceof HTMLElement)
72
+ el.blur();
73
+ });
74
+ console.log("🌫️ Blurred stored element");
75
+ });
76
+ // ==================================================
77
+ // 4. BROWSER STORAGE (Cookies / Local Storage)
78
+ // ==================================================
79
+ /**
80
+ * Sets a cookie for the current context.
81
+ * Pattern: When I set cookie "session_id" to "12345"
82
+ */
83
+ (0, registry_1.Step)("I set cookie {string} to {string}", async (page, name, value) => {
84
+ const context = page.context();
85
+ const url = page.url();
86
+ // We need a domain or url to set cookies. We use the current page URL.
87
+ await context.addCookies([{ name, value, url }]);
88
+ console.log(`🍪 Set cookie "${name}"`);
89
+ });
90
+ /**
91
+ * Clears all cookies.
92
+ * Pattern: When I clear all cookies
93
+ */
94
+ (0, registry_1.Step)("I clear all cookies", async (page) => {
95
+ const context = page.context();
96
+ await context.clearCookies();
97
+ console.log("🍪 Cleared all cookies");
98
+ });
99
+ /**
100
+ * Sets a Local Storage item.
101
+ * Pattern: When I set local storage item "theme" to "dark"
102
+ */
103
+ (0, registry_1.Step)("I set local storage item {string} to {string}", async (page, key, value) => {
104
+ await page.evaluate(({ k, v }) => localStorage.setItem(k, v), {
105
+ k: key,
106
+ v: value,
107
+ });
108
+ console.log(`📦 Set local storage "${key}" = "${value}"`);
109
+ });
110
+ /**
111
+ * Gets a Local Storage item and prints it.
112
+ * Pattern: When I get local storage item "token"
113
+ */
114
+ (0, registry_1.Step)("I get local storage item {string}", async (page, key) => {
115
+ const value = await page.evaluate((k) => localStorage.getItem(k), key);
116
+ console.log(`📦 Local Storage "${key}": ${value}`);
117
+ });
118
+ /**
119
+ * Clears all Local Storage.
120
+ * Pattern: When I clear local storage
121
+ */
122
+ (0, registry_1.Step)("I clear local storage", async (page) => {
123
+ await page.evaluate(() => localStorage.clear());
124
+ console.log("📦 Cleared local storage");
125
+ });
126
+ /**
127
+ * Sets a Session Storage item.
128
+ * Pattern: When I set session storage item "user" to "admin"
129
+ */
130
+ (0, registry_1.Step)("I set session storage item {string} to {string}", async (page, key, value) => {
131
+ await page.evaluate(({ k, v }) => sessionStorage.setItem(k, v), {
132
+ k: key,
133
+ v: value,
134
+ });
135
+ console.log(`📦 Set session storage "${key}" = "${value}"`);
136
+ });
137
+ /**
138
+ * Clears all Session Storage.
139
+ * Pattern: When I clear session storage
140
+ */
141
+ (0, registry_1.Step)("I clear session storage", async (page) => {
142
+ await page.evaluate(() => sessionStorage.clear());
143
+ console.log("📦 Cleared session storage");
144
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mobile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mobile.d.ts","sourceRoot":"","sources":["../../../src/backend/actions/mobile.ts"],"names":[],"mappings":""}
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const registry_1 = require("../../core/registry");
4
+ const state_1 = require("../utils/state");
5
+ /**
6
+ * Helper: Tries to tap. If the context doesn't support touch, falls back to click.
7
+ */
8
+ async function safeTap(target) {
9
+ try {
10
+ // Try native touch tap
11
+ await target.tap();
12
+ return "tapped";
13
+ }
14
+ catch (error) {
15
+ if (error.message.includes("does not support tap")) {
16
+ // Fallback for Desktop Contexts
17
+ await target.click();
18
+ return "clicked (fallback)";
19
+ }
20
+ throw error;
21
+ }
22
+ }
23
+ // ==================================================
24
+ // 1. TOUCH INTERACTIONS
25
+ // ==================================================
26
+ /**
27
+ * Taps on the currently stored element.
28
+ * Pattern: When I tap
29
+ */
30
+ (0, registry_1.Step)("I tap", async (page) => {
31
+ const element = (0, state_1.getActiveElement)(page);
32
+ const action = await safeTap(element);
33
+ console.log(`👆 ${action === "tapped" ? "Tapped" : "Clicked"} stored element`);
34
+ });
35
+ /**
36
+ * Finds an element by selector and taps it.
37
+ * Pattern: When I tap element "#submit-btn"
38
+ */
39
+ (0, registry_1.Step)("I tap element {string}", async (page, selector) => {
40
+ const element = page.locator(selector);
41
+ const action = await safeTap(element);
42
+ console.log(`👆 ${action === "tapped" ? "Tapped" : "Clicked"} element "${selector}"`);
43
+ });
44
+ /**
45
+ * Taps at specific coordinates.
46
+ * Pattern: When I tap coordinates x:100 y:200
47
+ */
48
+ (0, registry_1.Step)("I tap coordinates x:{int} y:{int}", async (page, x, y) => {
49
+ // page.mouse.click works for both desktop and mobile viewports
50
+ await page.mouse.click(x, y);
51
+ console.log(`👆 Tapped at (${x}, ${y})`);
52
+ });
53
+ // ==================================================
54
+ // 2. VIEWPORT & EMULATION
55
+ // ==================================================
56
+ (0, registry_1.Step)("I resize window to width {int} and height {int}", async (page, width, height) => {
57
+ await page.setViewportSize({ width, height });
58
+ console.log(`📱 Resized viewport to ${width}x${height}`);
59
+ });
60
+ (0, registry_1.Step)("I simulate device {string}", async (page, deviceName) => {
61
+ const devices = {
62
+ "iPhone 12": { width: 390, height: 844 },
63
+ "iPhone SE": { width: 375, height: 667 },
64
+ iPad: { width: 768, height: 1024 },
65
+ "Pixel 5": { width: 393, height: 851 },
66
+ "Samsung Galaxy S8": { width: 360, height: 740 },
67
+ Desktop: { width: 1920, height: 1080 },
68
+ };
69
+ const size = devices[deviceName];
70
+ if (!size) {
71
+ throw new Error(`❌ Unknown device preset: "${deviceName}".`);
72
+ }
73
+ await page.setViewportSize(size);
74
+ console.log(`📱 Simulated device "${deviceName}" (${size.width}x${size.height})`);
75
+ });
76
+ // ==================================================
77
+ // 3. GEOLOCATION & PERMISSIONS
78
+ // ==================================================
79
+ (0, registry_1.Step)("I set geolocation to lat: {float} long: {float}", async (page, lat, long) => {
80
+ await page.context().setGeolocation({ latitude: lat, longitude: long });
81
+ await page.context().grantPermissions(["geolocation"]);
82
+ console.log(`🌍 Geolocation set to ${lat}, ${long}`);
83
+ });
84
+ (0, registry_1.Step)("I grant permission {string}", async (page, permission) => {
85
+ await page.context().grantPermissions([permission]);
86
+ console.log(`🛡️ Granted permission: "${permission}"`);
87
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mouse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mouse.d.ts","sourceRoot":"","sources":["../../../src/backend/actions/mouse.ts"],"names":[],"mappings":""}
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const registry_1 = require("../../core/registry");
4
+ const state_1 = require("../utils/state");
5
+ // ===================================================================================
6
+ // MOUSE ACTIONS: SCROLLING
7
+ // ===================================================================================
8
+ /**
9
+ * Scrolls the element matching the selector into view.
10
+ * Pattern: When I scroll ".footer" into view
11
+ */
12
+ (0, registry_1.Step)("I scroll {string} into view", async (page, selector) => {
13
+ const locator = page.locator(selector);
14
+ await locator.scrollIntoViewIfNeeded();
15
+ console.log(`🖱️ Scrolled element "${selector}" into view.`);
16
+ });
17
+ /**
18
+ * Scrolls a specific element to internal X, Y coordinates.
19
+ * Pattern: When I scroll "#box" to position x:100 y:200
20
+ */
21
+ (0, registry_1.Step)("I scroll {string} to position x:{int} y:{int}", async (page, selector, x, y) => {
22
+ const locator = page.locator(selector);
23
+ await locator.evaluate((el, coords) => {
24
+ el.scrollTo(coords.x, coords.y);
25
+ }, { x, y });
26
+ console.log(`🖱️ Scrolled element "${selector}" to position x:${x} y:${y}.`);
27
+ });
28
+ /**
29
+ * Scrolls the entire window to specific coordinates.
30
+ * Pattern: When I scroll to coordinates x:0 y:500
31
+ */
32
+ (0, registry_1.Step)("I scroll to coordinates x:{int} y:{int}", async (page, x, y) => {
33
+ await page.evaluate((coords) => {
34
+ window.scrollTo(coords.x, coords.y);
35
+ }, { x, y });
36
+ console.log(`🖱️ Scrolled window to coordinates x:${x} y:${y}.`);
37
+ });
38
+ /**
39
+ * Scrolls the window smoothly to specific coordinates.
40
+ * Pattern: When I scroll mouse window to position top:0 left:100
41
+ */
42
+ (0, registry_1.Step)("I scroll mouse window to position top:{int} left:{int}", async (page, top, left) => {
43
+ await page.evaluate((coords) => {
44
+ window.scrollTo({
45
+ top: coords.top,
46
+ left: coords.left,
47
+ behavior: "smooth",
48
+ });
49
+ }, { top, left });
50
+ console.log(`🖱️ Scrolled window to position top:${top} left:${left} (smooth).`);
51
+ });
52
+ /**
53
+ * Scrolls to a general direction (top, bottom, left, right).
54
+ * Pattern: When I scroll to "bottom"
55
+ */
56
+ (0, registry_1.Step)("I scroll to {string}", async (page, direction) => {
57
+ const validDirections = ["top", "bottom", "left", "right"];
58
+ const dir = direction.toLowerCase();
59
+ if (!validDirections.includes(dir)) {
60
+ throw new Error(`Invalid scroll direction "${direction}". Must be one of: ${validDirections.join(", ")}.`);
61
+ }
62
+ await page.evaluate((d) => {
63
+ const scrollOptions = { behavior: "smooth" };
64
+ switch (d) {
65
+ case "top":
66
+ scrollOptions.top = 0;
67
+ break;
68
+ case "bottom":
69
+ scrollOptions.top = document.body.scrollHeight;
70
+ break;
71
+ case "left":
72
+ scrollOptions.left = 0;
73
+ break;
74
+ case "right":
75
+ scrollOptions.left = document.body.scrollWidth;
76
+ break;
77
+ }
78
+ window.scrollTo(scrollOptions);
79
+ }, dir);
80
+ console.log(`🖱️ Scrolled to "${dir}".`);
81
+ // Wait a moment for smooth scroll to finish
82
+ await page.waitForTimeout(500);
83
+ });
84
+ // ===================================================================================
85
+ // MOUSE ACTIONS: HOVERING / MOVEMENT
86
+ // ===================================================================================
87
+ /**
88
+ * Hovers over an element and sets it as the active element.
89
+ * Pattern: When I hover over the element ".menu-item"
90
+ */
91
+ (0, registry_1.Step)("I hover over the element {string}", async (page, selector) => {
92
+ const element = page.locator(selector);
93
+ await element.hover();
94
+ // 🔥 Store the hovered element so we can click/assert on it immediately after
95
+ (0, state_1.setActiveElement)(page, element);
96
+ console.log(`🖱️ Hovered over: "${selector}".`);
97
+ });
98
+ /**
99
+ * Moves mouse to absolute coordinates.
100
+ * Pattern: When I move mouse to coordinates 100, 200
101
+ */
102
+ (0, registry_1.Step)("I move mouse to coordinates {int}, {int}", async (page, x, y) => {
103
+ await page.mouse.move(x, y);
104
+ console.log(`🧭 Mouse moved to (${x}, ${y}).`);
105
+ });
@@ -1,19 +1,43 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const registry_1 = require("../../core/registry");
4
- // Basic visit
4
+ /**
5
+ * Visits a specific URL.
6
+ * Pattern: Given I visit "https://google.com"
7
+ */
5
8
  (0, registry_1.Step)("I visit {string}", async (page, url) => {
6
9
  await page.goto(url);
10
+ console.log(`🌍 Visiting: ${url}`);
7
11
  });
8
- // Visit and wait for network (useful for slow apps)
9
- (0, registry_1.Step)("I visit {string} and wait for network idle", async (page, url) => {
10
- await page.goto(url, { waitUntil: "networkidle" });
11
- });
12
- // Reload page
12
+ /**
13
+ * Reloads the current page.
14
+ * Pattern: When I reload the page
15
+ */
13
16
  (0, registry_1.Step)("I reload the page", async (page) => {
14
17
  await page.reload();
18
+ console.log("🔄 Page reloaded");
15
19
  });
16
- // Go back
20
+ /**
21
+ * Navigates back in browser history.
22
+ * Pattern: When I go back
23
+ */
17
24
  (0, registry_1.Step)("I go back", async (page) => {
18
25
  await page.goBack();
26
+ console.log("⬅️ Went back");
27
+ });
28
+ /**
29
+ * Navigates forward in browser history.
30
+ * Pattern: When I go forward
31
+ */
32
+ (0, registry_1.Step)("I go forward", async (page) => {
33
+ await page.goForward();
34
+ console.log("➡️ Went forward");
35
+ });
36
+ /**
37
+ * Navigates to a specific path (relative to base URL if set).
38
+ * Pattern: When I navigate to "/login"
39
+ */
40
+ (0, registry_1.Step)("I navigate to {string}", async (page, path) => {
41
+ await page.goto(path);
42
+ console.log(`🌍 Navigated to path: ${path}`);
19
43
  });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=waits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"waits.d.ts","sourceRoot":"","sources":["../../../src/backend/actions/waits.ts"],"names":[],"mappings":""}
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const registry_1 = require("../../core/registry");
4
+ const state_1 = require("../utils/state");
5
+ /**
6
+ * Waits for the network to be idle (no active connections for 500ms).
7
+ * Useful after clicking a button that triggers complex API calls.
8
+ * Pattern: When I wait for network idle
9
+ */
10
+ (0, registry_1.Step)("I wait for network idle", async (page) => {
11
+ await page.waitForLoadState("networkidle");
12
+ console.log("⏳ Network is idle");
13
+ });
14
+ /**
15
+ * Waits for the page to reach a specific load state.
16
+ * Options: "load", "domcontentloaded", "networkidle".
17
+ * Pattern: When I wait for load state "domcontentloaded"
18
+ */
19
+ (0, registry_1.Step)("I wait for load state {string}", async (page, state) => {
20
+ if (!["load", "domcontentloaded", "networkidle"].includes(state)) {
21
+ throw new Error(`❌ Invalid load state: "${state}". Use load, domcontentloaded, or networkidle.`);
22
+ }
23
+ await page.waitForLoadState(state);
24
+ console.log(`⏳ Reached load state: "${state}"`);
25
+ });
26
+ /**
27
+ * Explicitly waits for the stored element to be visible.
28
+ * Pattern: When I wait for element to be visible
29
+ */
30
+ (0, registry_1.Step)("I wait for element to be visible", async (page) => {
31
+ const element = (0, state_1.getActiveElement)(page);
32
+ await element.waitFor({ state: "visible" });
33
+ console.log("⏳ Element is now visible");
34
+ });
35
+ /**
36
+ * Explicitly waits for the stored element to be hidden/detached.
37
+ * Pattern: When I wait for element to be hidden
38
+ */
39
+ (0, registry_1.Step)("I wait for element to be hidden", async (page) => {
40
+ const element = (0, state_1.getActiveElement)(page);
41
+ await element.waitFor({ state: "hidden" });
42
+ console.log("⏳ Element is now hidden");
43
+ });
44
+ /**
45
+ * Waits for a specific URL pattern.
46
+ * Pattern: When I wait for URL to contain "dashboard"
47
+ */
48
+ (0, registry_1.Step)("I wait for URL to contain {string}", async (page, urlPart) => {
49
+ await page.waitForURL(new RegExp(urlPart));
50
+ console.log(`⏳ URL now contains: "${urlPart}"`);
51
+ });
@@ -1,4 +1,5 @@
1
1
  import "./requests";
2
2
  import "./assertions";
3
3
  import "./mock";
4
+ import "./network";
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/backend/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,CAAC;AACpB,OAAO,cAAc,CAAC;AACtB,OAAO,QAAQ,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/backend/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,CAAC;AACpB,OAAO,cAAc,CAAC;AACtB,OAAO,QAAQ,CAAC;AAChB,OAAO,WAAW,CAAC"}
@@ -3,3 +3,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  require("./requests");
4
4
  require("./assertions");
5
5
  require("./mock");
6
+ require("./network");
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=network.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../../src/backend/api/network.ts"],"names":[],"mappings":""}
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const registry_1 = require("../../core/registry");
4
+ const state_1 = require("../utils/state");
5
+ // Helper: Safely parse JSON even if it comes with DocString quotes or is undefined
6
+ function safeJsonParse(input, context) {
7
+ if (!input) {
8
+ throw new Error(`❌ Missing JSON input for ${context}. Did you forget the DocString?`);
9
+ }
10
+ let text = typeof input === "string" ? input : JSON.stringify(input);
11
+ // Clean up DocString artifacts if the runner passes them
12
+ // 1. Remove surrounding triple quotes """ if present
13
+ text = text.replace(/^"""/g, "").replace(/"""$/g, "");
14
+ // 2. Trim whitespace/newlines
15
+ text = text.trim();
16
+ // Debug Log: Show exactly what we are trying to parse
17
+ // console.log(`🔍 [Debug] Parsing JSON for ${context}:`, text);
18
+ try {
19
+ return JSON.parse(text);
20
+ }
21
+ catch (e) {
22
+ // If simple parse fails, it might be due to unquoted keys or single quotes
23
+ // You could add a 'loose JSON' parser here if needed, but for now, strict JSON is best.
24
+ throw new Error(`❌ Invalid JSON for ${context}.\nReceived: ${text.slice(0, 50)}...\nError: ${e.message}`);
25
+ }
26
+ }
27
+ // =============================
28
+ // 1. INTERCEPTION / MOCKING
29
+ // =============================
30
+ /**
31
+ * Intercepts a URL and returns a JSON response (Mocking).
32
+ * Uses Regex to support optional trailing colon.
33
+ */
34
+ (0, registry_1.Step)(/^I intercept URL "([^"]+)" and stub body:?$/, async (page, url, body) => {
35
+ const parsedBody = safeJsonParse(body, `stubbing "${url}"`);
36
+ await page.route(url, (route) => {
37
+ route.fulfill({
38
+ status: 200,
39
+ contentType: "application/json",
40
+ body: JSON.stringify(parsedBody),
41
+ });
42
+ });
43
+ console.log(`📡 Stubbed "${url}" with JSON response.`);
44
+ });
45
+ /**
46
+ * Intercepts a URL and stubs it with a raw string (non-JSON).
47
+ */
48
+ (0, registry_1.Step)("I intercept URL {string} and stub body {string}", async (page, url, body) => {
49
+ await page.route(url, (route) => {
50
+ route.fulfill({
51
+ status: 200,
52
+ contentType: "text/plain",
53
+ body: body,
54
+ });
55
+ });
56
+ console.log(`📡 Stubbed "${url}" with raw text: "${body}"`);
57
+ });
58
+ /**
59
+ * Intercepts a URL but allows it to continue (Spying).
60
+ */
61
+ (0, registry_1.Step)("I intercept URL {string}", async (page, url) => {
62
+ await page.route(url, async (route) => {
63
+ await route.continue();
64
+ });
65
+ console.log(`📡 Spying on URL "${url}" (allowed to continue).`);
66
+ });
67
+ // =============================
68
+ // 2. MAKING API REQUESTS
69
+ // =============================
70
+ /**
71
+ * Makes a GET request and stores the response.
72
+ */
73
+ (0, registry_1.Step)("I make request to {string}", async (page, url) => {
74
+ console.log(`⚡ GET request to: ${url}`);
75
+ const response = await page.request.get(url);
76
+ const status = response.status();
77
+ const body = await response.text();
78
+ let jsonBody;
79
+ try {
80
+ jsonBody = JSON.parse(body);
81
+ }
82
+ catch { }
83
+ (0, state_1.setVariable)(page, "lastResponse", { status, body, json: jsonBody });
84
+ (0, state_1.setVariable)(page, "lastStatusCode", status);
85
+ console.log(`✅ Status: ${status}`);
86
+ });
87
+ /**
88
+ * Makes a POST request with a JSON body.
89
+ * Uses Regex to support optional trailing colon.
90
+ */
91
+ (0, registry_1.Step)(/^I make a POST request to "([^"]+)" with JSON body:?$/, async (page, url, docString) => {
92
+ const payload = safeJsonParse(docString, `POST to "${url}"`);
93
+ console.log(`⚡ POST request to: ${url}`);
94
+ const response = await page.request.post(url, { data: payload });
95
+ const status = response.status();
96
+ const body = await response.text();
97
+ let jsonBody;
98
+ try {
99
+ jsonBody = JSON.parse(body);
100
+ }
101
+ catch { }
102
+ (0, state_1.setVariable)(page, "lastResponse", { status, body, json: jsonBody });
103
+ (0, state_1.setVariable)(page, "lastStatusCode", status);
104
+ console.log(`✅ Status: ${status}`);
105
+ });
106
+ /**
107
+ * Makes a generic browser-context HTTP request (fetch).
108
+ */
109
+ (0, registry_1.Step)('I make a "{word}" request to {string}', async (page, method, url, table) => {
110
+ const options = { method: method.toUpperCase() };
111
+ const rows = table && typeof table.rows === "function" ? table.rows() : table || [];
112
+ if (Array.isArray(rows)) {
113
+ rows.forEach((row) => {
114
+ const key = Array.isArray(row) ? row[0] : row.header || row.key;
115
+ const val = Array.isArray(row) ? row[1] : row.value;
116
+ if (!key)
117
+ return;
118
+ if (key.toLowerCase() === "body") {
119
+ options.body = val;
120
+ }
121
+ else {
122
+ if (!options.headers)
123
+ options.headers = {};
124
+ options.headers[key] = val;
125
+ }
126
+ });
127
+ }
128
+ console.log(`⚡ Browser Fetch: ${method} ${url}`);
129
+ const res = await page.evaluate(async ({ url, options }) => {
130
+ const response = await fetch(url, options);
131
+ return {
132
+ status: response.status,
133
+ body: await response.text(),
134
+ headers: Object.fromEntries(response.headers.entries()),
135
+ };
136
+ }, { url, options });
137
+ let jsonBody;
138
+ try {
139
+ jsonBody = JSON.parse(res.body);
140
+ }
141
+ catch { }
142
+ (0, state_1.setVariable)(page, "lastResponse", { ...res, json: jsonBody });
143
+ (0, state_1.setVariable)(page, "lastStatusCode", res.status);
144
+ console.log(`✅ Status: ${res.status}`);
145
+ });