pomwright 1.1.1 → 1.2.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.mjs CHANGED
@@ -63,6 +63,12 @@ var locatorSchemaDummy = {
63
63
  testId: void 0,
64
64
  dataCy: void 0,
65
65
  id: void 0,
66
+ filter: {
67
+ has: void 0,
68
+ hasNot: void 0,
69
+ hasNotText: void 0,
70
+ hasText: void 0
71
+ },
66
72
  locatorMethod: void 0,
67
73
  locatorSchemaPath: void 0
68
74
  };
@@ -235,52 +241,72 @@ var GetBy = class {
235
241
  var REQUIRED_PROPERTIES_FOR_LOCATOR_SCHEMA_WITH_METHODS = [
236
242
  "update",
237
243
  "updates",
244
+ "addFilter",
238
245
  "getNestedLocator",
239
246
  "getLocator",
240
247
  "locatorSchemaPath",
241
248
  "locatorMethod",
242
- "schemasMap"
249
+ "schemasMap",
250
+ "filterMap"
243
251
  ];
252
+ var safeStringifyOfNestedLocatorResults = (obj) => {
253
+ const seen = /* @__PURE__ */ new WeakSet();
254
+ return JSON.stringify(
255
+ obj,
256
+ (key, value) => {
257
+ if (value instanceof Map) {
258
+ return Array.from(value.entries());
259
+ }
260
+ if (value instanceof RegExp) {
261
+ return { type: "RegExp", source: value.source, flags: value.flags };
262
+ }
263
+ if (value && typeof value === "object" && value.constructor && value.constructor.name === "Locator") {
264
+ return { type: "Locator", note: "Custom placeholder - Locators are complex." };
265
+ }
266
+ if (typeof value === "object" && value !== null) {
267
+ if (seen.has(value)) return "[Circular]";
268
+ seen.add(value);
269
+ }
270
+ return value;
271
+ },
272
+ 2
273
+ );
274
+ };
244
275
  var GetLocatorBase = class {
245
- /**
246
- * Initializes the GetLocatorBase class with a page object class and a logger.
247
- */
248
- constructor(pageObjectClass, log) {
276
+ constructor(pageObjectClass, log, locatorSubstring) {
249
277
  this.pageObjectClass = pageObjectClass;
250
278
  this.log = log;
279
+ this.locatorSubstring = locatorSubstring;
251
280
  this.locatorSchemas = /* @__PURE__ */ new Map();
252
281
  this.getBy = new GetBy(this.pageObjectClass.page, this.log.getNewChildLogger("GetBy"));
253
282
  }
254
283
  getBy;
255
284
  locatorSchemas;
256
285
  /**
257
- * Retrieves a locator schema with additional methods for manipulation and retrieval of locators.
286
+ * getLocatorSchema:
287
+ * Given a path P, we:
288
+ * 1. Collect deep copies of the schemas involved.
289
+ * 2. Create a WithMethodsClass instance with LocatorSubstring = P.
290
+ * 3. Return a locator schema copy enriched with chainable methods.
258
291
  */
259
292
  getLocatorSchema(locatorSchemaPath) {
260
293
  const pathIndexPairs = this.extractPathsFromSchema(locatorSchemaPath);
261
294
  const schemasMap = this.collectDeepCopies(locatorSchemaPath, pathIndexPairs);
262
295
  const locatorSchemaCopy = schemasMap.get(locatorSchemaPath);
263
296
  locatorSchemaCopy.schemasMap = schemasMap;
264
- const self = this;
265
- locatorSchemaCopy.update = function(updates) {
266
- self.applyUpdate(schemasMap, locatorSchemaPath, updates);
267
- return this;
268
- };
269
- locatorSchemaCopy.updates = function(indexedUpdates) {
270
- self.applyUpdates(schemasMap, pathIndexPairs, indexedUpdates);
271
- return this;
272
- };
273
- locatorSchemaCopy.getNestedLocator = async (indices) => {
274
- return await this.buildNestedLocator(locatorSchemaPath, schemasMap, indices);
275
- };
276
- locatorSchemaCopy.getLocator = async () => {
277
- return this.getBy.getLocator(locatorSchemaCopy);
278
- };
279
- return locatorSchemaCopy;
297
+ locatorSchemaCopy.filterMap = /* @__PURE__ */ new Map();
298
+ const wrapper = new WithMethodsClass(
299
+ this.pageObjectClass,
300
+ this.log,
301
+ locatorSchemaPath,
302
+ schemasMap
303
+ );
304
+ return wrapper.init(locatorSchemaPath, locatorSchemaCopy);
280
305
  }
281
306
  /**
282
- * Collects deep copies of locator schemas based on a given locator schema path and path-index pairs.
283
- * It ensures that each locator schema and its sub-schemas are properly cloned and stored.
307
+ * collectDeepCopies:
308
+ * Clones and stores all schemas related to the chosen path and its sub-paths.
309
+ * Ensures updates and filters don't affect original schema definitions.
284
310
  */
285
311
  collectDeepCopies(locatorSchemaPath, pathIndexPairs) {
286
312
  const schemasMap = /* @__PURE__ */ new Map();
@@ -305,8 +331,25 @@ var GetLocatorBase = class {
305
331
  return REQUIRED_PROPERTIES_FOR_LOCATOR_SCHEMA_WITH_METHODS.every((p) => p in schema);
306
332
  }
307
333
  /**
308
- * Applies an update to a specific locator schema within the provided map of schemas.
309
- * This method ensures that the specified updates are merged into the targeted locator schema.
334
+ * applyUpdateToSubPath:
335
+ * Applies updates to a specific sub-path schema within schemasMap.
336
+ * Similar to applyUpdate, but we locate the sub-path schema directly by its path.
337
+ */
338
+ applyUpdateToSubPath(schemasMap, subPath, updates) {
339
+ const schema = schemasMap.get(subPath);
340
+ if (!schema) {
341
+ throw new Error(`No schema found for sub-path: '${subPath}'`);
342
+ }
343
+ const updatedSchema = this.deepMerge(schema, updates);
344
+ if (this.isLocatorSchemaWithMethods(schema)) {
345
+ Object.assign(schema, updatedSchema);
346
+ } else {
347
+ schemasMap.set(subPath, updatedSchema);
348
+ }
349
+ }
350
+ /**
351
+ * applyUpdate:
352
+ * Applies updates to a single schema within the schemasMap.
310
353
  */
311
354
  applyUpdate(schemasMap, locatorSchemaPath, updateData) {
312
355
  const schema = schemasMap.get(locatorSchemaPath);
@@ -320,8 +363,8 @@ var GetLocatorBase = class {
320
363
  }
321
364
  }
322
365
  /**
323
- * Applies multiple updates to locator schemas based on provided path-index pairs and update data.
324
- * This method facilitates batch updating of nested schemas within a complex locator structure.
366
+ * applyUpdates:
367
+ * Applies multiple updates to multiple schemas in the chain, identified by their path indexes.
325
368
  */
326
369
  applyUpdates(schemasMap, pathIndexPairs, updatesData) {
327
370
  for (const [index, updateAtIndex] of Object.entries(updatesData)) {
@@ -340,16 +383,17 @@ var GetLocatorBase = class {
340
383
  }
341
384
  }
342
385
  /**
343
- * Creates a new locator schema based on provided schema details and a schema path.
344
- * This method structures a new locator schema ready for inclusion in the locator management system.
386
+ * createLocatorSchema:
387
+ * Creates a fresh LocatorSchema object by merging provided schemaDetails with a required locatorSchemaPath.
345
388
  */
346
389
  createLocatorSchema(schemaDetails, locatorSchemaPath) {
347
390
  const schema = { ...schemaDetails, locatorSchemaPath };
348
391
  return schema;
349
392
  }
350
393
  /**
351
- * Adds a new locator schema to the internal map of locator schemas.
352
- * This method ensures that the new schema is properly registered and can be referenced and used in locator generation.
394
+ * addSchema:
395
+ * Registers a new LocatorSchema under the given locatorSchemaPath.
396
+ * Throws an error if a schema already exists at that path.
353
397
  */
354
398
  addSchema(locatorSchemaPath, schemaDetails) {
355
399
  const newLocatorSchema = this.createLocatorSchema(schemaDetails, locatorSchemaPath);
@@ -365,16 +409,16 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
365
409
  this.locatorSchemas.set(locatorSchemaPath, () => newLocatorSchema);
366
410
  }
367
411
  /**
368
- * Safely retrieves a locator schema function based on a given path.
369
- * This method provides a secure way to access locator schemas, ensuring that only valid paths are used.
412
+ * safeGetLocatorSchema:
413
+ * Safely retrieves a schema function if available for the given path.
370
414
  */
371
415
  safeGetLocatorSchema(path) {
372
416
  return this.locatorSchemas.get(path);
373
417
  }
374
418
  /**
375
- * Extracts path-index pairs from a given schema path.
376
- * This utility function breaks down a complex path into manageable parts,
377
- * associating each part with its corresponding index when necessary.
419
+ * extractPathsFromSchema:
420
+ * Splits a path into incremental sub-paths and associates them with optional indices.
421
+ * Used by updates and getNestedLocator methods.
378
422
  */
379
423
  extractPathsFromSchema = (paths, indices = {}) => {
380
424
  const schemaParts = paths.split(".");
@@ -388,8 +432,8 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
388
432
  });
389
433
  };
390
434
  /**
391
- * logError is a utility function for logging errors with detailed debug information
392
- * It re-throws the error after logging to ensure the test will fail.
435
+ * logError:
436
+ * Logs detailed error information and re-throws the error to ensure tests fail as expected.
393
437
  */
394
438
  logError = (error, locatorSchemaPath, currentLocator, currentPath, pathIndexPairs, nestedLocatorResults) => {
395
439
  const errorDetails = {
@@ -401,7 +445,7 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
401
445
  locatorString: currentLocator,
402
446
  isNotNull: true
403
447
  } : { isNotNull: false },
404
- nestedLocatorResults
448
+ nestedLocatorResults: safeStringifyOfNestedLocatorResults(nestedLocatorResults)
405
449
  };
406
450
  this.log.error(
407
451
  "An error occurred during nested locator construction.\n",
@@ -410,7 +454,11 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
410
454
  );
411
455
  throw error;
412
456
  };
413
- /** Merges 'source' into 'target', combining their properties into a new isolated object. */
457
+ /**
458
+ * deepMerge:
459
+ * Recursively merges source properties into target, validating them against LocatorSchema to ensure no invalid keys.
460
+ * Ensures immutability by creating a new object rather than modifying in place.
461
+ */
414
462
  deepMerge(target, source, schema = getLocatorSchemaDummy()) {
415
463
  const merged = { ...target };
416
464
  for (const key of Object.keys(source)) {
@@ -435,7 +483,6 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
435
483
  } else {
436
484
  merged[key] = this.deepMerge(
437
485
  {},
438
- // Updated type here
439
486
  sourceValue,
440
487
  schema[key]
441
488
  );
@@ -456,18 +503,17 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
456
503
  return merged;
457
504
  }
458
505
  /**
459
- * Assembles nested locators based on a locator schema path and optional indices for locating specific elements.
460
- * This method orchestrates the process of building a locator that can resolve to a specific element or set of
461
- * elements in the DOM.
506
+ * buildNestedLocator:
507
+ * Constructs a nested locator by iterating through each sub-path of locatorSchemaPath and chaining locators.
508
+ * Applies filters, indexing (nth), and logs details for debugging during test retries.
462
509
  */
463
- buildNestedLocator = async (locatorSchemaPath, schemasMap, indices = {}) => {
510
+ buildNestedLocator = async (locatorSchemaPath, schemasMap, filterMap, indices = {}) => {
464
511
  return await test.step(`${this.pageObjectClass.pocName}: Build Nested Locator`, async () => {
465
512
  const pathIndexPairs = this.extractPathsFromSchema(locatorSchemaPath, indices);
466
513
  let currentLocator = null;
467
514
  let currentIFrame = null;
468
515
  const nestedLocatorResults = {
469
516
  LocatorSchema: null,
470
- // Initialize as an empty object
471
517
  NestingSteps: []
472
518
  };
473
519
  for (const { path, index } of pathIndexPairs) {
@@ -475,32 +521,42 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
475
521
  if (!currentSchema) continue;
476
522
  try {
477
523
  const nextLocator = this.getBy.getLocator(currentSchema);
478
- if (currentLocator) {
479
- currentLocator = currentLocator.locator(nextLocator);
480
- if (index != null) {
481
- currentLocator = currentLocator.nth(index);
482
- }
483
- } else {
484
- currentLocator = nextLocator;
485
- if (index != null) {
486
- currentLocator = currentLocator.nth(index);
524
+ currentLocator = currentLocator ? currentLocator.locator(nextLocator) : nextLocator;
525
+ if (currentSchema.locatorMethod !== "frameLocator" /* frameLocator */ && currentSchema.filter) {
526
+ currentLocator = currentLocator.filter({
527
+ has: currentSchema.filter.has,
528
+ hasNot: currentSchema.filter.hasNot,
529
+ hasNotText: currentSchema.filter.hasNotText,
530
+ hasText: currentSchema.filter.hasText
531
+ });
532
+ }
533
+ const filterEntries = filterMap.get(path);
534
+ if (filterEntries) {
535
+ for (const filterData of filterEntries) {
536
+ currentLocator = currentLocator.filter({
537
+ has: filterData.has,
538
+ hasNot: filterData.hasNot,
539
+ hasNotText: filterData.hasNotText,
540
+ hasText: filterData.hasText
541
+ });
487
542
  }
488
543
  }
544
+ if (index != null) {
545
+ currentLocator = currentLocator.nth(index);
546
+ }
489
547
  if (this.log.isLogLevelEnabled("debug")) {
490
548
  if (!nestedLocatorResults.LocatorSchema) {
491
- const safeGetLocatorSchema = this.safeGetLocatorSchema(locatorSchemaPath);
492
- if (safeGetLocatorSchema !== void 0) {
493
- nestedLocatorResults.LocatorSchema = safeGetLocatorSchema();
549
+ const schemaFromMap = schemasMap.get(locatorSchemaPath);
550
+ if (schemaFromMap) {
551
+ nestedLocatorResults.LocatorSchema = schemaFromMap;
494
552
  }
495
553
  }
496
554
  if (currentSchema.locatorMethod === "frameLocator" /* frameLocator */) {
497
555
  if (!currentIFrame) {
498
556
  currentIFrame = currentSchema.frameLocator;
499
557
  }
500
- if (currentIFrame != null && currentSchema.frameLocator != null) {
501
- if (currentIFrame.endsWith(currentSchema.frameLocator)) {
502
- currentIFrame += ` -> ${currentSchema.frameLocator}`;
503
- }
558
+ if (currentIFrame && currentSchema.frameLocator && currentIFrame.endsWith(currentSchema.frameLocator)) {
559
+ currentIFrame += ` -> ${currentSchema.frameLocator}`;
504
560
  }
505
561
  }
506
562
  if (currentIFrame !== void 0) {
@@ -524,7 +580,7 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
524
580
  );
525
581
  }
526
582
  if (this.log.isLogLevelEnabled("debug")) {
527
- this.log.debug("Nested locator evaluation results:", JSON.stringify(nestedLocatorResults, null, 2));
583
+ this.log.debug("Nested locator evaluation results:", safeStringifyOfNestedLocatorResults(nestedLocatorResults));
528
584
  }
529
585
  if (currentLocator != null) {
530
586
  return currentLocator;
@@ -532,7 +588,9 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
532
588
  });
533
589
  };
534
590
  /**
535
- * Evaluates the current locator, capturing details about its resolution status and the elements it resolves to.
591
+ * evaluateCurrentLocator:
592
+ * Gathers debug information about the current locator's resolved elements.
593
+ * Helps with logging and debugging complex locator chains.
536
594
  */
537
595
  evaluateCurrentLocator = async (currentLocator, resultsArray, currentIFrame) => {
538
596
  if (currentIFrame) {
@@ -544,7 +602,7 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
544
602
  } else {
545
603
  const elementsData = await this.evaluateAndGetAttributes(currentLocator);
546
604
  resultsArray.push({
547
- currentLocatorString: currentLocator,
605
+ currentLocatorString: `${currentLocator}`,
548
606
  resolved: elementsData.length > 0,
549
607
  elementCount: elementsData.length,
550
608
  elementsResolvedTo: elementsData
@@ -552,8 +610,8 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
552
610
  }
553
611
  };
554
612
  /**
555
- * Retrieves and compiles attributes of elements resolved by a Playwright locator per nesting step.
556
- * This method provides insights into the elements targeted by the locator, aiding in debugging and verification.
613
+ * evaluateAndGetAttributes:
614
+ * Extracts tagName and attributes from all elements matched by the locator for debugging purposes.
557
615
  */
558
616
  evaluateAndGetAttributes = async (pwLocator) => {
559
617
  return await pwLocator.evaluateAll(
@@ -564,6 +622,126 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
564
622
  );
565
623
  };
566
624
  };
625
+ var WithMethodsClass = class extends GetLocatorBase {
626
+ constructor(pageObjectClass, log, locatorSubstring, schemasMap) {
627
+ super(pageObjectClass, log, locatorSubstring);
628
+ this.pageObjectClass = pageObjectClass;
629
+ this.log = log;
630
+ this.schemasMap = schemasMap;
631
+ }
632
+ locatorSchemaPath;
633
+ /**
634
+ * init:
635
+ * Assigns the locatorSchemaPath and binds methods (update, updates, addFilter, getNestedLocator, getLocator)
636
+ * directly on the locatorSchemaCopy. Returns the modified copy, now fully chainable and type-safe.
637
+ */
638
+ init(locatorSchemaPath, locatorSchemaCopy) {
639
+ this.locatorSchemaPath = locatorSchemaPath;
640
+ const self = this;
641
+ locatorSchemaCopy.update = function(a, b) {
642
+ const fullPath = this.locatorSchemaPath;
643
+ if (b === void 0) {
644
+ const updates = a;
645
+ self.applyUpdate(self.schemasMap, self.locatorSchemaPath, updates);
646
+ } else {
647
+ const subPath = a;
648
+ const updates = b;
649
+ if (!(subPath === fullPath || fullPath.startsWith(`${subPath}.`))) {
650
+ throw new Error(`Invalid sub-path: '${subPath}' is not a valid sub-path of '${fullPath}'.`);
651
+ }
652
+ self.applyUpdateToSubPath(self.schemasMap, subPath, updates);
653
+ }
654
+ return this;
655
+ };
656
+ locatorSchemaCopy.updates = function(indexedUpdates) {
657
+ const pathIndexPairs = self.extractPathsFromSchema(self.locatorSchemaPath);
658
+ self.applyUpdates(self.schemasMap, pathIndexPairs, indexedUpdates);
659
+ return this;
660
+ };
661
+ locatorSchemaCopy.addFilter = function(subPath, filterData) {
662
+ const fullPath = this.locatorSchemaPath;
663
+ if (!self.schemasMap.has(subPath)) {
664
+ const allowedPaths = self.extractPathsFromSchema(fullPath).map((p) => p.path).filter((path) => self.schemasMap.has(path));
665
+ throw new Error(
666
+ `Invalid sub-path '${subPath}' in addFilter. Allowed sub-paths are:
667
+ ${allowedPaths.join(",\n")}`
668
+ );
669
+ }
670
+ if (!this.filterMap) {
671
+ this.filterMap = /* @__PURE__ */ new Map();
672
+ }
673
+ const existingFilters = this.filterMap.get(subPath) || [];
674
+ existingFilters.push(filterData);
675
+ this.filterMap.set(subPath, existingFilters);
676
+ return this;
677
+ };
678
+ locatorSchemaCopy.getNestedLocator = async function(arg) {
679
+ if (arg !== void 0 && arg !== null && typeof arg !== "object") {
680
+ throw new Error("Invalid argument passed to getNestedLocator: Expected an object or null.");
681
+ }
682
+ if (!arg || Object.keys(arg).length === 0) {
683
+ return await self.buildNestedLocator(
684
+ self.locatorSchemaPath,
685
+ self.schemasMap,
686
+ this.filterMap,
687
+ {}
688
+ );
689
+ }
690
+ const keys = Object.keys(arg);
691
+ const isNumberKey = keys.every((key) => /^\d+$/.test(key));
692
+ const numericIndices = {};
693
+ if (isNumberKey) {
694
+ for (const [key, value] of Object.entries(arg)) {
695
+ const index = Number(key);
696
+ if (typeof value === "number" && value >= 0) {
697
+ numericIndices[index] = value;
698
+ } else if (value !== null) {
699
+ throw new Error(`Invalid index value at key '${key}': Expected a positive number or null.`);
700
+ }
701
+ }
702
+ } else {
703
+ const pathIndexPairs = self.extractPathsFromSchema(self.locatorSchemaPath);
704
+ const pathToIndexMap = new Map(pathIndexPairs.map((pair, idx) => [pair.path, idx]));
705
+ for (const [subPath, value] of Object.entries(arg)) {
706
+ if (!self.schemasMap.has(subPath)) {
707
+ const validPaths = Array.from(self.schemasMap.keys());
708
+ throw new Error(
709
+ `Invalid sub-path '${subPath}' in getNestedLocator. Allowed sub-paths are:
710
+ ${validPaths.join(",\n")}`
711
+ );
712
+ }
713
+ if (!pathToIndexMap.has(subPath)) {
714
+ const validPaths = pathIndexPairs.map((p) => p.path).filter((path) => self.schemasMap.has(path));
715
+ throw new Error(
716
+ `Invalid sub-path '${subPath}' in getNestedLocator. Allowed sub-paths are:
717
+ ${validPaths.join(",\n")}`
718
+ );
719
+ }
720
+ const numericIndex = pathToIndexMap.get(subPath);
721
+ if (numericIndex === void 0) {
722
+ throw new Error(`Sub-path '${subPath}' not found in pathToIndexMap.`);
723
+ }
724
+ if (value !== null && (typeof value !== "number" || value < 0)) {
725
+ throw new Error(`Invalid index for sub-path '${subPath}': Expected a positive number or null.`);
726
+ }
727
+ if (value !== null) {
728
+ numericIndices[numericIndex] = value;
729
+ }
730
+ }
731
+ }
732
+ return await self.buildNestedLocator(
733
+ self.locatorSchemaPath,
734
+ self.schemasMap,
735
+ this.filterMap,
736
+ numericIndices
737
+ );
738
+ };
739
+ locatorSchemaCopy.getLocator = async () => {
740
+ return self.getBy.getLocator(locatorSchemaCopy);
741
+ };
742
+ return locatorSchemaCopy;
743
+ }
744
+ };
567
745
 
568
746
  // src/helpers/sessionStorage.actions.ts
569
747
  import { test as test2 } from "@playwright/test";
@@ -746,12 +924,10 @@ var BasePage = class {
746
924
  /** Selectors can be used to install custom selector engines.*/
747
925
  selector;
748
926
  /** The base URL of the Page Object Class */
749
- // baseUrl: string;
750
927
  baseUrl;
751
928
  /** The URL path of the Page Object Class */
752
- // urlPath: string;
753
929
  urlPath;
754
- // fullUrl: string;
930
+ /** The full URL of the Page Object Class */
755
931
  fullUrl;
756
932
  /** The name of the Page Object Class */
757
933
  pocName;
@@ -759,8 +935,14 @@ var BasePage = class {
759
935
  log;
760
936
  /** The SessionStorage class provides methods for setting and getting session storage data in Playwright.*/
761
937
  sessionStorage;
938
+ /**
939
+ * locators:
940
+ * An instance of GetLocatorBase that handles schema management and provides getLocatorSchema calls.
941
+ * Initially, LocatorSubstring is undefined. Once getLocatorSchema(path) is called,
942
+ * we get a chainable object typed with LocatorSubstring = P.
943
+ */
762
944
  locators;
763
- constructor(page, testInfo, baseUrl, urlPath, pocName, pwrl) {
945
+ constructor(page, testInfo, baseUrl, urlPath, pocName, pwrl, locatorSubstring) {
764
946
  this.page = page;
765
947
  this.testInfo = testInfo;
766
948
  this.selector = selectors;
@@ -769,7 +951,11 @@ var BasePage = class {
769
951
  this.fullUrl = this.constructFullUrl(baseUrl, urlPath);
770
952
  this.pocName = pocName;
771
953
  this.log = pwrl.getNewChildLogger(pocName);
772
- this.locators = new GetLocatorBase(this, this.log.getNewChildLogger("GetLocator"));
954
+ this.locators = new GetLocatorBase(
955
+ this,
956
+ this.log.getNewChildLogger("GetLocator"),
957
+ locatorSubstring
958
+ );
773
959
  this.initLocatorSchemas();
774
960
  this.sessionStorage = new SessionStorage(this.page, this.pocName);
775
961
  if (!selectorRegistered) {
@@ -777,6 +963,11 @@ var BasePage = class {
777
963
  selectorRegistered = true;
778
964
  }
779
965
  }
966
+ /**
967
+ * constructFullUrl:
968
+ * Combines baseUrl and urlPath, handling both strings and RegExps.
969
+ * Ensures a flexible approach to URL matching (string or regex-based).
970
+ */
780
971
  constructFullUrl(baseUrl, urlPath) {
781
972
  const escapeStringForRegExp = (str) => str.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
782
973
  if (typeof baseUrl === "string" && typeof urlPath === "string") {
@@ -794,64 +985,67 @@ var BasePage = class {
794
985
  throw new Error("Invalid baseUrl or urlPath types. Expected string or RegExp.");
795
986
  }
796
987
  /**
797
- * getNestedLocator(indices?: { [key: number]: number | null } | null)
798
- * - Asynchronously retrieves a nested locator based on the LocatorSchemaPath provided by getLocatorSchema("...")
799
- * - Can be chained after the update and updates methods, getNestedLocator will end the chain.
800
- * - The optional parameter of the method takes an object with 0-based indices "{0: 0, 3: 1}" for one or more locators
801
- * to be nested given by sub-paths (indices correspond to last "word" of a sub-path).
802
- * - Returns a promise that resolves to the nested locator.
988
+ * Implementation of getNestedLocator.
803
989
  */
804
- getNestedLocator = async (locatorSchemaPath, indices) => {
805
- return await this.getLocatorSchema(locatorSchemaPath).getNestedLocator(indices);
806
- };
990
+ async getNestedLocator(locatorSchemaPath, subPathIndices) {
991
+ const withValidation = new WithSubPathValidation(
992
+ this,
993
+ this.log.getNewChildLogger("SubPathValidation"),
994
+ locatorSchemaPath
995
+ );
996
+ return await withValidation.getNestedLocator(subPathIndices);
997
+ }
807
998
  /**
808
- * getLocator()
809
- * - Asynchronously retrieves a locator based on the current LocatorSchema. This method does not perform nesting,
810
- * and will return the locator for which the full LocatorSchemaPath resolves to, provided by getLocatorSchema("...")
811
- * - Can be chained after the update and updates methods, getLocator will end the chain.
812
- * - Returns a promise that resolves to the locator.
999
+ * Short-hand wrapper method for calling .getLocatorSchema(LocatorSchemaPath).getLocator()
1000
+ *
1001
+ * This method does not perform nesting,and will return the locator for which the full LocatorSchemaPath resolves to,
1002
+ * provided by getLocatorSchema("...")
1003
+ *
1004
+ * Note: This short-hand wrapper method is useful for quickly getting a locator without having to call
1005
+ * getLocatorSchema("...") first. On the other hand, it can't be used to update or add filters to the LocatorSchema.
1006
+ *
1007
+ * @example
1008
+ * // Usage:
1009
+ * const submitButton = await poc.getLocator("main.form.button@submit");
1010
+ * await expect(submitButton, "should only exist one submit button").toHaveCount(1);
813
1011
  */
814
1012
  getLocator = async (locatorSchemaPath) => {
815
1013
  return await this.getLocatorSchema(locatorSchemaPath).getLocator();
816
1014
  };
817
1015
  /**
1016
+ * getLocatorSchema:
1017
+ * Delegates to this.locators.getLocatorSchema.
1018
+ * Returns a chainable schema object for the given path.
1019
+ * Once called with a specific path P, the update and addFilter methods are restricted to sub-paths of P.
1020
+ *
818
1021
  * The "getLocatorSchema" method is used to retrieve an updatable deep copy of a LocatorSchema defined in the
819
1022
  * GetLocatorBase class. It enriches the returned schema with additional methods to handle updates and retrieval of
820
1023
  * deep copy locators.
821
1024
  *
822
1025
  * getLocatorSchema adds the following chainable methods to the returned LocatorSchemaWithMethods object:
823
1026
  *
824
- * update(updates: Partial< UpdatableLocatorSchemaProperties >)
825
- * - Allows updating the properties of the LocatorSchema which the full LocatorSchemaPath resolves to.
826
- * - This method is used for modifying the current schema without affecting the original schema.
827
- * - Takes a "LocatorSchema" object which omits the locatorSchemaPath parameter as input, the parameters provided
828
- * will overwrite the corresponding property in the current schema.
829
- * - Returns the updated deep copy of the "LocatorSchema" with methods.
830
- * - Can be chained with the update and updates methods, and the getLocator or getNestedLocator method.
1027
+ * update
1028
+ * - Allows updating any schema in the chain by specifying the subPath directly.
1029
+ * - Gives compile-time suggestions for valid sub-paths of the LocatorSchemaPath provided to .getLocatorSchema().
1030
+ * - If you want to update multiple schemas, chain multiple .update() calls.
831
1031
  *
832
- * updates(indexedUpdates: { [index: number]: Partial< UpdatableLocatorSchemaProperties > | null }):
833
- * - Similar to update, but allows updating any locator in the nested chain (all sub-paths of the LocatorSchemaPath).
834
- * - This method can modify the current deep copy of each LocatorSchema that each sub-path resolves to without
835
- * affecting the original schemas
836
- * - Takes an object where keys represent the index of the last "word" of a sub-path, where the value per key is a
837
- * "LocatorSchema" object which omits the locatorSchemaPath parameter as input, the parameters provided will overwrite
838
- * the corresponding property in the given schema.
839
- * - Returns the updated deep copy of the LocatorSchema object with methods and its own updated deep copies for all
840
- * LocatorSchema each sub-path resolved to.
841
- * - Can be chained with the update and updates methods, and the getLocator or getNestedLocator method.
1032
+ * addFilter(subPath: SubPaths<LocatorSchemaPathType, LocatorSubstring>, filterData: FilterEntry)
1033
+ * - The equivalent of the Playwright locator.filter() method
1034
+ * - This method is used for filtering the specified locator based on the provided filterData.
1035
+ * - Can be chained multiple times to add multiple filters to the same or different LocatorSchema.
842
1036
  *
843
- * getNestedLocator(indices?: { [key: number]: number | null } | null)
844
- * - Asynchronously retrieves a nested locator based on the LocatorSchemaPath provided by getLocatorSchema("...")
845
- * - Can be chained after the update and updates methods, getNestedLocator will end the chain.
846
- * - The optional parameter of the method takes an object with 0-based indices "{0: 0, 3: 1}" for one or more locators
847
- * to be nested given by sub-paths (indices correspond to last "word" of a sub-path).
848
- * - Returns a promise that resolves to the nested locator.
1037
+ * getNestedLocator
1038
+ * - Asynchronously builds a nested locator based on the LocatorSchemaPath provided by getLocatorSchema("...")
1039
+ * - Can be chained once after the update and addFilter methods or directly on the .getLocatorSchema method.
1040
+ * - getNestedLocator will end the method chain and return a nested Playwright Locator.
1041
+ * - Optionally parameter takes a list of key(subPath)-value(index) pairs, the locator constructed from the LocatorSchema
1042
+ * with the specified subPath will resolve to the .nth(n) occurrence of the element, within the chain.
849
1043
  *
850
1044
  * getLocator()
851
- * - Asynchronously retrieves a locator based on the current LocatorSchema. This method does not perform nesting,
852
- * and will return the locator for which the full LocatorSchemaPath resolves to, provided by getLocatorSchema("...")
853
- * - Can be chained after the update and updates methods, getLocator will end the chain.
854
- * - Returns a promise that resolves to the locator.
1045
+ * - Asynchronously retrieves a locator based on the current LocatorSchemaPath.
1046
+ * - This method does not perform nesting and will return the locator for which the full LocatorSchemaPath resolves to, provided by getLocatorSchema("...")
1047
+ * - Can be chained once after the update and addFilter methods or directly on the .getLocatorSchema method.
1048
+ * - getLocator will end the method chain and return a Playwright Locator.
855
1049
  *
856
1050
  * Note: Calling getLocator() and getNestedLocator() on the same LocatorSchemaPath will return a Locator for the same
857
1051
  * element, but the Locator returned by getNestedLocator() will be a locator resolving to said same element through
@@ -861,10 +1055,19 @@ var BasePage = class {
861
1055
  * That said, for certain use cases, getLocator() can be useful, and you could use it to manually chain locators
862
1056
  * yourself if some edge case required it. Though, it would be likely be more prudent to expand your LocatorSchemaPath
863
1057
  * type and initLocatorSchemas() method to include the additional locators you need for the given POC, and then use
864
- * getNestedLocator() instead.
1058
+ * getNestedLocator() instead, or by implementing a helper method on your Page Object Class.
865
1059
  */
866
- getLocatorSchema(locatorSchemaPath) {
867
- return this.locators.getLocatorSchema(locatorSchemaPath);
1060
+ getLocatorSchema(path) {
1061
+ return this.locators.getLocatorSchema(path);
1062
+ }
1063
+ };
1064
+ var WithSubPathValidation = class extends GetLocatorBase {
1065
+ constructor(pageObjectClass, log, locatorSchemaPath) {
1066
+ super(pageObjectClass, log, locatorSchemaPath);
1067
+ this.locatorSchemaPath = locatorSchemaPath;
1068
+ }
1069
+ async getNestedLocator(arg) {
1070
+ return await this.pageObjectClass.getLocatorSchema(this.locatorSchemaPath).getNestedLocator(arg);
868
1071
  }
869
1072
  };
870
1073