@immense/vue-pom-generator 1.0.59 → 1.0.60

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
@@ -558,6 +558,75 @@ function uniquePomStringPatterns(primary, alternates) {
558
558
  }
559
559
  return out;
560
560
  }
561
+ function splitDiscoverabilityWords(value) {
562
+ const normalized = value.replace(/ByKey/g, "").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/[._-]+/g, " ").trim();
563
+ if (!normalized) {
564
+ return [];
565
+ }
566
+ return normalized.split(/\s+/).map((word) => word.toLowerCase()).filter(Boolean);
567
+ }
568
+ function joinDiscoverabilityWords(words) {
569
+ return words.join(" ").replace(/\s+/g, " ").trim();
570
+ }
571
+ function toSentenceCase(value) {
572
+ return value.charAt(0).toUpperCase() + value.slice(1);
573
+ }
574
+ function stripComponentKindSuffix(componentName) {
575
+ for (const suffix of ["Page", "Component", "Layout"]) {
576
+ if (componentName.endsWith(suffix) && componentName.length > suffix.length) {
577
+ return componentName.slice(0, -suffix.length);
578
+ }
579
+ }
580
+ return componentName;
581
+ }
582
+ function removeLeadingWords(words, prefixWords) {
583
+ if (!prefixWords.length || words.length < prefixWords.length) {
584
+ return [...words];
585
+ }
586
+ for (let i = 0; i < prefixWords.length; i++) {
587
+ if (words[i] !== prefixWords[i]) {
588
+ return [...words];
589
+ }
590
+ }
591
+ return words.slice(prefixWords.length);
592
+ }
593
+ function removeTrailingRoleWord(words, roleWord) {
594
+ if (!words.length || words[words.length - 1] !== roleWord) {
595
+ return [...words];
596
+ }
597
+ return words.slice(0, -1);
598
+ }
599
+ function humanizePomMethodName(methodName) {
600
+ return joinDiscoverabilityWords(splitDiscoverabilityWords(methodName));
601
+ }
602
+ function stripPomActionPrefix(actionName) {
603
+ for (const prefix of ["click", "select", "type", "goTo"]) {
604
+ if (actionName.startsWith(prefix) && actionName.length > prefix.length) {
605
+ return actionName.slice(prefix.length);
606
+ }
607
+ }
608
+ return actionName;
609
+ }
610
+ function normalizePomRoleLabel(nativeRole) {
611
+ if (nativeRole === "vselect") {
612
+ return "select";
613
+ }
614
+ return nativeRole || "element";
615
+ }
616
+ function buildPomLocatorDescription(args) {
617
+ const componentWords = splitDiscoverabilityWords(args.componentName ? stripComponentKindSuffix(args.componentName) : "");
618
+ const roleWord = normalizePomRoleLabel(args.nativeRole).toLowerCase();
619
+ const semanticWords = removeLeadingWords(
620
+ removeTrailingRoleWord(splitDiscoverabilityWords(args.methodName), roleWord),
621
+ componentWords
622
+ );
623
+ const phrase = joinDiscoverabilityWords([
624
+ ...componentWords,
625
+ ...semanticWords,
626
+ roleWord
627
+ ]);
628
+ return toSentenceCase(phrase || "Generated element");
629
+ }
561
630
  function upperFirst$1(value) {
562
631
  if (!value) {
563
632
  return value;
@@ -574,7 +643,7 @@ function createInlineParameter(name, options = {}) {
574
643
  initializer: options.initializer
575
644
  };
576
645
  }
577
- function removeByKeySegment(value) {
646
+ function removeByKeySegment$1(value) {
578
647
  const idx = value.lastIndexOf("ByKey");
579
648
  if (idx < 0) {
580
649
  return value;
@@ -589,9 +658,14 @@ function createAsyncMethod(name, parameters, statements) {
589
658
  statements
590
659
  });
591
660
  }
592
- function generateClickMethod(methodName, selector, alternateSelectors, parameters) {
661
+ function generateClickMethod(componentName, methodName, selector, alternateSelectors, parameters) {
593
662
  const name = `click${methodName}`;
594
663
  const noWaitName = `${name}NoWait`;
664
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
665
+ componentName,
666
+ methodName,
667
+ nativeRole: "button"
668
+ }));
595
669
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
596
670
  const hasSelectorVariables = hasPomPatternVariables(selector);
597
671
  const baseParameters = createParameters(selectorParams);
@@ -614,7 +688,7 @@ function generateClickMethod(methodName, selector, alternateSelectors, parameter
614
688
  writer.writeLine(`const candidates = [${candidatesExpr}] as const;`);
615
689
  writer.writeLine("let lastError: unknown;");
616
690
  writer.write("for (const testId of candidates) ").block(() => {
617
- writer.writeLine("const locator = this.locatorByTestId(testId);");
691
+ writer.writeLine(`const locator = this.locatorByTestId(testId, ${locatorDescription});`);
618
692
  writer.write("try ").block(() => {
619
693
  writer.write("if (await locator.count()) ").block(() => {
620
694
  writer.writeLine("await this.clickLocator(locator, annotationText, wait);");
@@ -648,7 +722,7 @@ function generateClickMethod(methodName, selector, alternateSelectors, parameter
648
722
  createInlineParameter("annotationText", { type: "string", initializer: '""' })
649
723
  ],
650
724
  (writer) => {
651
- writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait);`);
725
+ writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait, ${locatorDescription});`);
652
726
  }
653
727
  ),
654
728
  createAsyncMethod(
@@ -668,7 +742,7 @@ function generateClickMethod(methodName, selector, alternateSelectors, parameter
668
742
  createInlineParameter("annotationText", { type: "string", initializer: '""' })
669
743
  ],
670
744
  (writer) => {
671
- writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait);`);
745
+ writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait, ${locatorDescription});`);
672
746
  }
673
747
  ),
674
748
  createAsyncMethod(
@@ -680,55 +754,76 @@ function generateClickMethod(methodName, selector, alternateSelectors, parameter
680
754
  )
681
755
  ];
682
756
  }
683
- function generateRadioMethod(methodName, selector, parameters) {
757
+ function generateRadioMethod(componentName, methodName, selector, parameters) {
684
758
  const name = `select${methodName}`;
759
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
760
+ componentName,
761
+ methodName,
762
+ nativeRole: "radio"
763
+ }));
685
764
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
686
765
  const methodParameters = createParameters(selectorParams);
687
766
  const testIdExpr = toTypeScriptPomPatternExpression(selector);
688
767
  return [
689
768
  createAsyncMethod(name, methodParameters, (writer) => {
690
- writer.writeLine(`await this.clickByTestId(${testIdExpr}, annotationText);`);
769
+ writer.writeLine(`await this.clickByTestId(${testIdExpr}, annotationText, true, ${locatorDescription});`);
691
770
  })
692
771
  ];
693
772
  }
694
- function generateSelectMethod(methodName, selector, parameters) {
773
+ function generateSelectMethod(componentName, methodName, selector, parameters) {
695
774
  const name = `select${methodName}`;
775
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
776
+ componentName,
777
+ methodName,
778
+ nativeRole: "select"
779
+ }));
696
780
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
697
- const selectorExpr = `this.selectorForTestId(${toTypeScriptPomPatternExpression(selector)})`;
781
+ const testIdExpr = toTypeScriptPomPatternExpression(selector);
698
782
  return [
699
783
  createAsyncMethod(
700
784
  name,
701
785
  createParameters(selectorParams),
702
786
  (writer) => {
703
- writer.writeLine(`const selector = ${selectorExpr};`);
704
- writer.writeLine("await this.animateCursorToElement(selector, false, 500, annotationText);");
705
- writer.writeLine("await this.page.selectOption(selector, value);");
787
+ writer.writeLine(`const testId = ${testIdExpr};`);
788
+ writer.writeLine(`const locator = this.locatorByTestId(testId, ${locatorDescription});`);
789
+ writer.writeLine("await this.animateCursorToElement(locator, false, 500, annotationText);");
790
+ writer.writeLine("await locator.selectOption(value);");
706
791
  }
707
792
  )
708
793
  ];
709
794
  }
710
- function generateVSelectMethod(methodName, selector, parameters) {
795
+ function generateVSelectMethod(componentName, methodName, selector, parameters) {
711
796
  const name = `select${methodName}`;
797
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
798
+ componentName,
799
+ methodName,
800
+ nativeRole: "vselect"
801
+ }));
712
802
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
713
803
  return [
714
804
  createAsyncMethod(
715
805
  name,
716
806
  createParameters(selectorParams),
717
807
  (writer) => {
718
- writer.writeLine(`await this.selectVSelectByTestId(${toTypeScriptPomPatternExpression(selector)}, value, timeOut, annotationText);`);
808
+ writer.writeLine(`await this.selectVSelectByTestId(${toTypeScriptPomPatternExpression(selector)}, value, timeOut, annotationText, ${locatorDescription});`);
719
809
  }
720
810
  )
721
811
  ];
722
812
  }
723
- function generateTypeMethod(methodName, selector, parameters) {
813
+ function generateTypeMethod(componentName, methodName, selector, parameters) {
724
814
  const name = `type${methodName}`;
815
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
816
+ componentName,
817
+ methodName,
818
+ nativeRole: "input"
819
+ }));
725
820
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
726
821
  return [
727
822
  createAsyncMethod(
728
823
  name,
729
824
  createParameters(selectorParams),
730
825
  (writer) => {
731
- writer.writeLine(`await this.fillInputByTestId(${toTypeScriptPomPatternExpression(selector)}, text, annotationText);`);
826
+ writer.writeLine(`await this.fillInputByTestId(${toTypeScriptPomPatternExpression(selector)}, text, annotationText, ${locatorDescription});`);
732
827
  }
733
828
  )
734
829
  ];
@@ -743,22 +838,27 @@ function isAllDigits(value) {
743
838
  }
744
839
  return true;
745
840
  }
746
- function generateGetElementByDataTestId(methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
841
+ function generateGetElementByDataTestId(componentName, methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
842
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
843
+ componentName,
844
+ methodName,
845
+ nativeRole
846
+ }));
747
847
  const roleSuffix = upperFirst$1(nativeRole || "Element");
748
848
  const baseName = upperFirst$1(methodName);
749
849
  const numericSuffix = baseName.startsWith(roleSuffix) ? baseName.slice(roleSuffix.length) : "";
750
- const hasRoleSuffix = baseName.endsWith(roleSuffix) || baseName.startsWith(roleSuffix) && isAllDigits(numericSuffix);
751
- const propertyName = hasRoleSuffix ? `${baseName}` : `${baseName}${roleSuffix}`;
850
+ const hasRoleSuffix2 = baseName.endsWith(roleSuffix) || baseName.startsWith(roleSuffix) && isAllDigits(numericSuffix);
851
+ const propertyName = hasRoleSuffix2 ? `${baseName}` : `${baseName}${roleSuffix}`;
752
852
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
753
853
  const indexedVariable = getIndexedPomPatternVariable(selector);
754
854
  if (indexedVariable) {
755
855
  const keyType = getPomParameter(selectorParams, indexedVariable)?.typeExpression || "string";
756
- const keyedPropertyName = getterNameOverride ?? removeByKeySegment(propertyName);
856
+ const keyedPropertyName = getterNameOverride ?? removeByKeySegment$1(propertyName);
757
857
  return [
758
858
  createClassGetter({
759
859
  name: keyedPropertyName,
760
860
  statements: [
761
- `return this.keyedLocators((${indexedVariable}: ${keyType}) => this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)}));`
861
+ `return this.keyedLocators((${indexedVariable}: ${keyType}) => this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)}, ${locatorDescription}));`
762
862
  ]
763
863
  })
764
864
  ];
@@ -771,20 +871,25 @@ function generateGetElementByDataTestId(methodName, nativeRole, selector, altern
771
871
  return [
772
872
  createClassGetter({
773
873
  name: finalPropertyName,
774
- statements: [`return ${locatorExpr};`]
874
+ statements: [`return this.describeLocator(${locatorExpr}, ${locatorDescription});`]
775
875
  })
776
876
  ];
777
877
  }
778
878
  return [
779
879
  createClassGetter({
780
880
  name: finalPropertyName,
781
- statements: [`return this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)});`]
881
+ statements: [`return this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)}, ${locatorDescription});`]
782
882
  })
783
883
  ];
784
884
  }
785
885
  function generateNavigationMethod(args) {
786
- const { targetPageObjectModelClass: target, baseMethodName, selector, alternateSelectors, parameters } = args;
886
+ const { componentName, targetPageObjectModelClass: target, baseMethodName, selector, alternateSelectors, parameters } = args;
787
887
  const methodName = baseMethodName ? `goTo${upperFirst$1(baseMethodName)}` : `goTo${target.endsWith("Page") ? target.slice(0, -"Page".length) : target}`;
888
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
889
+ componentName,
890
+ methodName: baseMethodName,
891
+ nativeRole: "button"
892
+ }));
788
893
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
789
894
  const methodParameters = createParameters(selectorParams);
790
895
  const alternates = uniquePomStringPatterns(selector, alternateSelectors).slice(1);
@@ -800,7 +905,7 @@ function generateNavigationMethod(args) {
800
905
  writer.writeLine(`const candidates = [${candidatesExpr}] as const;`);
801
906
  writer.writeLine("let lastError: unknown;");
802
907
  writer.write("for (const testId of candidates) ").block(() => {
803
- writer.writeLine("const locator = this.locatorByTestId(testId);");
908
+ writer.writeLine(`const locator = this.locatorByTestId(testId, ${locatorDescription});`);
804
909
  writer.write("try ").block(() => {
805
910
  writer.write("if (await locator.count()) ").block(() => {
806
911
  writer.writeLine("await this.clickLocator(locator);");
@@ -825,7 +930,8 @@ function generateNavigationMethod(args) {
825
930
  returnType: `Fluent<${target}>`,
826
931
  statements: (writer) => {
827
932
  writer.write("return this.fluent(async () => ").block(() => {
828
- writer.writeLine(`await this.clickByTestId(${toTypeScriptPomPatternExpression(selector)});`);
933
+ writer.writeLine(`const locator = this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)}, ${locatorDescription});`);
934
+ writer.writeLine("await this.clickLocator(locator);");
829
935
  writer.writeLine(`return new ${target}(this.page);`);
830
936
  });
831
937
  writer.writeLine(");");
@@ -833,9 +939,10 @@ function generateNavigationMethod(args) {
833
939
  })
834
940
  ];
835
941
  }
836
- function generateViewObjectModelMembers(targetPageObjectModelClass, methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
942
+ function generateViewObjectModelMembers(componentName, targetPageObjectModelClass, methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
837
943
  const baseMethodName = nativeRole === "radio" ? methodName || "Radio" : methodName;
838
944
  const members = generateGetElementByDataTestId(
945
+ componentName,
839
946
  baseMethodName,
840
947
  nativeRole,
841
948
  selector,
@@ -847,6 +954,7 @@ function generateViewObjectModelMembers(targetPageObjectModelClass, methodName,
847
954
  return [
848
955
  ...members,
849
956
  ...generateNavigationMethod({
957
+ componentName,
850
958
  targetPageObjectModelClass,
851
959
  baseMethodName,
852
960
  selector,
@@ -856,18 +964,18 @@ function generateViewObjectModelMembers(targetPageObjectModelClass, methodName,
856
964
  ];
857
965
  }
858
966
  if (nativeRole === "select") {
859
- return [...members, ...generateSelectMethod(baseMethodName, selector, parameters)];
967
+ return [...members, ...generateSelectMethod(componentName, baseMethodName, selector, parameters)];
860
968
  }
861
969
  if (nativeRole === "vselect") {
862
- return [...members, ...generateVSelectMethod(baseMethodName, selector, parameters)];
970
+ return [...members, ...generateVSelectMethod(componentName, baseMethodName, selector, parameters)];
863
971
  }
864
972
  if (nativeRole === "input") {
865
- return [...members, ...generateTypeMethod(baseMethodName, selector, parameters)];
973
+ return [...members, ...generateTypeMethod(componentName, baseMethodName, selector, parameters)];
866
974
  }
867
975
  if (nativeRole === "radio") {
868
- return [...members, ...generateRadioMethod(baseMethodName || "Radio", selector, parameters)];
976
+ return [...members, ...generateRadioMethod(componentName, baseMethodName || "Radio", selector, parameters)];
869
977
  }
870
- return [...members, ...generateClickMethod(baseMethodName, selector, alternateSelectors, parameters)];
978
+ return [...members, ...generateClickMethod(componentName, baseMethodName, selector, alternateSelectors, parameters)];
871
979
  }
872
980
  function isSimpleExpressionNode(value) {
873
981
  return value !== null && "type" in value && value.type === NodeTypes.SIMPLE_EXPRESSION;
@@ -2747,7 +2855,7 @@ Fix: either (1) include ${bestKeyPreservePlaceholder} in your :${attrLabel} temp
2747
2855
  }
2748
2856
  return value.slice(0, idx) + value.slice(idx + "ByKey".length);
2749
2857
  };
2750
- const hasRoleSuffix = (baseName, roleSuffix) => {
2858
+ const hasRoleSuffix2 = (baseName, roleSuffix) => {
2751
2859
  if (baseName.endsWith(roleSuffix)) {
2752
2860
  return true;
2753
2861
  }
@@ -2757,13 +2865,13 @@ Fix: either (1) include ${bestKeyPreservePlaceholder} in your :${attrLabel} temp
2757
2865
  const getPrimaryGetterName = (primaryMethodName) => {
2758
2866
  const roleSuffix = upperFirst(normalizedRole || "Element");
2759
2867
  const baseName = upperFirst(primaryMethodName);
2760
- const propertyName = hasRoleSuffix(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
2868
+ const propertyName = hasRoleSuffix2(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
2761
2869
  return selectorIsParameterized ? removeByKeySegment2(propertyName) : propertyName;
2762
2870
  };
2763
2871
  const getPrimaryGetterNameCandidates = (primaryMethodName) => {
2764
2872
  const roleSuffix = upperFirst(normalizedRole || "Element");
2765
2873
  const baseName = upperFirst(primaryMethodName);
2766
- const propertyName = hasRoleSuffix(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
2874
+ const propertyName = hasRoleSuffix2(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
2767
2875
  if (!selectorIsParameterized) {
2768
2876
  return { primary: propertyName };
2769
2877
  }
@@ -2890,7 +2998,7 @@ Fix: either (1) include ${bestKeyPreservePlaceholder} in your :${attrLabel} temp
2890
2998
  if (conflicts && nameCollisionBehavior === "error") {
2891
2999
  const roleSuffix = upperFirst(normalizedRole || "Element");
2892
3000
  const baseNameUpper = upperFirst(baseWithSuffix);
2893
- if (!hasRoleSuffix(baseNameUpper, roleSuffix)) {
3001
+ if (!hasRoleSuffix2(baseNameUpper, roleSuffix)) {
2894
3002
  const baseWithRoleSuffix = `${baseWithSuffix}${roleSuffix}`;
2895
3003
  const candidateWithRoleSuffix = selectorIsParameterized ? `${baseWithRoleSuffix}ByKey` : baseWithRoleSuffix;
2896
3004
  const actionNameWithRoleSuffix = getPrimaryActionMethodName(candidateWithRoleSuffix);
@@ -4442,7 +4550,7 @@ function generateGoToSelfMethod(componentName) {
4442
4550
  function getSelectorPatterns(selector) {
4443
4551
  return selector.kind === "testId" ? [selector.testId] : [selector.rootTestId, selector.label];
4444
4552
  }
4445
- function generateExtraClickMethodMembers(spec) {
4553
+ function generateExtraClickMethodMembers(spec, componentName) {
4446
4554
  if (spec.kind !== "click") {
4447
4555
  return [];
4448
4556
  }
@@ -4457,16 +4565,13 @@ function generateExtraClickMethodMembers(spec) {
4457
4565
  const hasWait = signatureSpecs.some((param) => param.name === "wait");
4458
4566
  const annotationArg = hasAnnotationText ? "annotationText" : '""';
4459
4567
  const waitArg = hasWait ? "wait" : "true";
4568
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
4569
+ componentName,
4570
+ methodName: stripPomActionPrefix(spec.name),
4571
+ nativeRole: "button"
4572
+ }));
4460
4573
  if (spec.selector.kind === "testId") {
4461
4574
  const testIdBinding = bindTypeScriptPomPattern(spec.selector.testId, "testId");
4462
- const clickArgs = [];
4463
- clickArgs.push(testIdBinding.expression);
4464
- if (hasAnnotationText || hasWait) {
4465
- clickArgs.push(annotationArg);
4466
- }
4467
- if (hasWait) {
4468
- clickArgs.push(waitArg);
4469
- }
4470
4575
  return [
4471
4576
  createClassMethod({
4472
4577
  name: spec.name,
@@ -4479,7 +4584,7 @@ function generateExtraClickMethodMembers(spec) {
4479
4584
  for (const statement of testIdBinding.setupStatements) {
4480
4585
  writer.writeLine(statement);
4481
4586
  }
4482
- writer.writeLine(`await this.clickByTestId(${clickArgs.join(", ")});`);
4587
+ writer.writeLine(`await this.clickByTestId(${testIdBinding.expression}, ${annotationArg}, ${waitArg}, ${locatorDescription});`);
4483
4588
  }
4484
4589
  })
4485
4590
  ];
@@ -4501,16 +4606,17 @@ function generateExtraClickMethodMembers(spec) {
4501
4606
  for (const statement of labelBinding.setupStatements) {
4502
4607
  writer.writeLine(statement);
4503
4608
  }
4504
- writer.writeLine(`await this.clickWithinTestIdByLabel(${rootBinding.expression}, ${labelBinding.expression}, ${annotationArg}, ${waitArg});`);
4609
+ writer.writeLine(`await this.clickWithinTestIdByLabel(${rootBinding.expression}, ${labelBinding.expression}, ${annotationArg}, ${waitArg}, { description: ${locatorDescription} });`);
4505
4610
  }
4506
4611
  })
4507
4612
  ];
4508
4613
  }
4509
- function generateMethodMembersFromPom(primary, targetPageObjectModelClass) {
4614
+ function generateMethodMembersFromPom(componentName, primary, targetPageObjectModelClass) {
4510
4615
  if (primary.emitPrimary === false) {
4511
4616
  return [];
4512
4617
  }
4513
4618
  return generateViewObjectModelMembers(
4619
+ componentName,
4514
4620
  targetPageObjectModelClass,
4515
4621
  primary.methodName,
4516
4622
  primary.nativeRole,
@@ -4520,7 +4626,7 @@ function generateMethodMembersFromPom(primary, targetPageObjectModelClass) {
4520
4626
  primary.parameters
4521
4627
  );
4522
4628
  }
4523
- function generateMethodsContentForDependencies(dependencies) {
4629
+ function generateMethodsContentForDependencies(componentName, dependencies) {
4524
4630
  const entries = Array.from(dependencies.dataTestIdSet ?? []);
4525
4631
  const primarySpecsAll = entries.map((e) => ({ pom: e.pom, target: e.targetPageObjectModelClass })).filter((x) => !!x.pom).sort((a, b) => a.pom.methodName.localeCompare(b.pom.methodName));
4526
4632
  const seenPrimaryKeys = /* @__PURE__ */ new Set();
@@ -4545,10 +4651,10 @@ function generateMethodsContentForDependencies(dependencies) {
4545
4651
  const extras = (dependencies.pomExtraMethods ?? []).slice().sort((a, b) => a.name.localeCompare(b.name));
4546
4652
  const members = [];
4547
4653
  for (const { pom, target } of primarySpecs) {
4548
- members.push(...generateMethodMembersFromPom(pom, target));
4654
+ members.push(...generateMethodMembersFromPom(componentName, pom, target));
4549
4655
  }
4550
4656
  for (const extra of extras) {
4551
- members.push(...generateExtraClickMethodMembers(extra));
4657
+ members.push(...generateExtraClickMethodMembers(extra, componentName));
4552
4658
  }
4553
4659
  return members;
4554
4660
  }
@@ -5402,7 +5508,7 @@ function prepareViewObjectModelClass(componentName, dependencies, componentHiera
5402
5508
  members.push(...generateRouteProperty(routeMeta));
5403
5509
  members.push(...generateGoToSelfMethod(className));
5404
5510
  }
5405
- members.push(...generateMethodsContentForDependencies(dependencies));
5511
+ members.push(...generateMethodsContentForDependencies(componentName, dependencies));
5406
5512
  return {
5407
5513
  className,
5408
5514
  componentRefsForInstances,
@@ -8067,8 +8173,119 @@ function createDevProcessorPlugin(options) {
8067
8173
  }
8068
8174
  };
8069
8175
  }
8070
- function generateTestIdsModule(componentTestIds) {
8071
- const manifestEntries = Array.from(componentTestIds.entries()).sort((a, b) => a[0].localeCompare(b[0]));
8176
+ function removeByKeySegment(value) {
8177
+ const idx = value.indexOf("ByKey");
8178
+ if (idx < 0) {
8179
+ return value;
8180
+ }
8181
+ return value.slice(0, idx) + value.slice(idx + "ByKey".length);
8182
+ }
8183
+ function hasRoleSuffix(baseName, roleSuffix) {
8184
+ if (baseName.endsWith(roleSuffix)) {
8185
+ return true;
8186
+ }
8187
+ const re = new RegExp(`^${roleSuffix}\\d+$`);
8188
+ return re.test(baseName);
8189
+ }
8190
+ function getGeneratedPropertyName(pom) {
8191
+ if (pom.getterNameOverride) {
8192
+ return pom.getterNameOverride;
8193
+ }
8194
+ const roleSuffix = upperFirst(pom.nativeRole || "Element");
8195
+ const baseName = upperFirst(pom.methodName);
8196
+ const propertyName = hasRoleSuffix(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
8197
+ return pom.selector.patternKind === "parameterized" ? removeByKeySegment(propertyName) : propertyName;
8198
+ }
8199
+ function getGeneratedActionName(entry, pom) {
8200
+ const methodNameUpper = upperFirst(pom.methodName);
8201
+ const radioMethodNameUpper = upperFirst(pom.methodName || "Radio");
8202
+ const isNavigation = !!entry.targetPageObjectModelClass;
8203
+ if (isNavigation) {
8204
+ return `goTo${methodNameUpper}`;
8205
+ }
8206
+ switch (pom.nativeRole) {
8207
+ case "input":
8208
+ return `type${methodNameUpper}`;
8209
+ case "select":
8210
+ case "vselect":
8211
+ return `select${methodNameUpper}`;
8212
+ case "radio":
8213
+ return `select${radioMethodNameUpper}`;
8214
+ default:
8215
+ return `click${methodNameUpper}`;
8216
+ }
8217
+ }
8218
+ function matchesPrimarySelector(extraMethod, pom) {
8219
+ if (extraMethod.selector.kind !== "testId") {
8220
+ return false;
8221
+ }
8222
+ return extraMethod.selector.testId.formatted === pom.selector.formatted && extraMethod.selector.testId.patternKind === pom.selector.patternKind;
8223
+ }
8224
+ function getManifestEntry(componentName, entry, componentMetadata, extraMethods) {
8225
+ const testId = entry.selectorValue.formatted;
8226
+ const metadata = componentMetadata?.get(testId);
8227
+ const pom = entry.pom;
8228
+ const generatedActionName = pom ? getGeneratedActionName(entry, pom) : null;
8229
+ const extraActionNames = pom ? extraMethods.filter((extraMethod) => matchesPrimarySelector(extraMethod, pom)).map((extraMethod) => extraMethod.name).sort((a, b) => a.localeCompare(b)) : [];
8230
+ const generatedActionNames = Array.from(/* @__PURE__ */ new Set([
8231
+ ...generatedActionName ? [generatedActionName] : [],
8232
+ ...extraActionNames.filter((name) => name !== generatedActionName)
8233
+ ]));
8234
+ return {
8235
+ testId,
8236
+ selectorPatternKind: entry.selectorValue.patternKind,
8237
+ semanticName: metadata?.semanticName ?? (pom ? humanizePomMethodName(pom.methodName) : testId),
8238
+ locatorDescription: pom ? buildPomLocatorDescription({
8239
+ componentName,
8240
+ methodName: pom.methodName,
8241
+ nativeRole: pom.nativeRole
8242
+ }) : componentName,
8243
+ inferredRole: pom?.nativeRole ?? null,
8244
+ generatedPropertyName: pom ? getGeneratedPropertyName(pom) : null,
8245
+ generatedActionName,
8246
+ generatedActionNames,
8247
+ emitPrimary: pom?.emitPrimary !== false,
8248
+ ...entry.targetPageObjectModelClass ? { targetPageObjectModelClass: entry.targetPageObjectModelClass } : {},
8249
+ ...metadata?.tag ? { sourceTag: metadata.tag } : {},
8250
+ ...metadata ? { sourceTagType: metadata.tagType } : {},
8251
+ ...metadata?.patchFlag !== void 0 ? { patchFlag: metadata.patchFlag } : {},
8252
+ ...metadata?.dynamicProps?.length ? { dynamicProps: metadata.dynamicProps } : {},
8253
+ ...metadata?.hasClickHandler !== void 0 ? { hasClickHandler: metadata.hasClickHandler } : {},
8254
+ ...metadata?.hasDynamicClass !== void 0 ? { hasDynamicClass: metadata.hasDynamicClass } : {},
8255
+ ...metadata?.hasDynamicStyle !== void 0 ? { hasDynamicStyle: metadata.hasDynamicStyle } : {},
8256
+ ...metadata?.hasDynamicText !== void 0 ? { hasDynamicText: metadata.hasDynamicText } : {}
8257
+ };
8258
+ }
8259
+ function buildPomManifest(componentHierarchyMap, elementMetadata) {
8260
+ const manifestEntries = Array.from(componentHierarchyMap.entries()).sort((a, b) => a[0].localeCompare(b[0])).map(([componentName, dependencies]) => {
8261
+ const entries = Array.from(dependencies.dataTestIdSet).sort((a, b) => a.selectorValue.formatted.localeCompare(b.selectorValue.formatted)).map((entry) => getManifestEntry(componentName, entry, elementMetadata.get(componentName), dependencies.pomExtraMethods ?? []));
8262
+ if (!entries.length) {
8263
+ return null;
8264
+ }
8265
+ return [componentName, {
8266
+ componentName,
8267
+ className: componentName,
8268
+ sourceFile: dependencies.filePath,
8269
+ kind: dependencies.isView ? "view" : "component",
8270
+ testIds: Array.from(new Set(entries.map((entry) => entry.testId))),
8271
+ entries
8272
+ }];
8273
+ }).filter((entry) => entry !== null);
8274
+ return Object.fromEntries(manifestEntries);
8275
+ }
8276
+ function buildTestIdManifest(pomManifest) {
8277
+ return Object.fromEntries(
8278
+ Object.entries(pomManifest).map(([componentName, component]) => [componentName, Array.from(new Set(component.testIds)).sort((a, b) => a.localeCompare(b))])
8279
+ );
8280
+ }
8281
+ function writeConstJson(value) {
8282
+ return (writer) => {
8283
+ writer.write(`${JSON.stringify(value, null, 2)} as const`);
8284
+ };
8285
+ }
8286
+ function generateTestIdsModule(componentHierarchyMap, elementMetadata) {
8287
+ const pomManifest = buildPomManifest(componentHierarchyMap, elementMetadata);
8288
+ const testIdManifest = buildTestIdManifest(pomManifest);
8072
8289
  return renderSourceFile("virtual-testids.ts", (sourceFile) => {
8073
8290
  sourceFile.addStatements("// Virtual module: test id manifest");
8074
8291
  sourceFile.addVariableStatement({
@@ -8076,16 +8293,15 @@ function generateTestIdsModule(componentTestIds) {
8076
8293
  isExported: true,
8077
8294
  declarations: [{
8078
8295
  name: "testIdManifest",
8079
- initializer: (writer) => {
8080
- writer.write("{").newLine();
8081
- writer.indent(() => {
8082
- manifestEntries.forEach(([componentName, testIds], index) => {
8083
- const suffix = index === manifestEntries.length - 1 ? "" : ",";
8084
- writer.writeLine(`${JSON.stringify(componentName)}: ${JSON.stringify(Array.from(testIds).sort())}${suffix}`);
8085
- });
8086
- });
8087
- writer.write("} as const");
8088
- }
8296
+ initializer: writeConstJson(testIdManifest)
8297
+ }]
8298
+ });
8299
+ sourceFile.addVariableStatement({
8300
+ declarationKind: VariableDeclarationKind.Const,
8301
+ isExported: true,
8302
+ declarations: [{
8303
+ name: "pomManifest",
8304
+ initializer: writeConstJson(pomManifest)
8089
8305
  }]
8090
8306
  });
8091
8307
  sourceFile.addTypeAlias({
@@ -8098,27 +8314,67 @@ function generateTestIdsModule(componentTestIds) {
8098
8314
  name: "ComponentName",
8099
8315
  type: "keyof TestIdManifest"
8100
8316
  });
8317
+ sourceFile.addTypeAlias({
8318
+ isExported: true,
8319
+ name: "PomManifest",
8320
+ type: "typeof pomManifest"
8321
+ });
8322
+ sourceFile.addTypeAlias({
8323
+ isExported: true,
8324
+ name: "PomManifestComponentName",
8325
+ type: "keyof PomManifest"
8326
+ });
8327
+ });
8328
+ }
8329
+ function generatePomManifestModule(componentHierarchyMap, elementMetadata) {
8330
+ const pomManifest = buildPomManifest(componentHierarchyMap, elementMetadata);
8331
+ return renderSourceFile("virtual-pom-manifest.ts", (sourceFile) => {
8332
+ sourceFile.addStatements("// Virtual module: richer POM discoverability manifest");
8333
+ sourceFile.addVariableStatement({
8334
+ declarationKind: VariableDeclarationKind.Const,
8335
+ isExported: true,
8336
+ declarations: [{
8337
+ name: "pomManifest",
8338
+ initializer: writeConstJson(pomManifest)
8339
+ }]
8340
+ });
8341
+ sourceFile.addTypeAlias({
8342
+ isExported: true,
8343
+ name: "PomManifest",
8344
+ type: "typeof pomManifest"
8345
+ });
8346
+ sourceFile.addTypeAlias({
8347
+ isExported: true,
8348
+ name: "PomManifestComponentName",
8349
+ type: "keyof PomManifest"
8350
+ });
8101
8351
  });
8102
8352
  }
8103
- const VIRTUAL_ID = "virtual:testids";
8104
- const RESOLVED_ID = `\0${VIRTUAL_ID}`;
8105
- function createTestIdsVirtualModulesPlugin(componentTestIds) {
8353
+ const TEST_IDS_VIRTUAL_ID = "virtual:testids";
8354
+ const TEST_IDS_RESOLVED_ID = `\0${TEST_IDS_VIRTUAL_ID}`;
8355
+ const POM_MANIFEST_VIRTUAL_ID = "virtual:pom-manifest";
8356
+ const POM_MANIFEST_RESOLVED_ID = `\0${POM_MANIFEST_VIRTUAL_ID}`;
8357
+ function createTestIdsVirtualModulesPlugin(componentHierarchyMap, elementMetadata) {
8106
8358
  return {
8107
8359
  name: "vue-pom-generator:virtual-testids",
8108
8360
  resolveId(id) {
8109
- if (id === VIRTUAL_ID)
8110
- return RESOLVED_ID;
8361
+ if (id === TEST_IDS_VIRTUAL_ID)
8362
+ return TEST_IDS_RESOLVED_ID;
8363
+ if (id === POM_MANIFEST_VIRTUAL_ID)
8364
+ return POM_MANIFEST_RESOLVED_ID;
8111
8365
  },
8112
8366
  load(id) {
8113
- if (id === RESOLVED_ID)
8114
- return generateTestIdsModule(componentTestIds);
8367
+ if (id === TEST_IDS_RESOLVED_ID)
8368
+ return generateTestIdsModule(componentHierarchyMap, elementMetadata);
8369
+ if (id === POM_MANIFEST_RESOLVED_ID)
8370
+ return generatePomManifestModule(componentHierarchyMap, elementMetadata);
8115
8371
  }
8116
8372
  };
8117
8373
  }
8118
8374
  function createSupportPlugins(options) {
8119
8375
  const {
8120
- componentTestIds,
8121
8376
  componentHierarchyMap,
8377
+ elementMetadata,
8122
8378
  vueFilesPathMap,
8123
8379
  nativeWrappers,
8124
8380
  excludedComponents,
@@ -8204,7 +8460,7 @@ function createSupportPlugins(options) {
8204
8460
  getResolvedRouterEntry: resolveRouterEntry2,
8205
8461
  loggerRef
8206
8462
  });
8207
- const virtualModules = createTestIdsVirtualModulesPlugin(componentTestIds);
8463
+ const virtualModules = createTestIdsVirtualModulesPlugin(componentHierarchyMap, elementMetadata);
8208
8464
  return [tsProcessor, devProcessor, virtualModules];
8209
8465
  }
8210
8466
  function findDataTestIdProp(element, attributeName = "data-testid") {
@@ -8694,7 +8950,6 @@ function getSharedGeneratorState(key) {
8694
8950
  let state = sharedGeneratorStateRegistry.get(key);
8695
8951
  if (!state) {
8696
8952
  state = {
8697
- componentTestIds: /* @__PURE__ */ new Map(),
8698
8953
  elementMetadata: /* @__PURE__ */ new Map(),
8699
8954
  semanticNameMap: /* @__PURE__ */ new Map(),
8700
8955
  componentHierarchyMap: /* @__PURE__ */ new Map(),
@@ -8893,7 +9148,7 @@ function createVuePomGeneratorPlugins(options = {}) {
8893
9148
  };
8894
9149
  const getViewsDirAbs = () => resolveFromProjectRoot(projectRootRef.current, getViewsDir());
8895
9150
  const getWrapperSearchRootsAbs = () => getWrapperSearchRoots().map((root) => resolveFromProjectRoot(projectRootRef.current, root));
8896
- const { componentTestIds, elementMetadata, semanticNameMap, componentHierarchyMap, vueFilesPathMap } = sharedState;
9151
+ const { elementMetadata, semanticNameMap, componentHierarchyMap, vueFilesPathMap } = sharedState;
8897
9152
  const { metadataCollectorPlugin, internalVuePlugin, templateCompilerOptions } = createVuePluginWithTestIds({
8898
9153
  vueOptions,
8899
9154
  existingIdBehavior: resolvedGenerationOptions.existingIdBehavior,
@@ -8913,8 +9168,8 @@ function createVuePomGeneratorPlugins(options = {}) {
8913
9168
  });
8914
9169
  templateCompilerOptionsForResolvedPlugin = templateCompilerOptions;
8915
9170
  const supportPlugins = createSupportPlugins({
8916
- componentTestIds,
8917
9171
  componentHierarchyMap,
9172
+ elementMetadata,
8918
9173
  vueFilesPathMap,
8919
9174
  nativeWrappers,
8920
9175
  excludedComponents,
@@ -8941,7 +9196,7 @@ function createVuePomGeneratorPlugins(options = {}) {
8941
9196
  ...supportPlugins
8942
9197
  ];
8943
9198
  if (!generationEnabled) {
8944
- const virtualModules = createTestIdsVirtualModulesPlugin(componentTestIds);
9199
+ const virtualModules = createTestIdsVirtualModulesPlugin(componentHierarchyMap, elementMetadata);
8945
9200
  return [
8946
9201
  configPlugin,
8947
9202
  metadataCollectorPlugin,