playwright-cucumber-ts-steps 0.1.7 → 1.0.1

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 (79) hide show
  1. package/README.md +21 -11
  2. package/lib/actions/clickSteps.d.ts +251 -1
  3. package/lib/actions/clickSteps.js +297 -47
  4. package/lib/actions/cookieSteps.d.ts +18 -1
  5. package/lib/actions/cookieSteps.js +65 -0
  6. package/lib/actions/debugSteps.d.ts +14 -1
  7. package/lib/actions/debugSteps.js +18 -3
  8. package/lib/actions/elementFindSteps.d.ts +668 -1
  9. package/lib/actions/elementFindSteps.js +808 -94
  10. package/lib/actions/fillFormSteps.d.ts +69 -1
  11. package/lib/actions/fillFormSteps.js +178 -71
  12. package/lib/actions/index.d.ts +11 -0
  13. package/lib/actions/index.js +28 -0
  14. package/lib/actions/inputSteps.d.ts +218 -1
  15. package/lib/actions/inputSteps.js +303 -57
  16. package/lib/actions/interceptionSteps.d.ts +169 -1
  17. package/lib/actions/interceptionSteps.js +258 -38
  18. package/lib/actions/miscSteps.d.ts +645 -1
  19. package/lib/actions/miscSteps.js +898 -157
  20. package/lib/actions/mouseSteps.d.ts +143 -1
  21. package/lib/actions/mouseSteps.js +200 -32
  22. package/lib/actions/scrollSteps.d.ts +82 -1
  23. package/lib/actions/scrollSteps.js +116 -16
  24. package/lib/actions/storageSteps.d.ts +174 -1
  25. package/lib/actions/storageSteps.js +253 -33
  26. package/lib/assertions/buttonAndTextVisibilitySteps.d.ts +245 -1
  27. package/lib/assertions/buttonAndTextVisibilitySteps.js +342 -91
  28. package/lib/assertions/cookieSteps.d.ts +75 -1
  29. package/lib/assertions/cookieSteps.js +97 -29
  30. package/lib/assertions/elementSteps.d.ts +264 -1
  31. package/lib/assertions/elementSteps.js +376 -78
  32. package/lib/assertions/formInputSteps.d.ts +248 -1
  33. package/lib/assertions/formInputSteps.js +342 -79
  34. package/lib/assertions/index.d.ts +10 -0
  35. package/lib/assertions/index.js +27 -0
  36. package/lib/assertions/interceptionRequestsSteps.d.ts +353 -1
  37. package/lib/assertions/interceptionRequestsSteps.js +569 -177
  38. package/lib/assertions/locationSteps.d.ts +217 -1
  39. package/lib/assertions/locationSteps.js +287 -64
  40. package/lib/assertions/roleTestIdSteps.d.ts +159 -1
  41. package/lib/assertions/roleTestIdSteps.js +217 -22
  42. package/lib/assertions/semanticSteps.d.ts +176 -1
  43. package/lib/assertions/semanticSteps.js +245 -60
  44. package/lib/assertions/storageSteps.d.ts +149 -1
  45. package/lib/assertions/storageSteps.js +201 -65
  46. package/lib/assertions/visualSteps.d.ts +74 -1
  47. package/lib/assertions/visualSteps.js +178 -45
  48. package/lib/custom_setups/loginHooks.js +19 -2
  49. package/lib/helpers/world.d.ts +3 -0
  50. package/lib/helpers/world.js +11 -5
  51. package/lib/index.d.ts +3 -21
  52. package/lib/index.js +3 -23
  53. package/package.json +9 -2
  54. package/src/actions/clickSteps.ts +364 -142
  55. package/src/actions/cookieSteps.ts +66 -0
  56. package/src/actions/debugSteps.ts +17 -3
  57. package/src/actions/elementFindSteps.ts +822 -117
  58. package/src/actions/fillFormSteps.ts +234 -177
  59. package/src/actions/index.ts +12 -0
  60. package/src/actions/inputSteps.ts +318 -82
  61. package/src/actions/interceptionSteps.ts +295 -57
  62. package/src/actions/miscSteps.ts +984 -254
  63. package/src/actions/mouseSteps.ts +212 -55
  64. package/src/actions/scrollSteps.ts +114 -16
  65. package/src/actions/storageSteps.ts +267 -42
  66. package/src/assertions/buttonAndTextVisibilitySteps.ts +353 -95
  67. package/src/assertions/cookieSteps.ts +115 -36
  68. package/src/assertions/elementSteps.ts +414 -85
  69. package/src/assertions/formInputSteps.ts +375 -108
  70. package/src/assertions/index.ts +11 -0
  71. package/src/assertions/interceptionRequestsSteps.ts +619 -195
  72. package/src/assertions/locationSteps.ts +280 -64
  73. package/src/assertions/roleTestIdSteps.ts +244 -26
  74. package/src/assertions/semanticSteps.ts +257 -69
  75. package/src/assertions/storageSteps.ts +234 -73
  76. package/src/assertions/visualSteps.ts +245 -68
  77. package/src/custom_setups/loginHooks.ts +21 -2
  78. package/src/helpers/world.ts +30 -4
  79. package/src/index.ts +4 -25
@@ -1,52 +1,131 @@
1
1
  import { Then } from "@cucumber/cucumber";
2
- import type { CustomWorld } from "../helpers/world";
2
+ import { expect } from "@playwright/test"; // Import expect for more robust assertions where applicable
3
+ import type { CustomWorld } from "../helpers/world"; // Assuming this path is correct
4
+
5
+ // ===================================================================================
6
+ // ASSERTIONS: COOKIES
7
+ // ===================================================================================
3
8
 
4
9
  /**
5
- * THEN: I see cookie "myCookie"
10
+ * Asserts that a cookie with the given name exists in the browser context.
11
+ *
12
+ * ```gherkin
13
+ * Then I see cookie {string}
14
+ * ```
15
+ *
16
+ * @param cookieName - The name of the cookie expected to exist.
17
+ *
18
+ * @example
19
+ * Then I see cookie "session_id"
20
+ *
21
+ * @remarks
22
+ * This step retrieves all cookies from the current Playwright browser context
23
+ * and checks if a cookie with `cookieName` is present.
24
+ * @category Cookie Assertion Steps
6
25
  */
7
- Then("I see cookie {string}", async function (this: CustomWorld, cookieName: string) {
26
+ export async function Then_I_see_cookie(this: CustomWorld, cookieName: string) {
8
27
  const cookies = await this.context.cookies();
9
28
  const cookie = cookies.find((c) => c.name === cookieName);
10
- if (!cookie) throw new Error(`Cookie "${cookieName}" not found`);
11
- });
29
+
30
+ // Using Playwright's expect for better assertion messages and retries (if configured)
31
+ expect(cookie, `Cookie "${cookieName}" should exist but was not found.`).toBeDefined();
32
+ this.log?.(`✅ Verified cookie "${cookieName}" exists.`);
33
+ }
34
+ Then("I see cookie {string}", Then_I_see_cookie);
12
35
 
13
36
  /**
14
- * THEN: I do not see cookie "myCookie"
37
+ * Asserts that a cookie with the given name does NOT exist in the browser context.
38
+ *
39
+ * ```gherkin
40
+ * Then I do not see cookie {string}
41
+ * ```
42
+ *
43
+ * @param cookieName - The name of the cookie expected NOT to exist.
44
+ *
45
+ * @example
46
+ * Then I do not see cookie "expired_token"
47
+ *
48
+ * @remarks
49
+ * This step retrieves all cookies from the current Playwright browser context
50
+ * and asserts that no cookie with `cookieName` is present.
51
+ * @category Cookie Assertion Steps
15
52
  */
16
- Then("I do not see cookie {string}", async function (this: CustomWorld, name: string) {
53
+ export async function Then_I_do_not_see_cookie(this: CustomWorld, cookieName: string) {
17
54
  const cookies = await this.context.cookies();
18
- const cookie = cookies.find((c) => c.name === name);
19
- if (cookie) throw new Error(`Cookie "${name}" was found but should not exist`);
20
- });
55
+ const cookie = cookies.find((c) => c.name === cookieName);
56
+
57
+ // Using Playwright's expect for better assertion messages and retries
58
+ expect(cookie, `Cookie "${cookieName}" should NOT exist but was found.`).toBeUndefined();
59
+ this.log?.(`✅ Verified cookie "${cookieName}" does not exist.`);
60
+ }
61
+ Then("I do not see cookie {string}", Then_I_do_not_see_cookie);
21
62
 
22
63
  /**
23
- * THEN: I see cookie "sessionId" has value "abc123"
64
+ * Asserts that a cookie with the given name exists and has an exact expected value.
65
+ *
66
+ * ```gherkin
67
+ * Then I see cookie {string} has value {string}
68
+ * ```
69
+ *
70
+ * @param cookieName - The name of the cookie to check.
71
+ * @param expectedValue - The exact value the cookie is expected to have.
72
+ *
73
+ * @example
74
+ * Then I see cookie "sessionId" has value "abc123"
75
+ *
76
+ * @remarks
77
+ * This step finds the cookie by name and then asserts that its `value` property
78
+ * strictly matches `expectedValue`.
79
+ * @category Cookie Assertion Steps
24
80
  */
25
- Then(
26
- "I see cookie {string} has value {string}",
27
- async function (this: CustomWorld, name: string, expectedValue: string) {
28
- const cookies = await this.context.cookies();
29
- const cookie = cookies.find((c) => c.name === name);
30
- if (!cookie) throw new Error(`Cookie "${name}" not found`);
31
- if (cookie.value !== expectedValue) {
32
- throw new Error(
33
- `Expected cookie "${name}" to have value "${expectedValue}", but got "${cookie.value}"`
34
- );
35
- }
36
- }
37
- );
81
+ export async function Then_I_see_cookie_has_value(
82
+ this: CustomWorld,
83
+ cookieName: string,
84
+ expectedValue: string
85
+ ) {
86
+ const cookies = await this.context.cookies();
87
+ const cookie = cookies.find((c) => c.name === cookieName);
88
+
89
+ expect(cookie, `Cookie "${cookieName}" not found.`).toBeDefined();
90
+ expect(
91
+ cookie?.value,
92
+ `Expected cookie "${cookieName}" to have value "${expectedValue}", but got "${cookie?.value}".`
93
+ ).toBe(expectedValue);
94
+ this.log?.(`✅ Verified cookie "${cookieName}" has value "${expectedValue}".`);
95
+ }
96
+ Then("I see cookie {string} has value {string}", Then_I_see_cookie_has_value);
38
97
 
39
98
  /**
40
- * THEN: I see cookie "token" contains value "auth"
99
+ * Asserts that a cookie with the given name exists and its value contains a specific substring.
100
+ *
101
+ * ```gherkin
102
+ * Then I see cookie {string} contains value {string}
103
+ * ```
104
+ *
105
+ * @param cookieName - The name of the cookie to check.
106
+ * @param valuePart - The substring expected to be present within the cookie's value.
107
+ *
108
+ * @example
109
+ * Then I see cookie "token" contains value "auth_type"
110
+ *
111
+ * @remarks
112
+ * This step finds the cookie by name and then asserts that its `value` property
113
+ * includes the `valuePart` string. Useful for tokens or complex cookie values.
114
+ * @category Cookie Assertion Steps
41
115
  */
42
- Then(
43
- "I see cookie {string} contains value {string}",
44
- async function (this: CustomWorld, name: string, valuePart: string) {
45
- const cookies = await this.context.cookies();
46
- const cookie = cookies.find((c) => c.name === name);
47
- if (!cookie) throw new Error(`Cookie "${name}" not found`);
48
- if (!cookie.value.includes(valuePart)) {
49
- throw new Error(`Cookie "${name}" does not contain value "${valuePart}"`);
50
- }
51
- }
52
- );
116
+ export async function Then_I_see_cookie_contains_value(
117
+ this: CustomWorld,
118
+ cookieName: string,
119
+ valuePart: string
120
+ ) {
121
+ const cookies = await this.context.cookies();
122
+ const cookie = cookies.find((c) => c.name === cookieName);
123
+
124
+ expect(cookie, `Cookie "${cookieName}" not found.`).toBeDefined();
125
+ expect(
126
+ cookie?.value,
127
+ `Expected cookie "${cookieName}" to contain "${valuePart}", but got "${cookie?.value}".`
128
+ ).toContain(valuePart);
129
+ this.log?.(`✅ Verified cookie "${cookieName}" contains value "${valuePart}".`);
130
+ }
131
+ Then("I see cookie {string} contains value {string}", Then_I_see_cookie_contains_value);
@@ -1,103 +1,432 @@
1
1
  import { Then } from "@cucumber/cucumber";
2
2
  import { expect } from "@playwright/test";
3
- import type { CustomWorld } from "../helpers/world";
4
- //
5
- // ✅ ELEMENT EXISTS
6
- //
7
-
8
- Then(/^I see element "([^"]+)" exists$/, async function (this: any, selector: string) {
9
- const el = await this.page.locator(selector);
10
- await expect(el).toHaveCount(1);
11
- });
12
- Then("I see element exists", async function (this: CustomWorld) {
13
- if (!this.element) throw new Error("No element stored in context");
14
- const count = (await this.element.count?.()) ?? 1;
15
- if (count === 0) throw new Error("Element does not exist");
16
- });
17
- Then("I see element does not exist", async function (this: CustomWorld) {
18
- if (!this.element) throw new Error("No element stored in context");
19
- const count = (await this.element.count?.()) ?? 1;
20
- if (count > 0) throw new Error("Element exists but should not");
21
- });
22
- Then("I see element is visible", async function (this: CustomWorld) {
23
- if (!this.element) throw new Error("No element in context");
24
- const isVisible = await this.element.isVisible();
25
- if (!isVisible) throw new Error("Element is not visible");
26
- });
27
- Then("I see element is not visible", async function (this: CustomWorld) {
28
- if (!this.element) throw new Error("No element in context");
29
- const isVisible = await this.element.isVisible();
30
- if (isVisible) throw new Error("Element is visible but should not be");
31
- });
32
-
33
- Then(/^I see element "([^"]+)" does not exist$/, async function (this: any, selector: string) {
34
- const el = await this.page.locator(selector);
35
- await expect(el).toHaveCount(0);
36
- });
37
-
38
- //
39
- // 👁️ ELEMENT VISIBILITY
40
- //
41
-
42
- Then(/^I see element "([^"]+)" is visible$/, async function (this: any, selector: string) {
43
- const el = this.page.locator(selector);
44
- await expect(el).toBeVisible();
45
- });
46
-
47
- Then(/^I see element "([^"]+)" is not visible$/, async function (this: any, selector: string) {
48
- const el = this.page.locator(selector);
49
- await expect(el).not.toBeVisible();
50
- });
51
-
52
- //
53
- // 🔎 ATTRIBUTE ASSERTIONS
54
- //
3
+ import type { CustomWorld } from "../helpers/world"; // Assuming this path is correct
55
4
 
5
+ // ===================================================================================
6
+ // ASSERTIONS: ELEMENT EXISTENCE (IN DOM)
7
+ // ===================================================================================
8
+
9
+ /**
10
+ * Asserts that at least one element matching the given selector exists in the DOM.
11
+ * It does not necessarily check for visibility.
12
+ *
13
+ * ```gherkin
14
+ * Then I see element {string} exists
15
+ * ```
16
+ *
17
+ * @param selector - The CSS selector of the element expected to exist.
18
+ *
19
+ * @example
20
+ * Then I see element ".user-profile-card" exists
21
+ *
22
+ * @remarks
23
+ * This step uses Playwright's `expect(locator).toHaveCount(1)` as a robust way to
24
+ * assert that exactly one matching element is present in the DOM. If multiple elements
25
+ * match, it will still pass as long as at least one exists (though `toHaveCount(1)`
26
+ * would strictly mean *only one*). For asserting multiple elements, use "Then I count X elements".
27
+ * @category Assertion Steps
28
+ */
29
+ export async function Then_I_see_element_exists(this: CustomWorld, selector: string) {
30
+ const locator = this.page.locator(selector);
31
+ // Using toHaveCount(1) for "exists" implies expecting at least one, or exactly one.
32
+ // If the intent is *at least one*, expect(locator).first().waitFor({state: 'attached'}) is also an option.
33
+ // For strict "exists", toHaveCount(1) is good if you expect uniqueness.
34
+ await expect(locator).toHaveCount(1, { timeout: 5000 });
35
+ this.log?.(`✅ Verified element "${selector}" exists in the DOM.`);
36
+ }
37
+ Then(/^I see element "([^"]+)" exists$/, Then_I_see_element_exists);
38
+
39
+ /**
40
+ * Asserts that the previously stored element exists in the DOM.
41
+ * This checks for its presence, not necessarily its visibility.
42
+ *
43
+ * ```gherkin
44
+ * Then I see element exists
45
+ * ```
46
+ *
47
+ * @example
48
+ * When I find element by selector ".my-dialog"
49
+ * Then I see element exists
50
+ *
51
+ * @remarks
52
+ * This step requires a preceding step that sets the {@link CustomWorld.element | current element}.
53
+ * It asserts that the stored element is attached to the DOM.
54
+ * @category Assertion Steps
55
+ */
56
+ export async function Then_I_see_stored_element_exists(this: CustomWorld) {
57
+ if (!this.element)
58
+ throw new Error("No element stored in context. Use a 'find' step before asserting.");
59
+
60
+ // Playwright's toBeAttached is the most robust way to check for DOM existence
61
+ await expect(this.element).toBeAttached({ timeout: 5000 });
62
+ this.log?.(`✅ Verified stored element exists in the DOM.`);
63
+ }
64
+ Then("I see element exists", Then_I_see_stored_element_exists);
65
+
66
+ /**
67
+ * Asserts that the previously stored element does NOT exist in the DOM.
68
+ *
69
+ * ```gherkin
70
+ * Then I see element does not exist
71
+ * ```
72
+ *
73
+ * @example
74
+ * When I find element by selector "#deleted-item"
75
+ * Then I see element does not exist
76
+ *
77
+ * @remarks
78
+ * This step requires a preceding step that sets the {@link CustomWorld.element | current element}.
79
+ * It asserts that the stored element is detached from the DOM.
80
+ * @category Assertion Steps
81
+ */
82
+ export async function Then_I_see_stored_element_does_not_exist(this: CustomWorld) {
83
+ if (!this.element)
84
+ throw new Error("No element stored in context. Use a 'find' step before asserting.");
85
+
86
+ // Playwright's not.toBeAttached is the most robust way to check for DOM non-existence
87
+ await expect(this.element).not.toBeAttached({ timeout: 5000 });
88
+ this.log?.(`✅ Verified stored element does NOT exist in the DOM.`);
89
+ }
90
+ Then("I see element does not exist", Then_I_see_stored_element_does_not_exist);
91
+
92
+ // ===================================================================================
93
+ // ASSERTIONS: ELEMENT VISIBILITY (Stored Element)
94
+ // ===================================================================================
95
+
96
+ /**
97
+ * Asserts that the previously stored element is visible in the viewport.
98
+ *
99
+ * ```gherkin
100
+ * Then I see element is visible
101
+ * ```
102
+ *
103
+ * @example
104
+ * When I find element by selector ".success-message"
105
+ * Then I see element is visible
106
+ *
107
+ * @remarks
108
+ * This step requires a preceding step that sets the {@link CustomWorld.element | current element}.
109
+ * It uses Playwright's `expect(locator).toBeVisible()` which automatically waits
110
+ * for the element to become visible.
111
+ * @category Assertion Steps
112
+ */
113
+ export async function Then_I_see_stored_element_is_visible(this: CustomWorld) {
114
+ if (!this.element) throw new Error("No element stored in context to check visibility.");
115
+
116
+ await expect(this.element).toBeVisible({ timeout: 5000 });
117
+ this.log?.(`✅ Verified stored element is visible.`);
118
+ }
119
+ Then("I see element is visible", Then_I_see_stored_element_is_visible);
120
+
121
+ /**
122
+ * Asserts that the previously stored element is NOT visible in the viewport.
123
+ *
124
+ * ```gherkin
125
+ * Then I see element is not visible
126
+ * ```
127
+ *
128
+ * @example
129
+ * When I find element by selector ".loading-spinner"
130
+ * Then I see element is not visible
131
+ *
132
+ * @remarks
133
+ * This step requires a preceding step that sets the {@link CustomWorld.element | current element}.
134
+ * It uses Playwright's `expect(locator).not.toBeVisible()` which automatically waits
135
+ * for the element to become hidden.
136
+ * @category Assertion Steps
137
+ */
138
+ export async function Then_I_see_stored_element_is_not_visible(this: CustomWorld) {
139
+ if (!this.element) throw new Error("No element stored in context to check non-visibility.");
140
+
141
+ await expect(this.element).not.toBeVisible({ timeout: 5000 });
142
+ this.log?.(`✅ Verified stored element is NOT visible.`);
143
+ }
144
+ Then("I see element is not visible", Then_I_see_stored_element_is_not_visible);
145
+
146
+ // ===================================================================================
147
+ // ASSERTIONS: ELEMENT EXISTENCE (Absence by Selector)
148
+ // ===================================================================================
149
+
150
+ /**
151
+ * Asserts that no element matching the given selector exists in the DOM.
152
+ *
153
+ * ```gherkin
154
+ * Then I see element {string} does not exist
155
+ * ```
156
+ *
157
+ * @param selector - The CSS selector of the element expected NOT to exist.
158
+ *
159
+ * @example
160
+ * Then I see element ".old-feature-flag" does not exist
161
+ *
162
+ * @remarks
163
+ * This step uses Playwright's `expect(locator).toHaveCount(0)` as a robust way to
164
+ * assert that no matching elements are present in the DOM.
165
+ * @category Assertion Steps
166
+ */
167
+ export async function Then_I_see_element_does_not_exist(this: CustomWorld, selector: string) {
168
+ const locator = this.page.locator(selector);
169
+ await expect(locator).toHaveCount(0, { timeout: 5000 });
170
+ this.log?.(`✅ Verified element "${selector}" does NOT exist in the DOM.`);
171
+ }
172
+ Then(/^I see element "([^"]+)" does not exist$/, Then_I_see_element_does_not_exist);
173
+
174
+ // ===================================================================================
175
+ // ASSERTIONS: ELEMENT VISIBILITY (by Selector)
176
+ // ===================================================================================
177
+
178
+ /**
179
+ * Asserts that an element matching the given selector is visible in the viewport.
180
+ *
181
+ * ```gherkin
182
+ * Then I see element {string} is visible
183
+ * ```
184
+ *
185
+ * @param selector - The CSS selector of the element expected to be visible.
186
+ *
187
+ * @example
188
+ * Then I see element ".main-content" is visible
189
+ *
190
+ * @remarks
191
+ * This step uses Playwright's `expect(locator).toBeVisible()` which automatically waits
192
+ * for the element to become visible.
193
+ * @category Assertion Steps
194
+ */
195
+ export async function Then_I_see_element_is_visible(this: CustomWorld, selector: string) {
196
+ const locator = this.page.locator(selector);
197
+ await expect(locator).toBeVisible({ timeout: 5000 });
198
+ this.log?.(`✅ Verified element "${selector}" is visible.`);
199
+ }
200
+ Then(/^I see element "([^"]+)" is visible$/, Then_I_see_element_is_visible);
201
+
202
+ /**
203
+ * Asserts that an element matching the given selector is NOT visible in the viewport.
204
+ *
205
+ * ```gherkin
206
+ * Then I see element {string} is not visible
207
+ * ```
208
+ *
209
+ * @param selector - The CSS selector of the element expected NOT to be visible.
210
+ *
211
+ * @example
212
+ * Then I see element ".modal-overlay" is not visible
213
+ *
214
+ * @remarks
215
+ * This step uses Playwright's `expect(locator).not.toBeVisible()` which automatically waits
216
+ * for the element to become hidden.
217
+ * @category Assertion Steps
218
+ */
219
+ export async function Then_I_see_element_is_not_visible(this: CustomWorld, selector: string) {
220
+ const locator = this.page.locator(selector);
221
+ await expect(locator).not.toBeVisible({ timeout: 5000 });
222
+ this.log?.(`✅ Verified element "${selector}" is NOT visible.`);
223
+ }
224
+ Then(/^I see element "([^"]+)" is not visible$/, Then_I_see_element_is_not_visible);
225
+
226
+ // ===================================================================================
227
+ // ASSERTIONS: ATTRIBUTE VALUES
228
+ // ===================================================================================
229
+
230
+ /**
231
+ * Asserts that an element matching the given selector has a specific attribute with an exact value.
232
+ *
233
+ * ```gherkin
234
+ * Then I see element {string} attribute {string} equals {string}
235
+ * ```
236
+ *
237
+ * @param selector - The CSS selector of the element to check.
238
+ * @param attribute - The name of the attribute (e.g., "id", "class", "data-test").
239
+ * @param expectedValue - The exact expected value of the attribute.
240
+ *
241
+ * @example
242
+ * Then I see element ".submit-button" attribute "disabled" equals "true"
243
+ *
244
+ * @remarks
245
+ * This step uses Playwright's `expect(locator).toHaveAttribute()`.
246
+ * @category Attribute Assertion Steps
247
+ */
248
+ export async function Then_I_see_element_attribute_equals(
249
+ this: CustomWorld,
250
+ selector: string,
251
+ attribute: string,
252
+ expectedValue: string
253
+ ) {
254
+ const locator = this.page.locator(selector);
255
+ await expect(locator).toHaveAttribute(attribute, expectedValue, { timeout: 5000 });
256
+ this.log?.(
257
+ `✅ Verified element "${selector}" has attribute "${attribute}" equal to "${expectedValue}".`
258
+ );
259
+ }
56
260
  Then(
57
261
  /^I see element "([^"]+)" attribute "([^"]+)" equals "(.*)"$/,
58
- async function (this: any, selector: string, attribute: string, expected: string) {
59
- const el = this.page.locator(selector);
60
- await expect(el).toHaveAttribute(attribute, expected);
61
- }
262
+ Then_I_see_element_attribute_equals
62
263
  );
264
+
265
+ /**
266
+ * Asserts that the previously stored element has a specific attribute with an exact value.
267
+ *
268
+ * ```gherkin
269
+ * Then I see element attribute {string} equals {string}
270
+ * ```
271
+ *
272
+ * @param attr - The name of the attribute to check.
273
+ * @param expectedValue - The exact expected value of the attribute.
274
+ *
275
+ * @example
276
+ * When I find element by selector "input[name='rememberMe']"
277
+ * Then I see element attribute "checked" equals "checked"
278
+ *
279
+ * @remarks
280
+ * This step requires a preceding step that sets the {@link CustomWorld.element | current element}.
281
+ * It uses Playwright's `expect(locator).toHaveAttribute()`.
282
+ * @category Attribute Assertion Steps
283
+ */
284
+ export async function Then_I_see_stored_element_attribute_equals(
285
+ this: CustomWorld,
286
+ attr: string,
287
+ expectedValue: string
288
+ ) {
289
+ if (!this.element) {
290
+ throw new Error(
291
+ "No element is currently selected. Use a 'find' step before asserting its attributes."
292
+ );
293
+ }
294
+ await expect(this.element).toHaveAttribute(attr, expectedValue, { timeout: 5000 });
295
+ this.log?.(`✅ Verified stored element has attribute "${attr}" equal to "${expectedValue}".`);
296
+ }
63
297
  Then(
64
298
  'I see element attribute "{word}" equals {string}',
65
- async function (this: CustomWorld, attr: string, expected: string) {
66
- if (!this.element) {
67
- throw new Error("No element is currently selected. Use a 'find' step before asserting.");
68
- }
69
- await expect(this.element).toHaveAttribute(attr, expected);
70
- }
299
+ Then_I_see_stored_element_attribute_equals
71
300
  );
72
301
 
73
- Then("I see element has attribute {string}", async function (this: CustomWorld, attr: string) {
74
- if (!this.element) throw new Error("No element in context");
75
- const value = await this.element.getAttribute(attr);
76
- if (value === null) throw new Error(`Attribute "${attr}" not found`);
77
- });
302
+ /**
303
+ * Asserts that the previously stored element has a specific attribute, regardless of its value.
304
+ *
305
+ * ```gherkin
306
+ * Then I see element has attribute {string}
307
+ * ```
308
+ *
309
+ * @param attr - The name of the attribute expected to exist.
310
+ *
311
+ * @example
312
+ * When I find element by selector "img.user-avatar"
313
+ * Then I see element has attribute "alt"
314
+ *
315
+ * @remarks
316
+ * This step requires a preceding step that sets the {@link CustomWorld.element | current element}.
317
+ * It retrieves the attribute's value and asserts that it's not `null` (meaning the attribute is present).
318
+ * @category Attribute Assertion Steps
319
+ */
320
+ export async function Then_I_see_stored_element_has_attribute(this: CustomWorld, attr: string) {
321
+ if (!this.element) throw new Error("No element stored in context to check for attribute.");
322
+
323
+ // Use Playwright's expect.toHaveAttribute for robust checking of existence (value can be empty string, but not null)
324
+ // For simply checking existence, we can assert that the attribute is not null or undefined.
325
+ const attributeValue = await this.element.getAttribute(attr, { timeout: 5000 });
326
+ expect(attributeValue).not.toBeNull();
327
+ this.log?.(`✅ Verified stored element has attribute "${attr}".`);
328
+ }
329
+ Then("I see element has attribute {string}", Then_I_see_stored_element_has_attribute);
330
+
331
+ /**
332
+ * Asserts that the previously stored element's specific attribute contains a given substring.
333
+ *
334
+ * ```gherkin
335
+ * Then I see element attribute {string} contains {string}
336
+ * ```
337
+ *
338
+ * @param attr - The name of the attribute to check.
339
+ * @param part - The substring expected to be contained within the attribute's value.
340
+ *
341
+ * @example
342
+ * When I find element by selector ".product-image"
343
+ * Then I see element attribute "src" contains "thumbnail"
344
+ *
345
+ * @remarks
346
+ * This step requires a preceding step that sets the {@link CustomWorld.element | current element}.
347
+ * It retrieves the attribute's value and asserts that it includes the `part` substring.
348
+ * @category Attribute Assertion Steps
349
+ */
350
+ export async function Then_I_see_stored_element_attribute_contains(
351
+ this: CustomWorld,
352
+ attr: string,
353
+ part: string
354
+ ) {
355
+ if (!this.element) throw new Error("No element stored in context to check attribute content.");
356
+
357
+ // Use Playwright's expect.toHaveAttribute with a regex for 'contains' check
358
+ await expect(this.element).toHaveAttribute(attr, new RegExp(part), { timeout: 5000 });
359
+ this.log?.(`✅ Verified stored element attribute "${attr}" contains "${part}".`);
360
+ }
78
361
  Then(
79
362
  'I see element attribute "{word}" contains {string}',
80
- async function (this: CustomWorld, attr: string, part: string) {
81
- if (!this.element) throw new Error("No element in context");
82
- const value = await this.element.getAttribute(attr);
83
- if (!value?.includes(part)) {
84
- throw new Error(`Attribute "${attr}" does not contain "${part}". Got: "${value}"`);
85
- }
86
- }
363
+ Then_I_see_stored_element_attribute_contains
87
364
  );
88
365
 
366
+ /**
367
+ * Asserts that an element matching the given selector has a specific attribute containing a given substring.
368
+ *
369
+ * ```gherkin
370
+ * Then I see element {string} attribute {string} contains {string}
371
+ * ```
372
+ *
373
+ * @param selector - The CSS selector of the element to check.
374
+ * @param attribute - The name of the attribute.
375
+ * @param substring - The substring expected to be contained within the attribute's value.
376
+ *
377
+ * @example
378
+ * Then I see element "a.download-link" attribute "href" contains ".pdf"
379
+ *
380
+ * @remarks
381
+ * This step retrieves the attribute's value from the element matching the selector
382
+ * and asserts that it includes the `substring`.
383
+ * @category Attribute Assertion Steps
384
+ */
385
+ export async function Then_I_see_element_attribute_contains(
386
+ this: CustomWorld,
387
+ selector: string,
388
+ attribute: string,
389
+ substring: string
390
+ ) {
391
+ const locator = this.page.locator(selector);
392
+ // Use Playwright's expect.toHaveAttribute with a regex for 'contains' check
393
+ await expect(locator).toHaveAttribute(attribute, new RegExp(substring), { timeout: 5000 });
394
+ this.log?.(`✅ Verified element "${selector}" attribute "${attribute}" contains "${substring}".`);
395
+ }
89
396
  Then(
90
397
  /^I see element "([^"]+)" attribute "([^"]+)" contains "(.*)"$/,
91
- async function (this: any, selector: string, attribute: string, substring: string) {
92
- const attr = await this.page.locator(selector).getAttribute(attribute);
93
- expect(attr?.includes(substring)).toBeTruthy();
94
- }
398
+ Then_I_see_element_attribute_contains
95
399
  );
96
400
 
97
- Then(
98
- /^I see element "([^"]+)" has attribute "([^"]+)"$/,
99
- async function (this: any, selector: string, attribute: string) {
100
- const attr = await this.page.locator(selector).getAttribute(attribute);
101
- expect(attr).not.toBeNull();
102
- }
103
- );
401
+ /**
402
+ * Asserts that an element matching the given selector has a specific attribute, regardless of its value.
403
+ *
404
+ * ```gherkin
405
+ * Then I see element {string} has attribute {string}
406
+ * ```
407
+ *
408
+ * @param selector - The CSS selector of the element to check.
409
+ * @param attribute - The name of the attribute expected to exist.
410
+ *
411
+ * @example
412
+ * Then I see element "img.avatar" has attribute "src"
413
+ *
414
+ * @remarks
415
+ * This step retrieves the attribute's value from the element matching the selector
416
+ * and asserts that it's not `null` (meaning the attribute is present).
417
+ * @category Attribute Assertion Steps
418
+ */
419
+ export async function Then_I_see_element_has_attribute(
420
+ this: CustomWorld,
421
+ selector: string,
422
+ attribute: string
423
+ ) {
424
+ const locator = this.page.locator(selector);
425
+ // Playwright's toHaveAttribute with an empty string or regex can assert existence effectively.
426
+ // toHaveAttribute(attr, /.+/) ensures it exists and has *some* non-empty value.
427
+ // For just existence, checking if getAttribute is not null is explicit.
428
+ const attributeValue = await locator.getAttribute(attribute, { timeout: 5000 });
429
+ expect(attributeValue).not.toBeNull();
430
+ this.log?.(`✅ Verified element "${selector}" has attribute "${attribute}".`);
431
+ }
432
+ Then(/^I see element "([^"]+)" has attribute "([^"]+)"$/, Then_I_see_element_has_attribute);