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,412 +0,0 @@
1
- import { expect, test } from "@fixtures-v2/testApp.fixtures";
2
- import type { Page } from "@playwright/test";
3
- import { LocatorRegistryInternal } from "../../../../srcV2/locators";
4
-
5
- const createTestRegistry = <Paths extends string>(page: Page) => new LocatorRegistryInternal<Paths>(page);
6
-
7
- test("getNestedLocator should resolve chained locators automatically", async ({ page }) => {
8
- type LocatorSchemaPaths =
9
- | "topMenu"
10
- | "topMenu.notifications"
11
- | "topMenu.notifications.dropdown"
12
- | "topMenu.notifications.dropdown.item";
13
-
14
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
15
-
16
- registry.add("topMenu").locator(".w3-top");
17
- registry.add("topMenu.notifications").locator(".w3-dropdown-hover");
18
- registry.add("topMenu.notifications.dropdown").locator(".w3-dropdown-content");
19
- registry.add("topMenu.notifications.dropdown.item").locator(".w3-bar-item");
20
-
21
- const manuallyChained = page
22
- .locator(".w3-top")
23
- .locator(".w3-dropdown-hover")
24
- .locator(".w3-dropdown-content")
25
- .locator(".w3-bar-item");
26
-
27
- const automaticallyChained = registry.getNestedLocator("topMenu.notifications.dropdown.item");
28
-
29
- expect(`${automaticallyChained}`).toEqual(
30
- "locator('.w3-top').locator('.w3-dropdown-hover').locator('.w3-dropdown-content').locator('.w3-bar-item')",
31
- );
32
-
33
- expect(`${automaticallyChained}`).toEqual(`${manuallyChained}`);
34
- });
35
-
36
- test("getLocator returns the terminal locator while getNestedLocator builds the chain", async ({ page }) => {
37
- type LocatorSchemaPaths =
38
- | "topMenu"
39
- | "topMenu.notifications"
40
- | "topMenu.notifications.dropdown"
41
- | "topMenu.notifications.dropdown.item";
42
-
43
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
44
-
45
- registry.add("topMenu").locator(".w3-top");
46
- registry.add("topMenu.notifications").locator(".w3-dropdown-hover");
47
- registry.add("topMenu.notifications.dropdown").locator(".w3-dropdown-content");
48
- registry.add("topMenu.notifications.dropdown.item").locator(".w3-bar-item");
49
-
50
- const direct = registry.getLocator("topMenu.notifications.dropdown.item");
51
- const nested = registry.getNestedLocator("topMenu.notifications.dropdown.item");
52
-
53
- expect(`${direct}`).toEqual("locator('.w3-bar-item')");
54
- expect(`${nested}`).toEqual(
55
- "locator('.w3-top').locator('.w3-dropdown-hover').locator('.w3-dropdown-content').locator('.w3-bar-item')",
56
- );
57
- });
58
-
59
- test("getNestedLocator returns a fresh locator each time", async ({ page }) => {
60
- type LocatorSchemaPaths =
61
- | "topMenu"
62
- | "topMenu.notifications"
63
- | "topMenu.notifications.dropdown"
64
- | "topMenu.notifications.dropdown.item";
65
-
66
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
67
-
68
- registry.add("topMenu").locator(".w3-top");
69
- registry.add("topMenu.notifications").locator(".w3-dropdown-hover");
70
- registry.add("topMenu.notifications.dropdown").locator(".w3-dropdown-content");
71
- registry.add("topMenu.notifications.dropdown.item").locator(".w3-bar-item");
72
-
73
- const first = registry.getNestedLocator("topMenu.notifications.dropdown.item");
74
- const second = registry.getNestedLocator("topMenu.notifications.dropdown.item");
75
- expect(first).not.toBe(second);
76
- expect(`${first}`).toEqual(`${second}`);
77
- });
78
-
79
- test.describe("getNestedLocator for locatorSchema with filter property", () => {
80
- const cases = [
81
- {
82
- label: "role",
83
- register: (registry: LocatorRegistryInternal<"fictional.filter@undefined">) =>
84
- registry.add("fictional.filter@undefined").getByRole("button"),
85
- expected: "getByRole('button')",
86
- },
87
- {
88
- label: "text",
89
- register: (registry: LocatorRegistryInternal<"fictional.filter@undefined">) =>
90
- registry.add("fictional.filter@undefined").getByText("text"),
91
- expected: "getByText('text')",
92
- },
93
- {
94
- label: "label",
95
- register: (registry: LocatorRegistryInternal<"fictional.filter@undefined">) =>
96
- registry.add("fictional.filter@undefined").getByLabel("label"),
97
- expected: "getByLabel('label')",
98
- },
99
- {
100
- label: "placeholder",
101
- register: (registry: LocatorRegistryInternal<"fictional.filter@undefined">) =>
102
- registry.add("fictional.filter@undefined").getByPlaceholder("placeholder"),
103
- expected: "getByPlaceholder('placeholder')",
104
- },
105
- {
106
- label: "altText",
107
- register: (registry: LocatorRegistryInternal<"fictional.filter@undefined">) =>
108
- registry.add("fictional.filter@undefined").getByAltText("altText"),
109
- expected: "getByAltText('altText')",
110
- },
111
- {
112
- label: "title",
113
- register: (registry: LocatorRegistryInternal<"fictional.filter@undefined">) =>
114
- registry.add("fictional.filter@undefined").getByTitle("title"),
115
- expected: "getByTitle('title')",
116
- },
117
- {
118
- label: "locator",
119
- register: (registry: LocatorRegistryInternal<"fictional.filter@undefined">) =>
120
- registry.add("fictional.filter@undefined").locator("locator"),
121
- expected: "locator('locator')",
122
- },
123
- {
124
- label: "testId",
125
- register: (registry: LocatorRegistryInternal<"fictional.filter@undefined">) =>
126
- registry.add("fictional.filter@undefined").getByTestId("testId"),
127
- expected: "getByTestId('testId')",
128
- },
129
- {
130
- label: "id",
131
- register: (registry: LocatorRegistryInternal<"fictional.filter@undefined">) =>
132
- registry.add("fictional.filter@undefined").getById("id"),
133
- expected: "locator('#id')",
134
- },
135
- ];
136
-
137
- for (const { label, register, expected } of cases) {
138
- test(`definition ${label}: should apply filter`, async ({ page }) => {
139
- const registry = createTestRegistry<"fictional.filter@undefined">(page);
140
- register(registry);
141
-
142
- const nested = registry.getNestedLocator("fictional.filter@undefined");
143
- expect(`${nested}`).toEqual(expected);
144
- });
145
- }
146
-
147
- test("frameLocator keeps the chain inside the frame and skips frame-level filters", async ({ page }) => {
148
- type LocatorSchemaPaths =
149
- | "fictional.filter@hasNotText"
150
- | "fictional.filter@hasNotText.filter@hasText"
151
- | "fictional.filter@hasNotText.filter@hasText.filter@hasNotText";
152
-
153
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
154
-
155
- registry.add("fictional.filter@hasNotText").getByRole("button").filter({ hasNotText: "hasNotText" });
156
- registry.add("fictional.filter@hasNotText.filter@hasText").frameLocator('iframe[title="name"]');
157
- registry
158
- .add("fictional.filter@hasNotText.filter@hasText.filter@hasNotText")
159
- .getByRole("button", { name: "inside frame" })
160
- .filter({ hasNotText: "hasNotText" });
161
-
162
- const nested = registry.getNestedLocator("fictional.filter@hasNotText.filter@hasText.filter@hasNotText");
163
- const manualNested = page
164
- .getByRole("button")
165
- .filter({ hasNotText: "hasNotText" })
166
- .frameLocator('iframe[title="name"]')
167
- .getByRole("button", { name: "inside frame" })
168
- .filter({ hasNotText: "hasNotText" });
169
-
170
- expect(`${nested}`).toEqual(`${manualNested}`);
171
- });
172
-
173
- test("multiple nesting/chaining retains filters across the chain", async ({ page }) => {
174
- type LocatorSchemaPaths =
175
- | "fictional.filter@hasNotText"
176
- | "fictional.filter@hasNotText.filter@hasText"
177
- | "fictional.filter@hasNotText.filter@hasText.filter@hasNotText"
178
- | "fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText";
179
-
180
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
181
-
182
- registry
183
- .add("fictional.filter@hasNotText")
184
- .getByRole("button", { name: "roleOptions" })
185
- .filter({ hasNotText: "hasNotText" });
186
- registry
187
- .add("fictional.filter@hasNotText.filter@hasText")
188
- .locator("locator", {
189
- hasText: "locatorOptionsHasText",
190
- hasNotText: "locatorOptionshasNotText",
191
- })
192
- .filter({ hasText: "hasText" });
193
- registry
194
- .add("fictional.filter@hasNotText.filter@hasText.filter@hasNotText")
195
- .getByTestId("testId")
196
- .filter({ hasNotText: "hasNotText" });
197
- registry
198
- .add("fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText")
199
- .getByLabel("label", { exact: true })
200
- .filter({ hasText: "hasText" });
201
-
202
- const multiChain = registry.getNestedLocator(
203
- "fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText",
204
- );
205
-
206
- expect(`${multiChain}`).toEqual(
207
- "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' })",
208
- );
209
- });
210
-
211
- test("filter definitions that omit options stay stable", async ({ page }) => {
212
- type LocatorSchemaPaths = "fictional.filter@optionsUndefined";
213
-
214
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
215
-
216
- registry
217
- .add("fictional.filter@optionsUndefined")
218
- .getByRole("button")
219
- .filter({ has: undefined, hasNot: undefined, hasText: undefined, hasNotText: undefined });
220
-
221
- const nested = registry.getNestedLocator("fictional.filter@optionsUndefined");
222
- expect(`${nested}`).toEqual("getByRole('button')");
223
- });
224
-
225
- test("schema filters resolve path string references", async ({ page }) => {
226
- type LocatorSchemaPaths =
227
- | "body.section.heading"
228
- | "body.section.button"
229
- | "fictional.locatorAndOptionsWithfilter@allOptions";
230
-
231
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
232
-
233
- registry.add("body.section.heading").getByRole("heading", { level: 2 });
234
- registry.add("body.section.button").getByRole("button");
235
- registry
236
- .add("fictional.locatorAndOptionsWithfilter@allOptions")
237
- .getByRole("button", { name: "roleOptions" })
238
- .filter({
239
- has: "body.section.heading",
240
- hasNot: "body.section.button",
241
- hasText: "hasText",
242
- hasNotText: "hasNotText",
243
- });
244
-
245
- const nested = registry.getNestedLocator("fictional.locatorAndOptionsWithfilter@allOptions");
246
-
247
- const manual = page.getByRole("button", { name: "roleOptions" }).filter({
248
- has: page.getByRole("heading", { level: 2 }),
249
- hasNot: page.getByRole("button"),
250
- hasText: "hasText",
251
- hasNotText: "hasNotText",
252
- });
253
-
254
- expect(`${nested}`).toEqual(`${manual}`);
255
- expect(`${nested}`).toEqual(
256
- "getByRole('button', { name: 'roleOptions' }).filter({ hasText: 'hasText' }).filter({ hasNotText: 'hasNotText' }).filter({ has: getByRole('heading', { level: 2 }) }).filter({ hasNot: getByRole('button') })",
257
- );
258
- });
259
-
260
- test("getNestedLocator resolves has/hasNot path string references", async ({ page }) => {
261
- type LocatorSchemaPaths =
262
- | "body.section.heading"
263
- | "body.section@playground.button@red"
264
- | "fictional.filter@hasNotText";
265
-
266
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
267
-
268
- registry.add("body.section.heading").getByRole("heading", { level: 2 });
269
- registry.add("body.section@playground.button@red").getByRole("button", { name: "Red" });
270
- registry
271
- .add("fictional.filter@hasNotText")
272
- .getByRole("button")
273
- .filter({ hasNotText: "hasNotText" })
274
- .filter({ has: "body.section.heading" })
275
- .filter({ hasNot: "body.section@playground.button@red" });
276
-
277
- const nested = registry.getNestedLocator("fictional.filter@hasNotText");
278
-
279
- expect(`${nested}`).toEqual(
280
- "getByRole('button').filter({ hasNotText: 'hasNotText' }).filter({ has: getByRole('heading', { level: 2 }) }).filter({ hasNot: getByRole('button', { name: 'Red' }) })",
281
- );
282
- });
283
-
284
- });
285
-
286
- const fullPath = "fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText" as const;
287
- const expectedChain =
288
- "getByRole('button').filter({ hasNotText: 'hasNotText' }).nth(2).getByRole('button').filter({ hasText: 'hasText' }).getByRole('button').filter({ hasNotText: 'hasNotText' }).getByRole('button').filter({ hasText: 'hasText' })";
289
-
290
- test("getNestedLocator applies chained indices", async ({ page }) => {
291
- type LocatorSchemaPaths =
292
- | "fictional.filter@hasNotText"
293
- | "fictional.filter@hasNotText.filter@hasText"
294
- | "fictional.filter@hasNotText.filter@hasText.filter@hasNotText"
295
- | "fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText";
296
-
297
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
298
-
299
- registry.add("fictional.filter@hasNotText").getByRole("button").filter({ hasNotText: "hasNotText" }).nth(2);
300
- registry.add("fictional.filter@hasNotText.filter@hasText").getByRole("button").filter({ hasText: "hasText" });
301
- registry
302
- .add("fictional.filter@hasNotText.filter@hasText.filter@hasNotText")
303
- .getByRole("button")
304
- .filter({ hasNotText: "hasNotText" });
305
- registry
306
- .add("fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText")
307
- .getByRole("button")
308
- .filter({ hasText: "hasText" });
309
-
310
- const locator = registry.getNestedLocator(fullPath);
311
-
312
- expect(`${locator}`).toContain(".nth(2)");
313
- expect(`${locator}`).toEqual(expectedChain);
314
- });
315
-
316
- test("getNestedLocator honors chained filters and indices", async ({ page }) => {
317
- type LocatorSchemaPaths = "fictional.filter@hasText";
318
-
319
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
320
-
321
- registry
322
- .add("fictional.filter@hasText")
323
- .getByRole("button")
324
- .filter({ hasText: "hasText" })
325
- .filter({ hasText: "extra" })
326
- .nth(1)
327
- .filter({ hasNotText: "tail" });
328
-
329
- const locator = registry.getNestedLocator("fictional.filter@hasText");
330
-
331
- expect(`${locator}`).toEqual(
332
- "getByRole('button').filter({ hasText: 'hasText' }).filter({ hasText: 'extra' }).nth(1).filter({ hasNotText: 'tail' })",
333
- );
334
- });
335
-
336
- test("getNestedLocator supports explicit last() selection", async ({ page }) => {
337
- type LocatorSchemaPaths =
338
- | "fictional.filter@hasNotText"
339
- | "fictional.filter@hasNotText.filter@hasText"
340
- | "fictional.filter@hasNotText.filter@hasText.filter@hasNotText"
341
- | "fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText";
342
-
343
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
344
-
345
- registry.add("fictional.filter@hasNotText").getByRole("button").filter({ hasNotText: "hasNotText" }).nth("last");
346
- registry.add("fictional.filter@hasNotText.filter@hasText").getByRole("button").filter({ hasText: "hasText" });
347
- registry
348
- .add("fictional.filter@hasNotText.filter@hasText.filter@hasNotText")
349
- .getByRole("button")
350
- .filter({ hasNotText: "hasNotText" });
351
- registry
352
- .add("fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText")
353
- .getByRole("button")
354
- .filter({ hasText: "hasText" });
355
-
356
- const locator = registry.getNestedLocator(fullPath);
357
-
358
- expect(`${locator}`).toEqual(
359
- "getByRole('button').filter({ hasNotText: 'hasNotText' }).last().getByRole('button').filter({ hasText: 'hasText' }).getByRole('button').filter({ hasNotText: 'hasNotText' }).getByRole('button').filter({ hasText: 'hasText' })",
360
- );
361
- });
362
-
363
- test('getNestedLocator accepts "first" and "last" selections', async ({ page }) => {
364
- type LocatorSchemaPaths =
365
- | "fictional.filter@hasNotText"
366
- | "fictional.filter@hasNotText.filter@hasText"
367
- | "fictional.filter@hasNotText.filter@hasText.filter@hasNotText"
368
- | "fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText";
369
-
370
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
371
-
372
- registry.add("fictional.filter@hasNotText").getByRole("button").filter({ hasNotText: "hasNotText" }).nth("first");
373
- registry.add("fictional.filter@hasNotText.filter@hasText").getByRole("button").filter({ hasText: "hasText" });
374
- registry
375
- .add("fictional.filter@hasNotText.filter@hasText.filter@hasNotText")
376
- .getByRole("button")
377
- .filter({ hasNotText: "hasNotText" })
378
- .nth("last");
379
- registry
380
- .add("fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText")
381
- .getByRole("button")
382
- .filter({ hasText: "hasText" });
383
-
384
- const locator = registry.getNestedLocator(fullPath);
385
-
386
- expect(`${locator}`).toContain("first()");
387
- expect(`${locator}`).toContain("last()");
388
- });
389
-
390
- test("getNestedLocator rejects unknown paths", async ({ page }) => {
391
- type LocatorSchemaPaths = "fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText";
392
-
393
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
394
-
395
- registry.add("fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText").getByRole("button");
396
-
397
- // @ts-expect-error Testing invalid argument
398
- expect(() => registry.getNestedLocator("fictional")).toThrow('No locator schema registered for path "fictional".');
399
- });
400
-
401
- test("getNestedLocator rejects partially matching paths", async ({ page }) => {
402
- type LocatorSchemaPaths = "fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText";
403
-
404
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
405
-
406
- registry.add("fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText").getByRole("button");
407
-
408
- // @ts-expect-error Testing invalid argument
409
- expect(() => registry.getNestedLocator("fictional.filter@has")).toThrow(
410
- 'No locator schema registered for path "fictional.filter@has".',
411
- );
412
- });
@@ -1,50 +0,0 @@
1
- import { expect, test } from "@fixtures-v2/testApp.fixtures";
2
- import type { Page } from "@playwright/test";
3
- import { createRegistryWithAccessors } from "pomwright";
4
-
5
- const createRegistry = <Paths extends string>(page: Page) => createRegistryWithAccessors<Paths>(page);
6
-
7
- test("createRegistryWithAccessors creates isolated bound wrappers per registry", async ({ page }) => {
8
- type LocatorSchemaPaths = "main";
9
-
10
- const registryA = createRegistry<LocatorSchemaPaths>(page);
11
- registryA.registry.add("main").locator("div.a");
12
-
13
- const registryB = createRegistry<LocatorSchemaPaths>(page);
14
- registryB.registry.add("main").locator("div.b");
15
-
16
- const locatorA = registryA.getNestedLocator("main");
17
- const locatorB = registryB.getNestedLocator("main");
18
-
19
- expect(`${locatorA}`).toEqual("locator('div.a')");
20
- expect(`${locatorB}`).toEqual("locator('div.b')");
21
- });
22
-
23
- test("factory-based wrappers preserve fluent helpers without BasePage", async ({ page }) => {
24
- type LocatorSchemaPaths = "chain" | "chain.child";
25
-
26
- const { registry, getNestedLocator } = createRegistry<LocatorSchemaPaths>(page);
27
- registry.add("chain").locator("div.root");
28
- registry.add("chain.child").locator("div.child").filter({ hasText: "x" }).nth(1).filter({ hasText: "y" });
29
-
30
- const locator = getNestedLocator("chain.child");
31
-
32
- expect(`${locator}`).toEqual(
33
- "locator('div.root').locator('div.child').filter({ hasText: 'x' }).nth(1).filter({ hasText: 'y' })",
34
- );
35
- });
36
-
37
- test("createRegistryWithAccessors exposes getLocatorSchema builder", async ({ page }) => {
38
- type LocatorSchemaPaths = "tree" | "tree.leaf";
39
-
40
- const { registry, getLocatorSchema } = createRegistry<LocatorSchemaPaths>(page);
41
- registry.add("tree").locator("div.tree");
42
- registry.add("tree.leaf").locator("div.leaf");
43
-
44
- const schemaBuilder = getLocatorSchema("tree.leaf");
45
- schemaBuilder.nth("tree", 1).filter("tree.leaf", { hasText: "leaf" });
46
-
47
- const locator = schemaBuilder.getNestedLocator();
48
-
49
- expect(`${locator}`).toEqual("locator('div.tree').nth(1).locator('div.leaf').filter({ hasText: 'leaf' })");
50
- });
@@ -1,115 +0,0 @@
1
- import { expect, test } from "@fixtures-v2/testApp.fixtures.js";
2
- import type { Page } from "@playwright/test";
3
- import { createRegistryWithAccessors } from "pomwright";
4
- import { formatLocatorSchemaPathForError } from "../../../../srcV2/locators/utils.js";
5
-
6
- const createTestRegistry = <Paths extends string>(page: Page) => createRegistryWithAccessors<Paths>(page).registry;
7
-
8
- test("flags invalid LocatorSchemaPaths at compile time and rejects them at runtime", async ({ page }) => {
9
- type Paths =
10
- | "valid"
11
- | "valid.path"
12
- | "valid.path@special-characters!$&'()*+,-./:;=?^_`{|}~"
13
- // invalid paths below:
14
- | "" // empty string
15
- | ".leading" // leading dot
16
- | "a..b" // consecutive dots
17
- | "trailing." // trailing dot
18
- | " " // SPACE
19
- | "a b" // SPACE anywhere
20
- | "a\u0020b" // SPACE unicode
21
- | "\t" // CHARACTER TABULATION
22
- | "\n" // LINE FEED
23
- | "\v" // LINE TABULATION
24
- | "\f" // FORM FEED
25
- | "\r" // CARRIAGE RETURN
26
- | "a\u0085b" // NEXT LINE: "\u0085"
27
- | " " // NO-BREAK SPACE
28
- | " " // OGHAM SPACE MARK
29
- | " " // EN QUAD
30
- | " " // EM QUAD
31
- | " " // EN SPACE
32
- | " " // EM SPACE
33
- | " " // THREE-PER-EM SPACE
34
- | " " // FOUR-PER-EM SPACE
35
- | " " // SIX-PER-EM SPACE
36
- | " " // FIGURE SPACE
37
- | " " // PUNCTUATION SPACE
38
- | " " // THIN SPACE
39
- | " " // HAIR SPACE
40
- | "\u2028" // LINE SEPARATOR
41
- | "\u2029" // PARAGRAPH SEPARATOR
42
- | " " // NARROW NO-BREAK SPACE
43
- | " " // MEDIUM MATHEMATICAL SPACE
44
- | " "; // IDEOGRAPHIC SPACE
45
-
46
- // Compile-time errors expected for invalid paths above,
47
- // but they won't fail the test run; they are reported in the IDE
48
- // via the createTestRegistry<Paths> helper and as a result NOT listed/suggested on registry.add(...) calls below.
49
- const registry = createTestRegistry<Paths>(page);
50
-
51
- // Valid paths should work fine at runtime:
52
- registry.add("valid").locator("body");
53
- registry.add("valid.path").locator("div.class");
54
- registry.add("valid.path@special-characters!$&'()*+,-./:;=?^_`{|}~").locator("span#id");
55
-
56
- // --- Runtime errors for non-whitespace structural rules ---
57
-
58
- // @ts-expect-error: testing compile-time validation
59
- expect(() => registry.add("").locator("invalid")).toThrow("LocatorSchemaPath string cannot be empty");
60
-
61
- // @ts-expect-error: testing compile-time validation
62
- expect(() => registry.add(".leading").locator("invalid")).toThrow(
63
- "LocatorSchemaPath string cannot start with a dot: .leading",
64
- );
65
-
66
- // @ts-expect-error: testing compile-time validation
67
- expect(() => registry.add("a..b").locator("invalid")).toThrow(
68
- "LocatorSchemaPath string cannot contain consecutive dots: a..b",
69
- );
70
-
71
- // @ts-expect-error: testing compile-time validation
72
- expect(() => registry.add("trailing.").locator("invalid")).toThrow(
73
- "LocatorSchemaPath string cannot end with a dot: trailing.",
74
- );
75
-
76
- // --- Runtime errors for all whitespace-related invalid paths ---
77
-
78
- const whitespacePaths = [
79
- " ", // SPACE
80
- "a b", // SPACE anywhere
81
- "a\u0020b", // SPACE unicode
82
- "\t", // CHARACTER TABULATION
83
- "\n", // LINE FEED
84
- "\v", // LINE TABULATION
85
- "\f", // FORM FEED
86
- "\r", // CARRIAGE RETURN
87
- "a\u0085b", // NEXT LINE: "\u0085"
88
- " ", // NO-BREAK SPACE
89
- " ", // OGHAM SPACE MARK
90
- " ", // EN QUAD
91
- " ", // EM QUAD
92
- " ", // EN SPACE
93
- " ", // EM SPACE
94
- " ", // THREE-PER-EM SPACE
95
- " ", // FOUR-PER-EM SPACE
96
- " ", // SIX-PER-EM SPACE
97
- " ", // FIGURE SPACE
98
- " ", // PUNCTUATION SPACE
99
- " ", // THIN SPACE
100
- " ", // HAIR SPACE
101
- "\u2028", // LINE SEPARATOR
102
- "\u2029", // PARAGRAPH SEPARATOR
103
- " ", // NARROW NO-BREAK SPACE
104
- " ", // MEDIUM MATHEMATICAL SPACE
105
- " ", // IDEOGRAPHIC SPACE
106
- ] as const;
107
-
108
- for (const path of whitespacePaths) {
109
- const escaped = formatLocatorSchemaPathForError(path);
110
- // @ts-expect-error: testing compile-time validation of whitespace in paths
111
- expect(() => registry.add(path).locator("invalid")).toThrow(
112
- `LocatorSchemaPath string cannot contain whitespace chars: ${escaped}`,
113
- );
114
- }
115
- });
@@ -1,86 +0,0 @@
1
- import type { Page } from "@playwright/test";
2
- import { createRegistryWithAccessors } from "pomwright";
3
-
4
- declare const page: Page;
5
-
6
- type ValidPaths = "valid" | "validReuse";
7
-
8
- const validRegistryFactoryResult = createRegistryWithAccessors<ValidPaths>(page);
9
-
10
- validRegistryFactoryResult.add("valid").locator("body");
11
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
12
- validRegistryFactoryResult.add("does.not.exist").locator("body");
13
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
14
- validRegistryFactoryResult.add("valid", { reuse: "does.not.exist" });
15
- // @ts-expect-error reuse path cannot be the same as registration path
16
- validRegistryFactoryResult.add("validReuse", { reuse: "validReuse" });
17
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
18
- validRegistryFactoryResult.getLocator("does.not.exist");
19
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
20
- validRegistryFactoryResult.getLocatorSchema("does.not.exist");
21
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
22
- validRegistryFactoryResult.getNestedLocator("does.not.exist");
23
-
24
- type InvalidPaths = ValidPaths | "" | ".leading" | "trailing." | "double..dot" | "contains whitespace";
25
-
26
- const invalidRegistryFactoryResult = createRegistryWithAccessors<InvalidPaths>(page);
27
-
28
- invalidRegistryFactoryResult.add("valid").locator("body");
29
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
30
- invalidRegistryFactoryResult.add("").locator("body");
31
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
32
- invalidRegistryFactoryResult.add(".leading").locator("body");
33
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
34
- invalidRegistryFactoryResult.add("trailing.").locator("body");
35
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
36
- invalidRegistryFactoryResult.add("double..dot").locator("body");
37
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
38
- invalidRegistryFactoryResult.add("contains whitespace").locator("body");
39
-
40
- invalidRegistryFactoryResult.add("validReuse", { reuse: "valid" });
41
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
42
- invalidRegistryFactoryResult.add("validReuse", { reuse: "" });
43
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
44
- invalidRegistryFactoryResult.add("validReuse", { reuse: ".leading" });
45
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
46
- invalidRegistryFactoryResult.add("validReuse", { reuse: "trailing." });
47
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
48
- invalidRegistryFactoryResult.add("validReuse", { reuse: "double..dot" });
49
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
50
- invalidRegistryFactoryResult.add("validReuse", { reuse: "contains whitespace" });
51
-
52
- invalidRegistryFactoryResult.getLocator("valid");
53
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
54
- invalidRegistryFactoryResult.getLocator("");
55
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
56
- invalidRegistryFactoryResult.getLocator(".leading");
57
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
58
- invalidRegistryFactoryResult.getLocator("trailing.");
59
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
60
- invalidRegistryFactoryResult.getLocator("double..dot");
61
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
62
- invalidRegistryFactoryResult.getLocator("contains whitespace");
63
-
64
- invalidRegistryFactoryResult.getLocatorSchema("valid");
65
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
66
- invalidRegistryFactoryResult.getLocatorSchema("");
67
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
68
- invalidRegistryFactoryResult.getLocatorSchema(".leading");
69
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
70
- invalidRegistryFactoryResult.getLocatorSchema("trailing.");
71
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
72
- invalidRegistryFactoryResult.getLocatorSchema("double..dot");
73
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
74
- invalidRegistryFactoryResult.getLocatorSchema("contains whitespace");
75
-
76
- invalidRegistryFactoryResult.getNestedLocator("valid");
77
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
78
- invalidRegistryFactoryResult.getNestedLocator("");
79
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
80
- invalidRegistryFactoryResult.getNestedLocator(".leading");
81
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
82
- invalidRegistryFactoryResult.getNestedLocator("trailing.");
83
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
84
- invalidRegistryFactoryResult.getNestedLocator("double..dot");
85
- // @ts-expect-error invalid path literal should fail at the accessor argument with path-format details
86
- invalidRegistryFactoryResult.getNestedLocator("contains whitespace");