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,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,45 +0,0 @@
1
- import { expect, test } from "@fixtures-v2/testApp.fixtures";
2
-
3
- const chainedPath = "fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText" as const;
4
-
5
- test.describe("sub-path validation", () => {
6
- test("addFilter rejects invalid root-level sub-paths", ({ testFilters }) => {
7
- expect(() =>
8
- testFilters
9
- .getLocatorSchema("fictional.filter@hasNotText.filter@hasText")
10
- // @ts-expect-error Testing invalid path handling
11
- .filter("fictional", { hasText: "nope" }),
12
- ).toThrow('"fictional" is not a valid sub-path of "fictional.filter@hasNotText.filter@hasText".');
13
- });
14
-
15
- test("addFilter rejects invalid intermediate sub-paths", ({ testFilters }) => {
16
- expect(() =>
17
- testFilters
18
- .getLocatorSchema(chainedPath)
19
- // @ts-expect-error Testing invalid path handling
20
- .filter("fictional.filter@hasNotText.filter@missing", { hasText: "nope" }),
21
- ).toThrow(
22
- '"fictional.filter@hasNotText.filter@missing" is not a valid sub-path of "fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText".',
23
- );
24
- });
25
-
26
- test("update rejects missing/invalid subPaths", ({ testFilters }) => {
27
- expect(() =>
28
- testFilters
29
- .getLocatorSchema("fictional.filter@hasNotText.filter@hasText")
30
- // @ts-expect-error Testing invalid path handling
31
- .update("fictional.filter@missing")
32
- .getByText("nope"),
33
- ).toThrow('"fictional.filter@missing" is not a valid sub-path of "fictional.filter@hasNotText.filter@hasText".');
34
- });
35
-
36
- test("update rejects a valid LocatorSchemaPath when it's an invalid subPath", ({ testFilters }) => {
37
- expect(() =>
38
- testFilters
39
- .getLocatorSchema("body.section.heading")
40
- // @ts-expect-error Testing invalid path handling
41
- .update("body.section.button")
42
- .locator("noop"),
43
- ).toThrow('"body.section.button" is not a valid sub-path of "body.section.heading"');
44
- });
45
- });
@@ -1,49 +0,0 @@
1
- import { expect, test } from "@fixtures-v2/testApp.fixtures";
2
-
3
- // These tests need to be verified manually by checking the test report
4
-
5
- test("stepNoArgs should return the expected message", async ({ testPage }) => {
6
- await expect(testPage.stepNoArgs()).resolves.toBe("Hello, World!");
7
- });
8
-
9
- test("stepWithTitle should return the expected message", async ({ testPage }) => {
10
- await expect(testPage.stepWithTitle()).resolves.toBe("Hello, World!");
11
- });
12
-
13
- test("stepWithOptionBox should return the expected message", async ({ testPage }) => {
14
- await expect(testPage.stepWithOptionBox()).resolves.toBe("Hello, World!");
15
- });
16
-
17
- test("stepWithOptionTimeout should return the expected message", async ({ testPage }) => {
18
- await expect(testPage.stepWithOptionTimeout()).resolves.toBe("Hello, World!");
19
- });
20
-
21
- test("stepWithOptionLocation should return the expected message", async ({ testPage }) => {
22
- await expect(testPage.stepWithOptionLocation()).resolves.toBe("Hello, World!");
23
- });
24
-
25
- test("stepWithTitleAndAllOptions should return the expected message", async ({ testPage }) => {
26
- await expect(testPage.stepWithTitleAndAllOptions()).resolves.toBe("Hello, World!");
27
- });
28
-
29
- test("stepWithArgs should support required/default parameters", async ({ testPage }) => {
30
- await expect(testPage.stepWithArgs("Starter"))
31
- .resolves.toBe("Starter:1");
32
- await expect(testPage.stepWithArgs("Starter", 3)).resolves.toBe("Starter:3");
33
- });
34
-
35
- test("stepWithArgsAndObjectReturn should preserve typed argument/return flows", async ({ testPage }) => {
36
- await expect(testPage.stepWithArgsAndObjectReturn({ id: "abc" })).resolves.toEqual({
37
- ok: true,
38
- id: "abc",
39
- mode: "basic",
40
- retry: 0,
41
- });
42
-
43
- await expect(testPage.stepWithArgsAndObjectReturn({ id: "xyz", mode: "advanced" }, 2)).resolves.toEqual({
44
- ok: true,
45
- id: "xyz",
46
- mode: "advanced",
47
- retry: 2,
48
- });
49
- });