pomwright 1.1.1 → 1.3.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.
package/dist/index.d.mts CHANGED
@@ -101,6 +101,13 @@ interface LocatorSchema {
101
101
  dataCy?: string;
102
102
  /** The ID of the element. 'id' string format: "value", or a regex expression of the value */
103
103
  id?: string | RegExp;
104
+ /** The equivalent of the Playwright locator.filter() method */
105
+ filter?: {
106
+ has?: Locator;
107
+ hasNot?: Locator;
108
+ hasNotText?: string | RegExp;
109
+ hasText?: string | RegExp;
110
+ };
104
111
  /** Defines the preferred Playwright locator method to be used on this LocatorSchema Object */
105
112
  locatorMethod: GetByMethod;
106
113
  /** The human-readable name of the defined locator object, used for debug logging and test report enrichment. */
@@ -203,104 +210,422 @@ declare class PlaywrightReportLogger {
203
210
  attachLogsToTest(testInfo: TestInfo): void;
204
211
  }
205
212
 
206
- type UpdatableLocatorSchemaProperties = Omit<LocatorSchema, "locatorSchemaPath">;
207
- interface WithUpdateMethod {
208
- update(updates: Partial<UpdatableLocatorSchemaProperties>): LocatorSchemaWithMethods;
213
+ /**
214
+ * The GetBy class encapsulates methods for generating and obtaining Playwright Locators using LocatorSchema.
215
+ * It maps locator methods to corresponding Playwright page functions and provides a convenient interface to interact
216
+ * with these locators. It holds a reference to a Playwright Page object and a PlaywrightReportLogger for logging.
217
+ * The constructor initializes the logger and sets up method mappings for locator creation.
218
+ */
219
+ declare class GetBy {
220
+ private page;
221
+ private log;
222
+ private methodMap;
223
+ private subMethodMap;
224
+ constructor(page: Page, pwrl: PlaywrightReportLogger);
225
+ /**
226
+ * Retrieves a Locator based on the details provided in a LocatorSchema.
227
+ * The method identifies the appropriate locator creation function from methodMap and invokes it.
228
+ * Throws an error if the locator method is unsupported.
229
+ */
230
+ getLocator: (locatorSchema: LocatorSchema) => Locator;
231
+ /**
232
+ * Internal method to retrieve a Locator using a specified GetByMethodSubset and LocatorSchema.
233
+ * It identifies the appropriate locator creation function from subMethodMap and invokes it.
234
+ * Throws an error if the caller is unknown or if the initial locator is not found.
235
+ */
236
+ private getBy;
237
+ /**
238
+ * Creates a method for generating a Locator using a specific GetByMethodSubset.
239
+ * Returns a function that takes a LocatorSchema and returns a Locator.
240
+ * The returned function is a locator creation function corresponding to the specified methodName.
241
+ */
242
+ private createByMethod;
243
+ private role;
244
+ private text;
245
+ private label;
246
+ private placeholder;
247
+ private altText;
248
+ private title;
249
+ private locator;
250
+ /**
251
+ * Returns a FrameLocator using the 'frameLocator' selector from a LocatorSchema.
252
+ * Throws an error if the frameLocator is not defined.
253
+ */
254
+ private frameLocator;
255
+ /**
256
+ * Returns a Locator using the 'testId' selector from a LocatorSchema.
257
+ * Throws an error if the testId is not defined.
258
+ */
259
+ private testId;
260
+ /**
261
+ * Returns a Locator using the 'dataCy' selector from a LocatorSchema.
262
+ * Throws an error if the dataCy is undefined.
263
+ */
264
+ private dataCy;
265
+ /**
266
+ * Returns a Locator using the 'id' selector from a LocatorSchema.
267
+ * Throws an error if the id is not defined or the id type is unsupported.
268
+ */
269
+ private id;
209
270
  }
210
- interface WithUpdatesMethod {
271
+
272
+ /**
273
+ * A FilterEntry describes filtering criteria passed to .filter() calls on Playwright locators.
274
+ * has, hasNot: Locator | undefined - Used to filter elements that contain or exclude a certain element.
275
+ * hasText, hasNotText: string | RegExp | undefined - Used to filter elements based on text content.
276
+ */
277
+ type FilterEntry = {
278
+ has?: Locator;
279
+ hasNot?: Locator;
280
+ hasNotText?: string | RegExp;
281
+ hasText?: string | RegExp;
282
+ };
283
+ /**
284
+ * ExtractSubPaths splits a path on '.' and returns a union of progressively longer sub-paths.
285
+ * For example: ExtractSubPaths<"body.section@playground.button@reset"> produces:
286
+ * "body" | "body.section@playground" | "body.section@playground.button@reset"
287
+ */
288
+ type ExtractSubPaths<Path extends string> = Path extends `${infer Head}.${infer Tail}` ? Head | `${Head}.${ExtractSubPaths<Tail>}` : Path;
289
+ /**
290
+ * SubPaths computes valid sub-paths for a given chosen substring (LocatorSubstring).
291
+ * If LocatorSubstring is a string:
292
+ * We return only sub-paths that belong to the chosen substring. For example, if
293
+ * LocatorSubstring = "body.section@playground.button@reset"
294
+ * SubPaths returns only "body", "body.section@playground", and "body.section@playground.button@reset"
295
+ * from the entire union of LocatorSchemaPathType, if they exist.
296
+ */
297
+ type SubPaths<LocatorSchemaPathType extends string, LocatorSubstring extends LocatorSchemaPathType | undefined> = LocatorSubstring extends string ? Extract<LocatorSchemaPathType, LocatorSubstring | ExtractSubPaths<LocatorSubstring>> : never;
298
+ /**
299
+ * UpdatableLocatorSchemaProperties represent the properties of LocatorSchema that can be changed by update/updates,
300
+ * excluding the locatorSchemaPath itself, which remains immutable.
301
+ */
302
+ type LocatorSchemaWithoutPath = Omit<LocatorSchema, "locatorSchemaPath">;
303
+ /** PathIndexPairs links each sub-part of a path to an optional index used in getNestedLocator calls. */
304
+ type PathIndexPairs = {
305
+ path: string;
306
+ index?: number;
307
+ }[];
308
+ /**
309
+ * Ensures LocatorSchemaPath strings are non-empty, do not start/end with dots, and avoid consecutive dots.
310
+ */
311
+ type LocatorSchemaPathValid<Path extends string> = Path extends "" ? never : Path extends `.${string}` | `${string}.` ? never : Path extends `${string}..${string}` ? never : Path;
312
+ /**
313
+ * LocatorSchemaWithMethods is the type returned by getLocatorSchema. It merges LocatorSchema with chainable methods:
314
+ * - update: Modify any properties of any LocatorSchema in the chain that make up the LocatorSchemaPath. Can be chained multiple times, applies updates in the order chained.
315
+ * - addFilter: Add additonal filters to any LocatorSchema in the chain that make up the LocatorSchemaPath. Can be chained multiple times, applies updates in the order chained.
316
+ * - getNestedLocator: Obtain a fully resolved nested locator. Can be chained once after update and addFilter methods if used, ends the chain and returns the nested locator.
317
+ * - getLocator: Obtain the direct locator of the LocatorSchema the full LocatorSchemaPath resolves to. Can be chained once after update and addFilter methods if used, ends the chain and returns the nested locator.
318
+ *
319
+ * schemasMap and filterMap store the deep-copied schemas and associated filters, ensuring immutability and isolation from originals.
320
+ */
321
+ type LocatorSchemaWithMethods<LocatorSchemaPathType extends string, LocatorSubstring extends LocatorSchemaPathType | undefined> = LocatorSchema & {
322
+ /**
323
+ * Contains deepCopies of all the LocatorSchema which make up the full LocatorSchemaPath
324
+ *
325
+ * Not inteded to be directly iteracted with, though you can if needed (debug).
326
+ * Use the chainable methods available on the .getLocatorSchema(LocatorSchemaPath) method instead.
327
+ */
328
+ schemasMap: Map<string, LocatorSchema>;
329
+ /**
330
+ * Contains deepCopies of all the LocatorSchema which make up the full LocatorSchemaPath
331
+ *
332
+ * Not inteded to be directly iteracted with, though you can if needed (debug).
333
+ * Use the chainable methods available on the .getLocatorSchema(LocatorSchemaPath) method instead.
334
+ */
335
+ filterMap: Map<string, FilterEntry[]>;
336
+ /**
337
+ * Allows updating any schema in the chain by specifying the subPath and a partial LocatorSchemaWithoutPath.
338
+ * - Gives compile-time suggestions for valid sub-paths of the LocatorSchemaPath provided to .getLocatorSchema().
339
+ * - If you want to update multiple schemas, chain multiple .update() calls.
340
+ *
341
+ * @example
342
+ * // Direct usage:
343
+ * const submitButton = await poc.getLocatorSchema("main.form.button@submit").update("main.form.button@submit")
344
+ */
345
+ update(subPath: SubPaths<LocatorSchemaPathType, LocatorSubstring>, updates: Partial<LocatorSchemaWithoutPath>): LocatorSchemaWithMethods<LocatorSchemaPathType, LocatorSubstring>;
346
+ /**
347
+ * @deprecated To be removed in version 2.0.0. Use the new `.update(subPath, updates)` method instead, see example.
348
+ *
349
+ * This deprecated update method takes one argument and only updates the LocatorSchema which the full LocatorSchemaPath resolves to.
350
+ *
351
+ * @example
352
+ * // New update method usage:
353
+ * const userInfoSection = await poc
354
+ * .getLocatorSchema("main.form.section")
355
+ * .update("main.form.section", { locatorOptions: { hasText: "User Info:" } })
356
+ * .getNestedLocator();
357
+ */
358
+ update(updates: Partial<LocatorSchemaWithoutPath>): LocatorSchemaWithMethods<LocatorSchemaPathType, LocatorSubstring>;
359
+ /**
360
+ * @deprecated To be removed in version 2.0.0. Use the new `.update(subPath, updates)` method instead, chain the
361
+ * method for each update if multiple, see example.
362
+ *
363
+ * This deprecated updates method uses indices to identify which schema to update.
364
+ *
365
+ * @example
366
+ * // New update method usage:
367
+ * const userInfoSection = await poc
368
+ * .getLocatorSchema("main.form.section")
369
+ * .update("main.form", {
370
+ * role: "form",
371
+ * roleOptions: { name: "Personalia" },
372
+ * locatorMethod: GetByMethod.role,
373
+ * })
374
+ * .update("main.form.section", { locatorOptions: { hasText: /User Info:/i } })
375
+ * .getNestedLocator();
376
+ */
211
377
  updates(indexedUpdates: {
212
- [index: number]: Partial<UpdatableLocatorSchemaProperties> | null;
213
- }): LocatorSchemaWithMethods;
214
- }
215
- interface WithGetNestedLocatorMethod {
378
+ [index: number]: Partial<LocatorSchemaWithoutPath> | null;
379
+ }): LocatorSchemaWithMethods<LocatorSchemaPathType, LocatorSubstring>;
380
+ /**
381
+ * The equivalent of the Playwright locator.filter({...}) method and chainable on .getLocatorSchema(LocatorSchemaPath).
382
+ * Can be chained multiple times to add multiple filters to the same or different LocatorSchema.
383
+ *
384
+ * **See examples further down for usage.**
385
+ *
386
+ * A filter will search for a particular string/RegExp/Locator somewhere inside the element, possibly in a descendant element,
387
+ * case-insensitively (string).
388
+ *
389
+ * The filterData object can contain the following properties:
390
+ * - has: Locator - Filters elements that contain a certain element.
391
+ * - hasNot: Locator - Filters elements that do not contain a certain element.
392
+ * - hasText: string | RegExp - Filters elements based on text content.
393
+ * - hasNotText: string | RegExp - Filters elements that do not contain a certain text content.
394
+ *
395
+ * If you define multiple filterData properties in a single addFilter call instead of multiple addFilter calls, they
396
+ * will be chained after each other(playwright decides the order). If you want to add multiple filters of the same
397
+ * type, you must chain multiple addFilter calls.
398
+ *
399
+ * @example
400
+ * // Adding a filter to the last LocatorSchema in the chain:
401
+ * const userInfoSection = await poc
402
+ * .getLocatorSchema("main.form.section")
403
+ * .addFilter("main.form.section", { hasText: "User Info:" })
404
+ * .getNestedLocator();
405
+ *
406
+ * // Adding multiple filter to the last LocatorSchema in the chain:
407
+ * const userInfoSection = await poc
408
+ * .getLocatorSchema("main.form.section")
409
+ * .addFilter("main.form.section", { hasText: "User Info:" })
410
+ * .addFilter("main.form.section", { hasText: `First Name: ${user.firstName}` })
411
+ * .getNestedLocator();
412
+ *
413
+ * // Adding filters to multiple LocatorSchema in the chain:
414
+ * const submitButton = await poc.getLocator("main.form.button@submit");
415
+ *
416
+ * const userInfoSection = await poc
417
+ * .getLocatorSchema("main.form.section")
418
+ * .addFilter("main.form", { has: submitButton })
419
+ * .addFilter("main.form.section", { hasText: "User Info:" })
420
+ * .getNestedLocator();
421
+ */
422
+ addFilter(subPath: SubPaths<LocatorSchemaPathType, LocatorSubstring>, filterData: FilterEntry): LocatorSchemaWithMethods<LocatorSchemaPathType, LocatorSubstring>;
423
+ /**
424
+ * Asynchronously builds a nested locator based on the LocatorSchemaPath provided by getLocatorSchema("...")
425
+ *
426
+ * Builds a nested locator from all LocatorSchema that make up the full LocatorSchemaPath given by
427
+ * .getLocatorSchema(LocatorSchemaPath). Optionally, you can provide a list of subPaths and indices to have one or more
428
+ * LocatorSchema that make up the full LocatorSchemaPath each resolved to a specific .nth(n) occurrence of the element(s).
429
+ *
430
+ * - Can be chained once after the update and addFilter methods or directly on the .getLocatorSchema method.
431
+ * - getNestedLocator will end the method chain and return a nested Playwright Locator.
432
+ * - Optionally parameter takes a list of key(subPath)-value(index) pairs, the locator constructed from the LocatorSchema
433
+ * with the specified subPath will resolve to the .nth(n) occurrence of the element, within the chain.
434
+ *
435
+ * Test retry: POMWright will set the log level to debug during retries of tests. This will trigger getNestedLocator
436
+ * to resolve the locator in DOM per nesting step and attach the log results to the HTML report for debugging purposes.
437
+ * This enables us to easily see which locator in the chain failed to resolve, making it easier to identify an issue
438
+ * or which LocatorSchema needs to be updated.
439
+ *
440
+ * Debug: Using POMWright's "log" fixture, you can set the log level to "debug" to see the nested locator evaluation
441
+ * results when a test isn't running retry.
442
+ *
443
+ * @example
444
+ * // Usage:
445
+ * const submitButton = await poc.getLocatorSchema("main.form.button@submit").getNestedLocator();
446
+ * await submitButton.click();
447
+ *
448
+ * // With indexing:
449
+ * for (const [index, subscription] of subscriptions.entries()) {
450
+ * const inputUsername = await poc
451
+ * .getLocatorSchema("main.form.item.input@username")
452
+ * .getNestedLocator({ "main.form.item": index });
453
+ * await inputUsername.fill(subscription.username);
454
+ * await inputUsername.blur();
455
+ *
456
+ * const enableServiceCheckbox = await poc
457
+ * .getLocatorSchema("main.form.item.checkbox@enableService")
458
+ * .getNestedLocator({ "main.form.item": index });
459
+ * await enableServiceCheckbox.check();
460
+ * }
461
+ *
462
+ * // indexing multiple subPaths:
463
+ * const something = await poc
464
+ * .getLocatorSchema("main.form.item.something")
465
+ * .getNestedLocator({
466
+ * "main.form": 0, // locator.first() / locator.nth(0)
467
+ * "main.form.item": 1, // locator.nth(1)
468
+ * });
469
+ * await something.click();
470
+ */
471
+ getNestedLocator(subPathIndices?: {
472
+ [K in SubPaths<LocatorSchemaPathType, LocatorSubstring>]?: number | null;
473
+ }): Promise<Locator>;
474
+ /**
475
+ * @deprecated To be removed in version 2.0.0. Use getNestedLocator({ LocatorSchemaPath: index }) instead of
476
+ * number-based indexing, see example.
477
+ *
478
+ * @example
479
+ * // New usage:
480
+ * for (const [index, subscription] of subscriptions.entries()) {
481
+ * const inputUsername = await poc
482
+ * .getLocatorSchema("main.form.item.input@username")
483
+ * .getNestedLocator({ "main.form.item": index });
484
+ * await inputUsername.fill(subscription.username);
485
+ * await inputUsername.blur();
486
+ *
487
+ * const enableServiceCheckbox = await poc
488
+ * .getLocatorSchema("main.form.item.checkbox@enableService")
489
+ * .getNestedLocator({ "main.form.item": index });
490
+ * await enableServiceCheckbox.check();
491
+ * }
492
+ *
493
+ * // indexing multiple subPaths:
494
+ * const something = await poc
495
+ * .getLocatorSchema("main.form.item.something")
496
+ * .getNestedLocator({
497
+ * "main.form": 0, // locator.first() / locator.nth(0)
498
+ * "main.form.item": 1, // locator.nth(1)
499
+ * });
500
+ * await something.click();
501
+ */
216
502
  getNestedLocator(indices?: {
217
503
  [key: number]: number | null;
218
- } | null): Promise<Locator>;
219
- }
220
- interface WithGetLocatorMethod {
504
+ }): Promise<Locator>;
505
+ /**
506
+ * This method does not perform nesting,and will return the locator for which the full LocatorSchemaPath resolves to,
507
+ * provided by getLocatorSchema("...")
508
+ *
509
+ * - Can be chained once after the update and addFilter methods or directly on the .getLocatorSchema method.
510
+ * - getLocator will end the method chain and return a Playwright Locator.
511
+ *
512
+ * @example
513
+ * // Usage:
514
+ * const submitButton = await poc.getLocatorSchema("main.form.button@submit").getLocator();
515
+ * await expect(submitButton, "should only exist one submit button").toHaveCount(1);
516
+ */
221
517
  getLocator(): Promise<Locator>;
222
- }
223
- type LocatorSchemaWithMethods = LocatorSchema & WithUpdateMethod & WithUpdatesMethod & WithGetNestedLocatorMethod & WithGetLocatorMethod & {
224
- schemasMap: Map<string, LocatorSchema>;
225
518
  };
226
- interface ModifiedLocatorSchema extends UpdatableLocatorSchemaProperties {
227
- }
228
519
  /**
229
- * Provides core functionality for dynamically generating nested locators.
230
- * Nested locators help pinpoint elements with higher precision in Playwright tests.
231
- * The class includes methods for adding, updating, and retrieving locator schemas,
232
- * and for building nested locators based on these schemas.
520
+ * GetLocatorBase:
521
+ * The foundational class for managing and constructing nested locators based on LocatorSchemas.
522
+ *
523
+ * Key points:
524
+ * - Generics: <LocatorSchemaPathType, LocatorSubstring>
525
+ * LocatorSchemaPathType is a union of all possible locator paths.
526
+ * LocatorSubstring is either undefined or narrowed to a chosen path.
527
+ *
528
+ * - getLocatorSchema(path):
529
+ * Returns a deep-copied schema and a chainable object (LocatorSchemaWithMethods) that
530
+ * allows calling update, updates, addFilter, and finally getNestedLocator or getLocator.
531
+ *
532
+ * - By using WithMethodsClass, we lock LocatorSubstring = P, the chosen path,
533
+ * ensuring addFilter suggests only valid sub-paths of P.
534
+ *
535
+ * - applyUpdate / applyUpdates and deepMerge:
536
+ * Handle schema modifications without affecting the original definitions.
537
+ *
538
+ * - buildNestedLocator:
539
+ * Assembles nested Playwright locators step-by-step according to the path.
233
540
  */
234
- declare class GetLocatorBase<LocatorSchemaPathType extends string> {
235
- protected pageObjectClass: BasePage<LocatorSchemaPathType, BasePageOptions>;
541
+ declare class GetLocatorBase<LocatorSchemaPathType extends string, LocatorSubstring extends LocatorSchemaPathType | undefined = undefined> {
542
+ protected pageObjectClass: BasePage<LocatorSchemaPathType, BasePageOptions, LocatorSubstring>;
236
543
  protected log: PlaywrightReportLogger;
237
- private getBy;
544
+ protected locatorSubstring?: LocatorSubstring | undefined;
545
+ getBy: GetBy;
238
546
  private locatorSchemas;
547
+ constructor(pageObjectClass: BasePage<LocatorSchemaPathType, BasePageOptions, LocatorSubstring>, log: PlaywrightReportLogger, locatorSubstring?: LocatorSubstring | undefined);
239
548
  /**
240
- * Initializes the GetLocatorBase class with a page object class and a logger.
549
+ * getLocatorSchema:
550
+ * Given a path P, we:
551
+ * 1. Collect deep copies of the schemas involved.
552
+ * 2. Create a WithMethodsClass instance with LocatorSubstring = P.
553
+ * 3. Return a locator schema copy enriched with chainable methods.
241
554
  */
242
- constructor(pageObjectClass: BasePage<LocatorSchemaPathType, BasePageOptions>, log: PlaywrightReportLogger);
555
+ getLocatorSchema<P extends LocatorSchemaPathType>(locatorSchemaPath: P): LocatorSchemaWithMethods<LocatorSchemaPathType, P>;
243
556
  /**
244
- * Retrieves a locator schema with additional methods for manipulation and retrieval of locators.
245
- */
246
- getLocatorSchema(locatorSchemaPath: LocatorSchemaPathType): LocatorSchemaWithMethods;
247
- /**
248
- * Collects deep copies of locator schemas based on a given locator schema path and path-index pairs.
249
- * It ensures that each locator schema and its sub-schemas are properly cloned and stored.
557
+ * collectDeepCopies:
558
+ * Clones and stores all schemas related to the chosen path and its sub-paths.
559
+ * Ensures updates and filters don't affect original schema definitions.
250
560
  */
251
561
  private collectDeepCopies;
252
562
  private isLocatorSchemaWithMethods;
253
563
  /**
254
- * Applies an update to a specific locator schema within the provided map of schemas.
255
- * This method ensures that the specified updates are merged into the targeted locator schema.
564
+ * applyUpdateToSubPath:
565
+ * Applies updates to a specific sub-path schema within schemasMap.
566
+ * Similar to applyUpdate, but we locate the sub-path schema directly by its path.
567
+ */
568
+ applyUpdateToSubPath(schemasMap: Map<string, LocatorSchema>, subPath: LocatorSchemaPathType, updates: Partial<LocatorSchemaWithoutPath>): void;
569
+ /**
570
+ * applyUpdate:
571
+ * Applies updates to a single schema within the schemasMap.
256
572
  */
257
- private applyUpdate;
573
+ applyUpdate(schemasMap: Map<string, LocatorSchema>, locatorSchemaPath: LocatorSchemaPathType, updateData: Partial<LocatorSchema>): void;
258
574
  /**
259
- * Applies multiple updates to locator schemas based on provided path-index pairs and update data.
260
- * This method facilitates batch updating of nested schemas within a complex locator structure.
575
+ * applyUpdates:
576
+ * Applies multiple updates to multiple schemas in the chain, identified by their path indexes.
261
577
  */
262
- private applyUpdates;
578
+ applyUpdates(schemasMap: Map<string, LocatorSchema>, pathIndexPairs: PathIndexPairs, updatesData: {
579
+ [index: number]: Partial<LocatorSchema>;
580
+ }): void;
263
581
  /**
264
- * Creates a new locator schema based on provided schema details and a schema path.
265
- * This method structures a new locator schema ready for inclusion in the locator management system.
582
+ * createLocatorSchema:
583
+ * Creates a fresh LocatorSchema object by merging provided schemaDetails with a required locatorSchemaPath.
266
584
  */
267
585
  private createLocatorSchema;
268
586
  /**
269
- * Adds a new locator schema to the internal map of locator schemas.
270
- * This method ensures that the new schema is properly registered and can be referenced and used in locator generation.
587
+ * addSchema:
588
+ * Registers a new LocatorSchema under the given locatorSchemaPath.
589
+ * Throws an error if a schema already exists at that path.
271
590
  */
272
- addSchema(locatorSchemaPath: LocatorSchemaPathType, schemaDetails: ModifiedLocatorSchema): void;
591
+ addSchema(locatorSchemaPath: LocatorSchemaPathType & LocatorSchemaPathValid<LocatorSchemaPathType>, schemaDetails: LocatorSchemaWithoutPath): void;
273
592
  /**
274
- * Safely retrieves a locator schema function based on a given path.
275
- * This method provides a secure way to access locator schemas, ensuring that only valid paths are used.
593
+ * safeGetLocatorSchema:
594
+ * Safely retrieves a schema function if available for the given path.
276
595
  */
277
596
  private safeGetLocatorSchema;
278
597
  /**
279
- * Extracts path-index pairs from a given schema path.
280
- * This utility function breaks down a complex path into manageable parts,
281
- * associating each part with its corresponding index when necessary.
598
+ * extractPathsFromSchema:
599
+ * Splits a path into incremental sub-paths and associates them with optional indices.
600
+ * Used by updates and getNestedLocator methods.
282
601
  */
283
- private extractPathsFromSchema;
602
+ extractPathsFromSchema: (paths: string, indices?: Record<number, number>) => PathIndexPairs;
284
603
  /**
285
- * logError is a utility function for logging errors with detailed debug information
286
- * It re-throws the error after logging to ensure the test will fail.
604
+ * logError:
605
+ * Logs detailed error information and re-throws the error to ensure tests fail as expected.
287
606
  */
288
607
  private logError;
289
- /** Merges 'source' into 'target', combining their properties into a new isolated object. */
608
+ /**
609
+ * deepMerge:
610
+ * Recursively merges source properties into target, validating them against LocatorSchema to ensure no invalid keys.
611
+ * Ensures immutability by creating a new object rather than modifying in place.
612
+ */
290
613
  private deepMerge;
291
614
  /**
292
- * Assembles nested locators based on a locator schema path and optional indices for locating specific elements.
293
- * This method orchestrates the process of building a locator that can resolve to a specific element or set of
294
- * elements in the DOM.
615
+ * buildNestedLocator:
616
+ * Constructs a nested locator by iterating through each sub-path of locatorSchemaPath and chaining locators.
617
+ * Applies filters, indexing (nth), and logs details for debugging during test retries.
295
618
  */
296
- protected buildNestedLocator: (locatorSchemaPath: LocatorSchemaPathType, schemasMap: Map<string, LocatorSchema>, indices?: Record<number, number>) => Promise<Locator>;
619
+ buildNestedLocator: (locatorSchemaPath: LocatorSchemaPathType, schemasMap: Map<string, LocatorSchema>, filterMap: Map<string, FilterEntry[]>, indices?: Record<number, number>) => Promise<Locator>;
297
620
  /**
298
- * Evaluates the current locator, capturing details about its resolution status and the elements it resolves to.
621
+ * evaluateCurrentLocator:
622
+ * Gathers debug information about the current locator's resolved elements.
623
+ * Helps with logging and debugging complex locator chains.
299
624
  */
300
625
  private evaluateCurrentLocator;
301
626
  /**
302
- * Retrieves and compiles attributes of elements resolved by a Playwright locator per nesting step.
303
- * This method provides insights into the elements targeted by the locator, aiding in debugging and verification.
627
+ * evaluateAndGetAttributes:
628
+ * Extracts tagName and attributes from all elements matched by the locator for debugging purposes.
304
629
  */
305
630
  private evaluateAndGetAttributes;
306
631
  }
@@ -363,6 +688,10 @@ declare class SessionStorage {
363
688
  clear(): Promise<void>;
364
689
  }
365
690
 
691
+ /**
692
+ * BasePageOptions can define optional patterns for baseUrl and urlPath.
693
+ * Defaults assume they are strings if not specified.
694
+ */
366
695
  type BasePageOptions = {
367
696
  urlOptions?: {
368
697
  baseUrlType?: string | RegExp;
@@ -380,13 +709,29 @@ type ExtractFullUrlType<T extends BasePageOptions> = T["urlOptions"] extends {
380
709
  } | {
381
710
  urlPathType: RegExp;
382
711
  } ? RegExp : string;
383
- /** The BasePage class, extended by all Page Object Classes */
712
+ /**
713
+ * BasePage:
714
+ * The foundational class for all Page Object Classes.
715
+ *
716
+ * Generics:
717
+ * - LocatorSchemaPathType: A union of valid locator paths.
718
+ * - Options: Configuration type for URLs.
719
+ * - LocatorSubstring: The chosen substring locator.
720
+ *
721
+ * We instantiate GetLocatorBase with these generics. When calling getLocatorSchema,
722
+ * the chosen path P sets LocatorSubstring = P, ensuring methods like addFilter only suggests valid sub-paths.
723
+ *
724
+ * BasePage provides:
725
+ * - Common properties: page, testInfo, selectors, URLs, logging, sessionStorage.
726
+ * - getNestedLocator & getLocator methods that delegate to getLocatorSchema.
727
+ * - Abstract initLocatorSchemas method to be implemented by concrete POCs.
728
+ */
384
729
  declare abstract class BasePage<LocatorSchemaPathType extends string, Options extends BasePageOptions = {
385
730
  urlOptions: {
386
731
  baseUrlType: string;
387
732
  urlPathType: string;
388
733
  };
389
- }> {
734
+ }, LocatorSubstring extends LocatorSchemaPathType | undefined = undefined> {
390
735
  /** Provides Playwright page methods */
391
736
  page: Page;
392
737
  /** Playwright TestInfo contains information about currently running test, available to any test function */
@@ -397,6 +742,7 @@ declare abstract class BasePage<LocatorSchemaPathType extends string, Options ex
397
742
  baseUrl: ExtractBaseUrlType<Options>;
398
743
  /** The URL path of the Page Object Class */
399
744
  urlPath: ExtractUrlPathType<Options>;
745
+ /** The full URL of the Page Object Class */
400
746
  fullUrl: ExtractFullUrlType<Options>;
401
747
  /** The name of the Page Object Class */
402
748
  pocName: string;
@@ -404,33 +750,137 @@ declare abstract class BasePage<LocatorSchemaPathType extends string, Options ex
404
750
  protected log: PlaywrightReportLogger;
405
751
  /** The SessionStorage class provides methods for setting and getting session storage data in Playwright.*/
406
752
  sessionStorage: SessionStorage;
407
- protected locators: GetLocatorBase<LocatorSchemaPathType>;
408
- constructor(page: Page, testInfo: TestInfo, baseUrl: ExtractBaseUrlType<Options>, urlPath: ExtractUrlPathType<Options>, pocName: string, pwrl: PlaywrightReportLogger);
753
+ /**
754
+ * locators:
755
+ * An instance of GetLocatorBase that handles schema management and provides getLocatorSchema calls.
756
+ * Initially, LocatorSubstring is undefined. Once getLocatorSchema(path) is called,
757
+ * we get a chainable object typed with LocatorSubstring = P.
758
+ */
759
+ protected locators: GetLocatorBase<LocatorSchemaPathType, LocatorSubstring>;
760
+ constructor(page: Page, testInfo: TestInfo, baseUrl: ExtractBaseUrlType<Options>, urlPath: ExtractUrlPathType<Options>, pocName: string, pwrl: PlaywrightReportLogger, locatorSubstring?: LocatorSubstring);
761
+ /**
762
+ * constructFullUrl:
763
+ * Combines baseUrl and urlPath, handling both strings and RegExps.
764
+ * Ensures a flexible approach to URL matching (string or regex-based).
765
+ */
409
766
  private constructFullUrl;
410
767
  /**
411
- * getNestedLocator(indices?: { [key: number]: number | null } | null)
412
- * - Asynchronously retrieves a nested locator based on the LocatorSchemaPath provided by getLocatorSchema("...")
413
- * - Can be chained after the update and updates methods, getNestedLocator will end the chain.
414
- * - The optional parameter of the method takes an object with 0-based indices "{0: 0, 3: 1}" for one or more locators
415
- * to be nested given by sub-paths (indices correspond to last "word" of a sub-path).
416
- * - Returns a promise that resolves to the nested locator.
768
+ * Short-hand wrapper method for calling .getLocatorSchema(LocatorSchemaPath).getNestedLocator(subPathIndices?)
769
+ *
770
+ * Asynchronously builds a nested locator from all LocatorSchema that make up the full LocatorSchemaPath. Optionally,
771
+ * you can provide a list of subPaths and indices to have one or more LocatorSchema that make up the full
772
+ * LocatorSchemaPath each resolved to a specific .nth(n) occurrence of the element(s).
773
+ *
774
+ * Note: This short-hand wrapper method is useful for quickly building nested locators without having to call
775
+ * getLocatorSchema("...") first. On the other hand, it can't be used to update or add filters to the LocatorSchema.
776
+ *
777
+ * Test retry: POMWright will set the log level to debug during retries of tests. This will trigger getNestedLocator
778
+ * to resolve the locator in DOM per nesting step and attach the log results to the HTML report for debugging purposes.
779
+ * This enables us to easily see which locator in the chain failed to resolve, making it easier to identify an issue
780
+ * or which LocatorSchema needs to be updated.
781
+ *
782
+ * Debug: Using POMWright's "log" fixture, you can set the log level to "debug" to see the nested locator evaluation
783
+ * results when a test isn't running retry.
784
+ *
785
+ * @example
786
+ * // Usage:
787
+ * const submitButton = await poc.getNestedLocator("main.form.button@submit");
788
+ * await submitButton.click();
789
+ *
790
+ * // With indexing:
791
+ * const something = await poc.getNestedLocator("main.form.item.something", {
792
+ * "main.form": 0, // locator.first() / locator.nth(0)
793
+ * "main.form.item": 1, // locator.nth(1)
794
+ * });
795
+ * await something.click();
417
796
  */
418
- getNestedLocator: (locatorSchemaPath: LocatorSchemaPathType, indices?: {
797
+ getNestedLocator<P extends LocatorSchemaPathType>(locatorSchemaPath: P, subPathIndices?: {
798
+ [K in SubPaths<LocatorSchemaPathType, P>]?: number | null;
799
+ }): Promise<Locator>;
800
+ /**
801
+ * @deprecated Use { SubPaths: index } instead of {4:2}, i.e. subPath-based keys instead of indices, see example.
802
+ *
803
+ * Deprecated short-hand wrapper method for calling .getLocatorSchema(LocatorSchemaPath).getNestedLocator(subPathIndices?)
804
+ *
805
+ * @example
806
+ * // New Usage:
807
+ * const something = await poc.getNestedLocator("main.form.item.something", {
808
+ * "main.form": 0, // locator.first() / locator.nth(0)
809
+ * "main.form.item": 1, // locator.nth(1)
810
+ * });
811
+ * await something.click();
812
+ */
813
+ getNestedLocator(locatorSchemaPath: LocatorSchemaPathType, indices?: {
419
814
  [key: number]: number | null;
420
- } | null) => Promise<Locator>;
815
+ } | null): Promise<Locator>;
421
816
  /**
422
- * getLocator()
423
- * - Asynchronously retrieves a locator based on the current LocatorSchema. This method does not perform nesting,
424
- * and will return the locator for which the full LocatorSchemaPath resolves to, provided by getLocatorSchema("...")
425
- * - Can be chained after the update and updates methods, getLocator will end the chain.
426
- * - Returns a promise that resolves to the locator.
817
+ * Short-hand wrapper method for calling .getLocatorSchema(LocatorSchemaPath).getLocator()
818
+ *
819
+ * This method does not perform nesting,and will return the locator for which the full LocatorSchemaPath resolves to,
820
+ * provided by getLocatorSchema("...")
821
+ *
822
+ * Note: This short-hand wrapper method is useful for quickly getting a locator without having to call
823
+ * getLocatorSchema("...") first. On the other hand, it can't be used to update or add filters to the LocatorSchema.
824
+ *
825
+ * @example
826
+ * // Usage:
827
+ * const submitButton = await poc.getLocator("main.form.button@submit");
828
+ * await expect(submitButton, "should only exist one submit button").toHaveCount(1);
427
829
  */
428
830
  getLocator: (locatorSchemaPath: LocatorSchemaPathType) => Promise<Locator>;
429
831
  /**
430
- * Abstract method to initialize locator schemas.
832
+ * getLocatorSchema:
833
+ * Delegates to this.locators.getLocatorSchema.
834
+ * Returns a chainable schema object for the given path.
835
+ * Once called with a specific path P, the update and addFilter methods are restricted to sub-paths of P.
836
+ *
837
+ * The "getLocatorSchema" method is used to retrieve an updatable deep copy of a LocatorSchema defined in the
838
+ * GetLocatorBase class. It enriches the returned schema with additional methods to handle updates and retrieval of
839
+ * deep copy locators.
840
+ *
841
+ * getLocatorSchema adds the following chainable methods to the returned LocatorSchemaWithMethods object:
842
+ *
843
+ * update
844
+ * - Allows updating any schema in the chain by specifying the subPath directly.
845
+ * - Gives compile-time suggestions for valid sub-paths of the LocatorSchemaPath provided to .getLocatorSchema().
846
+ * - If you want to update multiple schemas, chain multiple .update() calls.
847
+ *
848
+ * addFilter(subPath: SubPaths<LocatorSchemaPathType, LocatorSubstring>, filterData: FilterEntry)
849
+ * - The equivalent of the Playwright locator.filter() method
850
+ * - This method is used for filtering the specified locator based on the provided filterData.
851
+ * - Can be chained multiple times to add multiple filters to the same or different LocatorSchema.
852
+ *
853
+ * getNestedLocator
854
+ * - Asynchronously builds a nested locator based on the LocatorSchemaPath provided by getLocatorSchema("...")
855
+ * - Can be chained once after the update and addFilter methods or directly on the .getLocatorSchema method.
856
+ * - getNestedLocator will end the method chain and return a nested Playwright Locator.
857
+ * - Optionally parameter takes a list of key(subPath)-value(index) pairs, the locator constructed from the LocatorSchema
858
+ * with the specified subPath will resolve to the .nth(n) occurrence of the element, within the chain.
859
+ *
860
+ * getLocator()
861
+ * - Asynchronously retrieves a locator based on the current LocatorSchemaPath.
862
+ * - This method does not perform nesting and will return the locator for which the full LocatorSchemaPath resolves to, provided by getLocatorSchema("...")
863
+ * - Can be chained once after the update and addFilter methods or directly on the .getLocatorSchema method.
864
+ * - getLocator will end the method chain and return a Playwright Locator.
865
+ *
866
+ * Note: Calling getLocator() and getNestedLocator() on the same LocatorSchemaPath will return a Locator for the same
867
+ * element, but the Locator returned by getNestedLocator() will be a locator resolving to said same element through
868
+ * a chain of locators. While the Locator returned by getLocator() will be a single locator which resolves directly
869
+ * to said element. Thus getLocator() is rarely used, while getNestedLocator() is used extensively.
870
+ *
871
+ * That said, for certain use cases, getLocator() can be useful, and you could use it to manually chain locators
872
+ * yourself if some edge case required it. Though, it would be likely be more prudent to expand your LocatorSchemaPath
873
+ * type and initLocatorSchemas() method to include the additional locators you need for the given POC, and then use
874
+ * getNestedLocator() instead, or by implementing a helper method on your Page Object Class.
875
+ */
876
+ getLocatorSchema<P extends LocatorSchemaPathType>(path: P): LocatorSchemaWithMethods<LocatorSchemaPathType, P>;
877
+ /**
878
+ * initLocatorSchemas:
879
+ * Abstract method to be implemented by each POC.
880
+ * POCs define their own type LocatorSchemaPath and add their schemas using locators.addSchema(...).
431
881
  *
432
882
  * Each Page Object Class (POC) extending BasePage should define its own
433
- * LocatorSchemaPathType, which is a string type using dot (".") notation.
883
+ * LocatorSchemaPath type, which is a string type using dot (".") notation.
434
884
  * The format should start and end with a word, and words should be separated by dots.
435
885
  * For example: "section.subsection.element".
436
886
  *
@@ -506,56 +956,6 @@ declare abstract class BasePage<LocatorSchemaPathType extends string, Options ex
506
956
  * }
507
957
  */
508
958
  protected abstract initLocatorSchemas(): void;
509
- /**
510
- * The "getLocatorSchema" method is used to retrieve an updatable deep copy of a LocatorSchema defined in the
511
- * GetLocatorBase class. It enriches the returned schema with additional methods to handle updates and retrieval of
512
- * deep copy locators.
513
- *
514
- * getLocatorSchema adds the following chainable methods to the returned LocatorSchemaWithMethods object:
515
- *
516
- * update(updates: Partial< UpdatableLocatorSchemaProperties >)
517
- * - Allows updating the properties of the LocatorSchema which the full LocatorSchemaPath resolves to.
518
- * - This method is used for modifying the current schema without affecting the original schema.
519
- * - Takes a "LocatorSchema" object which omits the locatorSchemaPath parameter as input, the parameters provided
520
- * will overwrite the corresponding property in the current schema.
521
- * - Returns the updated deep copy of the "LocatorSchema" with methods.
522
- * - Can be chained with the update and updates methods, and the getLocator or getNestedLocator method.
523
- *
524
- * updates(indexedUpdates: { [index: number]: Partial< UpdatableLocatorSchemaProperties > | null }):
525
- * - Similar to update, but allows updating any locator in the nested chain (all sub-paths of the LocatorSchemaPath).
526
- * - This method can modify the current deep copy of each LocatorSchema that each sub-path resolves to without
527
- * affecting the original schemas
528
- * - Takes an object where keys represent the index of the last "word" of a sub-path, where the value per key is a
529
- * "LocatorSchema" object which omits the locatorSchemaPath parameter as input, the parameters provided will overwrite
530
- * the corresponding property in the given schema.
531
- * - Returns the updated deep copy of the LocatorSchema object with methods and its own updated deep copies for all
532
- * LocatorSchema each sub-path resolved to.
533
- * - Can be chained with the update and updates methods, and the getLocator or getNestedLocator method.
534
- *
535
- * getNestedLocator(indices?: { [key: number]: number | null } | null)
536
- * - Asynchronously retrieves a nested locator based on the LocatorSchemaPath provided by getLocatorSchema("...")
537
- * - Can be chained after the update and updates methods, getNestedLocator will end the chain.
538
- * - The optional parameter of the method takes an object with 0-based indices "{0: 0, 3: 1}" for one or more locators
539
- * to be nested given by sub-paths (indices correspond to last "word" of a sub-path).
540
- * - Returns a promise that resolves to the nested locator.
541
- *
542
- * getLocator()
543
- * - Asynchronously retrieves a locator based on the current LocatorSchema. This method does not perform nesting,
544
- * and will return the locator for which the full LocatorSchemaPath resolves to, provided by getLocatorSchema("...")
545
- * - Can be chained after the update and updates methods, getLocator will end the chain.
546
- * - Returns a promise that resolves to the locator.
547
- *
548
- * Note: Calling getLocator() and getNestedLocator() on the same LocatorSchemaPath will return a Locator for the same
549
- * element, but the Locator returned by getNestedLocator() will be a locator resolving to said same element through
550
- * a chain of locators. While the Locator returned by getLocator() will be a single locator which resolves directly
551
- * to said element. Thus getLocator() is rarely used, while getNestedLocator() is used extensively.
552
- *
553
- * That said, for certain use cases, getLocator() can be useful, and you could use it to manually chain locators
554
- * yourself if some edge case required it. Though, it would be likely be more prudent to expand your LocatorSchemaPath
555
- * type and initLocatorSchemas() method to include the additional locators you need for the given POC, and then use
556
- * getNestedLocator() instead.
557
- */
558
- getLocatorSchema(locatorSchemaPath: LocatorSchemaPathType): LocatorSchemaWithMethods;
559
959
  }
560
960
 
561
961
  type baseFixtures = {
@@ -571,4 +971,4 @@ declare class BaseApi {
571
971
  constructor(baseUrl: string, apiName: string, context: APIRequestContext, pwrl: PlaywrightReportLogger);
572
972
  }
573
973
 
574
- export { type AriaRoleType, BaseApi, BasePage, type BasePageOptions, type ExtractBaseUrlType, type ExtractFullUrlType, type ExtractUrlPathType, GetByMethod, GetLocatorBase, type LocatorSchema, PlaywrightReportLogger, test };
974
+ export { type AriaRoleType, BaseApi, BasePage, type BasePageOptions, type ExtractBaseUrlType, type ExtractFullUrlType, type ExtractUrlPathType, GetByMethod, GetLocatorBase, type LocatorSchema, type LocatorSchemaWithoutPath, PlaywrightReportLogger, test };