@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.cjs CHANGED
@@ -599,6 +599,75 @@ function uniquePomStringPatterns(primary, alternates) {
599
599
  }
600
600
  return out;
601
601
  }
602
+ function splitDiscoverabilityWords(value) {
603
+ 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();
604
+ if (!normalized) {
605
+ return [];
606
+ }
607
+ return normalized.split(/\s+/).map((word) => word.toLowerCase()).filter(Boolean);
608
+ }
609
+ function joinDiscoverabilityWords(words) {
610
+ return words.join(" ").replace(/\s+/g, " ").trim();
611
+ }
612
+ function toSentenceCase(value) {
613
+ return value.charAt(0).toUpperCase() + value.slice(1);
614
+ }
615
+ function stripComponentKindSuffix(componentName) {
616
+ for (const suffix of ["Page", "Component", "Layout"]) {
617
+ if (componentName.endsWith(suffix) && componentName.length > suffix.length) {
618
+ return componentName.slice(0, -suffix.length);
619
+ }
620
+ }
621
+ return componentName;
622
+ }
623
+ function removeLeadingWords(words, prefixWords) {
624
+ if (!prefixWords.length || words.length < prefixWords.length) {
625
+ return [...words];
626
+ }
627
+ for (let i = 0; i < prefixWords.length; i++) {
628
+ if (words[i] !== prefixWords[i]) {
629
+ return [...words];
630
+ }
631
+ }
632
+ return words.slice(prefixWords.length);
633
+ }
634
+ function removeTrailingRoleWord(words, roleWord) {
635
+ if (!words.length || words[words.length - 1] !== roleWord) {
636
+ return [...words];
637
+ }
638
+ return words.slice(0, -1);
639
+ }
640
+ function humanizePomMethodName(methodName) {
641
+ return joinDiscoverabilityWords(splitDiscoverabilityWords(methodName));
642
+ }
643
+ function stripPomActionPrefix(actionName) {
644
+ for (const prefix of ["click", "select", "type", "goTo"]) {
645
+ if (actionName.startsWith(prefix) && actionName.length > prefix.length) {
646
+ return actionName.slice(prefix.length);
647
+ }
648
+ }
649
+ return actionName;
650
+ }
651
+ function normalizePomRoleLabel(nativeRole) {
652
+ if (nativeRole === "vselect") {
653
+ return "select";
654
+ }
655
+ return nativeRole || "element";
656
+ }
657
+ function buildPomLocatorDescription(args) {
658
+ const componentWords = splitDiscoverabilityWords(args.componentName ? stripComponentKindSuffix(args.componentName) : "");
659
+ const roleWord = normalizePomRoleLabel(args.nativeRole).toLowerCase();
660
+ const semanticWords = removeLeadingWords(
661
+ removeTrailingRoleWord(splitDiscoverabilityWords(args.methodName), roleWord),
662
+ componentWords
663
+ );
664
+ const phrase = joinDiscoverabilityWords([
665
+ ...componentWords,
666
+ ...semanticWords,
667
+ roleWord
668
+ ]);
669
+ return toSentenceCase(phrase || "Generated element");
670
+ }
602
671
  function upperFirst$1(value) {
603
672
  if (!value) {
604
673
  return value;
@@ -615,7 +684,7 @@ function createInlineParameter(name, options = {}) {
615
684
  initializer: options.initializer
616
685
  };
617
686
  }
618
- function removeByKeySegment(value) {
687
+ function removeByKeySegment$1(value) {
619
688
  const idx = value.lastIndexOf("ByKey");
620
689
  if (idx < 0) {
621
690
  return value;
@@ -630,9 +699,14 @@ function createAsyncMethod(name, parameters, statements) {
630
699
  statements
631
700
  });
632
701
  }
633
- function generateClickMethod(methodName, selector, alternateSelectors, parameters) {
702
+ function generateClickMethod(componentName, methodName, selector, alternateSelectors, parameters) {
634
703
  const name = `click${methodName}`;
635
704
  const noWaitName = `${name}NoWait`;
705
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
706
+ componentName,
707
+ methodName,
708
+ nativeRole: "button"
709
+ }));
636
710
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
637
711
  const hasSelectorVariables = hasPomPatternVariables(selector);
638
712
  const baseParameters = createParameters(selectorParams);
@@ -655,7 +729,7 @@ function generateClickMethod(methodName, selector, alternateSelectors, parameter
655
729
  writer.writeLine(`const candidates = [${candidatesExpr}] as const;`);
656
730
  writer.writeLine("let lastError: unknown;");
657
731
  writer.write("for (const testId of candidates) ").block(() => {
658
- writer.writeLine("const locator = this.locatorByTestId(testId);");
732
+ writer.writeLine(`const locator = this.locatorByTestId(testId, ${locatorDescription});`);
659
733
  writer.write("try ").block(() => {
660
734
  writer.write("if (await locator.count()) ").block(() => {
661
735
  writer.writeLine("await this.clickLocator(locator, annotationText, wait);");
@@ -689,7 +763,7 @@ function generateClickMethod(methodName, selector, alternateSelectors, parameter
689
763
  createInlineParameter("annotationText", { type: "string", initializer: '""' })
690
764
  ],
691
765
  (writer) => {
692
- writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait);`);
766
+ writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait, ${locatorDescription});`);
693
767
  }
694
768
  ),
695
769
  createAsyncMethod(
@@ -709,7 +783,7 @@ function generateClickMethod(methodName, selector, alternateSelectors, parameter
709
783
  createInlineParameter("annotationText", { type: "string", initializer: '""' })
710
784
  ],
711
785
  (writer) => {
712
- writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait);`);
786
+ writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait, ${locatorDescription});`);
713
787
  }
714
788
  ),
715
789
  createAsyncMethod(
@@ -721,55 +795,76 @@ function generateClickMethod(methodName, selector, alternateSelectors, parameter
721
795
  )
722
796
  ];
723
797
  }
724
- function generateRadioMethod(methodName, selector, parameters) {
798
+ function generateRadioMethod(componentName, methodName, selector, parameters) {
725
799
  const name = `select${methodName}`;
800
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
801
+ componentName,
802
+ methodName,
803
+ nativeRole: "radio"
804
+ }));
726
805
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
727
806
  const methodParameters = createParameters(selectorParams);
728
807
  const testIdExpr = toTypeScriptPomPatternExpression(selector);
729
808
  return [
730
809
  createAsyncMethod(name, methodParameters, (writer) => {
731
- writer.writeLine(`await this.clickByTestId(${testIdExpr}, annotationText);`);
810
+ writer.writeLine(`await this.clickByTestId(${testIdExpr}, annotationText, true, ${locatorDescription});`);
732
811
  })
733
812
  ];
734
813
  }
735
- function generateSelectMethod(methodName, selector, parameters) {
814
+ function generateSelectMethod(componentName, methodName, selector, parameters) {
736
815
  const name = `select${methodName}`;
816
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
817
+ componentName,
818
+ methodName,
819
+ nativeRole: "select"
820
+ }));
737
821
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
738
- const selectorExpr = `this.selectorForTestId(${toTypeScriptPomPatternExpression(selector)})`;
822
+ const testIdExpr = toTypeScriptPomPatternExpression(selector);
739
823
  return [
740
824
  createAsyncMethod(
741
825
  name,
742
826
  createParameters(selectorParams),
743
827
  (writer) => {
744
- writer.writeLine(`const selector = ${selectorExpr};`);
745
- writer.writeLine("await this.animateCursorToElement(selector, false, 500, annotationText);");
746
- writer.writeLine("await this.page.selectOption(selector, value);");
828
+ writer.writeLine(`const testId = ${testIdExpr};`);
829
+ writer.writeLine(`const locator = this.locatorByTestId(testId, ${locatorDescription});`);
830
+ writer.writeLine("await this.animateCursorToElement(locator, false, 500, annotationText);");
831
+ writer.writeLine("await locator.selectOption(value);");
747
832
  }
748
833
  )
749
834
  ];
750
835
  }
751
- function generateVSelectMethod(methodName, selector, parameters) {
836
+ function generateVSelectMethod(componentName, methodName, selector, parameters) {
752
837
  const name = `select${methodName}`;
838
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
839
+ componentName,
840
+ methodName,
841
+ nativeRole: "vselect"
842
+ }));
753
843
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
754
844
  return [
755
845
  createAsyncMethod(
756
846
  name,
757
847
  createParameters(selectorParams),
758
848
  (writer) => {
759
- writer.writeLine(`await this.selectVSelectByTestId(${toTypeScriptPomPatternExpression(selector)}, value, timeOut, annotationText);`);
849
+ writer.writeLine(`await this.selectVSelectByTestId(${toTypeScriptPomPatternExpression(selector)}, value, timeOut, annotationText, ${locatorDescription});`);
760
850
  }
761
851
  )
762
852
  ];
763
853
  }
764
- function generateTypeMethod(methodName, selector, parameters) {
854
+ function generateTypeMethod(componentName, methodName, selector, parameters) {
765
855
  const name = `type${methodName}`;
856
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
857
+ componentName,
858
+ methodName,
859
+ nativeRole: "input"
860
+ }));
766
861
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
767
862
  return [
768
863
  createAsyncMethod(
769
864
  name,
770
865
  createParameters(selectorParams),
771
866
  (writer) => {
772
- writer.writeLine(`await this.fillInputByTestId(${toTypeScriptPomPatternExpression(selector)}, text, annotationText);`);
867
+ writer.writeLine(`await this.fillInputByTestId(${toTypeScriptPomPatternExpression(selector)}, text, annotationText, ${locatorDescription});`);
773
868
  }
774
869
  )
775
870
  ];
@@ -784,22 +879,27 @@ function isAllDigits(value) {
784
879
  }
785
880
  return true;
786
881
  }
787
- function generateGetElementByDataTestId(methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
882
+ function generateGetElementByDataTestId(componentName, methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
883
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
884
+ componentName,
885
+ methodName,
886
+ nativeRole
887
+ }));
788
888
  const roleSuffix = upperFirst$1(nativeRole || "Element");
789
889
  const baseName = upperFirst$1(methodName);
790
890
  const numericSuffix = baseName.startsWith(roleSuffix) ? baseName.slice(roleSuffix.length) : "";
791
- const hasRoleSuffix = baseName.endsWith(roleSuffix) || baseName.startsWith(roleSuffix) && isAllDigits(numericSuffix);
792
- const propertyName = hasRoleSuffix ? `${baseName}` : `${baseName}${roleSuffix}`;
891
+ const hasRoleSuffix2 = baseName.endsWith(roleSuffix) || baseName.startsWith(roleSuffix) && isAllDigits(numericSuffix);
892
+ const propertyName = hasRoleSuffix2 ? `${baseName}` : `${baseName}${roleSuffix}`;
793
893
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
794
894
  const indexedVariable = getIndexedPomPatternVariable(selector);
795
895
  if (indexedVariable) {
796
896
  const keyType = getPomParameter(selectorParams, indexedVariable)?.typeExpression || "string";
797
- const keyedPropertyName = getterNameOverride ?? removeByKeySegment(propertyName);
897
+ const keyedPropertyName = getterNameOverride ?? removeByKeySegment$1(propertyName);
798
898
  return [
799
899
  createClassGetter({
800
900
  name: keyedPropertyName,
801
901
  statements: [
802
- `return this.keyedLocators((${indexedVariable}: ${keyType}) => this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)}));`
902
+ `return this.keyedLocators((${indexedVariable}: ${keyType}) => this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)}, ${locatorDescription}));`
803
903
  ]
804
904
  })
805
905
  ];
@@ -812,20 +912,25 @@ function generateGetElementByDataTestId(methodName, nativeRole, selector, altern
812
912
  return [
813
913
  createClassGetter({
814
914
  name: finalPropertyName,
815
- statements: [`return ${locatorExpr};`]
915
+ statements: [`return this.describeLocator(${locatorExpr}, ${locatorDescription});`]
816
916
  })
817
917
  ];
818
918
  }
819
919
  return [
820
920
  createClassGetter({
821
921
  name: finalPropertyName,
822
- statements: [`return this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)});`]
922
+ statements: [`return this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)}, ${locatorDescription});`]
823
923
  })
824
924
  ];
825
925
  }
826
926
  function generateNavigationMethod(args) {
827
- const { targetPageObjectModelClass: target, baseMethodName, selector, alternateSelectors, parameters } = args;
927
+ const { componentName, targetPageObjectModelClass: target, baseMethodName, selector, alternateSelectors, parameters } = args;
828
928
  const methodName = baseMethodName ? `goTo${upperFirst$1(baseMethodName)}` : `goTo${target.endsWith("Page") ? target.slice(0, -"Page".length) : target}`;
929
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
930
+ componentName,
931
+ methodName: baseMethodName,
932
+ nativeRole: "button"
933
+ }));
829
934
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
830
935
  const methodParameters = createParameters(selectorParams);
831
936
  const alternates = uniquePomStringPatterns(selector, alternateSelectors).slice(1);
@@ -841,7 +946,7 @@ function generateNavigationMethod(args) {
841
946
  writer.writeLine(`const candidates = [${candidatesExpr}] as const;`);
842
947
  writer.writeLine("let lastError: unknown;");
843
948
  writer.write("for (const testId of candidates) ").block(() => {
844
- writer.writeLine("const locator = this.locatorByTestId(testId);");
949
+ writer.writeLine(`const locator = this.locatorByTestId(testId, ${locatorDescription});`);
845
950
  writer.write("try ").block(() => {
846
951
  writer.write("if (await locator.count()) ").block(() => {
847
952
  writer.writeLine("await this.clickLocator(locator);");
@@ -866,7 +971,8 @@ function generateNavigationMethod(args) {
866
971
  returnType: `Fluent<${target}>`,
867
972
  statements: (writer) => {
868
973
  writer.write("return this.fluent(async () => ").block(() => {
869
- writer.writeLine(`await this.clickByTestId(${toTypeScriptPomPatternExpression(selector)});`);
974
+ writer.writeLine(`const locator = this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)}, ${locatorDescription});`);
975
+ writer.writeLine("await this.clickLocator(locator);");
870
976
  writer.writeLine(`return new ${target}(this.page);`);
871
977
  });
872
978
  writer.writeLine(");");
@@ -874,9 +980,10 @@ function generateNavigationMethod(args) {
874
980
  })
875
981
  ];
876
982
  }
877
- function generateViewObjectModelMembers(targetPageObjectModelClass, methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
983
+ function generateViewObjectModelMembers(componentName, targetPageObjectModelClass, methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
878
984
  const baseMethodName = nativeRole === "radio" ? methodName || "Radio" : methodName;
879
985
  const members = generateGetElementByDataTestId(
986
+ componentName,
880
987
  baseMethodName,
881
988
  nativeRole,
882
989
  selector,
@@ -888,6 +995,7 @@ function generateViewObjectModelMembers(targetPageObjectModelClass, methodName,
888
995
  return [
889
996
  ...members,
890
997
  ...generateNavigationMethod({
998
+ componentName,
891
999
  targetPageObjectModelClass,
892
1000
  baseMethodName,
893
1001
  selector,
@@ -897,18 +1005,18 @@ function generateViewObjectModelMembers(targetPageObjectModelClass, methodName,
897
1005
  ];
898
1006
  }
899
1007
  if (nativeRole === "select") {
900
- return [...members, ...generateSelectMethod(baseMethodName, selector, parameters)];
1008
+ return [...members, ...generateSelectMethod(componentName, baseMethodName, selector, parameters)];
901
1009
  }
902
1010
  if (nativeRole === "vselect") {
903
- return [...members, ...generateVSelectMethod(baseMethodName, selector, parameters)];
1011
+ return [...members, ...generateVSelectMethod(componentName, baseMethodName, selector, parameters)];
904
1012
  }
905
1013
  if (nativeRole === "input") {
906
- return [...members, ...generateTypeMethod(baseMethodName, selector, parameters)];
1014
+ return [...members, ...generateTypeMethod(componentName, baseMethodName, selector, parameters)];
907
1015
  }
908
1016
  if (nativeRole === "radio") {
909
- return [...members, ...generateRadioMethod(baseMethodName || "Radio", selector, parameters)];
1017
+ return [...members, ...generateRadioMethod(componentName, baseMethodName || "Radio", selector, parameters)];
910
1018
  }
911
- return [...members, ...generateClickMethod(baseMethodName, selector, alternateSelectors, parameters)];
1019
+ return [...members, ...generateClickMethod(componentName, baseMethodName, selector, alternateSelectors, parameters)];
912
1020
  }
913
1021
  function isSimpleExpressionNode(value) {
914
1022
  return value !== null && "type" in value && value.type === compilerCore.NodeTypes.SIMPLE_EXPRESSION;
@@ -2788,7 +2896,7 @@ Fix: either (1) include ${bestKeyPreservePlaceholder} in your :${attrLabel} temp
2788
2896
  }
2789
2897
  return value.slice(0, idx) + value.slice(idx + "ByKey".length);
2790
2898
  };
2791
- const hasRoleSuffix = (baseName, roleSuffix) => {
2899
+ const hasRoleSuffix2 = (baseName, roleSuffix) => {
2792
2900
  if (baseName.endsWith(roleSuffix)) {
2793
2901
  return true;
2794
2902
  }
@@ -2798,13 +2906,13 @@ Fix: either (1) include ${bestKeyPreservePlaceholder} in your :${attrLabel} temp
2798
2906
  const getPrimaryGetterName = (primaryMethodName) => {
2799
2907
  const roleSuffix = upperFirst(normalizedRole || "Element");
2800
2908
  const baseName = upperFirst(primaryMethodName);
2801
- const propertyName = hasRoleSuffix(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
2909
+ const propertyName = hasRoleSuffix2(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
2802
2910
  return selectorIsParameterized ? removeByKeySegment2(propertyName) : propertyName;
2803
2911
  };
2804
2912
  const getPrimaryGetterNameCandidates = (primaryMethodName) => {
2805
2913
  const roleSuffix = upperFirst(normalizedRole || "Element");
2806
2914
  const baseName = upperFirst(primaryMethodName);
2807
- const propertyName = hasRoleSuffix(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
2915
+ const propertyName = hasRoleSuffix2(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
2808
2916
  if (!selectorIsParameterized) {
2809
2917
  return { primary: propertyName };
2810
2918
  }
@@ -2931,7 +3039,7 @@ Fix: either (1) include ${bestKeyPreservePlaceholder} in your :${attrLabel} temp
2931
3039
  if (conflicts && nameCollisionBehavior === "error") {
2932
3040
  const roleSuffix = upperFirst(normalizedRole || "Element");
2933
3041
  const baseNameUpper = upperFirst(baseWithSuffix);
2934
- if (!hasRoleSuffix(baseNameUpper, roleSuffix)) {
3042
+ if (!hasRoleSuffix2(baseNameUpper, roleSuffix)) {
2935
3043
  const baseWithRoleSuffix = `${baseWithSuffix}${roleSuffix}`;
2936
3044
  const candidateWithRoleSuffix = selectorIsParameterized ? `${baseWithRoleSuffix}ByKey` : baseWithRoleSuffix;
2937
3045
  const actionNameWithRoleSuffix = getPrimaryActionMethodName(candidateWithRoleSuffix);
@@ -4483,7 +4591,7 @@ function generateGoToSelfMethod(componentName) {
4483
4591
  function getSelectorPatterns(selector) {
4484
4592
  return selector.kind === "testId" ? [selector.testId] : [selector.rootTestId, selector.label];
4485
4593
  }
4486
- function generateExtraClickMethodMembers(spec) {
4594
+ function generateExtraClickMethodMembers(spec, componentName) {
4487
4595
  if (spec.kind !== "click") {
4488
4596
  return [];
4489
4597
  }
@@ -4498,16 +4606,13 @@ function generateExtraClickMethodMembers(spec) {
4498
4606
  const hasWait = signatureSpecs.some((param) => param.name === "wait");
4499
4607
  const annotationArg = hasAnnotationText ? "annotationText" : '""';
4500
4608
  const waitArg = hasWait ? "wait" : "true";
4609
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
4610
+ componentName,
4611
+ methodName: stripPomActionPrefix(spec.name),
4612
+ nativeRole: "button"
4613
+ }));
4501
4614
  if (spec.selector.kind === "testId") {
4502
4615
  const testIdBinding = bindTypeScriptPomPattern(spec.selector.testId, "testId");
4503
- const clickArgs = [];
4504
- clickArgs.push(testIdBinding.expression);
4505
- if (hasAnnotationText || hasWait) {
4506
- clickArgs.push(annotationArg);
4507
- }
4508
- if (hasWait) {
4509
- clickArgs.push(waitArg);
4510
- }
4511
4616
  return [
4512
4617
  createClassMethod({
4513
4618
  name: spec.name,
@@ -4520,7 +4625,7 @@ function generateExtraClickMethodMembers(spec) {
4520
4625
  for (const statement of testIdBinding.setupStatements) {
4521
4626
  writer.writeLine(statement);
4522
4627
  }
4523
- writer.writeLine(`await this.clickByTestId(${clickArgs.join(", ")});`);
4628
+ writer.writeLine(`await this.clickByTestId(${testIdBinding.expression}, ${annotationArg}, ${waitArg}, ${locatorDescription});`);
4524
4629
  }
4525
4630
  })
4526
4631
  ];
@@ -4542,16 +4647,17 @@ function generateExtraClickMethodMembers(spec) {
4542
4647
  for (const statement of labelBinding.setupStatements) {
4543
4648
  writer.writeLine(statement);
4544
4649
  }
4545
- writer.writeLine(`await this.clickWithinTestIdByLabel(${rootBinding.expression}, ${labelBinding.expression}, ${annotationArg}, ${waitArg});`);
4650
+ writer.writeLine(`await this.clickWithinTestIdByLabel(${rootBinding.expression}, ${labelBinding.expression}, ${annotationArg}, ${waitArg}, { description: ${locatorDescription} });`);
4546
4651
  }
4547
4652
  })
4548
4653
  ];
4549
4654
  }
4550
- function generateMethodMembersFromPom(primary, targetPageObjectModelClass) {
4655
+ function generateMethodMembersFromPom(componentName, primary, targetPageObjectModelClass) {
4551
4656
  if (primary.emitPrimary === false) {
4552
4657
  return [];
4553
4658
  }
4554
4659
  return generateViewObjectModelMembers(
4660
+ componentName,
4555
4661
  targetPageObjectModelClass,
4556
4662
  primary.methodName,
4557
4663
  primary.nativeRole,
@@ -4561,7 +4667,7 @@ function generateMethodMembersFromPom(primary, targetPageObjectModelClass) {
4561
4667
  primary.parameters
4562
4668
  );
4563
4669
  }
4564
- function generateMethodsContentForDependencies(dependencies) {
4670
+ function generateMethodsContentForDependencies(componentName, dependencies) {
4565
4671
  const entries = Array.from(dependencies.dataTestIdSet ?? []);
4566
4672
  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));
4567
4673
  const seenPrimaryKeys = /* @__PURE__ */ new Set();
@@ -4586,10 +4692,10 @@ function generateMethodsContentForDependencies(dependencies) {
4586
4692
  const extras = (dependencies.pomExtraMethods ?? []).slice().sort((a, b) => a.name.localeCompare(b.name));
4587
4693
  const members = [];
4588
4694
  for (const { pom, target } of primarySpecs) {
4589
- members.push(...generateMethodMembersFromPom(pom, target));
4695
+ members.push(...generateMethodMembersFromPom(componentName, pom, target));
4590
4696
  }
4591
4697
  for (const extra of extras) {
4592
- members.push(...generateExtraClickMethodMembers(extra));
4698
+ members.push(...generateExtraClickMethodMembers(extra, componentName));
4593
4699
  }
4594
4700
  return members;
4595
4701
  }
@@ -5443,7 +5549,7 @@ function prepareViewObjectModelClass(componentName, dependencies, componentHiera
5443
5549
  members.push(...generateRouteProperty(routeMeta));
5444
5550
  members.push(...generateGoToSelfMethod(className));
5445
5551
  }
5446
- members.push(...generateMethodsContentForDependencies(dependencies));
5552
+ members.push(...generateMethodsContentForDependencies(componentName, dependencies));
5447
5553
  return {
5448
5554
  className,
5449
5555
  componentRefsForInstances,
@@ -8108,8 +8214,119 @@ function createDevProcessorPlugin(options) {
8108
8214
  }
8109
8215
  };
8110
8216
  }
8111
- function generateTestIdsModule(componentTestIds) {
8112
- const manifestEntries = Array.from(componentTestIds.entries()).sort((a, b) => a[0].localeCompare(b[0]));
8217
+ function removeByKeySegment(value) {
8218
+ const idx = value.indexOf("ByKey");
8219
+ if (idx < 0) {
8220
+ return value;
8221
+ }
8222
+ return value.slice(0, idx) + value.slice(idx + "ByKey".length);
8223
+ }
8224
+ function hasRoleSuffix(baseName, roleSuffix) {
8225
+ if (baseName.endsWith(roleSuffix)) {
8226
+ return true;
8227
+ }
8228
+ const re = new RegExp(`^${roleSuffix}\\d+$`);
8229
+ return re.test(baseName);
8230
+ }
8231
+ function getGeneratedPropertyName(pom) {
8232
+ if (pom.getterNameOverride) {
8233
+ return pom.getterNameOverride;
8234
+ }
8235
+ const roleSuffix = upperFirst(pom.nativeRole || "Element");
8236
+ const baseName = upperFirst(pom.methodName);
8237
+ const propertyName = hasRoleSuffix(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
8238
+ return pom.selector.patternKind === "parameterized" ? removeByKeySegment(propertyName) : propertyName;
8239
+ }
8240
+ function getGeneratedActionName(entry, pom) {
8241
+ const methodNameUpper = upperFirst(pom.methodName);
8242
+ const radioMethodNameUpper = upperFirst(pom.methodName || "Radio");
8243
+ const isNavigation = !!entry.targetPageObjectModelClass;
8244
+ if (isNavigation) {
8245
+ return `goTo${methodNameUpper}`;
8246
+ }
8247
+ switch (pom.nativeRole) {
8248
+ case "input":
8249
+ return `type${methodNameUpper}`;
8250
+ case "select":
8251
+ case "vselect":
8252
+ return `select${methodNameUpper}`;
8253
+ case "radio":
8254
+ return `select${radioMethodNameUpper}`;
8255
+ default:
8256
+ return `click${methodNameUpper}`;
8257
+ }
8258
+ }
8259
+ function matchesPrimarySelector(extraMethod, pom) {
8260
+ if (extraMethod.selector.kind !== "testId") {
8261
+ return false;
8262
+ }
8263
+ return extraMethod.selector.testId.formatted === pom.selector.formatted && extraMethod.selector.testId.patternKind === pom.selector.patternKind;
8264
+ }
8265
+ function getManifestEntry(componentName, entry, componentMetadata, extraMethods) {
8266
+ const testId = entry.selectorValue.formatted;
8267
+ const metadata = componentMetadata?.get(testId);
8268
+ const pom = entry.pom;
8269
+ const generatedActionName = pom ? getGeneratedActionName(entry, pom) : null;
8270
+ const extraActionNames = pom ? extraMethods.filter((extraMethod) => matchesPrimarySelector(extraMethod, pom)).map((extraMethod) => extraMethod.name).sort((a, b) => a.localeCompare(b)) : [];
8271
+ const generatedActionNames = Array.from(/* @__PURE__ */ new Set([
8272
+ ...generatedActionName ? [generatedActionName] : [],
8273
+ ...extraActionNames.filter((name) => name !== generatedActionName)
8274
+ ]));
8275
+ return {
8276
+ testId,
8277
+ selectorPatternKind: entry.selectorValue.patternKind,
8278
+ semanticName: metadata?.semanticName ?? (pom ? humanizePomMethodName(pom.methodName) : testId),
8279
+ locatorDescription: pom ? buildPomLocatorDescription({
8280
+ componentName,
8281
+ methodName: pom.methodName,
8282
+ nativeRole: pom.nativeRole
8283
+ }) : componentName,
8284
+ inferredRole: pom?.nativeRole ?? null,
8285
+ generatedPropertyName: pom ? getGeneratedPropertyName(pom) : null,
8286
+ generatedActionName,
8287
+ generatedActionNames,
8288
+ emitPrimary: pom?.emitPrimary !== false,
8289
+ ...entry.targetPageObjectModelClass ? { targetPageObjectModelClass: entry.targetPageObjectModelClass } : {},
8290
+ ...metadata?.tag ? { sourceTag: metadata.tag } : {},
8291
+ ...metadata ? { sourceTagType: metadata.tagType } : {},
8292
+ ...metadata?.patchFlag !== void 0 ? { patchFlag: metadata.patchFlag } : {},
8293
+ ...metadata?.dynamicProps?.length ? { dynamicProps: metadata.dynamicProps } : {},
8294
+ ...metadata?.hasClickHandler !== void 0 ? { hasClickHandler: metadata.hasClickHandler } : {},
8295
+ ...metadata?.hasDynamicClass !== void 0 ? { hasDynamicClass: metadata.hasDynamicClass } : {},
8296
+ ...metadata?.hasDynamicStyle !== void 0 ? { hasDynamicStyle: metadata.hasDynamicStyle } : {},
8297
+ ...metadata?.hasDynamicText !== void 0 ? { hasDynamicText: metadata.hasDynamicText } : {}
8298
+ };
8299
+ }
8300
+ function buildPomManifest(componentHierarchyMap, elementMetadata) {
8301
+ const manifestEntries = Array.from(componentHierarchyMap.entries()).sort((a, b) => a[0].localeCompare(b[0])).map(([componentName, dependencies]) => {
8302
+ 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 ?? []));
8303
+ if (!entries.length) {
8304
+ return null;
8305
+ }
8306
+ return [componentName, {
8307
+ componentName,
8308
+ className: componentName,
8309
+ sourceFile: dependencies.filePath,
8310
+ kind: dependencies.isView ? "view" : "component",
8311
+ testIds: Array.from(new Set(entries.map((entry) => entry.testId))),
8312
+ entries
8313
+ }];
8314
+ }).filter((entry) => entry !== null);
8315
+ return Object.fromEntries(manifestEntries);
8316
+ }
8317
+ function buildTestIdManifest(pomManifest) {
8318
+ return Object.fromEntries(
8319
+ Object.entries(pomManifest).map(([componentName, component]) => [componentName, Array.from(new Set(component.testIds)).sort((a, b) => a.localeCompare(b))])
8320
+ );
8321
+ }
8322
+ function writeConstJson(value) {
8323
+ return (writer) => {
8324
+ writer.write(`${JSON.stringify(value, null, 2)} as const`);
8325
+ };
8326
+ }
8327
+ function generateTestIdsModule(componentHierarchyMap, elementMetadata) {
8328
+ const pomManifest = buildPomManifest(componentHierarchyMap, elementMetadata);
8329
+ const testIdManifest = buildTestIdManifest(pomManifest);
8113
8330
  return renderSourceFile("virtual-testids.ts", (sourceFile) => {
8114
8331
  sourceFile.addStatements("// Virtual module: test id manifest");
8115
8332
  sourceFile.addVariableStatement({
@@ -8117,16 +8334,15 @@ function generateTestIdsModule(componentTestIds) {
8117
8334
  isExported: true,
8118
8335
  declarations: [{
8119
8336
  name: "testIdManifest",
8120
- initializer: (writer) => {
8121
- writer.write("{").newLine();
8122
- writer.indent(() => {
8123
- manifestEntries.forEach(([componentName, testIds], index) => {
8124
- const suffix = index === manifestEntries.length - 1 ? "" : ",";
8125
- writer.writeLine(`${JSON.stringify(componentName)}: ${JSON.stringify(Array.from(testIds).sort())}${suffix}`);
8126
- });
8127
- });
8128
- writer.write("} as const");
8129
- }
8337
+ initializer: writeConstJson(testIdManifest)
8338
+ }]
8339
+ });
8340
+ sourceFile.addVariableStatement({
8341
+ declarationKind: tsMorph.VariableDeclarationKind.Const,
8342
+ isExported: true,
8343
+ declarations: [{
8344
+ name: "pomManifest",
8345
+ initializer: writeConstJson(pomManifest)
8130
8346
  }]
8131
8347
  });
8132
8348
  sourceFile.addTypeAlias({
@@ -8139,27 +8355,67 @@ function generateTestIdsModule(componentTestIds) {
8139
8355
  name: "ComponentName",
8140
8356
  type: "keyof TestIdManifest"
8141
8357
  });
8358
+ sourceFile.addTypeAlias({
8359
+ isExported: true,
8360
+ name: "PomManifest",
8361
+ type: "typeof pomManifest"
8362
+ });
8363
+ sourceFile.addTypeAlias({
8364
+ isExported: true,
8365
+ name: "PomManifestComponentName",
8366
+ type: "keyof PomManifest"
8367
+ });
8368
+ });
8369
+ }
8370
+ function generatePomManifestModule(componentHierarchyMap, elementMetadata) {
8371
+ const pomManifest = buildPomManifest(componentHierarchyMap, elementMetadata);
8372
+ return renderSourceFile("virtual-pom-manifest.ts", (sourceFile) => {
8373
+ sourceFile.addStatements("// Virtual module: richer POM discoverability manifest");
8374
+ sourceFile.addVariableStatement({
8375
+ declarationKind: tsMorph.VariableDeclarationKind.Const,
8376
+ isExported: true,
8377
+ declarations: [{
8378
+ name: "pomManifest",
8379
+ initializer: writeConstJson(pomManifest)
8380
+ }]
8381
+ });
8382
+ sourceFile.addTypeAlias({
8383
+ isExported: true,
8384
+ name: "PomManifest",
8385
+ type: "typeof pomManifest"
8386
+ });
8387
+ sourceFile.addTypeAlias({
8388
+ isExported: true,
8389
+ name: "PomManifestComponentName",
8390
+ type: "keyof PomManifest"
8391
+ });
8142
8392
  });
8143
8393
  }
8144
- const VIRTUAL_ID = "virtual:testids";
8145
- const RESOLVED_ID = `\0${VIRTUAL_ID}`;
8146
- function createTestIdsVirtualModulesPlugin(componentTestIds) {
8394
+ const TEST_IDS_VIRTUAL_ID = "virtual:testids";
8395
+ const TEST_IDS_RESOLVED_ID = `\0${TEST_IDS_VIRTUAL_ID}`;
8396
+ const POM_MANIFEST_VIRTUAL_ID = "virtual:pom-manifest";
8397
+ const POM_MANIFEST_RESOLVED_ID = `\0${POM_MANIFEST_VIRTUAL_ID}`;
8398
+ function createTestIdsVirtualModulesPlugin(componentHierarchyMap, elementMetadata) {
8147
8399
  return {
8148
8400
  name: "vue-pom-generator:virtual-testids",
8149
8401
  resolveId(id) {
8150
- if (id === VIRTUAL_ID)
8151
- return RESOLVED_ID;
8402
+ if (id === TEST_IDS_VIRTUAL_ID)
8403
+ return TEST_IDS_RESOLVED_ID;
8404
+ if (id === POM_MANIFEST_VIRTUAL_ID)
8405
+ return POM_MANIFEST_RESOLVED_ID;
8152
8406
  },
8153
8407
  load(id) {
8154
- if (id === RESOLVED_ID)
8155
- return generateTestIdsModule(componentTestIds);
8408
+ if (id === TEST_IDS_RESOLVED_ID)
8409
+ return generateTestIdsModule(componentHierarchyMap, elementMetadata);
8410
+ if (id === POM_MANIFEST_RESOLVED_ID)
8411
+ return generatePomManifestModule(componentHierarchyMap, elementMetadata);
8156
8412
  }
8157
8413
  };
8158
8414
  }
8159
8415
  function createSupportPlugins(options) {
8160
8416
  const {
8161
- componentTestIds,
8162
8417
  componentHierarchyMap,
8418
+ elementMetadata,
8163
8419
  vueFilesPathMap,
8164
8420
  nativeWrappers,
8165
8421
  excludedComponents,
@@ -8245,7 +8501,7 @@ function createSupportPlugins(options) {
8245
8501
  getResolvedRouterEntry: resolveRouterEntry2,
8246
8502
  loggerRef
8247
8503
  });
8248
- const virtualModules = createTestIdsVirtualModulesPlugin(componentTestIds);
8504
+ const virtualModules = createTestIdsVirtualModulesPlugin(componentHierarchyMap, elementMetadata);
8249
8505
  return [tsProcessor, devProcessor, virtualModules];
8250
8506
  }
8251
8507
  function findDataTestIdProp(element, attributeName = "data-testid") {
@@ -8735,7 +8991,6 @@ function getSharedGeneratorState(key) {
8735
8991
  let state = sharedGeneratorStateRegistry.get(key);
8736
8992
  if (!state) {
8737
8993
  state = {
8738
- componentTestIds: /* @__PURE__ */ new Map(),
8739
8994
  elementMetadata: /* @__PURE__ */ new Map(),
8740
8995
  semanticNameMap: /* @__PURE__ */ new Map(),
8741
8996
  componentHierarchyMap: /* @__PURE__ */ new Map(),
@@ -8934,7 +9189,7 @@ function createVuePomGeneratorPlugins(options = {}) {
8934
9189
  };
8935
9190
  const getViewsDirAbs = () => resolveFromProjectRoot(projectRootRef.current, getViewsDir());
8936
9191
  const getWrapperSearchRootsAbs = () => getWrapperSearchRoots().map((root) => resolveFromProjectRoot(projectRootRef.current, root));
8937
- const { componentTestIds, elementMetadata, semanticNameMap, componentHierarchyMap, vueFilesPathMap } = sharedState;
9192
+ const { elementMetadata, semanticNameMap, componentHierarchyMap, vueFilesPathMap } = sharedState;
8938
9193
  const { metadataCollectorPlugin, internalVuePlugin, templateCompilerOptions } = createVuePluginWithTestIds({
8939
9194
  vueOptions,
8940
9195
  existingIdBehavior: resolvedGenerationOptions.existingIdBehavior,
@@ -8954,8 +9209,8 @@ function createVuePomGeneratorPlugins(options = {}) {
8954
9209
  });
8955
9210
  templateCompilerOptionsForResolvedPlugin = templateCompilerOptions;
8956
9211
  const supportPlugins = createSupportPlugins({
8957
- componentTestIds,
8958
9212
  componentHierarchyMap,
9213
+ elementMetadata,
8959
9214
  vueFilesPathMap,
8960
9215
  nativeWrappers,
8961
9216
  excludedComponents,
@@ -8982,7 +9237,7 @@ function createVuePomGeneratorPlugins(options = {}) {
8982
9237
  ...supportPlugins
8983
9238
  ];
8984
9239
  if (!generationEnabled) {
8985
- const virtualModules = createTestIdsVirtualModulesPlugin(componentTestIds);
9240
+ const virtualModules = createTestIdsVirtualModulesPlugin(componentHierarchyMap, elementMetadata);
8986
9241
  return [
8987
9242
  configPlugin,
8988
9243
  metadataCollectorPlugin,