pomwright 1.5.1 → 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 (119) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +5 -5
  3. package/dist/index.d.mts +75 -970
  4. package/dist/index.d.ts +75 -970
  5. package/dist/index.js +585 -1872
  6. package/dist/index.mjs +598 -1880
  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 -695
  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.get.clone.spec.ts +0 -76
  58. package/intTestV2/tests/locatorRegistry/add/add.getByAltText.spec.ts +0 -23
  59. package/intTestV2/tests/locatorRegistry/add/add.getById.spec.ts +0 -45
  60. package/intTestV2/tests/locatorRegistry/add/add.getByLabel.spec.ts +0 -23
  61. package/intTestV2/tests/locatorRegistry/add/add.getByPlaceholder.spec.ts +0 -23
  62. package/intTestV2/tests/locatorRegistry/add/add.getByRole.spec.ts +0 -23
  63. package/intTestV2/tests/locatorRegistry/add/add.getByTestId.spec.ts +0 -23
  64. package/intTestV2/tests/locatorRegistry/add/add.getByText.spec.ts +0 -23
  65. package/intTestV2/tests/locatorRegistry/add/add.getByTitle.spec.ts +0 -23
  66. package/intTestV2/tests/locatorRegistry/add/add.locator.spec.ts +0 -23
  67. package/intTestV2/tests/locatorRegistry/add/add.reuseExisting.spec.ts +0 -107
  68. package/intTestV2/tests/locatorRegistry/add/add.reuseReusable.spec.ts +0 -311
  69. package/intTestV2/tests/locatorRegistry/add/add.spec.ts +0 -159
  70. package/intTestV2/tests/locatorRegistry/filter.cycle.spec.ts +0 -39
  71. package/intTestV2/tests/locatorRegistry/getLocator/getLocator.spec.ts +0 -253
  72. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.clearSteps.spec.ts +0 -105
  73. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.describe.spec.ts +0 -23
  74. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.filter.spec.ts +0 -368
  75. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.getLocator.spec.ts +0 -56
  76. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.getNestedLocator.spec.ts +0 -175
  77. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.nth.spec.ts +0 -60
  78. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.remove.spec.ts +0 -32
  79. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.replace.spec.ts +0 -24
  80. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.spec.ts +0 -110
  81. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.update.spec.ts +0 -322
  82. package/intTestV2/tests/locatorRegistry/getNestedLocator/getNestedLocator.spec.ts +0 -412
  83. package/intTestV2/tests/locatorRegistry/registry/registry.binding.spec.ts +0 -50
  84. package/intTestV2/tests/locatorRegistry/validation/validation.locatorSchemaPath.spec.ts +0 -115
  85. package/intTestV2/tests/locatorRegistry/validation/validation.locatorSchemaPath.typecheck.ts +0 -86
  86. package/intTestV2/tests/locatorRegistry/validation/validation.sub-path.spec.ts +0 -45
  87. package/intTestV2/tests/step/step.spec.ts +0 -49
  88. package/intTestV2/tests/testApp/color.spec.ts +0 -15
  89. package/intTestV2/tests/testApp/iframe.spec.ts +0 -57
  90. package/intTestV2/tests/testApp/testFilters.spec.ts +0 -24
  91. package/intTestV2/tests/testApp/testPage.spec.ts +0 -161
  92. package/intTestV2/tests/testApp/testPath.spec.ts +0 -18
  93. package/pack-build.sh +0 -11
  94. package/pack-test-v2.sh +0 -36
  95. package/playwright.base.ts +0 -42
  96. package/skills/README.md +0 -56
  97. package/skills/pomwright-v1-5-bridge-migration/SKILL.md +0 -40
  98. package/skills/pomwright-v1-5-bridge-migration/references/call-site-migration.md +0 -178
  99. package/skills/pomwright-v1-5-bridge-migration/references/schema-translation.md +0 -183
  100. package/skills/pomwright-v2-migration/SKILL.md +0 -63
  101. package/skills/pomwright-v2-migration/references/call-site-migration.md +0 -265
  102. package/skills/pomwright-v2-migration/references/class-migration.md +0 -266
  103. package/skills/pomwright-v2-migration/references/fixture-and-helpers.md +0 -423
  104. package/skills/pomwright-v2-migration/references/locator-registration.md +0 -344
  105. package/srcV2/fixture/base.fixtures.ts +0 -23
  106. package/srcV2/helpers/navigation.ts +0 -153
  107. package/srcV2/helpers/playwrightReportLogger.ts +0 -196
  108. package/srcV2/helpers/sessionStorage.ts +0 -251
  109. package/srcV2/helpers/stepDecorator.ts +0 -106
  110. package/srcV2/locators/index.ts +0 -15
  111. package/srcV2/locators/locatorQueryBuilder.ts +0 -427
  112. package/srcV2/locators/locatorRegistrationBuilder.ts +0 -558
  113. package/srcV2/locators/locatorRegistry.ts +0 -583
  114. package/srcV2/locators/locatorUpdateBuilder.ts +0 -602
  115. package/srcV2/locators/reusableLocatorBuilder.ts +0 -200
  116. package/srcV2/locators/types.ts +0 -256
  117. package/srcV2/locators/utils.ts +0 -309
  118. package/srcV2/locators/v1SchemaTranslator.ts +0 -178
  119. 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
- });