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,427 +0,0 @@
1
- import type { Locator } from "@playwright/test";
2
- import type { LocatorRegistryInternal } from "./locatorRegistry";
3
- import { buildReplacementDefinition, LocatorUpdateBuilder, mergeLocatorDefinition } from "./locatorUpdateBuilder";
4
- import type {
5
- FilterDefinition,
6
- IndexSelector,
7
- LocatorChainPaths,
8
- LocatorDescription,
9
- LocatorStep,
10
- LocatorStrategyDefinition,
11
- LocatorUpdate,
12
- RegistryPath,
13
- } from "./types";
14
- import { cloneLocatorStrategyDefinition, expandSchemaPath, normalizeSteps } from "./utils";
15
-
16
- export type LocatorQueryBuilderPublic<
17
- LocatorSchemaPathType extends string,
18
- LocatorSubstring extends RegistryPath<LocatorSchemaPathType>,
19
- > = Pick<
20
- LocatorQueryBuilder<LocatorSchemaPathType, LocatorSubstring>,
21
- "filter" | "clearSteps" | "nth" | "describe" | "update" | "replace" | "remove" | "getLocator" | "getNestedLocator"
22
- >;
23
-
24
- export class LocatorQueryBuilder<
25
- LocatorSchemaPathType extends string,
26
- LocatorSubstring extends RegistryPath<LocatorSchemaPathType>,
27
- > {
28
- private readonly definitions = new Map<string, LocatorStrategyDefinition>();
29
- private readonly perPathTypeCache = new Map<
30
- string,
31
- Map<LocatorStrategyDefinition["type"], LocatorStrategyDefinition>
32
- >();
33
- private readonly steps = new Map<
34
- string,
35
- LocatorStep<RegistryPath<LocatorSchemaPathType>, RegistryPath<LocatorSchemaPathType>>[]
36
- >();
37
- private readonly descriptions = new Map<string, LocatorDescription>();
38
- private readonly tombstones = new Set<string>();
39
-
40
- constructor(
41
- private readonly registry: LocatorRegistryInternal<LocatorSchemaPathType>,
42
- private readonly path: LocatorSubstring,
43
- ) {
44
- const chain = expandSchemaPath(path);
45
- let hasTerminal = false;
46
- for (const part of chain) {
47
- const record = this.registry.getIfExists(part as RegistryPath<LocatorSchemaPathType>);
48
- if (!record) {
49
- continue;
50
- }
51
- const clonedDefinition = cloneLocatorStrategyDefinition(record.definition);
52
- this.definitions.set(part, clonedDefinition);
53
- this.ensureTypeCache(part, clonedDefinition);
54
- const recordSteps = normalizeSteps<RegistryPath<LocatorSchemaPathType>, RegistryPath<LocatorSchemaPathType>>(
55
- record.steps,
56
- );
57
- this.steps.set(part, recordSteps);
58
- if (record.description !== undefined) {
59
- this.descriptions.set(part, record.description);
60
- }
61
- if (part === path) {
62
- hasTerminal = true;
63
- }
64
- }
65
- if (!hasTerminal) {
66
- throw new Error(`No locator schema registered for path "${path}".`);
67
- }
68
- }
69
-
70
- /**
71
- * Starts a PATCH-style update for the locator definition at the given `subPath` within this
72
- * builder. Returns a builder exposing the same locator-type methods as `registry.add`, with all
73
- * arguments optional to merge with the existing definition. The registry remains unchanged until
74
- * you resolve with `getLocator`/`getNestedLocator`.
75
- *
76
- * If `subPath` is omitted, the terminal path for this builder (`path` passed to
77
- * `getLocatorSchema`) is used.
78
- *
79
- * @example
80
- * ```ts
81
- * getLocatorSchema("form.button")
82
- * .update("form.button")
83
- * .getByRole({ name: "Submit" })
84
- * .getNestedLocator();
85
- * ```
86
- */
87
- update(): LocatorUpdateBuilder<
88
- LocatorSchemaPathType,
89
- LocatorSubstring,
90
- LocatorChainPaths<RegistryPath<LocatorSchemaPathType>, LocatorSubstring>
91
- >;
92
- update<SubPath extends LocatorChainPaths<RegistryPath<LocatorSchemaPathType>, LocatorSubstring>>(
93
- subPath: SubPath,
94
- ): LocatorUpdateBuilder<LocatorSchemaPathType, LocatorSubstring, SubPath>;
95
- update<SubPath extends LocatorChainPaths<RegistryPath<LocatorSchemaPathType>, LocatorSubstring>>(subPath?: SubPath) {
96
- const resolvedSubPath = (subPath ?? this.path) as LocatorChainPaths<
97
- RegistryPath<LocatorSchemaPathType>,
98
- LocatorSubstring
99
- >;
100
- this.ensureSubPath(resolvedSubPath);
101
- return new LocatorUpdateBuilder<LocatorSchemaPathType, LocatorSubstring, typeof resolvedSubPath>(
102
- this,
103
- resolvedSubPath,
104
- );
105
- }
106
-
107
- /**
108
- * Records a Playwright-style filter for the specified `subPath` within this builder. Unlike
109
- * `registry.add`, `subPath` is optional here: when omitted, the terminal path for this builder is
110
- * used. Provide an explicit `subPath` when targeting an ancestor segment.
111
- *
112
- * @example
113
- * ```ts
114
- * getLocatorSchema("list.item")
115
- * .filter("list", { hasText: "List" })
116
- * .filter("list.item", { hasText: "Row" });
117
- * ```
118
- */
119
- filter(filter: FilterDefinition<RegistryPath<LocatorSchemaPathType>, RegistryPath<LocatorSchemaPathType>>): this;
120
- filter<SubPath extends LocatorChainPaths<RegistryPath<LocatorSchemaPathType>, LocatorSubstring>>(
121
- subPath: SubPath,
122
- filter: FilterDefinition<RegistryPath<LocatorSchemaPathType>, RegistryPath<LocatorSchemaPathType>>,
123
- ): this;
124
- filter<SubPath extends LocatorChainPaths<RegistryPath<LocatorSchemaPathType>, LocatorSubstring>>(
125
- ...args:
126
- | [FilterDefinition<RegistryPath<LocatorSchemaPathType>, RegistryPath<LocatorSchemaPathType>>]
127
- | [SubPath, FilterDefinition<RegistryPath<LocatorSchemaPathType>, RegistryPath<LocatorSchemaPathType>>]
128
- ) {
129
- const hasExplicitSubPath = args.length === 2;
130
- const [subPathOrFilter, maybeFilter] = args;
131
- const resolvedSubPath = (hasExplicitSubPath ? subPathOrFilter : this.path) as LocatorChainPaths<
132
- RegistryPath<LocatorSchemaPathType>,
133
- LocatorSubstring
134
- >;
135
- const filter = (hasExplicitSubPath ? maybeFilter : subPathOrFilter) as FilterDefinition<
136
- RegistryPath<LocatorSchemaPathType>,
137
- RegistryPath<LocatorSchemaPathType>
138
- >;
139
-
140
- this.ensureSubPath(resolvedSubPath);
141
- const existing = this.steps.get(resolvedSubPath) ?? [];
142
- existing.push({ kind: "filter", filter });
143
- this.steps.set(resolvedSubPath, existing);
144
- return this;
145
- }
146
-
147
- /**
148
- * Clears all recorded `filter`/`nth` steps for the specified `subPath` in this builder, leaving
149
- * the locator definition intact. If `subPath` is omitted, the terminal path for this builder is
150
- * used; provide an explicit `subPath` to clear ancestor steps.
151
- *
152
- * @example
153
- * ```ts
154
- * getLocatorSchema("list.item").clearSteps("list.item").getNestedLocator();
155
- * ```
156
- */
157
- clearSteps(): this;
158
- clearSteps<SubPath extends LocatorChainPaths<RegistryPath<LocatorSchemaPathType>, LocatorSubstring>>(
159
- subPath: SubPath,
160
- ): this;
161
- clearSteps<SubPath extends LocatorChainPaths<RegistryPath<LocatorSchemaPathType>, LocatorSubstring>>(
162
- subPath?: SubPath,
163
- ) {
164
- const resolvedSubPath = (subPath ?? this.path) as LocatorChainPaths<
165
- RegistryPath<LocatorSchemaPathType>,
166
- LocatorSubstring
167
- >;
168
- this.ensureSubPath(resolvedSubPath);
169
- this.steps.set(resolvedSubPath, []);
170
- return this;
171
- }
172
-
173
- /**
174
- * Records an index selector for the specified `subPath` in this builder. Indices are applied in
175
- * the order they are chained and require that the subPath already exists on the builder. If
176
- * `subPath` is omitted, the terminal path for this builder is used; provide an explicit `subPath`
177
- * to target ancestor segments.
178
- *
179
- * @example
180
- * ```ts
181
- * getLocatorSchema("list.item").nth("list.item", 2).getNestedLocator();
182
- * ```
183
- */
184
- nth(index: IndexSelector): this;
185
- nth<SubPath extends LocatorChainPaths<RegistryPath<LocatorSchemaPathType>, LocatorSubstring>>(
186
- subPath: SubPath,
187
- index: IndexSelector,
188
- ): this;
189
- nth<SubPath extends LocatorChainPaths<RegistryPath<LocatorSchemaPathType>, LocatorSubstring>>(
190
- ...args: [IndexSelector] | [SubPath, IndexSelector]
191
- ) {
192
- const hasExplicitSubPath = args.length === 2;
193
- const [subPathOrIndex, maybeIndex] = args;
194
- const resolvedSubPath = (hasExplicitSubPath ? subPathOrIndex : this.path) as LocatorChainPaths<
195
- RegistryPath<LocatorSchemaPathType>,
196
- LocatorSubstring
197
- >;
198
- const index = (hasExplicitSubPath ? maybeIndex : subPathOrIndex) as IndexSelector;
199
-
200
- this.ensureSubPath(resolvedSubPath);
201
- const existing = this.steps.get(resolvedSubPath) ?? [];
202
- existing.push({ kind: "index", index });
203
- this.steps.set(resolvedSubPath, existing);
204
- return this;
205
- }
206
-
207
- /**
208
- * Adds or overrides the description for the terminal path of this builder. The description is
209
- * applied only to the resolved terminal locator and does not mutate registry state.
210
- *
211
- * @example
212
- * ```ts
213
- * getLocatorSchema("section.button")
214
- * .describe("Save button")
215
- * .getNestedLocator();
216
- * ```
217
- */
218
- describe(description: LocatorDescription) {
219
- this.ensureSubPath(this.path);
220
- this.descriptions.set(this.path, description);
221
- return this;
222
- }
223
-
224
- /** @internal */
225
- applyUpdate<SubPath extends LocatorChainPaths<RegistryPath<LocatorSchemaPathType>, LocatorSubstring>>(
226
- subPath: SubPath,
227
- updates: LocatorUpdate,
228
- ) {
229
- this.ensureSubPath(subPath);
230
- if (!this.definitions.has(subPath) && this.tombstones.has(subPath)) {
231
- const baseline = this.registry.get(subPath as RegistryPath<LocatorSchemaPathType>).definition;
232
- const baselineClone = cloneLocatorStrategyDefinition(baseline);
233
- this.definitions.set(subPath, baselineClone);
234
- this.steps.set(subPath, []);
235
- this.tombstones.delete(subPath);
236
- }
237
-
238
- const current = this.definitions.get(subPath);
239
- if (!current) {
240
- throw new Error(`No locator schema registered for sub-path "${subPath}".`);
241
- }
242
- const baseline = this.registry.get(subPath as RegistryPath<LocatorSchemaPathType>).definition;
243
- const cacheForPath = this.ensureTypeCache(subPath, cloneLocatorStrategyDefinition(baseline));
244
- const cachedDefinition = cacheForPath.get(updates.type);
245
- const merged = mergeLocatorDefinition(current, updates, subPath, cachedDefinition, baseline);
246
- const mergedClone = cloneLocatorStrategyDefinition(merged);
247
- cacheForPath.set(mergedClone.type, mergedClone);
248
- this.definitions.set(subPath, mergedClone);
249
- return this;
250
- }
251
-
252
- /**
253
- * Starts a POST-style replacement for the locator definition at the given `subPath` within this
254
- * builder. Returns a builder exposing the same locator-type methods as `registry.add`, requiring
255
- * primary arguments where Playwright does. Registry state is unchanged; the replacement applies
256
- * to the builder clone when resolved. If `subPath` is omitted, the terminal path for this
257
- * builder is used.
258
- *
259
- * @example
260
- * ```ts
261
- * getLocatorSchema("section.button")
262
- * .replace("section.button")
263
- * .getByRole("button", { name: "Save" })
264
- * .getNestedLocator();
265
- * ```
266
- */
267
- replace(): LocatorUpdateBuilder<
268
- LocatorSchemaPathType,
269
- LocatorSubstring,
270
- LocatorChainPaths<RegistryPath<LocatorSchemaPathType>, LocatorSubstring>
271
- >;
272
- replace<SubPath extends LocatorChainPaths<RegistryPath<LocatorSchemaPathType>, LocatorSubstring>>(
273
- subPath: SubPath,
274
- ): LocatorUpdateBuilder<LocatorSchemaPathType, LocatorSubstring, SubPath>;
275
- replace<SubPath extends LocatorChainPaths<RegistryPath<LocatorSchemaPathType>, LocatorSubstring>>(subPath?: SubPath) {
276
- const resolvedSubPath = (subPath ?? this.path) as LocatorChainPaths<
277
- RegistryPath<LocatorSchemaPathType>,
278
- LocatorSubstring
279
- >;
280
- this.ensureSubPath(resolvedSubPath);
281
- return new LocatorUpdateBuilder<LocatorSchemaPathType, LocatorSubstring, typeof resolvedSubPath>(
282
- this,
283
- resolvedSubPath,
284
- "replace",
285
- );
286
- }
287
-
288
- /** @internal */
289
- applyReplacement<SubPath extends LocatorChainPaths<RegistryPath<LocatorSchemaPathType>, LocatorSubstring>>(
290
- subPath: SubPath,
291
- updates: LocatorUpdate,
292
- ) {
293
- this.ensureSubPath(subPath);
294
- if (this.tombstones.has(subPath) && !this.definitions.has(subPath)) {
295
- this.steps.set(subPath, []);
296
- this.tombstones.delete(subPath);
297
- }
298
- const nextDefinition = buildReplacementDefinition(updates, subPath);
299
- const cloned = cloneLocatorStrategyDefinition(nextDefinition);
300
- const cacheForPath = this.ensureTypeCache(subPath, cloned);
301
- cacheForPath.set(cloned.type, cloneLocatorStrategyDefinition(cloned));
302
- this.definitions.set(subPath, cloned);
303
- return this;
304
- }
305
-
306
- /**
307
- * Soft-deletes the definition and steps for the given `subPath` on this builder clone, adding a
308
- * tombstone. Non-terminal removals are skipped during resolution; terminal removals throw unless
309
- * repopulated with `update`/`replace` before resolving. If `subPath` is omitted, the terminal
310
- * path for this builder is used.
311
- *
312
- * @example
313
- * ```ts
314
- * const builder = getLocatorSchema("section.button");
315
- * builder.remove("section.button");
316
- * expect(() => builder.getNestedLocator()).toThrow();
317
- * ```
318
- */
319
- remove(): this;
320
- remove<SubPath extends LocatorChainPaths<RegistryPath<LocatorSchemaPathType>, LocatorSubstring>>(
321
- subPath: SubPath,
322
- ): this;
323
- remove<SubPath extends LocatorChainPaths<RegistryPath<LocatorSchemaPathType>, LocatorSubstring>>(subPath?: SubPath) {
324
- const resolvedSubPath = (subPath ?? this.path) as LocatorChainPaths<
325
- RegistryPath<LocatorSchemaPathType>,
326
- LocatorSubstring
327
- >;
328
- this.ensureSubPath(resolvedSubPath);
329
- this.definitions.delete(resolvedSubPath);
330
- this.steps.delete(resolvedSubPath);
331
- this.perPathTypeCache.delete(resolvedSubPath);
332
- this.descriptions.delete(resolvedSubPath);
333
- this.tombstones.add(resolvedSubPath);
334
- return this;
335
- }
336
-
337
- /**
338
- * Resolves and returns the Playwright {@link Locator} for the terminal path of this builder,
339
- * applying only the terminal definition and its steps. Throws if the terminal path has been
340
- * removed or is otherwise missing.
341
- *
342
- * @example
343
- * ```ts
344
- * const locator = getLocatorSchema("form.submit").getLocator();
345
- * ```
346
- */
347
- getLocator() {
348
- const definition = this.definitions.get(this.path);
349
-
350
- if (!definition) {
351
- throw new Error(`No locator schema registered for path "${this.path}".`);
352
- }
353
-
354
- const stepsForPath = this.steps.get(this.path) ?? [];
355
-
356
- const definitions = new Map<string, LocatorStrategyDefinition>([[this.path, definition]]);
357
- const steps = new Map<
358
- string,
359
- LocatorStep<RegistryPath<LocatorSchemaPathType>, RegistryPath<LocatorSchemaPathType>>[]
360
- >([
361
- [
362
- this.path,
363
- normalizeSteps<RegistryPath<LocatorSchemaPathType>, RegistryPath<LocatorSchemaPathType>>(stepsForPath),
364
- ],
365
- ]);
366
-
367
- const { locator } = this.registry.buildLocatorChain(
368
- this.path,
369
- definitions,
370
- steps,
371
- this.tombstones,
372
- this.descriptions.get(this.path),
373
- );
374
- if (!locator) {
375
- throw new Error(`Unable to resolve direct locator for path "${this.path}".`);
376
- }
377
- return locator as Locator;
378
- }
379
-
380
- /**
381
- * Resolves the chained Playwright {@link Locator} for this builder’s root path, traversing each
382
- * registered segment and applying recorded steps. Throws if any required segment is missing.
383
- *
384
- * @example
385
- * ```ts
386
- * const nested = getLocatorSchema("list.item")
387
- * .filter("list", { hasText: "List" })
388
- * .getNestedLocator();
389
- * ```
390
- */
391
- getNestedLocator() {
392
- const { locator } = this.resolve();
393
- if (!locator) {
394
- throw new Error(`Unable to resolve nested locator for path "${this.path}".`);
395
- }
396
- return locator;
397
- }
398
-
399
- private ensureSubPath(subPath: string) {
400
- if (!this.definitions.has(subPath) && !this.tombstones.has(subPath)) {
401
- throw new Error(`"${subPath}" is not a valid sub-path of "${this.path}".`);
402
- }
403
- }
404
-
405
- private resolve() {
406
- return this.registry.buildLocatorChain(
407
- this.path,
408
- this.definitions,
409
- this.steps,
410
- this.tombstones,
411
- this.descriptions.get(this.path),
412
- );
413
- }
414
-
415
- private ensureTypeCache(subPath: string, baseline: LocatorStrategyDefinition) {
416
- if (!this.perPathTypeCache.has(subPath)) {
417
- this.perPathTypeCache.set(
418
- subPath,
419
- new Map<LocatorStrategyDefinition["type"], LocatorStrategyDefinition>([
420
- [baseline.type, cloneLocatorStrategyDefinition(baseline)],
421
- ]),
422
- );
423
- }
424
-
425
- return this.perPathTypeCache.get(subPath) as Map<LocatorStrategyDefinition["type"], LocatorStrategyDefinition>;
426
- }
427
- }