pomwright 1.5.0 → 2.0.0

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 (117) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +5 -5
  3. package/dist/index.d.mts +91 -989
  4. package/dist/index.d.ts +91 -989
  5. package/dist/index.js +627 -1887
  6. package/dist/index.mjs +633 -1888
  7. package/package.json +9 -11
  8. package/AGENTS.md +0 -37
  9. package/docs/v1/BaseApi-explanation.md +0 -63
  10. package/docs/v1/BasePage-explanation.md +0 -96
  11. package/docs/v1/LocatorSchema-explanation.md +0 -271
  12. package/docs/v1/LocatorSchemaPath-explanation.md +0 -165
  13. package/docs/v1/PlaywrightReportLogger-explanation.md +0 -56
  14. package/docs/v1/get-locator-methods-explanation.md +0 -250
  15. package/docs/v1/intro-to-using-pomwright.md +0 -899
  16. package/docs/v1/sessionStorage-methods-explanation.md +0 -38
  17. package/docs/v1/tips-folder-structure.md +0 -38
  18. package/docs/v1-to-v2-migration/bridge-migration-guide.md +0 -159
  19. package/docs/v1-to-v2-migration/direct-migration-guide.md +0 -238
  20. package/docs/v1-to-v2-migration/v1-to-v2-comparison.md +0 -547
  21. package/docs/v2/PageObject.md +0 -293
  22. package/docs/v2/composing-locator-modules.md +0 -93
  23. package/docs/v2/locator-registry.md +0 -693
  24. package/docs/v2/logging.md +0 -168
  25. package/docs/v2/overview.md +0 -515
  26. package/docs/v2/session-storage.md +0 -160
  27. package/index.ts +0 -75
  28. package/intTestV2/.env +0 -0
  29. package/intTestV2/fixtures/testApp.fixtures.ts +0 -43
  30. package/intTestV2/package.json +0 -22
  31. package/intTestV2/page-object-models/testApp/pages/iframe/iframe.locatorSchema.ts +0 -24
  32. package/intTestV2/page-object-models/testApp/pages/iframe/iframe.page.ts +0 -17
  33. package/intTestV2/page-object-models/testApp/pages/testPage.locatorSchema.ts +0 -32
  34. package/intTestV2/page-object-models/testApp/pages/testPage.page.ts +0 -119
  35. package/intTestV2/page-object-models/testApp/pages/testPath/[color]/color.locatorSchema.ts +0 -29
  36. package/intTestV2/page-object-models/testApp/pages/testPath/[color]/color.page.ts +0 -48
  37. package/intTestV2/page-object-models/testApp/pages/testPath/testPath.locatorSchema.ts +0 -9
  38. package/intTestV2/page-object-models/testApp/pages/testPath/testPath.page.ts +0 -23
  39. package/intTestV2/page-object-models/testApp/pages/testfilters/testfilters.locatorSchema.ts +0 -114
  40. package/intTestV2/page-object-models/testApp/pages/testfilters/testfilters.page.ts +0 -23
  41. package/intTestV2/page-object-models/testApp/testApp.base.ts +0 -20
  42. package/intTestV2/playwright.config.ts +0 -54
  43. package/intTestV2/server.js +0 -216
  44. package/intTestV2/test-data/staticPage/index.html +0 -280
  45. package/intTestV2/test-data/staticPage/w3images/avatar2.png +0 -0
  46. package/intTestV2/test-data/staticPage/w3images/avatar3.png +0 -0
  47. package/intTestV2/test-data/staticPage/w3images/avatar5.png +0 -0
  48. package/intTestV2/test-data/staticPage/w3images/avatar6.png +0 -0
  49. package/intTestV2/test-data/staticPage/w3images/forest.jpg +0 -0
  50. package/intTestV2/test-data/staticPage/w3images/lights.jpg +0 -0
  51. package/intTestV2/test-data/staticPage/w3images/mountains.jpg +0 -0
  52. package/intTestV2/test-data/staticPage/w3images/nature.jpg +0 -0
  53. package/intTestV2/test-data/staticPage/w3images/snow.jpg +0 -0
  54. package/intTestV2/tests/locatorRegistry/add/add.describe.spec.ts +0 -54
  55. package/intTestV2/tests/locatorRegistry/add/add.filter.spec.ts +0 -143
  56. package/intTestV2/tests/locatorRegistry/add/add.frameLocator.spec.ts +0 -23
  57. package/intTestV2/tests/locatorRegistry/add/add.getByAltText.spec.ts +0 -23
  58. package/intTestV2/tests/locatorRegistry/add/add.getById.spec.ts +0 -45
  59. package/intTestV2/tests/locatorRegistry/add/add.getByLabel.spec.ts +0 -23
  60. package/intTestV2/tests/locatorRegistry/add/add.getByPlaceholder.spec.ts +0 -23
  61. package/intTestV2/tests/locatorRegistry/add/add.getByRole.spec.ts +0 -23
  62. package/intTestV2/tests/locatorRegistry/add/add.getByTestId.spec.ts +0 -23
  63. package/intTestV2/tests/locatorRegistry/add/add.getByText.spec.ts +0 -23
  64. package/intTestV2/tests/locatorRegistry/add/add.getByTitle.spec.ts +0 -23
  65. package/intTestV2/tests/locatorRegistry/add/add.locator.spec.ts +0 -23
  66. package/intTestV2/tests/locatorRegistry/add/add.reuseExisting.spec.ts +0 -66
  67. package/intTestV2/tests/locatorRegistry/add/add.reuseReusable.spec.ts +0 -311
  68. package/intTestV2/tests/locatorRegistry/add/add.spec.ts +0 -159
  69. package/intTestV2/tests/locatorRegistry/filter.cycle.spec.ts +0 -39
  70. package/intTestV2/tests/locatorRegistry/getLocator/getLocator.spec.ts +0 -253
  71. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.clearSteps.spec.ts +0 -105
  72. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.describe.spec.ts +0 -23
  73. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.filter.spec.ts +0 -368
  74. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.getLocator.spec.ts +0 -56
  75. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.getNestedLocator.spec.ts +0 -175
  76. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.nth.spec.ts +0 -60
  77. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.remove.spec.ts +0 -32
  78. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.replace.spec.ts +0 -24
  79. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.spec.ts +0 -110
  80. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.update.spec.ts +0 -322
  81. package/intTestV2/tests/locatorRegistry/getNestedLocator/getNestedLocator.spec.ts +0 -412
  82. package/intTestV2/tests/locatorRegistry/registry/registry.binding.spec.ts +0 -50
  83. package/intTestV2/tests/locatorRegistry/validation/validation.locatorSchemaPath.spec.ts +0 -115
  84. package/intTestV2/tests/locatorRegistry/validation/validation.sub-path.spec.ts +0 -45
  85. package/intTestV2/tests/step/step.spec.ts +0 -49
  86. package/intTestV2/tests/testApp/color.spec.ts +0 -15
  87. package/intTestV2/tests/testApp/iframe.spec.ts +0 -57
  88. package/intTestV2/tests/testApp/testFilters.spec.ts +0 -24
  89. package/intTestV2/tests/testApp/testPage.spec.ts +0 -161
  90. package/intTestV2/tests/testApp/testPath.spec.ts +0 -18
  91. package/pack-build.sh +0 -11
  92. package/pack-test-v2.sh +0 -36
  93. package/playwright.base.ts +0 -42
  94. package/skills/README.md +0 -56
  95. package/skills/pomwright-v1-5-bridge-migration/SKILL.md +0 -40
  96. package/skills/pomwright-v1-5-bridge-migration/references/call-site-migration.md +0 -178
  97. package/skills/pomwright-v1-5-bridge-migration/references/schema-translation.md +0 -183
  98. package/skills/pomwright-v2-migration/SKILL.md +0 -63
  99. package/skills/pomwright-v2-migration/references/call-site-migration.md +0 -265
  100. package/skills/pomwright-v2-migration/references/class-migration.md +0 -266
  101. package/skills/pomwright-v2-migration/references/fixture-and-helpers.md +0 -423
  102. package/skills/pomwright-v2-migration/references/locator-registration.md +0 -344
  103. package/srcV2/fixture/base.fixtures.ts +0 -23
  104. package/srcV2/helpers/navigation.ts +0 -153
  105. package/srcV2/helpers/playwrightReportLogger.ts +0 -196
  106. package/srcV2/helpers/sessionStorage.ts +0 -251
  107. package/srcV2/helpers/stepDecorator.ts +0 -106
  108. package/srcV2/locators/index.ts +0 -15
  109. package/srcV2/locators/locatorQueryBuilder.ts +0 -427
  110. package/srcV2/locators/locatorRegistrationBuilder.ts +0 -558
  111. package/srcV2/locators/locatorRegistry.ts +0 -541
  112. package/srcV2/locators/locatorUpdateBuilder.ts +0 -602
  113. package/srcV2/locators/reusableLocatorBuilder.ts +0 -200
  114. package/srcV2/locators/types.ts +0 -256
  115. package/srcV2/locators/utils.ts +0 -309
  116. package/srcV2/locators/v1SchemaTranslator.ts +0 -178
  117. package/srcV2/pageObject.ts +0 -105
@@ -1,368 +0,0 @@
1
- import { expect, test } from "@fixtures-v2/testApp.fixtures";
2
- import type { Page } from "@playwright/test";
3
- import {
4
- type FilterDefinition,
5
- LocatorRegistryInternal,
6
- type LocatorStrategyDefinition,
7
- } from "../../../../srcV2/locators";
8
-
9
- test("filter adds an additional filter per sub-path", async ({ testFilters }) => {
10
- const filtered = testFilters
11
- .getLocatorSchema("body.section")
12
- .filter("body", { hasText: "Playground" })
13
- .filter("body.section", { hasText: "Primary Colors Playground" })
14
- .getNestedLocator();
15
-
16
- expect(`${filtered}`).toEqual(
17
- "locator('body').filter({ hasText: 'Playground' }).locator('section').filter({ hasText: 'Primary Colors Playground' })",
18
- );
19
- });
20
-
21
- test("filter with has locator", async ({ testFilters }) => {
22
- const heading = testFilters.getLocator("body.section.heading");
23
-
24
- const filtered = testFilters
25
- .getLocatorSchema("fictional.filter@hasNotText")
26
- .filter("fictional.filter@hasNotText", { has: heading })
27
- .getNestedLocator();
28
-
29
- expect(`${filtered}`).toEqual(
30
- "getByRole('button').filter({ hasNotText: 'hasNotText' }).filter({ has: getByRole('heading', { level: 2 }) })",
31
- );
32
- });
33
-
34
- test("filter chaining is non-destructive", async ({ testFilters }) => {
35
- const base = testFilters.getNestedLocator("body.section");
36
-
37
- const filtered = testFilters
38
- .getLocatorSchema("body.section")
39
- .filter("body.section", { hasText: /Playground/i })
40
- .getNestedLocator();
41
-
42
- expect(`${filtered}`).toEqual("locator('body').locator('section').filter({ hasText: /Playground/i })");
43
-
44
- const unchanged = testFilters.getNestedLocator("body.section");
45
- expect(`${unchanged}`).toEqual(`${base}`);
46
- });
47
-
48
- test("filter preserves call order when multiple filters target the same sub-path", async ({ testFilters }) => {
49
- const locator = testFilters
50
- .getLocatorSchema("body.section@playground.button@reset")
51
- .filter("body.section@playground.button@reset", { hasText: /Reset/i })
52
- .filter("body.section@playground.button@reset", { hasText: /Color/i })
53
- .getNestedLocator();
54
-
55
- expect(`${locator}`).toEqual(
56
- "locator('body').locator('section').filter({ hasText: /Playground/i }).getByRole('button', { name: 'Reset Color' }).filter({ hasText: /Reset/i }).filter({ hasText: /Color/i })",
57
- );
58
- });
59
-
60
- test("filter can layer ancestor and descendant filters simultaneously", async ({ testFilters }) => {
61
- const locator = testFilters
62
- .getLocatorSchema("body.section@playground.button@red")
63
- .filter("body.section@playground.button@red", { hasText: /Red/i })
64
- .filter("body", { hasText: /Playground/i })
65
- .filter("body.section@playground", { hasText: /Primary Colors/i })
66
- .getNestedLocator();
67
-
68
- expect(`${locator}`).toEqual(
69
- "locator('body').filter({ hasText: /Playground/i }).locator('section').filter({ hasText: /Playground/i }).filter({ hasText: /Primary Colors/i }).getByRole('button', { name: 'Red' }).filter({ hasText: /Red/i })",
70
- );
71
- });
72
-
73
- test("filter accepts registry path strings for has/hasNot", async ({ testFilters }) => {
74
- const locator = testFilters
75
- .getLocatorSchema("body.section@playground.button@reset")
76
- .filter("body.section@playground.button@reset", { has: "body.section.heading" })
77
- .filter("body.section@playground.button@reset", {
78
- hasNot: "body.section@playground.button@red",
79
- })
80
- .getNestedLocator();
81
-
82
- expect(`${locator}`).toEqual(
83
- "locator('body').locator('section').filter({ hasText: /Playground/i }).getByRole('button', { name: 'Reset Color' }).filter({ has: getByRole('heading', { level: 2 }) }).filter({ hasNot: getByRole('button', { name: 'Red' }) })",
84
- );
85
- });
86
-
87
- test("filter accepts Playwright locators for has/hasNot", async ({ testFilters }) => {
88
- const hasLocator = testFilters.getLocator("body.section.heading");
89
- const hasNotLocator = testFilters.getLocator("body.section@playground.button@red");
90
-
91
- const locator = testFilters
92
- .getLocatorSchema("body.section@playground")
93
- .filter("body.section@playground", { has: hasLocator })
94
- .filter("body.section@playground", { hasNot: hasNotLocator })
95
- .getNestedLocator();
96
-
97
- expect(`${locator}`).toEqual(
98
- "locator('body').locator('section').filter({ hasText: /Playground/i }).filter({ has: getByRole('heading', { level: 2 }) }).filter({ hasNot: getByRole('button', { name: 'Red' }) })",
99
- );
100
- });
101
-
102
- test("filters accept frame owner locators for has/hasNot", async ({ testFilters }) => {
103
- const locator = testFilters
104
- .getLocatorSchema("fictional.filter@hasNotText")
105
- .filter("fictional.filter@hasNotText", {
106
- has: testFilters.page.frameLocator("iframe").owner(),
107
- })
108
- .getNestedLocator();
109
-
110
- const manual = testFilters.page.getByRole("button").filter({
111
- hasNotText: "hasNotText",
112
- has: testFilters.page.frameLocator("iframe").owner(),
113
- });
114
-
115
- expect(`${locator}`).toEqual(`${manual}`);
116
- });
117
-
118
- test("multiple nesting/chaining retains filters across the chain", async ({ testFilters }) => {
119
- const multiChain = testFilters
120
- .getLocatorSchema("fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText")
121
- .update("fictional.filter@hasNotText")
122
- .getByRole("button", { name: "roleOptions" })
123
- .update("fictional.filter@hasNotText.filter@hasText")
124
- .locator("locator", {
125
- hasText: "locatorOptionsHasText",
126
- hasNotText: "locatorOptionshasNotText",
127
- })
128
- .update("fictional.filter@hasNotText.filter@hasText.filter@hasNotText")
129
- .getByTestId("testId")
130
- .update("fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText")
131
- .getByLabel("label", { exact: true })
132
- .getNestedLocator();
133
-
134
- expect(`${multiChain}`).toEqual(
135
- "getByRole('button', { name: 'roleOptions' }).filter({ hasNotText: 'hasNotText' }).locator('locator').filter({ hasText: 'locatorOptionsHasText' }).filter({ hasNotText: 'locatorOptionshasNotText' }).filter({ hasText: 'hasText' }).getByTestId('testId').filter({ hasNotText: 'hasNotText' }).getByLabel('label', { exact: true }).filter({ hasText: 'hasText' })",
136
- );
137
- });
138
-
139
- test("filter definitions that omit options stay stable", async ({ testFilters }) => {
140
- const nested = testFilters.getNestedLocator("fictional.filter@optionsUndefined");
141
- expect(`${nested}`).toEqual("getByRole('button')");
142
- });
143
-
144
- test("schema filters resolve path string references", async ({ testFilters, page }) => {
145
- const nested = testFilters.getNestedLocator("fictional.locatorAndOptionsWithfilter@allOptions");
146
-
147
- const manual = page.getByRole("button", { name: "roleOptions" }).filter({
148
- has: page.getByRole("heading", { level: 2 }),
149
- hasNot: page.getByRole("button"),
150
- hasText: "hasText",
151
- hasNotText: "hasNotText",
152
- });
153
-
154
- expect(`${nested}`).toEqual(`${manual}`);
155
-
156
- const has = page.getByRole("heading", { level: 2 });
157
- const hasNot = page.getByRole("button");
158
-
159
- const manual2 = page.getByRole("button", { name: "roleOptions" }).filter({
160
- has: has,
161
- hasNot: hasNot,
162
- hasText: "hasText",
163
- hasNotText: "hasNotText",
164
- });
165
-
166
- expect(`${nested}`).toEqual(`${manual2}`);
167
-
168
- expect(`${nested}`).toEqual(
169
- "getByRole('button', { name: 'roleOptions' }).filter({ hasText: 'hasText' }).filter({ hasNotText: 'hasNotText' }).filter({ has: getByRole('heading', { level: 2 }) }).filter({ hasNot: getByRole('button') })",
170
- );
171
- });
172
-
173
- test("frameLocator keeps the chain inside the frame and skips frame-level filters", async ({ testFilters, page }) => {
174
- const frameTerminalBuilder = testFilters
175
- .getLocatorSchema("fictional.filter@hasNotText.filter@hasText")
176
- .update("fictional.filter@hasNotText.filter@hasText")
177
- .frameLocator('iframe[title="name"]');
178
-
179
- const directFrameLocator = frameTerminalBuilder.getLocator();
180
- const manualFrame = page.frameLocator('iframe[title="name"]').owner();
181
-
182
- expect(`${directFrameLocator}`).toEqual(`${manualFrame}`);
183
-
184
- const nestedBuilder = testFilters
185
- .getLocatorSchema("fictional.filter@hasNotText.filter@hasText.filter@hasNotText")
186
- .update("fictional.filter@hasNotText.filter@hasText")
187
- .frameLocator('iframe[title="name"]')
188
- .update("fictional.filter@hasNotText.filter@hasText.filter@hasNotText")
189
- .getByRole("button", { name: "inside frame" });
190
-
191
- const nested = nestedBuilder.getNestedLocator();
192
- const manualNested = page
193
- .getByRole("button")
194
- .filter({ hasNotText: "hasNotText" })
195
- .frameLocator('iframe[title="name"]')
196
- .getByRole("button", { name: "inside frame" })
197
- .filter({ hasNotText: "hasNotText" });
198
-
199
- expect(`${nested}`).toEqual(`${manualNested}`);
200
- });
201
-
202
- test.describe("getNestedLocator for locatorSchema with filter property", () => {
203
- type TestCase = {
204
- definition: LocatorStrategyDefinition;
205
- expected: string;
206
- label: string;
207
- };
208
-
209
- const testCases: TestCase[] = [
210
- { label: "role", definition: { type: "role", role: "button" }, expected: "getByRole('button')" },
211
- { label: "text", definition: { type: "text", text: "text" }, expected: "getByText('text')" },
212
- { label: "label", definition: { type: "label", text: "label" }, expected: "getByLabel('label')" },
213
- {
214
- label: "placeholder",
215
- definition: { type: "placeholder", text: "placeholder" },
216
- expected: "getByPlaceholder('placeholder')",
217
- },
218
- { label: "altText", definition: { type: "altText", text: "altText" }, expected: "getByAltText('altText')" },
219
- { label: "title", definition: { type: "title", text: "title" }, expected: "getByTitle('title')" },
220
- { label: "locator", definition: { type: "locator", selector: "locator" }, expected: "locator('locator')" },
221
- { label: "testId", definition: { type: "testId", testId: "testId" }, expected: "getByTestId('testId')" },
222
- { label: "id", definition: { type: "id", id: "id" }, expected: "locator('#id')" },
223
- ];
224
-
225
- for (const { definition, expected, label } of testCases) {
226
- test(`definition ${label}: should apply filter`, async ({ testFilters }) => {
227
- const builder = testFilters.getLocatorSchema("fictional.filter@undefined").update("fictional.filter@undefined");
228
-
229
- const query = (() => {
230
- switch (definition.type) {
231
- case "role":
232
- return builder.getByRole(definition.role, definition.options);
233
- case "text":
234
- return builder.getByText(definition.text, definition.options);
235
- case "label":
236
- return builder.getByLabel(definition.text, definition.options);
237
- case "placeholder":
238
- return builder.getByPlaceholder(definition.text, definition.options);
239
- case "altText":
240
- return builder.getByAltText(definition.text, definition.options);
241
- case "title":
242
- return builder.getByTitle(definition.text, definition.options);
243
- case "locator":
244
- return builder.locator(definition.selector, definition.options);
245
- case "testId":
246
- return builder.getByTestId(definition.testId);
247
- case "id":
248
- return builder.getById(definition.id);
249
- case "frameLocator":
250
- return builder.frameLocator(definition.selector);
251
- default:
252
- throw new Error(`Unhandled definition type ${(definition as { type: string }).type}`);
253
- }
254
- })();
255
-
256
- const nested = query.getNestedLocator();
257
-
258
- expect(`${nested}`).toEqual(expected);
259
- });
260
- }
261
- });
262
-
263
- const createTestRegistry = <Paths extends string>(page: Page) => new LocatorRegistryInternal<Paths>(page);
264
-
265
- test("getLocatorSchema.filter supports Playwright Locator instances for has/hasNot", async ({ page }) => {
266
- type LocatorSchemaPaths = "item";
267
-
268
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
269
-
270
- registry.add("item").getByRole("button");
271
-
272
- const locator = registry
273
- .getLocatorSchema("item")
274
- .filter({ has: page.getByRole("heading", { level: 2 }) })
275
- .filter({ hasNot: page.getByRole("heading", { level: 3 }) })
276
- .getLocator();
277
-
278
- expect(`${locator}`).toEqual(
279
- "getByRole('button').filter({ has: getByRole('heading', { level: 2 }) }).filter({ hasNot: getByRole('heading', { level: 3 }) })",
280
- );
281
- });
282
-
283
- test("getLocatorSchema.filter supports registry path strings for has/hasNot", async ({ page }) => {
284
- type LocatorSchemaPaths = "item" | "heading.primary" | "heading.secondary";
285
-
286
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
287
-
288
- registry.add("heading.primary").getByRole("heading", { level: 2 });
289
- registry.add("heading.secondary").getByRole("heading", { level: 3 });
290
- registry.add("item").getByRole("button");
291
-
292
- const locator = registry
293
- .getLocatorSchema("item")
294
- .filter({ has: "heading.primary" })
295
- .filter({ hasNot: "heading.secondary" })
296
- .getLocator();
297
-
298
- expect(`${locator}`).toEqual(
299
- "getByRole('button').filter({ has: getByRole('heading', { level: 2 }) }).filter({ hasNot: getByRole('heading', { level: 3 }) })",
300
- );
301
- });
302
-
303
- test("getLocatorSchema.filter rejects inline locator strategy definitions for has/hasNot", async ({ page }) => {
304
- type LocatorSchemaPaths = "item";
305
-
306
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
307
-
308
- registry.add("item").getByRole("button");
309
- registry.getLocatorSchema("item");
310
- // @ts-expect-error inline locator definitions are no longer supported
311
- registry.getLocatorSchema("item").filter({ has: { type: "locator", selector: "section" } });
312
-
313
- const unsafeFilter = {
314
- has: { type: "locator", selector: "section" },
315
- } as unknown as FilterDefinition<LocatorSchemaPaths, LocatorSchemaPaths>;
316
-
317
- expect(() => registry.getLocatorSchema("item").filter(unsafeFilter).getLocator()).toThrow(
318
- /Unsupported filter reference/,
319
- );
320
- });
321
-
322
- test("getLocatorSchema.filter rejects locator wrappers for has/hasNot", async ({ page }) => {
323
- type LocatorSchemaPaths = "item";
324
-
325
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
326
-
327
- registry.add("item").getByRole("button");
328
- // @ts-expect-error locator wrapper is no longer supported
329
- registry.getLocatorSchema("item").filter({ has: { locator: { type: "locator", selector: "section" } } });
330
-
331
- const unsafeFilter = {
332
- has: { locator: { type: "locator", selector: "section" } },
333
- } as unknown as FilterDefinition<LocatorSchemaPaths, LocatorSchemaPaths>;
334
-
335
- expect(() => registry.getLocatorSchema("item").filter(unsafeFilter).getLocator()).toThrow(
336
- /Unsupported filter reference/,
337
- );
338
- });
339
-
340
- test("getLocatorSchema.filter rejects locatorPath wrappers for has/hasNot", async ({ page }) => {
341
- type LocatorSchemaPaths = "item";
342
-
343
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
344
-
345
- registry.add("item").getByRole("button");
346
- // @ts-expect-error locatorPath wrapper is no longer supported
347
- registry.getLocatorSchema("item").filter({ has: { locatorPath: "item" } });
348
-
349
- const unsafeFilter = {
350
- has: { locatorPath: "item" },
351
- } as unknown as FilterDefinition<LocatorSchemaPaths, LocatorSchemaPaths>;
352
-
353
- expect(() => registry.getLocatorSchema("item").filter(unsafeFilter).getLocator()).toThrow(
354
- /Unsupported filter reference/,
355
- );
356
- });
357
-
358
- test("getLocatorSchema.filter supports visible true/false", async ({ page }) => {
359
- type LocatorSchemaPaths = "item";
360
-
361
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
362
-
363
- registry.add("item").getByRole("button");
364
-
365
- const locator = registry.getLocatorSchema("item").filter({ visible: true }).filter({ visible: false }).getLocator();
366
-
367
- expect(`${locator}`).toEqual("getByRole('button').filter({ visible: true }).filter({ visible: false })");
368
- });
@@ -1,56 +0,0 @@
1
- import { expect, test } from "@fixtures-v2/testApp.fixtures";
2
-
3
- test("getLocator fluent wrapper supports update and clearSteps", async ({ testFilters }) => {
4
- const updated = testFilters
5
- .getLocatorSchema("body.section.heading")
6
- .update("body.section.heading")
7
- .getByRole("heading", { level: 3 })
8
- .getLocator();
9
-
10
- expect(`${updated}`).toEqual("getByRole('heading', { level: 3 })");
11
-
12
- const cleared = testFilters
13
- .getLocatorSchema("fictional.filter@hasText")
14
- .clearSteps("fictional.filter@hasText")
15
- .getLocator();
16
-
17
- expect(`${cleared}`).toEqual("getByRole('button')");
18
- });
19
-
20
- test("getLocator update accepts partial patch arguments", async ({ testFilters }) => {
21
- const baseline = testFilters.getLocator("body.section.heading");
22
-
23
- expect(`${baseline}`).toEqual("getByRole('heading', { level: 2 })");
24
-
25
- const noArgs = testFilters
26
- .getLocatorSchema("body.section.heading")
27
- .update("body.section.heading")
28
- .getByRole()
29
- .getLocator();
30
-
31
- expect(`${noArgs}`).toEqual("getByRole('heading', { level: 2 })");
32
-
33
- const optionsOnly = testFilters
34
- .getLocatorSchema("body.section.heading")
35
- .update("body.section.heading")
36
- .getByRole({ level: 4 })
37
- .getLocator();
38
-
39
- expect(`${optionsOnly}`).toEqual("getByRole('heading', { level: 4 })");
40
-
41
- const roleOnly = testFilters
42
- .getLocatorSchema("body.section.heading")
43
- .update("body.section.heading")
44
- .getByRole("heading")
45
- .getLocator();
46
-
47
- expect(`${roleOnly}`).toEqual("getByRole('heading', { level: 2 })");
48
-
49
- const roleAndOptions = testFilters
50
- .getLocatorSchema("body.section.heading")
51
- .update("body.section.heading")
52
- .getByRole("heading", { level: 5 })
53
- .getLocator();
54
-
55
- expect(`${roleAndOptions}`).toEqual("getByRole('heading', { level: 5 })");
56
- });
@@ -1,175 +0,0 @@
1
- import { expect, test } from "@fixtures-v2/testApp.fixtures";
2
-
3
- test("getNestedLocator fluent wrapper records filters and indices in call order", async ({ testFilters }) => {
4
- const chained = testFilters
5
- .getLocatorSchema("fictional.filter@hasText")
6
- .filter("fictional.filter@hasText", { hasText: "extra" })
7
- .nth("fictional.filter@hasText", 1)
8
- .getNestedLocator();
9
-
10
- expect(`${chained}`).toEqual(
11
- "getByRole('button').filter({ hasText: 'hasText' }).filter({ hasText: 'extra' }).nth(1)",
12
- );
13
- });
14
-
15
- test("getNestedLocator fluent wrapper supports update and clearSteps", async ({ testFilters }) => {
16
- const updated = testFilters
17
- .getLocatorSchema("body.section.heading")
18
- .update("body.section.heading")
19
- .getByRole("heading", { level: 3 })
20
- .getNestedLocator();
21
-
22
- expect(`${updated}`).toEqual("locator('body').locator('section').getByRole('heading', { level: 3 })");
23
-
24
- const cleared = testFilters
25
- .getLocatorSchema("fictional.filter@hasText")
26
- .clearSteps("fictional.filter@hasText")
27
- .getNestedLocator();
28
-
29
- expect(`${cleared}`).toEqual("getByRole('button')");
30
- });
31
-
32
- test("getNestedLocator fluent update can switch strategies without a terminator", async ({ testFilters }) => {
33
- const locator = testFilters
34
- .getLocatorSchema("body.section.heading")
35
- .update("body.section.heading")
36
- .getByText("Updated heading")
37
- .getNestedLocator();
38
-
39
- expect(`${locator}`).toEqual("locator('body').locator('section').getByText('Updated heading')");
40
- });
41
-
42
- test("getNestedLocator update accepts partial patch arguments", async ({ testFilters }) => {
43
- const baseline = testFilters.getNestedLocator("body.section.heading");
44
-
45
- expect(`${baseline}`).toEqual("locator('body').locator('section').getByRole('heading', { level: 2 })");
46
-
47
- const noArgs = testFilters
48
- .getLocatorSchema("body.section.heading")
49
- .update("body.section.heading")
50
- .getByRole()
51
- .getNestedLocator();
52
-
53
- expect(`${noArgs}`).toEqual("locator('body').locator('section').getByRole('heading', { level: 2 })");
54
-
55
- const optionsOnly = testFilters
56
- .getLocatorSchema("body.section.heading")
57
- .update("body.section.heading")
58
- .getByRole({ level: 4 })
59
- .getNestedLocator();
60
-
61
- expect(`${optionsOnly}`).toEqual("locator('body').locator('section').getByRole('heading', { level: 4 })");
62
-
63
- const roleOnly = testFilters
64
- .getLocatorSchema("body.section.heading")
65
- .update("body.section.heading")
66
- .getByRole("heading")
67
- .getNestedLocator();
68
-
69
- expect(`${roleOnly}`).toEqual("locator('body').locator('section').getByRole('heading', { level: 2 })");
70
-
71
- const roleAndOptions = testFilters
72
- .getLocatorSchema("body.section.heading")
73
- .update("body.section.heading")
74
- .getByRole("heading", { level: 5 })
75
- .getNestedLocator();
76
-
77
- expect(`${roleAndOptions}`).toEqual("locator('body').locator('section').getByRole('heading', { level: 5 })");
78
- });
79
-
80
- test("getNestedLocator resolves has/hasNot path string references", async ({ testFilters }) => {
81
- const nested = testFilters
82
- .getLocatorSchema("fictional.filter@hasNotText")
83
- .filter("fictional.filter@hasNotText", { has: "body.section.heading" })
84
- .filter("fictional.filter@hasNotText", { hasNot: "body.section@playground.button@red" })
85
- .getNestedLocator();
86
-
87
- expect(`${nested}`).toEqual(
88
- "getByRole('button').filter({ hasNotText: 'hasNotText' }).filter({ has: getByRole('heading', { level: 2 }) }).filter({ hasNot: getByRole('button', { name: 'Red' }) })",
89
- );
90
- });
91
-
92
- test("getNestedLocator resolves Playwright locators for has/hasNot", async ({ testFilters }) => {
93
- const sectionLocator = testFilters.page.locator("section");
94
- const missingLocator = testFilters.page.locator("[data-cy=missing]");
95
-
96
- const nested = testFilters
97
- .getLocatorSchema("fictional.filter@hasNotText")
98
- .filter("fictional.filter@hasNotText", { has: sectionLocator })
99
- .filter("fictional.filter@hasNotText", { hasNot: missingLocator })
100
- .getNestedLocator();
101
-
102
- expect(`${nested}`).toEqual(
103
- "getByRole('button').filter({ hasNotText: 'hasNotText' }).filter({ has: locator('section') }).filter({ hasNot: locator('[data-cy=missing]') })",
104
- );
105
- });
106
-
107
- const fullPath = "fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText" as const;
108
- const expectedChain =
109
- "getByRole('button').filter({ hasNotText: 'hasNotText' }).nth(2).getByRole('button').filter({ hasText: 'hasText' }).getByRole('button').filter({ hasNotText: 'hasNotText' }).getByRole('button').filter({ hasText: 'hasText' })";
110
-
111
- test("getNestedLocator applies chained indices", async ({ testFilters }) => {
112
- const locator = testFilters.getLocatorSchema(fullPath).nth("fictional.filter@hasNotText", 2).getNestedLocator();
113
-
114
- expect(`${locator}`).toContain(".nth(2)");
115
- expect(`${locator}`).toEqual(expectedChain);
116
- });
117
-
118
- test("getLocatorSchema.getNestedLocator applies chained indices", async ({ testFilters }) => {
119
- const locator = testFilters.getLocatorSchema(fullPath).nth("fictional.filter@hasNotText", 2).getNestedLocator();
120
-
121
- expect(`${locator}`).toContain(".nth(2)");
122
- expect(`${locator}`).toEqual(expectedChain);
123
- });
124
-
125
- test("getNestedLocator honors chained filters and indices", async ({ testFilters }) => {
126
- const locator = testFilters
127
- .getLocatorSchema("fictional.filter@hasText")
128
- .filter("fictional.filter@hasText", { hasText: "extra" })
129
- .nth("fictional.filter@hasText", 1)
130
- .filter("fictional.filter@hasText", { hasNotText: "tail" })
131
- .getNestedLocator();
132
-
133
- expect(`${locator}`).toEqual(
134
- "getByRole('button').filter({ hasText: 'hasText' }).filter({ hasText: 'extra' }).nth(1).filter({ hasNotText: 'tail' })",
135
- );
136
- });
137
-
138
- test("getNestedLocator supports explicit last() selection", async ({ testFilters }) => {
139
- const locator = testFilters.getLocatorSchema(fullPath).nth("fictional.filter@hasNotText", "last").getNestedLocator();
140
-
141
- expect(`${locator}`).toEqual(
142
- "getByRole('button').filter({ hasNotText: 'hasNotText' }).last().getByRole('button').filter({ hasText: 'hasText' }).getByRole('button').filter({ hasNotText: 'hasNotText' }).getByRole('button').filter({ hasText: 'hasText' })",
143
- );
144
- });
145
-
146
- test('getNestedLocator accepts "first" and "last" selections', async ({ testFilters }) => {
147
- const locator = testFilters
148
- .getLocatorSchema(fullPath)
149
- .nth("fictional.filter@hasNotText", "first")
150
- .nth("fictional.filter@hasNotText.filter@hasText.filter@hasNotText", "last")
151
- .getNestedLocator();
152
-
153
- expect(`${locator}`).toContain("first()");
154
- expect(`${locator}`).toContain("last()");
155
- });
156
-
157
- test("getNestedLocator rejects chained steps for unknown sub-paths", async ({ testFilters }) => {
158
- expect(() => {
159
- // @ts-expect-error Testing invalid argument
160
- testFilters.getLocatorSchema(fullPath).nth("fictional", 1).getNestedLocator();
161
- }).toThrow(
162
- '"fictional" is not a valid sub-path of "fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText".',
163
- );
164
- });
165
-
166
- test("getLocatorSchema.getNestedLocator rejects chained steps for partially matching paths", async ({
167
- testFilters,
168
- }) => {
169
- expect(() => {
170
- // @ts-expect-error Testing invalid argument
171
- testFilters.getLocatorSchema(fullPath).nth("fictional.filter@has", 1).getNestedLocator();
172
- }).toThrow(
173
- '"fictional.filter@has" is not a valid sub-path of "fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText".',
174
- );
175
- });
@@ -1,60 +0,0 @@
1
- import { expect, test } from "@fixtures-v2/testApp.fixtures";
2
-
3
- test("nth applies numeric index to targeted sub-path", async ({ testFilters }) => {
4
- const locator = testFilters.getLocatorSchema("body.section.button").nth("body.section.button", 1).getNestedLocator();
5
-
6
- expect(`${locator}`).toEqual("locator('body').locator('section').getByRole('button').nth(1)");
7
- });
8
-
9
- test('nth supports "first" and "last" selectors', async ({ testFilters }) => {
10
- const first = testFilters
11
- .getLocatorSchema("body.section.button")
12
- .nth("body.section.button", "first")
13
- .getNestedLocator();
14
-
15
- expect(`${first}`).toEqual("locator('body').locator('section').getByRole('button').first()");
16
-
17
- const last = testFilters
18
- .getLocatorSchema("body.section.button")
19
- .nth("body.section.button", "last")
20
- .getNestedLocator();
21
-
22
- expect(`${last}`).toEqual("locator('body').locator('section').getByRole('button').last()");
23
- });
24
-
25
- test("nth can target intermediate sub-paths", async ({ testFilters }) => {
26
- const locator = testFilters
27
- .getLocatorSchema("body.section.heading")
28
- .nth("body", 0)
29
- .nth("body.section", 1)
30
- .nth("body.section.heading", -1)
31
- .getNestedLocator();
32
-
33
- expect(`${locator}`).toEqual(
34
- "locator('body').first().locator('section').nth(1).getByRole('heading', { level: 2 }).last()",
35
- );
36
- });
37
-
38
- test("nth overrides are scoped to each builder instance", async ({ testFilters }) => {
39
- const builder = testFilters.getLocatorSchema("body.section.button");
40
- const first = builder.nth("body.section.button", "first").getNestedLocator();
41
- const second = testFilters.getLocatorSchema("body.section.button").getNestedLocator();
42
-
43
- expect(`${first}`).toEqual("locator('body').locator('section').getByRole('button').first()");
44
- expect(`${second}`).toEqual("locator('body').locator('section').getByRole('button')");
45
- });
46
-
47
- test("filter and nth ordering apply per sub-path when chained on getLocatorSchema", async ({ testFilters }) => {
48
- const locator = testFilters
49
- .getLocatorSchema("one.two")
50
- .clearSteps("one.two")
51
- .filter("one", { hasText: "outer" })
52
- .nth("one", "first")
53
- .filter("one.two", { hasText: "inner" })
54
- .nth("one.two", "last")
55
- .getNestedLocator();
56
-
57
- expect(`${locator}`).toEqual(
58
- "locator('div.one').filter({ hasText: 'outer' }).first().locator('div.two').filter({ hasText: 'inner' }).last()",
59
- );
60
- });