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,583 +0,0 @@
1
- import type { Locator, Page } from "@playwright/test";
2
- import { LocatorQueryBuilder, type LocatorQueryBuilderPublic } from "./locatorQueryBuilder";
3
- import {
4
- LocatorRegistrationBuilder,
5
- type LocatorRegistrationPreDefinitionBuilder,
6
- type LocatorRegistrationSeededBuilderForType,
7
- } from "./locatorRegistrationBuilder";
8
- import { ReusableLocatorFactory } from "./reusableLocatorBuilder";
9
- import type {
10
- FilterDefinition,
11
- FilterLocatorReference,
12
- IndexSelector,
13
- LocatorBuilderTarget,
14
- LocatorDescription,
15
- LocatorSchemaPathFormat,
16
- LocatorSchemaRecord,
17
- LocatorStep,
18
- LocatorStrategyDefinition,
19
- PlaywrightFilterDefinition,
20
- RegistryPath,
21
- ResolvedFilterDefinition,
22
- ReusableLocator,
23
- } from "./types";
24
- import {
25
- applyIndexSelector,
26
- cloneLocatorStrategyDefinition,
27
- createLocator,
28
- expandSchemaPath,
29
- isFrameLocatorDefinition,
30
- isLocatorInstance,
31
- normalizeSteps,
32
- stringifyForLog,
33
- validateLocatorSchemaPath,
34
- } from "./utils";
35
-
36
- type PathArgument<Paths extends string, Path extends Paths> = LocatorSchemaPathFormat<Path> extends Path
37
- ? Path
38
- : LocatorSchemaPathFormat<Path>;
39
-
40
- type InvalidReusePath<Path extends string> = [
41
- `Invalid reuse path, reuse path cannot be the same as registration path: ${Path}`,
42
- ];
43
-
44
- type ReusePathArgument<Paths extends string, Path extends Paths, ReusePath extends Paths> = Exclude<
45
- ReusePath,
46
- Path
47
- > extends never
48
- ? InvalidReusePath<Path>
49
- : PathArgument<Paths, ReusePath>;
50
-
51
- export class LocatorRegistryInternal<LocatorSchemaPathType extends string> {
52
- private readonly schemas = new Map<
53
- RegistryPath<LocatorSchemaPathType>,
54
- LocatorSchemaRecord<LocatorSchemaPathType, RegistryPath<LocatorSchemaPathType>>
55
- >();
56
-
57
- /**
58
- * Factory for reusable locator seeds that capture a locator strategy plus any chained
59
- * `filter`/`nth` steps without registering them. Pass the resulting seed to
60
- * {@link LocatorRegistryInternal.add} via `{ reuse }` to register a path that inherits the
61
- * stored definition and steps.
62
- *
63
- * @example
64
- * ```ts
65
- * const seed = registry.createReusable.getByRole("heading", { level: 2 }).filter({ hasText: /Summary/ });
66
- * registry.add("hero.title", { reuse: seed }).getByRole({ name: "Summary" });
67
- * ```
68
- */
69
- readonly createReusable: ReusableLocatorFactory<LocatorSchemaPathType>;
70
-
71
- constructor(private readonly page: Page) {
72
- this.createReusable = new ReusableLocatorFactory<LocatorSchemaPathType>();
73
- }
74
-
75
- private normalizeRecord(record: LocatorSchemaRecord<LocatorSchemaPathType, RegistryPath<LocatorSchemaPathType>>) {
76
- return {
77
- locatorSchemaPath: record.locatorSchemaPath,
78
- definition: record.definition,
79
- steps: normalizeSteps<RegistryPath<LocatorSchemaPathType>, RegistryPath<LocatorSchemaPathType>>(record.steps),
80
- ...(record.description !== undefined ? { description: record.description } : {}),
81
- } satisfies LocatorSchemaRecord<LocatorSchemaPathType, RegistryPath<LocatorSchemaPathType>>;
82
- }
83
-
84
- private cloneRecordForReuse(
85
- record: LocatorSchemaRecord<LocatorSchemaPathType, RegistryPath<LocatorSchemaPathType>>,
86
- path: RegistryPath<LocatorSchemaPathType>,
87
- ) {
88
- return {
89
- locatorSchemaPath: path,
90
- definition: cloneLocatorStrategyDefinition(record.definition),
91
- steps: normalizeSteps<LocatorSchemaPathType, RegistryPath<LocatorSchemaPathType>>(record.steps),
92
- ...(record.description !== undefined ? { description: record.description } : {}),
93
- } satisfies LocatorSchemaRecord<LocatorSchemaPathType, RegistryPath<LocatorSchemaPathType>>;
94
- }
95
-
96
- /**
97
- * Registers a locator schema at the provided dot-delimited path.
98
- * Accepts exactly one locator strategy (`getByRole`, `locator`, etc.) and any number of
99
- * `filter`/`nth` steps chained in call order. When `{ reuse }` is supplied, the seeded
100
- * definition is applied first and one matching override is allowed as a PATCH of the seed;
101
- * otherwise, calling multiple locator strategies will throw.
102
- *
103
- * @example
104
- * ```ts
105
- * registry
106
- * .add("list.item")
107
- * .getByRole("listitem", { name: /Row/ })
108
- * .filter({ hasText: "Row" })
109
- * .nth("last");
110
- * ```
111
- */
112
- add<
113
- Path extends LocatorSchemaPathType,
114
- Reuse extends
115
- | LocatorSchemaPathType
116
- | ReusableLocator<LocatorSchemaPathType, RegistryPath<LocatorSchemaPathType>, LocatorStrategyDefinition["type"]>
117
- | undefined = undefined,
118
- >(
119
- path: PathArgument<LocatorSchemaPathType, Path>,
120
- ...args: Reuse extends undefined
121
- ? []
122
- : [
123
- options: {
124
- reuse: Reuse extends LocatorSchemaPathType
125
- ? ReusePathArgument<LocatorSchemaPathType, Path, Reuse>
126
- : Extract<
127
- Reuse,
128
- ReusableLocator<
129
- LocatorSchemaPathType,
130
- RegistryPath<LocatorSchemaPathType>,
131
- LocatorStrategyDefinition["type"]
132
- >
133
- >;
134
- },
135
- ]
136
- ): Reuse extends undefined
137
- ? LocatorRegistrationPreDefinitionBuilder<LocatorSchemaPathType, RegistryPath<LocatorSchemaPathType>, false>
138
- : Reuse extends LocatorSchemaPathType
139
- ? void
140
- : LocatorRegistrationSeededBuilderForType<
141
- LocatorSchemaPathType,
142
- RegistryPath<LocatorSchemaPathType>,
143
- Extract<
144
- Reuse,
145
- ReusableLocator<
146
- LocatorSchemaPathType,
147
- RegistryPath<LocatorSchemaPathType>,
148
- LocatorStrategyDefinition["type"]
149
- >
150
- >["type"]
151
- > {
152
- const options = args[0] as
153
- | {
154
- reuse?:
155
- | ReusableLocator<
156
- LocatorSchemaPathType,
157
- RegistryPath<LocatorSchemaPathType>,
158
- LocatorStrategyDefinition["type"]
159
- >
160
- | LocatorSchemaPathType
161
- | InvalidReusePath<RegistryPath<LocatorSchemaPathType>>;
162
- }
163
- | undefined;
164
- const reuse = options?.reuse;
165
-
166
- if (reuse === undefined) {
167
- return new LocatorRegistrationBuilder<LocatorSchemaPathType, RegistryPath<LocatorSchemaPathType>, false>(
168
- this,
169
- path as RegistryPath<LocatorSchemaPathType>,
170
- ) as never;
171
- }
172
-
173
- if (typeof reuse === "string") {
174
- const targetPath = path as RegistryPath<LocatorSchemaPathType>;
175
- if (reuse === targetPath) {
176
- throw new Error(`Locator reuse path cannot be the same as registration path: "${targetPath}".`);
177
- }
178
- const sourceRecord = this.get(reuse as RegistryPath<LocatorSchemaPathType>);
179
- const cloned = this.cloneRecordForReuse(sourceRecord, targetPath);
180
-
181
- this.register(targetPath, cloned);
182
- return undefined as never;
183
- }
184
-
185
- if (Array.isArray(reuse)) {
186
- const [reason] = reuse;
187
- throw new Error(`Invalid reuse path configuration for "${path}": ${reason}`);
188
- }
189
-
190
- const reusedRecord: LocatorSchemaRecord<
191
- LocatorSchemaPathType,
192
- RegistryPath<LocatorSchemaPathType>
193
- > = this.cloneRecordForReuse(
194
- {
195
- locatorSchemaPath: path as RegistryPath<LocatorSchemaPathType>,
196
- definition: reuse.definition,
197
- steps: reuse.steps,
198
- description: reuse.description,
199
- },
200
- path as RegistryPath<LocatorSchemaPathType>,
201
- );
202
-
203
- return new LocatorRegistrationBuilder<LocatorSchemaPathType, RegistryPath<LocatorSchemaPathType>, true>(
204
- this,
205
- path as RegistryPath<LocatorSchemaPathType>,
206
- {
207
- initialDefinition: reusedRecord.definition,
208
- initialSteps: reusedRecord.steps,
209
- reuseType: reusedRecord.definition.type,
210
- initialDescription: reusedRecord.description,
211
- },
212
- ).persistSeededDefinition() as never;
213
- }
214
-
215
- register(
216
- path: RegistryPath<LocatorSchemaPathType>,
217
- record: LocatorSchemaRecord<LocatorSchemaPathType, RegistryPath<LocatorSchemaPathType>>,
218
- ) {
219
- validateLocatorSchemaPath(path);
220
- if (this.schemas.has(path)) {
221
- const existing = this.schemas.get(path);
222
- if (!existing) {
223
- throw new Error(`A locator schema with the path "${path}" already exists.`);
224
- }
225
- const errorDetails = stringifyForLog({
226
- existing: existing,
227
- attempted: record,
228
- });
229
- throw new Error(`A locator schema with the path "${path}" already exists.\nExisting Schema: ${errorDetails}`);
230
- }
231
- this.schemas.set(path, this.normalizeRecord(record));
232
- }
233
-
234
- replace(
235
- path: RegistryPath<LocatorSchemaPathType>,
236
- record: LocatorSchemaRecord<LocatorSchemaPathType, RegistryPath<LocatorSchemaPathType>>,
237
- ) {
238
- validateLocatorSchemaPath(path);
239
- if (!this.schemas.has(path)) {
240
- throw new Error(`No locator schema registered for path "${path}".`);
241
- }
242
- this.schemas.set(path, this.normalizeRecord(record));
243
- }
244
-
245
- get(
246
- path: RegistryPath<LocatorSchemaPathType>,
247
- ): LocatorSchemaRecord<LocatorSchemaPathType, RegistryPath<LocatorSchemaPathType>> {
248
- const record = this.schemas.get(path);
249
- if (!record) {
250
- throw new Error(`No locator schema registered for path "${path}".`);
251
- }
252
- return {
253
- locatorSchemaPath: record.locatorSchemaPath,
254
- definition: cloneLocatorStrategyDefinition(record.definition),
255
- steps: normalizeSteps(record.steps),
256
- ...(record.description !== undefined ? { description: record.description } : {}),
257
- } satisfies LocatorSchemaRecord<LocatorSchemaPathType, RegistryPath<LocatorSchemaPathType>>;
258
- }
259
-
260
- unregister(path: RegistryPath<LocatorSchemaPathType>) {
261
- this.schemas.delete(path);
262
- }
263
-
264
- getIfExists(
265
- path: RegistryPath<LocatorSchemaPathType>,
266
- ): LocatorSchemaRecord<LocatorSchemaPathType, RegistryPath<LocatorSchemaPathType>> | undefined {
267
- const record = this.schemas.get(path);
268
- if (!record) {
269
- return undefined as never;
270
- }
271
-
272
- return {
273
- locatorSchemaPath: record.locatorSchemaPath,
274
- definition: cloneLocatorStrategyDefinition(record.definition),
275
- steps: normalizeSteps(record.steps),
276
- ...(record.description !== undefined ? { description: record.description } : {}),
277
- } satisfies LocatorSchemaRecord<LocatorSchemaPathType, RegistryPath<LocatorSchemaPathType>>;
278
- }
279
-
280
- private resolveFilterLocator(
281
- locatorReference: FilterLocatorReference<RegistryPath<LocatorSchemaPathType>, RegistryPath<LocatorSchemaPathType>>,
282
- context: {
283
- rootPath: RegistryPath<LocatorSchemaPathType>;
284
- resolvingPaths: Set<string>;
285
- },
286
- ) {
287
- if (isLocatorInstance(locatorReference)) {
288
- return locatorReference;
289
- }
290
-
291
- if (typeof locatorReference === "string") {
292
- return this.getLocatorWithContext(locatorReference as RegistryPath<LocatorSchemaPathType>, context);
293
- }
294
-
295
- throw new Error(
296
- `Unsupported filter reference while resolving "${context.rootPath}". ` +
297
- "Filter has/hasNot supports Playwright Locator instances or registry path strings.",
298
- );
299
- }
300
-
301
- private createResolutionContext(rootPath: RegistryPath<LocatorSchemaPathType>) {
302
- return {
303
- rootPath,
304
- resolvingPaths: new Set<string>(),
305
- };
306
- }
307
-
308
- private getLocatorWithContext(
309
- path: RegistryPath<LocatorSchemaPathType>,
310
- context: {
311
- rootPath: RegistryPath<LocatorSchemaPathType>;
312
- resolvingPaths: Set<string>;
313
- },
314
- ) {
315
- if (context.resolvingPaths.has(path)) {
316
- throw new Error(`Detected cyclic filter reference while resolving "${context.rootPath}": "${path}".`);
317
- }
318
-
319
- context.resolvingPaths.add(path);
320
- try {
321
- const record = this.get(path);
322
- const definitions = new Map<string, LocatorStrategyDefinition>([[path, record.definition]]);
323
- const steps = new Map<
324
- string,
325
- LocatorStep<RegistryPath<LocatorSchemaPathType>, RegistryPath<LocatorSchemaPathType>>[]
326
- >([[path, normalizeSteps(record.steps)]]);
327
- const { locator } = this.buildLocatorChain(path, definitions, steps, undefined, record.description, context);
328
- if (!locator) {
329
- throw new Error(`Unable to resolve direct locator for path "${path}".`);
330
- }
331
- return locator as Locator;
332
- } finally {
333
- context.resolvingPaths.delete(path);
334
- }
335
- }
336
-
337
- private resolveFiltersForTarget(
338
- filters: FilterDefinition<RegistryPath<LocatorSchemaPathType>, RegistryPath<LocatorSchemaPathType>>[] | undefined,
339
- context: {
340
- rootPath: RegistryPath<LocatorSchemaPathType>;
341
- resolvingPaths: Set<string>;
342
- },
343
- ) {
344
- if (!filters || filters.length === 0) {
345
- return [] as ResolvedFilterDefinition[];
346
- }
347
-
348
- const resolved: ResolvedFilterDefinition[] = [];
349
- for (const filter of filters) {
350
- if (filter && typeof filter === "object" && ("has" in filter || "hasNot" in filter)) {
351
- const { has, hasNot, ...rest } = filter as FilterDefinition<
352
- RegistryPath<LocatorSchemaPathType>,
353
- RegistryPath<LocatorSchemaPathType>
354
- > & {
355
- has?: FilterLocatorReference<RegistryPath<LocatorSchemaPathType>, RegistryPath<LocatorSchemaPathType>>;
356
- hasNot?: FilterLocatorReference<RegistryPath<LocatorSchemaPathType>, RegistryPath<LocatorSchemaPathType>>;
357
- };
358
-
359
- const normalizedFilter: ResolvedFilterDefinition = {
360
- ...(rest as PlaywrightFilterDefinition),
361
- } as ResolvedFilterDefinition;
362
-
363
- if (has !== undefined) {
364
- normalizedFilter.has = this.resolveFilterLocator(has, context);
365
- }
366
-
367
- if (hasNot !== undefined) {
368
- normalizedFilter.hasNot = this.resolveFilterLocator(hasNot, context);
369
- }
370
-
371
- resolved.push(normalizedFilter);
372
- continue;
373
- }
374
-
375
- resolved.push(filter as ResolvedFilterDefinition);
376
- }
377
-
378
- return resolved;
379
- }
380
-
381
- /**
382
- * Returns a mutable query builder clone for the provided path. Use the builder to add or clear
383
- * `filter`/`nth` steps, or to `update`/`replace` locator definitions before resolving locators.
384
- * Changes affect only the builder clone; the registry’s stored schema remains untouched.
385
- *
386
- * @example
387
- * ```ts
388
- * const builder = registry
389
- * .getLocatorSchema("section.button")
390
- * .filter("section.button", { hasText: /Save/ })
391
- * .nth("section", 0);
392
- * const locator = builder.getNestedLocator();
393
- * ```
394
- */
395
- getLocatorSchema<Path extends LocatorSchemaPathType>(
396
- path: PathArgument<LocatorSchemaPathType, Path>,
397
- ): LocatorQueryBuilderPublic<LocatorSchemaPathType, Extract<Path, RegistryPath<LocatorSchemaPathType>>> {
398
- return new LocatorQueryBuilder<LocatorSchemaPathType, Extract<Path, RegistryPath<LocatorSchemaPathType>>>(
399
- this,
400
- path as Extract<Path, RegistryPath<LocatorSchemaPathType>>,
401
- );
402
- }
403
-
404
- /**
405
- * Resolves the Playwright {@link Locator} for the provided path, applying only the terminal
406
- * definition and its recorded steps (no ancestor chaining). Throws if the path is not registered.
407
- *
408
- * @example
409
- * ```ts
410
- * const button = registry.getLocator("form.submit");
411
- * await button.click();
412
- * ```
413
- */
414
- getLocator<Path extends LocatorSchemaPathType>(path: PathArgument<LocatorSchemaPathType, Path>): Locator {
415
- return this.getLocatorSchema(path).getLocator();
416
- }
417
-
418
- /**
419
- * Resolves a chained Playwright {@link Locator} for a nested path, traversing each registered
420
- * segment and applying their steps in order. Throws if any segment in the chain is missing.
421
- *
422
- * @example
423
- * ```ts
424
- * const nested = registry.getNestedLocator("list.item");
425
- * await expect(nested).toBeVisible();
426
- * ```
427
- */
428
- getNestedLocator<Path extends LocatorSchemaPathType>(path: PathArgument<LocatorSchemaPathType, Path>): Locator {
429
- return this.getLocatorSchema(path).getNestedLocator();
430
- }
431
-
432
- buildLocatorChain(
433
- path: RegistryPath<LocatorSchemaPathType>,
434
- definitions: Map<string, LocatorStrategyDefinition>,
435
- steps: Map<string, LocatorStep<RegistryPath<LocatorSchemaPathType>, RegistryPath<LocatorSchemaPathType>>[]>,
436
- tombstones?: Set<string>,
437
- terminalDescription?: LocatorDescription,
438
- context?: {
439
- rootPath: RegistryPath<LocatorSchemaPathType>;
440
- resolvingPaths: Set<string>;
441
- },
442
- ) {
443
- const resolutionContext = context ?? this.createResolutionContext(path);
444
- const chain = expandSchemaPath(path);
445
- const registeredChain = chain.filter((part) => definitions.has(part));
446
-
447
- if (tombstones?.has(path) || !definitions.has(path)) {
448
- throw new Error(`No locator schema registered for path "${path}".`);
449
- }
450
-
451
- let currentTarget: LocatorBuilderTarget = this.page;
452
- let lastLocator: Locator | null = null;
453
- const debugSteps: {
454
- path: string;
455
- definition: LocatorStrategyDefinition;
456
- appliedFilters: ResolvedFilterDefinition[];
457
- index?: IndexSelector | null | undefined;
458
- recordedSteps: LocatorStep<RegistryPath<LocatorSchemaPathType>, RegistryPath<LocatorSchemaPathType>>[];
459
- }[] = [];
460
-
461
- const lastIndex = registeredChain.length - 1;
462
-
463
- for (const [index, part] of registeredChain.entries()) {
464
- const isTerminalStep = index === lastIndex;
465
- const definition = definitions.get(part);
466
- if (tombstones?.has(part)) {
467
- if (isTerminalStep) {
468
- throw new Error(`No locator schema registered for path "${path}".`);
469
- }
470
- continue;
471
- }
472
-
473
- if (!definition) {
474
- throw new Error(`Missing locator definition for "${part}" while resolving "${path}".`);
475
- }
476
-
477
- if (isFrameLocatorDefinition(definition)) {
478
- const frameLocator = createLocator(currentTarget, definition) as ReturnType<Page["frameLocator"]>;
479
- const isTerminalStep = index === lastIndex;
480
-
481
- if (isTerminalStep) {
482
- const ownerLocator = frameLocator.owner();
483
- currentTarget = ownerLocator;
484
- lastLocator = ownerLocator;
485
- } else {
486
- currentTarget = frameLocator as LocatorBuilderTarget;
487
- lastLocator = frameLocator as unknown as Locator;
488
- }
489
-
490
- if (isTerminalStep && terminalDescription && isLocatorInstance(lastLocator)) {
491
- lastLocator = lastLocator.describe(terminalDescription);
492
- currentTarget = lastLocator;
493
- }
494
-
495
- debugSteps.push({ path: part, definition, appliedFilters: [], recordedSteps: [] });
496
- continue;
497
- }
498
-
499
- const locatorResult = createLocator(currentTarget, definition) as Locator;
500
- const recordedSteps = steps.get(part) ?? [];
501
-
502
- let resolvedLocator = locatorResult;
503
- const appliedFilters: ResolvedFilterDefinition[] = [];
504
- let appliedIndex: IndexSelector | null | undefined;
505
-
506
- for (const step of recordedSteps) {
507
- if (step.kind === "filter") {
508
- const [resolvedFilter] = this.resolveFiltersForTarget([step.filter], resolutionContext);
509
- if (resolvedFilter) {
510
- resolvedLocator = resolvedLocator.filter(resolvedFilter);
511
- appliedFilters.push(resolvedFilter);
512
- }
513
- } else {
514
- appliedIndex = step.index ?? null;
515
- resolvedLocator = applyIndexSelector(resolvedLocator, appliedIndex ?? undefined);
516
- }
517
- }
518
-
519
- if (isTerminalStep && terminalDescription) {
520
- resolvedLocator = resolvedLocator.describe(terminalDescription);
521
- }
522
-
523
- currentTarget = resolvedLocator;
524
- lastLocator = resolvedLocator;
525
- debugSteps.push({
526
- path: part,
527
- definition,
528
- appliedFilters,
529
- index: appliedIndex,
530
- recordedSteps,
531
- });
532
- }
533
-
534
- return { locator: lastLocator, steps: debugSteps };
535
- }
536
- }
537
-
538
- export type GetLocatorAccessor<LocatorSchemaPathType extends string> = <Path extends LocatorSchemaPathType>(
539
- path: LocatorSchemaPathFormat<Path> extends Path ? Path : LocatorSchemaPathFormat<Path>,
540
- ) => Locator;
541
-
542
- export type AddAccessor<LocatorSchemaPathType extends string> = LocatorRegistryInternal<LocatorSchemaPathType>["add"];
543
-
544
- export type GetLocatorSchemaAccessor<LocatorSchemaPathType extends string> = <Path extends LocatorSchemaPathType>(
545
- path: LocatorSchemaPathFormat<Path> extends Path ? Path : LocatorSchemaPathFormat<Path>,
546
- ) => LocatorQueryBuilderPublic<LocatorSchemaPathType, Extract<Path, RegistryPath<LocatorSchemaPathType>>>;
547
-
548
- export type GetNestedLocatorAccessor<LocatorSchemaPathType extends string> = <Path extends LocatorSchemaPathType>(
549
- path: LocatorSchemaPathFormat<Path> extends Path ? Path : LocatorSchemaPathFormat<Path>,
550
- ) => Locator;
551
-
552
- export type LocatorRegistry<LocatorSchemaPathType extends string> = Pick<
553
- LocatorRegistryInternal<LocatorSchemaPathType>,
554
- "add" | "createReusable" | "getLocator" | "getLocatorSchema" | "getNestedLocator"
555
- >;
556
-
557
- /**
558
- * Creates a v2 locator registry bound to a Playwright {@link Page} and returns the registry plus
559
- * pre-bound helpers for ergonomic use in page objects and tests. Path unions are validated at
560
- * compile time and runtime. The returned `add`/`getLocator`/`getNestedLocator`/`getLocatorSchema`
561
- * accessors are pre-bound to the registry instance for dependency injection.
562
- *
563
- * @example
564
- * ```ts
565
- * const { registry, add, getNestedLocator, getLocatorSchema } =
566
- * createRegistryWithAccessors<"root" | "root.child">(page);
567
- *
568
- * registry.add("root").locator("section");
569
- * registry.add("root.child").getByRole("heading", { level: 2 });
570
- *
571
- * const nested = getNestedLocator("root.child");
572
- * const patched = getLocatorSchema("root.child").filter("root.child", { hasText: /Docs/ }).getNestedLocator();
573
- * ```
574
- */
575
- export const createRegistryWithAccessors = <Paths extends string>(page: Page) => {
576
- const registryInstance = new LocatorRegistryInternal<Paths>(page);
577
- const add: LocatorRegistryInternal<Paths>["add"] = registryInstance.add.bind(registryInstance);
578
- const getLocator: GetLocatorAccessor<Paths> = registryInstance.getLocator.bind(registryInstance);
579
- const getNestedLocator: GetNestedLocatorAccessor<Paths> = registryInstance.getNestedLocator.bind(registryInstance);
580
- const getLocatorSchema: GetLocatorSchemaAccessor<Paths> = registryInstance.getLocatorSchema.bind(registryInstance);
581
- const registry: LocatorRegistry<Paths> = registryInstance;
582
- return { registry, add, getLocator, getNestedLocator, getLocatorSchema } as const;
583
- };