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.
- package/README.md +10 -15
- package/dist/backend/actions/click.d.ts +2 -0
- package/dist/backend/actions/click.d.ts.map +1 -0
- package/dist/backend/actions/click.js +179 -0
- package/dist/backend/actions/find.d.ts +2 -0
- package/dist/backend/actions/find.d.ts.map +1 -0
- package/dist/backend/actions/find.js +291 -0
- package/dist/backend/actions/form.d.ts +2 -0
- package/dist/backend/actions/form.d.ts.map +1 -0
- package/dist/backend/actions/form.js +185 -0
- package/dist/backend/actions/formTable.js +1 -1
- package/dist/backend/actions/frames.d.ts +2 -0
- package/dist/backend/actions/frames.d.ts.map +1 -0
- package/dist/backend/actions/frames.js +60 -0
- package/dist/backend/actions/index.d.ts +10 -0
- package/dist/backend/actions/index.d.ts.map +1 -1
- package/dist/backend/actions/index.js +10 -0
- package/dist/backend/actions/inputs.d.ts +2 -0
- package/dist/backend/actions/inputs.d.ts.map +1 -0
- package/dist/backend/actions/inputs.js +177 -0
- package/dist/backend/actions/keyboard.d.ts +2 -0
- package/dist/backend/actions/keyboard.d.ts.map +1 -0
- package/dist/backend/actions/keyboard.js +62 -0
- package/dist/backend/actions/misc.d.ts +2 -0
- package/dist/backend/actions/misc.d.ts.map +1 -0
- package/dist/backend/actions/misc.js +144 -0
- package/dist/backend/actions/mobile.d.ts +2 -0
- package/dist/backend/actions/mobile.d.ts.map +1 -0
- package/dist/backend/actions/mobile.js +87 -0
- package/dist/backend/actions/mouse.d.ts +2 -0
- package/dist/backend/actions/mouse.d.ts.map +1 -0
- package/dist/backend/actions/mouse.js +105 -0
- package/dist/backend/actions/navigation.js +31 -7
- package/dist/backend/actions/waits.d.ts +2 -0
- package/dist/backend/actions/waits.d.ts.map +1 -0
- package/dist/backend/actions/waits.js +51 -0
- package/dist/backend/api/index.d.ts +1 -0
- package/dist/backend/api/index.d.ts.map +1 -1
- package/dist/backend/api/index.js +1 -0
- package/dist/backend/api/network.d.ts +2 -0
- package/dist/backend/api/network.d.ts.map +1 -0
- package/dist/backend/api/network.js +145 -0
- package/dist/backend/assertions/pageState.js +25 -14
- package/dist/backend/assertions/visibility.js +116 -12
- package/dist/backend/utils/state.d.ts +18 -0
- package/dist/backend/utils/state.d.ts.map +1 -0
- package/dist/backend/utils/state.js +84 -0
- package/dist/core/registry.d.ts +14 -14
- package/dist/core/registry.d.ts.map +1 -1
- package/dist/core/registry.js +13 -4
- package/dist/core/runner.d.ts.map +1 -1
- package/dist/core/runner.js +91 -37
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -104,34 +104,29 @@ npx playwright test
|
|
|
104
104
|
|
|
105
105
|
We support a **Friendly Syntax** for filtering tests via the `TAGS` environment variable.
|
|
106
106
|
|
|
107
|
-
| Logic | Symbol
|
|
108
|
-
| ------- |
|
|
109
|
-
| **
|
|
110
|
-
|
|
|
111
|
-
| **MIX** | `,`
|
|
107
|
+
| Logic | Symbol | Example | Description |
|
|
108
|
+
| ------- | ------ | ---------------- | ------------------------------------------------ |
|
|
109
|
+
| **AND** | `,` | `@login,@signup` | Run tests that have `@login` **AND** `@signup`. |
|
|
110
|
+
| |
|
|
111
|
+
| **MIX** | `,` | `@a,@b, @c` | Run tests with (`@a` AND `@b`) **OR** just `@c`. |
|
|
112
112
|
|
|
113
113
|
**Usage:**
|
|
114
114
|
|
|
115
115
|
```bash
|
|
116
116
|
# Run only smoke tests
|
|
117
117
|
TAGS='@smoke' npx playwright test
|
|
118
|
+
OR
|
|
118
119
|
npx playwright test -g "@smoke"
|
|
119
120
|
|
|
120
121
|
# Run smoke tests that are also critical
|
|
121
|
-
TAGS='@smoke
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
# Run login OR regression tests
|
|
125
|
-
TAGS='@login,@regression' npx playwright test
|
|
126
|
-
|
|
127
|
-
npx playwright test -g "@login|@regression"
|
|
122
|
+
TAGS='@smoke,@critical' npx playwright test
|
|
123
|
+
OR
|
|
124
|
+
npx playwright test -g "@smoke|@critical"
|
|
128
125
|
|
|
129
126
|
```
|
|
130
127
|
|
|
131
128
|
_(On Windows PowerShell, use `$env:TAGS="@smoke"; npx playwright test`)_
|
|
132
129
|
|
|
133
|
-
````
|
|
134
|
-
|
|
135
130
|
**In your Test Runner (`tests/bdd.spec.ts`):**
|
|
136
131
|
|
|
137
132
|
```typescript
|
|
@@ -142,7 +137,7 @@ import { runTests } from "playwright-cucumber-ts-steps";
|
|
|
142
137
|
|
|
143
138
|
// OPTION 2: Run only Smoke tests
|
|
144
139
|
runTests("features/*.feature", { tags: "@smoke" });
|
|
145
|
-
|
|
140
|
+
```
|
|
146
141
|
|
|
147
142
|
---
|
|
148
143
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"click.d.ts","sourceRoot":"","sources":["../../../src/backend/actions/click.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,179 @@
|
|
|
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
|
+
* Clicks on the previously stored element.
|
|
7
|
+
* Pattern: When I click
|
|
8
|
+
*/
|
|
9
|
+
(0, registry_1.Step)("I click", async (page, table) => {
|
|
10
|
+
const options = (0, state_1.parseClickOptions)(table);
|
|
11
|
+
const element = (0, state_1.getActiveElement)(page);
|
|
12
|
+
await element.click(options);
|
|
13
|
+
console.log("🖱️ Clicked on stored element");
|
|
14
|
+
});
|
|
15
|
+
/**
|
|
16
|
+
* Clicks on an element matching the given selector.
|
|
17
|
+
* Pattern: When I click on element {string}
|
|
18
|
+
*/
|
|
19
|
+
(0, registry_1.Step)("I click on element {string}", async (page, selector, table) => {
|
|
20
|
+
const options = (0, state_1.parseClickOptions)(table);
|
|
21
|
+
const element = page.locator(selector);
|
|
22
|
+
await element.click(options);
|
|
23
|
+
(0, state_1.setActiveElement)(page, element);
|
|
24
|
+
console.log(`🖱️ Clicked on element "${selector}"`);
|
|
25
|
+
});
|
|
26
|
+
/**
|
|
27
|
+
* Clicks on a button with the given label.
|
|
28
|
+
* Pattern: When I click on button {string}
|
|
29
|
+
*/
|
|
30
|
+
(0, registry_1.Step)("I click on button {string}", async (page, label, table) => {
|
|
31
|
+
const options = (0, state_1.parseClickOptions)(table);
|
|
32
|
+
const button = page.getByRole("button", { name: label });
|
|
33
|
+
await button.click(options);
|
|
34
|
+
(0, state_1.setActiveElement)(page, button);
|
|
35
|
+
console.log(`🖱️ Clicked on button "${label}"`);
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* Clicks on a link with the given text.
|
|
39
|
+
* Pattern: When I click on link {string}
|
|
40
|
+
*/
|
|
41
|
+
(0, registry_1.Step)("I click on link {string}", async (page, text, table) => {
|
|
42
|
+
const options = (0, state_1.parseClickOptions)(table);
|
|
43
|
+
const link = page.getByRole("link", { name: text });
|
|
44
|
+
await link.click(options);
|
|
45
|
+
(0, state_1.setActiveElement)(page, link);
|
|
46
|
+
console.log(`✅ Clicked on link "${text}"`);
|
|
47
|
+
});
|
|
48
|
+
/**
|
|
49
|
+
* Clicks on a label with the given text.
|
|
50
|
+
* Pattern: When I click on label {string}
|
|
51
|
+
*/
|
|
52
|
+
(0, registry_1.Step)("I click on label {string}", async (page, labelText, table) => {
|
|
53
|
+
const options = (0, state_1.parseClickOptions)(table);
|
|
54
|
+
const label = page.getByLabel(labelText);
|
|
55
|
+
await label.click(options);
|
|
56
|
+
(0, state_1.setActiveElement)(page, label);
|
|
57
|
+
console.log(`🏷️ Clicked on label "${labelText}"`);
|
|
58
|
+
});
|
|
59
|
+
/**
|
|
60
|
+
* Clicks on an element containing the given text (fuzzy match).
|
|
61
|
+
* Supports aliasing with @alias.
|
|
62
|
+
* Pattern: When I click on text {string}
|
|
63
|
+
*/
|
|
64
|
+
(0, registry_1.Step)("I click on text {string}", async (page, rawText, table) => {
|
|
65
|
+
const options = (0, state_1.parseClickOptions)(table);
|
|
66
|
+
let text = rawText;
|
|
67
|
+
// Handle Alias (e.g., "@username")
|
|
68
|
+
if (rawText.startsWith("@")) {
|
|
69
|
+
const aliasKey = rawText.slice(1);
|
|
70
|
+
const storedValue = (0, state_1.getVariable)(page, aliasKey);
|
|
71
|
+
if (!storedValue) {
|
|
72
|
+
throw new Error(`❌ No value found for alias "@${aliasKey}"`);
|
|
73
|
+
}
|
|
74
|
+
text = storedValue;
|
|
75
|
+
}
|
|
76
|
+
const locator = page.getByText(text, { exact: false }).first();
|
|
77
|
+
await locator.waitFor({ state: "visible", timeout: 5000 });
|
|
78
|
+
await locator.click(options);
|
|
79
|
+
(0, state_1.setActiveElement)(page, locator);
|
|
80
|
+
console.log(`🖱️ Clicked on text "${text}"`);
|
|
81
|
+
});
|
|
82
|
+
/**
|
|
83
|
+
* Clicks on an element containing the exact given text.
|
|
84
|
+
* Pattern: When I click on exact text {string}
|
|
85
|
+
*/
|
|
86
|
+
(0, registry_1.Step)("I click on exact text {string}", async (page, exactText, table) => {
|
|
87
|
+
const options = (0, state_1.parseClickOptions)(table);
|
|
88
|
+
const locator = page.getByText(exactText, { exact: true });
|
|
89
|
+
await locator.waitFor({ state: "visible", timeout: 5000 });
|
|
90
|
+
await locator.click(options);
|
|
91
|
+
(0, state_1.setActiveElement)(page, locator);
|
|
92
|
+
console.log(`🖱️ Clicked on exact text "${exactText}"`);
|
|
93
|
+
});
|
|
94
|
+
/**
|
|
95
|
+
* Clicks all previously stored elements.
|
|
96
|
+
* Pattern: When I click all
|
|
97
|
+
*/
|
|
98
|
+
(0, registry_1.Step)("I click all", async (page, table) => {
|
|
99
|
+
const options = (0, state_1.parseClickOptions)(table);
|
|
100
|
+
const elements = (0, state_1.getActiveElements)(page);
|
|
101
|
+
const count = await elements.count();
|
|
102
|
+
if (count === 0)
|
|
103
|
+
throw new Error("⚠️ No elements found to click.");
|
|
104
|
+
for (let i = 0; i < count; i++) {
|
|
105
|
+
const el = elements.nth(i);
|
|
106
|
+
await el.waitFor({ state: "visible", timeout: 5000 });
|
|
107
|
+
await el.click(options);
|
|
108
|
+
console.log(`🖱️ Clicked element #${i + 1}`);
|
|
109
|
+
}
|
|
110
|
+
console.log(`✅ Clicked all ${count} elements.`);
|
|
111
|
+
});
|
|
112
|
+
/**
|
|
113
|
+
* Double-clicks on the previously stored element.
|
|
114
|
+
* Pattern: When I double click
|
|
115
|
+
*/
|
|
116
|
+
(0, registry_1.Step)("I double click", async (page, table) => {
|
|
117
|
+
const options = (0, state_1.parseClickOptions)(table);
|
|
118
|
+
const element = (0, state_1.getActiveElement)(page);
|
|
119
|
+
await element.dblclick(options);
|
|
120
|
+
console.log("🖱️ Double-clicked on stored element");
|
|
121
|
+
});
|
|
122
|
+
/**
|
|
123
|
+
* Double-clicks on an element containing the given text.
|
|
124
|
+
* Pattern: When I double click on text {string}
|
|
125
|
+
*/
|
|
126
|
+
(0, registry_1.Step)("I double click on text {string}", async (page, text, table) => {
|
|
127
|
+
const options = (0, state_1.parseClickOptions)(table);
|
|
128
|
+
const element = page.getByText(text).first();
|
|
129
|
+
await element.dblclick(options);
|
|
130
|
+
console.log(`🖱️ Double-clicked on text "${text}"`);
|
|
131
|
+
});
|
|
132
|
+
/**
|
|
133
|
+
* Double-clicks at the given page coordinates.
|
|
134
|
+
* Pattern: When I double click position {int} {int}
|
|
135
|
+
*/
|
|
136
|
+
(0, registry_1.Step)("I double click position {int} {int}", async (page, x, y, table) => {
|
|
137
|
+
const options = (0, state_1.parseClickOptions)(table);
|
|
138
|
+
await page.mouse.dblclick(x, y, options);
|
|
139
|
+
console.log(`🖱️ Double-clicked at (${x}, ${y})`);
|
|
140
|
+
});
|
|
141
|
+
/**
|
|
142
|
+
* Right-clicks on the previously stored element.
|
|
143
|
+
* Pattern: When I right click
|
|
144
|
+
*/
|
|
145
|
+
(0, registry_1.Step)("I right click", async (page, table) => {
|
|
146
|
+
const options = (0, state_1.parseClickOptions)(table);
|
|
147
|
+
const element = (0, state_1.getActiveElement)(page);
|
|
148
|
+
await element.click({ button: "right", ...options });
|
|
149
|
+
console.log("🖱️ Right-clicked on stored element");
|
|
150
|
+
});
|
|
151
|
+
/**
|
|
152
|
+
* Right-clicks on an element containing the given text.
|
|
153
|
+
* Pattern: When I right click on text {string}
|
|
154
|
+
*/
|
|
155
|
+
(0, registry_1.Step)("I right click on text {string}", async (page, text, table) => {
|
|
156
|
+
const options = (0, state_1.parseClickOptions)(table);
|
|
157
|
+
const element = page.getByText(text).first();
|
|
158
|
+
await element.click({ button: "right", ...options });
|
|
159
|
+
console.log(`🖱️ Right-clicked on text "${text}"`);
|
|
160
|
+
});
|
|
161
|
+
/**
|
|
162
|
+
* Right-clicks at the given page coordinates.
|
|
163
|
+
* Pattern: When I right click position {int} {int}
|
|
164
|
+
*/
|
|
165
|
+
(0, registry_1.Step)("I right click position {int} {int}", async (page, x, y, table) => {
|
|
166
|
+
const options = (0, state_1.parseClickOptions)(table);
|
|
167
|
+
await page.mouse.click(x, y, { button: "right", ...options });
|
|
168
|
+
console.log(`🖱️ Right-clicked at (${x}, ${y})`);
|
|
169
|
+
});
|
|
170
|
+
/**
|
|
171
|
+
* Clicks on an element matching the given selector (Regex Version).
|
|
172
|
+
* Pattern: I click on selector "..."
|
|
173
|
+
*/
|
|
174
|
+
(0, registry_1.Step)(/^I click on selector "([^"]+)"$/, async (page, selector) => {
|
|
175
|
+
const locator = page.locator(selector);
|
|
176
|
+
await locator.click();
|
|
177
|
+
(0, state_1.setActiveElement)(page, locator);
|
|
178
|
+
console.log(`🖱️ Clicked on selector: ${selector}`);
|
|
179
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find.d.ts","sourceRoot":"","sources":["../../../src/backend/actions/find.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const test_1 = require("@playwright/test");
|
|
4
|
+
const registry_1 = require("../../core/registry");
|
|
5
|
+
const state_1 = require("../utils/state");
|
|
6
|
+
// =============================
|
|
7
|
+
// 1. GENERAL FINDING (Single)
|
|
8
|
+
// =============================
|
|
9
|
+
/**
|
|
10
|
+
* Finds an element by CSS selector.
|
|
11
|
+
* Pattern: When I find element by selector ".my-class"
|
|
12
|
+
*/
|
|
13
|
+
(0, registry_1.Step)("I find element by selector {string}", async (page, selector) => {
|
|
14
|
+
const element = page.locator(selector);
|
|
15
|
+
await (0, test_1.expect)(element).toHaveCount(1);
|
|
16
|
+
(0, state_1.setActiveElement)(page, element);
|
|
17
|
+
console.log(`🔍 Found element by selector: "${selector}"`);
|
|
18
|
+
});
|
|
19
|
+
/**
|
|
20
|
+
* Finds an element by exact text.
|
|
21
|
+
* Pattern: When I find element by text "Submit"
|
|
22
|
+
*/
|
|
23
|
+
(0, registry_1.Step)("I find element by text {string}", async (page, text) => {
|
|
24
|
+
const element = page.getByText(text, { exact: true });
|
|
25
|
+
await (0, test_1.expect)(element).toHaveCount(1);
|
|
26
|
+
(0, state_1.setActiveElement)(page, element);
|
|
27
|
+
console.log(`🔍 Found element by exact text: "${text}"`);
|
|
28
|
+
});
|
|
29
|
+
/**
|
|
30
|
+
* Finds an element by title attribute.
|
|
31
|
+
* Pattern: When I find element by title "Tooltip"
|
|
32
|
+
*/
|
|
33
|
+
(0, registry_1.Step)("I find element by title {string}", async (page, title) => {
|
|
34
|
+
const element = page.getByTitle(title);
|
|
35
|
+
await (0, test_1.expect)(element).toHaveCount(1);
|
|
36
|
+
(0, state_1.setActiveElement)(page, element);
|
|
37
|
+
console.log(`🔍 Found element by title: "${title}"`);
|
|
38
|
+
});
|
|
39
|
+
/**
|
|
40
|
+
* Finds an element by test id (data-testid).
|
|
41
|
+
* Pattern: When I find element by testid "submit-btn"
|
|
42
|
+
*/
|
|
43
|
+
(0, registry_1.Step)("I find element by testid {string}", async (page, testid) => {
|
|
44
|
+
const element = page.getByTestId(testid);
|
|
45
|
+
await (0, test_1.expect)(element).toHaveCount(1);
|
|
46
|
+
(0, state_1.setActiveElement)(page, element);
|
|
47
|
+
console.log(`🔍 Found element by testid: "${testid}"`);
|
|
48
|
+
});
|
|
49
|
+
/**
|
|
50
|
+
* Finds an element by ARIA role.
|
|
51
|
+
* Pattern: When I find element by role "button"
|
|
52
|
+
*/
|
|
53
|
+
(0, registry_1.Step)("I find element by role {string}", async (page, role) => {
|
|
54
|
+
const element = page.getByRole(role);
|
|
55
|
+
await (0, test_1.expect)(element).toHaveCount(1);
|
|
56
|
+
(0, state_1.setActiveElement)(page, element);
|
|
57
|
+
console.log(`🔍 Found element by role: "${role}"`);
|
|
58
|
+
});
|
|
59
|
+
/**
|
|
60
|
+
* Finds an element by placeholder.
|
|
61
|
+
* Pattern: When I find element by placeholder text "Search..."
|
|
62
|
+
*/
|
|
63
|
+
(0, registry_1.Step)("I find element by placeholder text {string}", async (page, text) => {
|
|
64
|
+
const element = page.getByPlaceholder(text);
|
|
65
|
+
await (0, test_1.expect)(element).toHaveCount(1);
|
|
66
|
+
(0, state_1.setActiveElement)(page, element);
|
|
67
|
+
console.log(`🔍 Found element by placeholder: "${text}"`);
|
|
68
|
+
});
|
|
69
|
+
/**
|
|
70
|
+
* Finds an element by label.
|
|
71
|
+
* Pattern: When I find element by label text "Username"
|
|
72
|
+
*/
|
|
73
|
+
(0, registry_1.Step)("I find element by label text {string}", async (page, label) => {
|
|
74
|
+
const element = page.getByLabel(label);
|
|
75
|
+
await (0, test_1.expect)(element).toHaveCount(1);
|
|
76
|
+
(0, state_1.setActiveElement)(page, element);
|
|
77
|
+
console.log(`🔍 Found element by label: "${label}"`);
|
|
78
|
+
});
|
|
79
|
+
/**
|
|
80
|
+
* Finds an element by alt text (images).
|
|
81
|
+
* Pattern: When I find element by alt text "Logo"
|
|
82
|
+
*/
|
|
83
|
+
(0, registry_1.Step)("I find element by alt text {string}", async (page, alt) => {
|
|
84
|
+
const element = page.getByAltText(alt);
|
|
85
|
+
await (0, test_1.expect)(element).toHaveCount(1);
|
|
86
|
+
(0, state_1.setActiveElement)(page, element);
|
|
87
|
+
console.log(`🔍 Found element by alt text: "${alt}"`);
|
|
88
|
+
});
|
|
89
|
+
// =============================
|
|
90
|
+
// 2. SPECIFIC ELEMENTS (Link/Heading/Name)
|
|
91
|
+
// =============================
|
|
92
|
+
/**
|
|
93
|
+
* Finds a link by text.
|
|
94
|
+
* Pattern: When I find link by text "Home"
|
|
95
|
+
*/
|
|
96
|
+
(0, registry_1.Step)("I find link by text {string}", async (page, text) => {
|
|
97
|
+
const element = page.getByRole("link", { name: text });
|
|
98
|
+
// We use .first() here because links often have duplicates (e.g. footer/header)
|
|
99
|
+
// but strict mode usually prefers uniqueness.
|
|
100
|
+
(0, state_1.setActiveElement)(page, element.first());
|
|
101
|
+
console.log(`🔍 Found link by text: "${text}"`);
|
|
102
|
+
});
|
|
103
|
+
/**
|
|
104
|
+
* Finds a heading by text.
|
|
105
|
+
* Pattern: When I find heading by text "Welcome"
|
|
106
|
+
*/
|
|
107
|
+
(0, registry_1.Step)("I find heading by text {string}", async (page, text) => {
|
|
108
|
+
const element = page.getByRole("heading", { name: text });
|
|
109
|
+
(0, state_1.setActiveElement)(page, element.first());
|
|
110
|
+
console.log(`🔍 Found heading by text: "${text}"`);
|
|
111
|
+
});
|
|
112
|
+
/**
|
|
113
|
+
* Finds an element by name attribute.
|
|
114
|
+
* Pattern: When I find element by name "email"
|
|
115
|
+
*/
|
|
116
|
+
(0, registry_1.Step)("I find element by name {string}", async (page, name) => {
|
|
117
|
+
// Broad selector for name attribute
|
|
118
|
+
const element = page.locator(`[name="${name}"]`);
|
|
119
|
+
await (0, test_1.expect)(element).toHaveCount(1);
|
|
120
|
+
(0, state_1.setActiveElement)(page, element);
|
|
121
|
+
console.log(`🔍 Found element by name: "${name}"`);
|
|
122
|
+
});
|
|
123
|
+
// =============================
|
|
124
|
+
// 3. GENERAL FINDING (Multiple)
|
|
125
|
+
// =============================
|
|
126
|
+
/**
|
|
127
|
+
* Finds all elements by selector.
|
|
128
|
+
* Pattern: When I find elements by selector ".items"
|
|
129
|
+
*/
|
|
130
|
+
(0, registry_1.Step)("I find elements by selector {string}", async (page, selector) => {
|
|
131
|
+
const elements = page.locator(selector);
|
|
132
|
+
const count = await elements.count();
|
|
133
|
+
(0, state_1.setActiveElements)(page, elements);
|
|
134
|
+
console.log(`🔍 Found ${count} elements with selector: "${selector}"`);
|
|
135
|
+
});
|
|
136
|
+
/**
|
|
137
|
+
* Finds all headings by text.
|
|
138
|
+
* Pattern: When I find headings by text "Chapter"
|
|
139
|
+
*/
|
|
140
|
+
(0, registry_1.Step)("I find headings by text {string}", async (page, text) => {
|
|
141
|
+
const elements = page.getByRole("heading", { name: text });
|
|
142
|
+
(0, state_1.setActiveElements)(page, elements);
|
|
143
|
+
console.log(`🔍 Found headings matching "${text}"`);
|
|
144
|
+
});
|
|
145
|
+
/**
|
|
146
|
+
* Finds all buttons by text (supports alias).
|
|
147
|
+
* Pattern: When I find buttons by text "Save"
|
|
148
|
+
*/
|
|
149
|
+
(0, registry_1.Step)("I find buttons by text {string}", async (page, text) => {
|
|
150
|
+
let searchText = text;
|
|
151
|
+
// Handle Alias
|
|
152
|
+
if (text.startsWith("@")) {
|
|
153
|
+
const alias = text.slice(1);
|
|
154
|
+
const val = (0, state_1.getVariable)(page, alias);
|
|
155
|
+
if (!val)
|
|
156
|
+
throw new Error(`❌ No value found for alias "@${alias}"`);
|
|
157
|
+
searchText = val;
|
|
158
|
+
}
|
|
159
|
+
const elements = page.getByRole("button", { name: searchText });
|
|
160
|
+
(0, state_1.setActiveElements)(page, elements);
|
|
161
|
+
console.log(`🔍 Found buttons matching "${searchText}"`);
|
|
162
|
+
});
|
|
163
|
+
// =============================
|
|
164
|
+
// 4. GET / REFINE SELECTION
|
|
165
|
+
// =============================
|
|
166
|
+
/**
|
|
167
|
+
* Gets the first element from a stored list.
|
|
168
|
+
* Pattern: When I get first element
|
|
169
|
+
*/
|
|
170
|
+
(0, registry_1.Step)("I get first element", async (page) => {
|
|
171
|
+
const elements = (0, state_1.getActiveElements)(page);
|
|
172
|
+
const element = elements.first();
|
|
173
|
+
(0, state_1.setActiveElement)(page, element);
|
|
174
|
+
console.log("👉 Selected FIRST element");
|
|
175
|
+
});
|
|
176
|
+
/**
|
|
177
|
+
* Gets the last element from a stored list.
|
|
178
|
+
* Pattern: When I get last element
|
|
179
|
+
*/
|
|
180
|
+
(0, registry_1.Step)("I get last element", async (page) => {
|
|
181
|
+
const elements = (0, state_1.getActiveElements)(page);
|
|
182
|
+
const element = elements.last();
|
|
183
|
+
(0, state_1.setActiveElement)(page, element);
|
|
184
|
+
console.log("👉 Selected LAST element");
|
|
185
|
+
});
|
|
186
|
+
/**
|
|
187
|
+
* Gets the nth element (1-based index).
|
|
188
|
+
* Pattern: When I get 2nd element
|
|
189
|
+
*/
|
|
190
|
+
(0, registry_1.Step)(/^I get (\d+)(?:st|nd|rd|th) element$/, async (page, indexStr) => {
|
|
191
|
+
const index = parseInt(indexStr, 10);
|
|
192
|
+
const elements = (0, state_1.getActiveElements)(page);
|
|
193
|
+
const count = await elements.count();
|
|
194
|
+
if (index < 1 || index > count) {
|
|
195
|
+
throw new Error(`❌ Cannot get element #${index} — only ${count} found.`);
|
|
196
|
+
}
|
|
197
|
+
// Playwright is 0-based, Gherkin is 1-based
|
|
198
|
+
const element = elements.nth(index - 1);
|
|
199
|
+
(0, state_1.setActiveElement)(page, element);
|
|
200
|
+
console.log(`👉 Selected element #${index}`);
|
|
201
|
+
});
|
|
202
|
+
/**
|
|
203
|
+
* Gets the currently focused element.
|
|
204
|
+
* Pattern: When I get focused element
|
|
205
|
+
*/
|
|
206
|
+
(0, registry_1.Step)("I get focused element", async (page) => {
|
|
207
|
+
// Use CSS selector for focused element
|
|
208
|
+
const element = page.locator("*:focus");
|
|
209
|
+
(0, state_1.setActiveElement)(page, element);
|
|
210
|
+
console.log("👉 Selected FOCUSED element");
|
|
211
|
+
});
|
|
212
|
+
// =============================
|
|
213
|
+
// 5. INPUTS & TEXTAREAS SPECIFICS
|
|
214
|
+
// =============================
|
|
215
|
+
/**
|
|
216
|
+
* Finds input by ID.
|
|
217
|
+
* Pattern: When I find input by ID "username"
|
|
218
|
+
*/
|
|
219
|
+
(0, registry_1.Step)("I find input by ID {string}", async (page, id) => {
|
|
220
|
+
const element = page.locator(`input#${id}`);
|
|
221
|
+
(0, state_1.setActiveElement)(page, element);
|
|
222
|
+
console.log(`🔍 Found input by ID: "${id}"`);
|
|
223
|
+
});
|
|
224
|
+
/**
|
|
225
|
+
* Finds input by name.
|
|
226
|
+
* Pattern: When I find input by name "email"
|
|
227
|
+
*/
|
|
228
|
+
(0, registry_1.Step)("I find input by name {string}", async (page, name) => {
|
|
229
|
+
const element = page.locator(`input[name="${name}"]`);
|
|
230
|
+
(0, state_1.setActiveElement)(page, element);
|
|
231
|
+
console.log(`🔍 Found input by name: "${name}"`);
|
|
232
|
+
});
|
|
233
|
+
/**
|
|
234
|
+
* Finds input by placeholder.
|
|
235
|
+
* Pattern: When I find input by placeholder text "Enter email"
|
|
236
|
+
*/
|
|
237
|
+
(0, registry_1.Step)("I find input by placeholder text {string}", async (page, placeholder) => {
|
|
238
|
+
const element = page.locator(`input[placeholder="${placeholder}"]`);
|
|
239
|
+
(0, state_1.setActiveElement)(page, element);
|
|
240
|
+
console.log(`🔍 Found input by placeholder: "${placeholder}"`);
|
|
241
|
+
});
|
|
242
|
+
/**
|
|
243
|
+
* Finds input by display value (supports alias).
|
|
244
|
+
* Pattern: When I find input by display value "John Doe"
|
|
245
|
+
*/
|
|
246
|
+
(0, registry_1.Step)("I find input by display value {string}", async (page, value) => {
|
|
247
|
+
let searchValue = value;
|
|
248
|
+
if (value.startsWith("@")) {
|
|
249
|
+
const alias = value.slice(1);
|
|
250
|
+
const val = (0, state_1.getVariable)(page, alias);
|
|
251
|
+
if (!val)
|
|
252
|
+
throw new Error(`❌ No value found for alias "@${alias}"`);
|
|
253
|
+
searchValue = val;
|
|
254
|
+
}
|
|
255
|
+
const element = page.locator(`input[value="${searchValue}"]`);
|
|
256
|
+
await (0, test_1.expect)(element).toBeVisible();
|
|
257
|
+
(0, state_1.setActiveElement)(page, element);
|
|
258
|
+
console.log(`🔍 Found input with value: "${searchValue}"`);
|
|
259
|
+
});
|
|
260
|
+
/**
|
|
261
|
+
* Finds textarea by label.
|
|
262
|
+
* Pattern: When I find textarea by label text "Comments"
|
|
263
|
+
*/
|
|
264
|
+
(0, registry_1.Step)("I find textarea by label text {string}", async (page, label) => {
|
|
265
|
+
const element = page.getByLabel(label).locator("textarea").first();
|
|
266
|
+
// Fallback if strict label matching fails, try locator strategy
|
|
267
|
+
const count = await element.count();
|
|
268
|
+
if (count === 0) {
|
|
269
|
+
// Try generic label finding
|
|
270
|
+
const altElement = page.getByLabel(label);
|
|
271
|
+
(0, state_1.setActiveElement)(page, altElement);
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
(0, state_1.setActiveElement)(page, element);
|
|
275
|
+
}
|
|
276
|
+
console.log(`🔍 Found textarea by label: "${label}"`);
|
|
277
|
+
});
|
|
278
|
+
// =============================
|
|
279
|
+
// 6. UTILITIES (Store Text)
|
|
280
|
+
// =============================
|
|
281
|
+
/**
|
|
282
|
+
* Stores the text of the current element into a variable.
|
|
283
|
+
* Pattern: When I store element text as "myVar"
|
|
284
|
+
*/
|
|
285
|
+
(0, registry_1.Step)("I store element text as {string}", async (page, alias) => {
|
|
286
|
+
const element = (0, state_1.getActiveElement)(page);
|
|
287
|
+
const text = await element.textContent();
|
|
288
|
+
const cleanText = text?.trim() || "";
|
|
289
|
+
(0, state_1.setVariable)(page, alias, cleanText);
|
|
290
|
+
console.log(`💾 Stored text "${cleanText}" as variable "@${alias}"`);
|
|
291
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../../../src/backend/actions/form.ts"],"names":[],"mappings":""}
|