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.js CHANGED
@@ -94,6 +94,12 @@ var locatorSchemaDummy = {
94
94
  testId: void 0,
95
95
  dataCy: void 0,
96
96
  id: void 0,
97
+ filter: {
98
+ has: void 0,
99
+ hasNot: void 0,
100
+ hasNotText: void 0,
101
+ hasText: void 0
102
+ },
97
103
  locatorMethod: void 0,
98
104
  locatorSchemaPath: void 0
99
105
  };
@@ -266,52 +272,72 @@ var GetBy = class {
266
272
  var REQUIRED_PROPERTIES_FOR_LOCATOR_SCHEMA_WITH_METHODS = [
267
273
  "update",
268
274
  "updates",
275
+ "addFilter",
269
276
  "getNestedLocator",
270
277
  "getLocator",
271
278
  "locatorSchemaPath",
272
279
  "locatorMethod",
273
- "schemasMap"
280
+ "schemasMap",
281
+ "filterMap"
274
282
  ];
283
+ var safeStringifyOfNestedLocatorResults = (obj) => {
284
+ const seen = /* @__PURE__ */ new WeakSet();
285
+ return JSON.stringify(
286
+ obj,
287
+ (key, value) => {
288
+ if (value instanceof Map) {
289
+ return Array.from(value.entries());
290
+ }
291
+ if (value instanceof RegExp) {
292
+ return { type: "RegExp", source: value.source, flags: value.flags };
293
+ }
294
+ if (value && typeof value === "object" && value.constructor && value.constructor.name === "Locator") {
295
+ return { type: "Locator", note: "Custom placeholder - Locators are complex." };
296
+ }
297
+ if (typeof value === "object" && value !== null) {
298
+ if (seen.has(value)) return "[Circular]";
299
+ seen.add(value);
300
+ }
301
+ return value;
302
+ },
303
+ 2
304
+ );
305
+ };
275
306
  var GetLocatorBase = class {
276
- /**
277
- * Initializes the GetLocatorBase class with a page object class and a logger.
278
- */
279
- constructor(pageObjectClass, log) {
307
+ constructor(pageObjectClass, log, locatorSubstring) {
280
308
  this.pageObjectClass = pageObjectClass;
281
309
  this.log = log;
310
+ this.locatorSubstring = locatorSubstring;
282
311
  this.locatorSchemas = /* @__PURE__ */ new Map();
283
312
  this.getBy = new GetBy(this.pageObjectClass.page, this.log.getNewChildLogger("GetBy"));
284
313
  }
285
314
  getBy;
286
315
  locatorSchemas;
287
316
  /**
288
- * Retrieves a locator schema with additional methods for manipulation and retrieval of locators.
317
+ * getLocatorSchema:
318
+ * Given a path P, we:
319
+ * 1. Collect deep copies of the schemas involved.
320
+ * 2. Create a WithMethodsClass instance with LocatorSubstring = P.
321
+ * 3. Return a locator schema copy enriched with chainable methods.
289
322
  */
290
323
  getLocatorSchema(locatorSchemaPath) {
291
324
  const pathIndexPairs = this.extractPathsFromSchema(locatorSchemaPath);
292
325
  const schemasMap = this.collectDeepCopies(locatorSchemaPath, pathIndexPairs);
293
326
  const locatorSchemaCopy = schemasMap.get(locatorSchemaPath);
294
327
  locatorSchemaCopy.schemasMap = schemasMap;
295
- const self = this;
296
- locatorSchemaCopy.update = function(updates) {
297
- self.applyUpdate(schemasMap, locatorSchemaPath, updates);
298
- return this;
299
- };
300
- locatorSchemaCopy.updates = function(indexedUpdates) {
301
- self.applyUpdates(schemasMap, pathIndexPairs, indexedUpdates);
302
- return this;
303
- };
304
- locatorSchemaCopy.getNestedLocator = async (indices) => {
305
- return await this.buildNestedLocator(locatorSchemaPath, schemasMap, indices);
306
- };
307
- locatorSchemaCopy.getLocator = async () => {
308
- return this.getBy.getLocator(locatorSchemaCopy);
309
- };
310
- return locatorSchemaCopy;
328
+ locatorSchemaCopy.filterMap = /* @__PURE__ */ new Map();
329
+ const wrapper = new WithMethodsClass(
330
+ this.pageObjectClass,
331
+ this.log,
332
+ locatorSchemaPath,
333
+ schemasMap
334
+ );
335
+ return wrapper.init(locatorSchemaPath, locatorSchemaCopy);
311
336
  }
312
337
  /**
313
- * Collects deep copies of locator schemas based on a given locator schema path and path-index pairs.
314
- * It ensures that each locator schema and its sub-schemas are properly cloned and stored.
338
+ * collectDeepCopies:
339
+ * Clones and stores all schemas related to the chosen path and its sub-paths.
340
+ * Ensures updates and filters don't affect original schema definitions.
315
341
  */
316
342
  collectDeepCopies(locatorSchemaPath, pathIndexPairs) {
317
343
  const schemasMap = /* @__PURE__ */ new Map();
@@ -336,8 +362,25 @@ var GetLocatorBase = class {
336
362
  return REQUIRED_PROPERTIES_FOR_LOCATOR_SCHEMA_WITH_METHODS.every((p) => p in schema);
337
363
  }
338
364
  /**
339
- * Applies an update to a specific locator schema within the provided map of schemas.
340
- * This method ensures that the specified updates are merged into the targeted locator schema.
365
+ * applyUpdateToSubPath:
366
+ * Applies updates to a specific sub-path schema within schemasMap.
367
+ * Similar to applyUpdate, but we locate the sub-path schema directly by its path.
368
+ */
369
+ applyUpdateToSubPath(schemasMap, subPath, updates) {
370
+ const schema = schemasMap.get(subPath);
371
+ if (!schema) {
372
+ throw new Error(`No schema found for sub-path: '${subPath}'`);
373
+ }
374
+ const updatedSchema = this.deepMerge(schema, updates);
375
+ if (this.isLocatorSchemaWithMethods(schema)) {
376
+ Object.assign(schema, updatedSchema);
377
+ } else {
378
+ schemasMap.set(subPath, updatedSchema);
379
+ }
380
+ }
381
+ /**
382
+ * applyUpdate:
383
+ * Applies updates to a single schema within the schemasMap.
341
384
  */
342
385
  applyUpdate(schemasMap, locatorSchemaPath, updateData) {
343
386
  const schema = schemasMap.get(locatorSchemaPath);
@@ -351,8 +394,8 @@ var GetLocatorBase = class {
351
394
  }
352
395
  }
353
396
  /**
354
- * Applies multiple updates to locator schemas based on provided path-index pairs and update data.
355
- * This method facilitates batch updating of nested schemas within a complex locator structure.
397
+ * applyUpdates:
398
+ * Applies multiple updates to multiple schemas in the chain, identified by their path indexes.
356
399
  */
357
400
  applyUpdates(schemasMap, pathIndexPairs, updatesData) {
358
401
  for (const [index, updateAtIndex] of Object.entries(updatesData)) {
@@ -371,16 +414,17 @@ var GetLocatorBase = class {
371
414
  }
372
415
  }
373
416
  /**
374
- * Creates a new locator schema based on provided schema details and a schema path.
375
- * This method structures a new locator schema ready for inclusion in the locator management system.
417
+ * createLocatorSchema:
418
+ * Creates a fresh LocatorSchema object by merging provided schemaDetails with a required locatorSchemaPath.
376
419
  */
377
420
  createLocatorSchema(schemaDetails, locatorSchemaPath) {
378
421
  const schema = { ...schemaDetails, locatorSchemaPath };
379
422
  return schema;
380
423
  }
381
424
  /**
382
- * Adds a new locator schema to the internal map of locator schemas.
383
- * This method ensures that the new schema is properly registered and can be referenced and used in locator generation.
425
+ * addSchema:
426
+ * Registers a new LocatorSchema under the given locatorSchemaPath.
427
+ * Throws an error if a schema already exists at that path.
384
428
  */
385
429
  addSchema(locatorSchemaPath, schemaDetails) {
386
430
  const newLocatorSchema = this.createLocatorSchema(schemaDetails, locatorSchemaPath);
@@ -396,16 +440,16 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
396
440
  this.locatorSchemas.set(locatorSchemaPath, () => newLocatorSchema);
397
441
  }
398
442
  /**
399
- * Safely retrieves a locator schema function based on a given path.
400
- * This method provides a secure way to access locator schemas, ensuring that only valid paths are used.
443
+ * safeGetLocatorSchema:
444
+ * Safely retrieves a schema function if available for the given path.
401
445
  */
402
446
  safeGetLocatorSchema(path) {
403
447
  return this.locatorSchemas.get(path);
404
448
  }
405
449
  /**
406
- * Extracts path-index pairs from a given schema path.
407
- * This utility function breaks down a complex path into manageable parts,
408
- * associating each part with its corresponding index when necessary.
450
+ * extractPathsFromSchema:
451
+ * Splits a path into incremental sub-paths and associates them with optional indices.
452
+ * Used by updates and getNestedLocator methods.
409
453
  */
410
454
  extractPathsFromSchema = (paths, indices = {}) => {
411
455
  const schemaParts = paths.split(".");
@@ -419,8 +463,8 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
419
463
  });
420
464
  };
421
465
  /**
422
- * logError is a utility function for logging errors with detailed debug information
423
- * It re-throws the error after logging to ensure the test will fail.
466
+ * logError:
467
+ * Logs detailed error information and re-throws the error to ensure tests fail as expected.
424
468
  */
425
469
  logError = (error, locatorSchemaPath, currentLocator, currentPath, pathIndexPairs, nestedLocatorResults) => {
426
470
  const errorDetails = {
@@ -432,7 +476,7 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
432
476
  locatorString: currentLocator,
433
477
  isNotNull: true
434
478
  } : { isNotNull: false },
435
- nestedLocatorResults
479
+ nestedLocatorResults: safeStringifyOfNestedLocatorResults(nestedLocatorResults)
436
480
  };
437
481
  this.log.error(
438
482
  "An error occurred during nested locator construction.\n",
@@ -441,7 +485,11 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
441
485
  );
442
486
  throw error;
443
487
  };
444
- /** Merges 'source' into 'target', combining their properties into a new isolated object. */
488
+ /**
489
+ * deepMerge:
490
+ * Recursively merges source properties into target, validating them against LocatorSchema to ensure no invalid keys.
491
+ * Ensures immutability by creating a new object rather than modifying in place.
492
+ */
445
493
  deepMerge(target, source, schema = getLocatorSchemaDummy()) {
446
494
  const merged = { ...target };
447
495
  for (const key of Object.keys(source)) {
@@ -466,7 +514,6 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
466
514
  } else {
467
515
  merged[key] = this.deepMerge(
468
516
  {},
469
- // Updated type here
470
517
  sourceValue,
471
518
  schema[key]
472
519
  );
@@ -487,18 +534,17 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
487
534
  return merged;
488
535
  }
489
536
  /**
490
- * Assembles nested locators based on a locator schema path and optional indices for locating specific elements.
491
- * This method orchestrates the process of building a locator that can resolve to a specific element or set of
492
- * elements in the DOM.
537
+ * buildNestedLocator:
538
+ * Constructs a nested locator by iterating through each sub-path of locatorSchemaPath and chaining locators.
539
+ * Applies filters, indexing (nth), and logs details for debugging during test retries.
493
540
  */
494
- buildNestedLocator = async (locatorSchemaPath, schemasMap, indices = {}) => {
541
+ buildNestedLocator = async (locatorSchemaPath, schemasMap, filterMap, indices = {}) => {
495
542
  return await import_test.test.step(`${this.pageObjectClass.pocName}: Build Nested Locator`, async () => {
496
543
  const pathIndexPairs = this.extractPathsFromSchema(locatorSchemaPath, indices);
497
544
  let currentLocator = null;
498
545
  let currentIFrame = null;
499
546
  const nestedLocatorResults = {
500
547
  LocatorSchema: null,
501
- // Initialize as an empty object
502
548
  NestingSteps: []
503
549
  };
504
550
  for (const { path, index } of pathIndexPairs) {
@@ -506,32 +552,42 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
506
552
  if (!currentSchema) continue;
507
553
  try {
508
554
  const nextLocator = this.getBy.getLocator(currentSchema);
509
- if (currentLocator) {
510
- currentLocator = currentLocator.locator(nextLocator);
511
- if (index != null) {
512
- currentLocator = currentLocator.nth(index);
513
- }
514
- } else {
515
- currentLocator = nextLocator;
516
- if (index != null) {
517
- currentLocator = currentLocator.nth(index);
555
+ currentLocator = currentLocator ? currentLocator.locator(nextLocator) : nextLocator;
556
+ if (currentSchema.locatorMethod !== "frameLocator" /* frameLocator */ && currentSchema.filter) {
557
+ currentLocator = currentLocator.filter({
558
+ has: currentSchema.filter.has,
559
+ hasNot: currentSchema.filter.hasNot,
560
+ hasNotText: currentSchema.filter.hasNotText,
561
+ hasText: currentSchema.filter.hasText
562
+ });
563
+ }
564
+ const filterEntries = filterMap.get(path);
565
+ if (filterEntries) {
566
+ for (const filterData of filterEntries) {
567
+ currentLocator = currentLocator.filter({
568
+ has: filterData.has,
569
+ hasNot: filterData.hasNot,
570
+ hasNotText: filterData.hasNotText,
571
+ hasText: filterData.hasText
572
+ });
518
573
  }
519
574
  }
575
+ if (index != null) {
576
+ currentLocator = currentLocator.nth(index);
577
+ }
520
578
  if (this.log.isLogLevelEnabled("debug")) {
521
579
  if (!nestedLocatorResults.LocatorSchema) {
522
- const safeGetLocatorSchema = this.safeGetLocatorSchema(locatorSchemaPath);
523
- if (safeGetLocatorSchema !== void 0) {
524
- nestedLocatorResults.LocatorSchema = safeGetLocatorSchema();
580
+ const schemaFromMap = schemasMap.get(locatorSchemaPath);
581
+ if (schemaFromMap) {
582
+ nestedLocatorResults.LocatorSchema = schemaFromMap;
525
583
  }
526
584
  }
527
585
  if (currentSchema.locatorMethod === "frameLocator" /* frameLocator */) {
528
586
  if (!currentIFrame) {
529
587
  currentIFrame = currentSchema.frameLocator;
530
588
  }
531
- if (currentIFrame != null && currentSchema.frameLocator != null) {
532
- if (currentIFrame.endsWith(currentSchema.frameLocator)) {
533
- currentIFrame += ` -> ${currentSchema.frameLocator}`;
534
- }
589
+ if (currentIFrame && currentSchema.frameLocator && currentIFrame.endsWith(currentSchema.frameLocator)) {
590
+ currentIFrame += ` -> ${currentSchema.frameLocator}`;
535
591
  }
536
592
  }
537
593
  if (currentIFrame !== void 0) {
@@ -555,7 +611,7 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
555
611
  );
556
612
  }
557
613
  if (this.log.isLogLevelEnabled("debug")) {
558
- this.log.debug("Nested locator evaluation results:", JSON.stringify(nestedLocatorResults, null, 2));
614
+ this.log.debug("Nested locator evaluation results:", safeStringifyOfNestedLocatorResults(nestedLocatorResults));
559
615
  }
560
616
  if (currentLocator != null) {
561
617
  return currentLocator;
@@ -563,7 +619,9 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
563
619
  });
564
620
  };
565
621
  /**
566
- * Evaluates the current locator, capturing details about its resolution status and the elements it resolves to.
622
+ * evaluateCurrentLocator:
623
+ * Gathers debug information about the current locator's resolved elements.
624
+ * Helps with logging and debugging complex locator chains.
567
625
  */
568
626
  evaluateCurrentLocator = async (currentLocator, resultsArray, currentIFrame) => {
569
627
  if (currentIFrame) {
@@ -575,7 +633,7 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
575
633
  } else {
576
634
  const elementsData = await this.evaluateAndGetAttributes(currentLocator);
577
635
  resultsArray.push({
578
- currentLocatorString: currentLocator,
636
+ currentLocatorString: `${currentLocator}`,
579
637
  resolved: elementsData.length > 0,
580
638
  elementCount: elementsData.length,
581
639
  elementsResolvedTo: elementsData
@@ -583,8 +641,8 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
583
641
  }
584
642
  };
585
643
  /**
586
- * Retrieves and compiles attributes of elements resolved by a Playwright locator per nesting step.
587
- * This method provides insights into the elements targeted by the locator, aiding in debugging and verification.
644
+ * evaluateAndGetAttributes:
645
+ * Extracts tagName and attributes from all elements matched by the locator for debugging purposes.
588
646
  */
589
647
  evaluateAndGetAttributes = async (pwLocator) => {
590
648
  return await pwLocator.evaluateAll(
@@ -595,6 +653,126 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
595
653
  );
596
654
  };
597
655
  };
656
+ var WithMethodsClass = class extends GetLocatorBase {
657
+ constructor(pageObjectClass, log, locatorSubstring, schemasMap) {
658
+ super(pageObjectClass, log, locatorSubstring);
659
+ this.pageObjectClass = pageObjectClass;
660
+ this.log = log;
661
+ this.schemasMap = schemasMap;
662
+ }
663
+ locatorSchemaPath;
664
+ /**
665
+ * init:
666
+ * Assigns the locatorSchemaPath and binds methods (update, updates, addFilter, getNestedLocator, getLocator)
667
+ * directly on the locatorSchemaCopy. Returns the modified copy, now fully chainable and type-safe.
668
+ */
669
+ init(locatorSchemaPath, locatorSchemaCopy) {
670
+ this.locatorSchemaPath = locatorSchemaPath;
671
+ const self = this;
672
+ locatorSchemaCopy.update = function(a, b) {
673
+ const fullPath = this.locatorSchemaPath;
674
+ if (b === void 0) {
675
+ const updates = a;
676
+ self.applyUpdate(self.schemasMap, self.locatorSchemaPath, updates);
677
+ } else {
678
+ const subPath = a;
679
+ const updates = b;
680
+ if (!(subPath === fullPath || fullPath.startsWith(`${subPath}.`))) {
681
+ throw new Error(`Invalid sub-path: '${subPath}' is not a valid sub-path of '${fullPath}'.`);
682
+ }
683
+ self.applyUpdateToSubPath(self.schemasMap, subPath, updates);
684
+ }
685
+ return this;
686
+ };
687
+ locatorSchemaCopy.updates = function(indexedUpdates) {
688
+ const pathIndexPairs = self.extractPathsFromSchema(self.locatorSchemaPath);
689
+ self.applyUpdates(self.schemasMap, pathIndexPairs, indexedUpdates);
690
+ return this;
691
+ };
692
+ locatorSchemaCopy.addFilter = function(subPath, filterData) {
693
+ const fullPath = this.locatorSchemaPath;
694
+ if (!self.schemasMap.has(subPath)) {
695
+ const allowedPaths = self.extractPathsFromSchema(fullPath).map((p) => p.path).filter((path) => self.schemasMap.has(path));
696
+ throw new Error(
697
+ `Invalid sub-path '${subPath}' in addFilter. Allowed sub-paths are:
698
+ ${allowedPaths.join(",\n")}`
699
+ );
700
+ }
701
+ if (!this.filterMap) {
702
+ this.filterMap = /* @__PURE__ */ new Map();
703
+ }
704
+ const existingFilters = this.filterMap.get(subPath) || [];
705
+ existingFilters.push(filterData);
706
+ this.filterMap.set(subPath, existingFilters);
707
+ return this;
708
+ };
709
+ locatorSchemaCopy.getNestedLocator = async function(arg) {
710
+ if (arg !== void 0 && arg !== null && typeof arg !== "object") {
711
+ throw new Error("Invalid argument passed to getNestedLocator: Expected an object or null.");
712
+ }
713
+ if (!arg || Object.keys(arg).length === 0) {
714
+ return await self.buildNestedLocator(
715
+ self.locatorSchemaPath,
716
+ self.schemasMap,
717
+ this.filterMap,
718
+ {}
719
+ );
720
+ }
721
+ const keys = Object.keys(arg);
722
+ const isNumberKey = keys.every((key) => /^\d+$/.test(key));
723
+ const numericIndices = {};
724
+ if (isNumberKey) {
725
+ for (const [key, value] of Object.entries(arg)) {
726
+ const index = Number(key);
727
+ if (typeof value === "number" && value >= 0) {
728
+ numericIndices[index] = value;
729
+ } else if (value !== null) {
730
+ throw new Error(`Invalid index value at key '${key}': Expected a positive number or null.`);
731
+ }
732
+ }
733
+ } else {
734
+ const pathIndexPairs = self.extractPathsFromSchema(self.locatorSchemaPath);
735
+ const pathToIndexMap = new Map(pathIndexPairs.map((pair, idx) => [pair.path, idx]));
736
+ for (const [subPath, value] of Object.entries(arg)) {
737
+ if (!self.schemasMap.has(subPath)) {
738
+ const validPaths = Array.from(self.schemasMap.keys());
739
+ throw new Error(
740
+ `Invalid sub-path '${subPath}' in getNestedLocator. Allowed sub-paths are:
741
+ ${validPaths.join(",\n")}`
742
+ );
743
+ }
744
+ if (!pathToIndexMap.has(subPath)) {
745
+ const validPaths = pathIndexPairs.map((p) => p.path).filter((path) => self.schemasMap.has(path));
746
+ throw new Error(
747
+ `Invalid sub-path '${subPath}' in getNestedLocator. Allowed sub-paths are:
748
+ ${validPaths.join(",\n")}`
749
+ );
750
+ }
751
+ const numericIndex = pathToIndexMap.get(subPath);
752
+ if (numericIndex === void 0) {
753
+ throw new Error(`Sub-path '${subPath}' not found in pathToIndexMap.`);
754
+ }
755
+ if (value !== null && (typeof value !== "number" || value < 0)) {
756
+ throw new Error(`Invalid index for sub-path '${subPath}': Expected a positive number or null.`);
757
+ }
758
+ if (value !== null) {
759
+ numericIndices[numericIndex] = value;
760
+ }
761
+ }
762
+ }
763
+ return await self.buildNestedLocator(
764
+ self.locatorSchemaPath,
765
+ self.schemasMap,
766
+ this.filterMap,
767
+ numericIndices
768
+ );
769
+ };
770
+ locatorSchemaCopy.getLocator = async () => {
771
+ return self.getBy.getLocator(locatorSchemaCopy);
772
+ };
773
+ return locatorSchemaCopy;
774
+ }
775
+ };
598
776
 
599
777
  // src/helpers/sessionStorage.actions.ts
600
778
  var import_test2 = require("@playwright/test");
@@ -777,12 +955,10 @@ var BasePage = class {
777
955
  /** Selectors can be used to install custom selector engines.*/
778
956
  selector;
779
957
  /** The base URL of the Page Object Class */
780
- // baseUrl: string;
781
958
  baseUrl;
782
959
  /** The URL path of the Page Object Class */
783
- // urlPath: string;
784
960
  urlPath;
785
- // fullUrl: string;
961
+ /** The full URL of the Page Object Class */
786
962
  fullUrl;
787
963
  /** The name of the Page Object Class */
788
964
  pocName;
@@ -790,8 +966,14 @@ var BasePage = class {
790
966
  log;
791
967
  /** The SessionStorage class provides methods for setting and getting session storage data in Playwright.*/
792
968
  sessionStorage;
969
+ /**
970
+ * locators:
971
+ * An instance of GetLocatorBase that handles schema management and provides getLocatorSchema calls.
972
+ * Initially, LocatorSubstring is undefined. Once getLocatorSchema(path) is called,
973
+ * we get a chainable object typed with LocatorSubstring = P.
974
+ */
793
975
  locators;
794
- constructor(page, testInfo, baseUrl, urlPath, pocName, pwrl) {
976
+ constructor(page, testInfo, baseUrl, urlPath, pocName, pwrl, locatorSubstring) {
795
977
  this.page = page;
796
978
  this.testInfo = testInfo;
797
979
  this.selector = import_test3.selectors;
@@ -800,7 +982,11 @@ var BasePage = class {
800
982
  this.fullUrl = this.constructFullUrl(baseUrl, urlPath);
801
983
  this.pocName = pocName;
802
984
  this.log = pwrl.getNewChildLogger(pocName);
803
- this.locators = new GetLocatorBase(this, this.log.getNewChildLogger("GetLocator"));
985
+ this.locators = new GetLocatorBase(
986
+ this,
987
+ this.log.getNewChildLogger("GetLocator"),
988
+ locatorSubstring
989
+ );
804
990
  this.initLocatorSchemas();
805
991
  this.sessionStorage = new SessionStorage(this.page, this.pocName);
806
992
  if (!selectorRegistered) {
@@ -808,6 +994,11 @@ var BasePage = class {
808
994
  selectorRegistered = true;
809
995
  }
810
996
  }
997
+ /**
998
+ * constructFullUrl:
999
+ * Combines baseUrl and urlPath, handling both strings and RegExps.
1000
+ * Ensures a flexible approach to URL matching (string or regex-based).
1001
+ */
811
1002
  constructFullUrl(baseUrl, urlPath) {
812
1003
  const escapeStringForRegExp = (str) => str.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
813
1004
  if (typeof baseUrl === "string" && typeof urlPath === "string") {
@@ -825,64 +1016,67 @@ var BasePage = class {
825
1016
  throw new Error("Invalid baseUrl or urlPath types. Expected string or RegExp.");
826
1017
  }
827
1018
  /**
828
- * getNestedLocator(indices?: { [key: number]: number | null } | null)
829
- * - Asynchronously retrieves a nested locator based on the LocatorSchemaPath provided by getLocatorSchema("...")
830
- * - Can be chained after the update and updates methods, getNestedLocator will end the chain.
831
- * - The optional parameter of the method takes an object with 0-based indices "{0: 0, 3: 1}" for one or more locators
832
- * to be nested given by sub-paths (indices correspond to last "word" of a sub-path).
833
- * - Returns a promise that resolves to the nested locator.
1019
+ * Implementation of getNestedLocator.
834
1020
  */
835
- getNestedLocator = async (locatorSchemaPath, indices) => {
836
- return await this.getLocatorSchema(locatorSchemaPath).getNestedLocator(indices);
837
- };
1021
+ async getNestedLocator(locatorSchemaPath, subPathIndices) {
1022
+ const withValidation = new WithSubPathValidation(
1023
+ this,
1024
+ this.log.getNewChildLogger("SubPathValidation"),
1025
+ locatorSchemaPath
1026
+ );
1027
+ return await withValidation.getNestedLocator(subPathIndices);
1028
+ }
838
1029
  /**
839
- * getLocator()
840
- * - Asynchronously retrieves a locator based on the current LocatorSchema. This method does not perform nesting,
841
- * and will return the locator for which the full LocatorSchemaPath resolves to, provided by getLocatorSchema("...")
842
- * - Can be chained after the update and updates methods, getLocator will end the chain.
843
- * - Returns a promise that resolves to the locator.
1030
+ * Short-hand wrapper method for calling .getLocatorSchema(LocatorSchemaPath).getLocator()
1031
+ *
1032
+ * This method does not perform nesting,and will return the locator for which the full LocatorSchemaPath resolves to,
1033
+ * provided by getLocatorSchema("...")
1034
+ *
1035
+ * Note: This short-hand wrapper method is useful for quickly getting a locator without having to call
1036
+ * getLocatorSchema("...") first. On the other hand, it can't be used to update or add filters to the LocatorSchema.
1037
+ *
1038
+ * @example
1039
+ * // Usage:
1040
+ * const submitButton = await poc.getLocator("main.form.button@submit");
1041
+ * await expect(submitButton, "should only exist one submit button").toHaveCount(1);
844
1042
  */
845
1043
  getLocator = async (locatorSchemaPath) => {
846
1044
  return await this.getLocatorSchema(locatorSchemaPath).getLocator();
847
1045
  };
848
1046
  /**
1047
+ * getLocatorSchema:
1048
+ * Delegates to this.locators.getLocatorSchema.
1049
+ * Returns a chainable schema object for the given path.
1050
+ * Once called with a specific path P, the update and addFilter methods are restricted to sub-paths of P.
1051
+ *
849
1052
  * The "getLocatorSchema" method is used to retrieve an updatable deep copy of a LocatorSchema defined in the
850
1053
  * GetLocatorBase class. It enriches the returned schema with additional methods to handle updates and retrieval of
851
1054
  * deep copy locators.
852
1055
  *
853
1056
  * getLocatorSchema adds the following chainable methods to the returned LocatorSchemaWithMethods object:
854
1057
  *
855
- * update(updates: Partial< UpdatableLocatorSchemaProperties >)
856
- * - Allows updating the properties of the LocatorSchema which the full LocatorSchemaPath resolves to.
857
- * - This method is used for modifying the current schema without affecting the original schema.
858
- * - Takes a "LocatorSchema" object which omits the locatorSchemaPath parameter as input, the parameters provided
859
- * will overwrite the corresponding property in the current schema.
860
- * - Returns the updated deep copy of the "LocatorSchema" with methods.
861
- * - Can be chained with the update and updates methods, and the getLocator or getNestedLocator method.
1058
+ * update
1059
+ * - Allows updating any schema in the chain by specifying the subPath directly.
1060
+ * - Gives compile-time suggestions for valid sub-paths of the LocatorSchemaPath provided to .getLocatorSchema().
1061
+ * - If you want to update multiple schemas, chain multiple .update() calls.
862
1062
  *
863
- * updates(indexedUpdates: { [index: number]: Partial< UpdatableLocatorSchemaProperties > | null }):
864
- * - Similar to update, but allows updating any locator in the nested chain (all sub-paths of the LocatorSchemaPath).
865
- * - This method can modify the current deep copy of each LocatorSchema that each sub-path resolves to without
866
- * affecting the original schemas
867
- * - Takes an object where keys represent the index of the last "word" of a sub-path, where the value per key is a
868
- * "LocatorSchema" object which omits the locatorSchemaPath parameter as input, the parameters provided will overwrite
869
- * the corresponding property in the given schema.
870
- * - Returns the updated deep copy of the LocatorSchema object with methods and its own updated deep copies for all
871
- * LocatorSchema each sub-path resolved to.
872
- * - Can be chained with the update and updates methods, and the getLocator or getNestedLocator method.
1063
+ * addFilter(subPath: SubPaths<LocatorSchemaPathType, LocatorSubstring>, filterData: FilterEntry)
1064
+ * - The equivalent of the Playwright locator.filter() method
1065
+ * - This method is used for filtering the specified locator based on the provided filterData.
1066
+ * - Can be chained multiple times to add multiple filters to the same or different LocatorSchema.
873
1067
  *
874
- * getNestedLocator(indices?: { [key: number]: number | null } | null)
875
- * - Asynchronously retrieves a nested locator based on the LocatorSchemaPath provided by getLocatorSchema("...")
876
- * - Can be chained after the update and updates methods, getNestedLocator will end the chain.
877
- * - The optional parameter of the method takes an object with 0-based indices "{0: 0, 3: 1}" for one or more locators
878
- * to be nested given by sub-paths (indices correspond to last "word" of a sub-path).
879
- * - Returns a promise that resolves to the nested locator.
1068
+ * getNestedLocator
1069
+ * - Asynchronously builds a nested locator based on the LocatorSchemaPath provided by getLocatorSchema("...")
1070
+ * - Can be chained once after the update and addFilter methods or directly on the .getLocatorSchema method.
1071
+ * - getNestedLocator will end the method chain and return a nested Playwright Locator.
1072
+ * - Optionally parameter takes a list of key(subPath)-value(index) pairs, the locator constructed from the LocatorSchema
1073
+ * with the specified subPath will resolve to the .nth(n) occurrence of the element, within the chain.
880
1074
  *
881
1075
  * getLocator()
882
- * - Asynchronously retrieves a locator based on the current LocatorSchema. This method does not perform nesting,
883
- * and will return the locator for which the full LocatorSchemaPath resolves to, provided by getLocatorSchema("...")
884
- * - Can be chained after the update and updates methods, getLocator will end the chain.
885
- * - Returns a promise that resolves to the locator.
1076
+ * - Asynchronously retrieves a locator based on the current LocatorSchemaPath.
1077
+ * - This method does not perform nesting and will return the locator for which the full LocatorSchemaPath resolves to, provided by getLocatorSchema("...")
1078
+ * - Can be chained once after the update and addFilter methods or directly on the .getLocatorSchema method.
1079
+ * - getLocator will end the method chain and return a Playwright Locator.
886
1080
  *
887
1081
  * Note: Calling getLocator() and getNestedLocator() on the same LocatorSchemaPath will return a Locator for the same
888
1082
  * element, but the Locator returned by getNestedLocator() will be a locator resolving to said same element through
@@ -892,10 +1086,19 @@ var BasePage = class {
892
1086
  * That said, for certain use cases, getLocator() can be useful, and you could use it to manually chain locators
893
1087
  * yourself if some edge case required it. Though, it would be likely be more prudent to expand your LocatorSchemaPath
894
1088
  * type and initLocatorSchemas() method to include the additional locators you need for the given POC, and then use
895
- * getNestedLocator() instead.
1089
+ * getNestedLocator() instead, or by implementing a helper method on your Page Object Class.
896
1090
  */
897
- getLocatorSchema(locatorSchemaPath) {
898
- return this.locators.getLocatorSchema(locatorSchemaPath);
1091
+ getLocatorSchema(path) {
1092
+ return this.locators.getLocatorSchema(path);
1093
+ }
1094
+ };
1095
+ var WithSubPathValidation = class extends GetLocatorBase {
1096
+ constructor(pageObjectClass, log, locatorSchemaPath) {
1097
+ super(pageObjectClass, log, locatorSchemaPath);
1098
+ this.locatorSchemaPath = locatorSchemaPath;
1099
+ }
1100
+ async getNestedLocator(arg) {
1101
+ return await this.pageObjectClass.getLocatorSchema(this.locatorSchemaPath).getNestedLocator(arg);
899
1102
  }
900
1103
  };
901
1104