@immense/vue-pom-generator 1.0.59 → 1.0.61

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.
Files changed (40) hide show
  1. package/README.md +38 -7
  2. package/RELEASE_NOTES.md +28 -72
  3. package/class-generation/base-page.ts +43 -12
  4. package/class-generation/index.ts +19 -18
  5. package/dist/accessibility-audit.d.ts +20 -0
  6. package/dist/accessibility-audit.d.ts.map +1 -0
  7. package/dist/class-generation/base-page.d.ts +7 -4
  8. package/dist/class-generation/base-page.d.ts.map +1 -1
  9. package/dist/class-generation/index.d.ts.map +1 -1
  10. package/dist/compiler-metadata-utils.d.ts.map +1 -1
  11. package/dist/index.cjs +451 -84
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.mjs +451 -84
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/manifest-generator.d.ts +37 -1
  16. package/dist/manifest-generator.d.ts.map +1 -1
  17. package/dist/metadata-collector.d.ts +5 -1
  18. package/dist/metadata-collector.d.ts.map +1 -1
  19. package/dist/method-generation.d.ts +2 -2
  20. package/dist/method-generation.d.ts.map +1 -1
  21. package/dist/plugin/create-vue-pom-generator-plugins.d.ts.map +1 -1
  22. package/dist/plugin/resolved-generation-options.d.ts +1 -0
  23. package/dist/plugin/resolved-generation-options.d.ts.map +1 -1
  24. package/dist/plugin/support/virtual-modules.d.ts +3 -1
  25. package/dist/plugin/support/virtual-modules.d.ts.map +1 -1
  26. package/dist/plugin/support-plugins.d.ts +2 -1
  27. package/dist/plugin/support-plugins.d.ts.map +1 -1
  28. package/dist/plugin/types.d.ts +7 -0
  29. package/dist/plugin/types.d.ts.map +1 -1
  30. package/dist/plugin/vue-plugin.d.ts +1 -0
  31. package/dist/plugin/vue-plugin.d.ts.map +1 -1
  32. package/dist/pom-discoverability.d.ts +10 -0
  33. package/dist/pom-discoverability.d.ts.map +1 -0
  34. package/dist/tests/accessibility-audit.test.d.ts +2 -0
  35. package/dist/tests/accessibility-audit.test.d.ts.map +1 -0
  36. package/dist/tests/fixtures/generated-tsc/base-page.full.d.ts +7 -4
  37. package/dist/tests/fixtures/generated-tsc/base-page.full.d.ts.map +1 -1
  38. package/dist/tests/resolved-generation-options.test.d.ts +2 -0
  39. package/dist/tests/resolved-generation-options.test.d.ts.map +1 -0
  40. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -260,6 +260,7 @@ function resolveGenerationSupportOptions(options) {
260
260
  nameCollisionBehavior: options.nameCollisionBehavior ?? "error",
261
261
  existingIdBehavior: options.existingIdBehavior ?? "error",
262
262
  testIdAttribute: (options.testIdAttribute ?? "data-testid").trim() || "data-testid",
263
+ accessibilityAudit: options.accessibilityAudit ?? false,
263
264
  routerAwarePoms: options.routerAwarePoms ?? false,
264
265
  routerEntry: options.routerEntry,
265
266
  routerType: options.routerType ?? "vue-router",
@@ -599,6 +600,75 @@ function uniquePomStringPatterns(primary, alternates) {
599
600
  }
600
601
  return out;
601
602
  }
603
+ function splitDiscoverabilityWords(value) {
604
+ 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();
605
+ if (!normalized) {
606
+ return [];
607
+ }
608
+ return normalized.split(/\s+/).map((word) => word.toLowerCase()).filter(Boolean);
609
+ }
610
+ function joinDiscoverabilityWords(words) {
611
+ return words.join(" ").replace(/\s+/g, " ").trim();
612
+ }
613
+ function toSentenceCase(value) {
614
+ return value.charAt(0).toUpperCase() + value.slice(1);
615
+ }
616
+ function stripComponentKindSuffix(componentName) {
617
+ for (const suffix of ["Page", "Component", "Layout"]) {
618
+ if (componentName.endsWith(suffix) && componentName.length > suffix.length) {
619
+ return componentName.slice(0, -suffix.length);
620
+ }
621
+ }
622
+ return componentName;
623
+ }
624
+ function removeLeadingWords(words, prefixWords) {
625
+ if (!prefixWords.length || words.length < prefixWords.length) {
626
+ return [...words];
627
+ }
628
+ for (let i = 0; i < prefixWords.length; i++) {
629
+ if (words[i] !== prefixWords[i]) {
630
+ return [...words];
631
+ }
632
+ }
633
+ return words.slice(prefixWords.length);
634
+ }
635
+ function removeTrailingRoleWord(words, roleWord) {
636
+ if (!words.length || words[words.length - 1] !== roleWord) {
637
+ return [...words];
638
+ }
639
+ return words.slice(0, -1);
640
+ }
641
+ function humanizePomMethodName(methodName) {
642
+ return joinDiscoverabilityWords(splitDiscoverabilityWords(methodName));
643
+ }
644
+ function stripPomActionPrefix(actionName) {
645
+ for (const prefix of ["click", "select", "type", "goTo"]) {
646
+ if (actionName.startsWith(prefix) && actionName.length > prefix.length) {
647
+ return actionName.slice(prefix.length);
648
+ }
649
+ }
650
+ return actionName;
651
+ }
652
+ function normalizePomRoleLabel(nativeRole) {
653
+ if (nativeRole === "vselect") {
654
+ return "select";
655
+ }
656
+ return nativeRole || "element";
657
+ }
658
+ function buildPomLocatorDescription(args) {
659
+ const componentWords = splitDiscoverabilityWords(args.componentName ? stripComponentKindSuffix(args.componentName) : "");
660
+ const roleWord = normalizePomRoleLabel(args.nativeRole).toLowerCase();
661
+ const semanticWords = removeLeadingWords(
662
+ removeTrailingRoleWord(splitDiscoverabilityWords(args.methodName), roleWord),
663
+ componentWords
664
+ );
665
+ const phrase = joinDiscoverabilityWords([
666
+ ...componentWords,
667
+ ...semanticWords,
668
+ roleWord
669
+ ]);
670
+ return toSentenceCase(phrase || "Generated element");
671
+ }
602
672
  function upperFirst$1(value) {
603
673
  if (!value) {
604
674
  return value;
@@ -615,7 +685,7 @@ function createInlineParameter(name, options = {}) {
615
685
  initializer: options.initializer
616
686
  };
617
687
  }
618
- function removeByKeySegment(value) {
688
+ function removeByKeySegment$1(value) {
619
689
  const idx = value.lastIndexOf("ByKey");
620
690
  if (idx < 0) {
621
691
  return value;
@@ -630,9 +700,14 @@ function createAsyncMethod(name, parameters, statements) {
630
700
  statements
631
701
  });
632
702
  }
633
- function generateClickMethod(methodName, selector, alternateSelectors, parameters) {
703
+ function generateClickMethod(componentName, methodName, selector, alternateSelectors, parameters) {
634
704
  const name = `click${methodName}`;
635
705
  const noWaitName = `${name}NoWait`;
706
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
707
+ componentName,
708
+ methodName,
709
+ nativeRole: "button"
710
+ }));
636
711
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
637
712
  const hasSelectorVariables = hasPomPatternVariables(selector);
638
713
  const baseParameters = createParameters(selectorParams);
@@ -655,7 +730,7 @@ function generateClickMethod(methodName, selector, alternateSelectors, parameter
655
730
  writer.writeLine(`const candidates = [${candidatesExpr}] as const;`);
656
731
  writer.writeLine("let lastError: unknown;");
657
732
  writer.write("for (const testId of candidates) ").block(() => {
658
- writer.writeLine("const locator = this.locatorByTestId(testId);");
733
+ writer.writeLine(`const locator = this.locatorByTestId(testId, ${locatorDescription});`);
659
734
  writer.write("try ").block(() => {
660
735
  writer.write("if (await locator.count()) ").block(() => {
661
736
  writer.writeLine("await this.clickLocator(locator, annotationText, wait);");
@@ -689,7 +764,7 @@ function generateClickMethod(methodName, selector, alternateSelectors, parameter
689
764
  createInlineParameter("annotationText", { type: "string", initializer: '""' })
690
765
  ],
691
766
  (writer) => {
692
- writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait);`);
767
+ writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait, ${locatorDescription});`);
693
768
  }
694
769
  ),
695
770
  createAsyncMethod(
@@ -709,7 +784,7 @@ function generateClickMethod(methodName, selector, alternateSelectors, parameter
709
784
  createInlineParameter("annotationText", { type: "string", initializer: '""' })
710
785
  ],
711
786
  (writer) => {
712
- writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait);`);
787
+ writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait, ${locatorDescription});`);
713
788
  }
714
789
  ),
715
790
  createAsyncMethod(
@@ -721,55 +796,76 @@ function generateClickMethod(methodName, selector, alternateSelectors, parameter
721
796
  )
722
797
  ];
723
798
  }
724
- function generateRadioMethod(methodName, selector, parameters) {
799
+ function generateRadioMethod(componentName, methodName, selector, parameters) {
725
800
  const name = `select${methodName}`;
801
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
802
+ componentName,
803
+ methodName,
804
+ nativeRole: "radio"
805
+ }));
726
806
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
727
807
  const methodParameters = createParameters(selectorParams);
728
808
  const testIdExpr = toTypeScriptPomPatternExpression(selector);
729
809
  return [
730
810
  createAsyncMethod(name, methodParameters, (writer) => {
731
- writer.writeLine(`await this.clickByTestId(${testIdExpr}, annotationText);`);
811
+ writer.writeLine(`await this.clickByTestId(${testIdExpr}, annotationText, true, ${locatorDescription});`);
732
812
  })
733
813
  ];
734
814
  }
735
- function generateSelectMethod(methodName, selector, parameters) {
815
+ function generateSelectMethod(componentName, methodName, selector, parameters) {
736
816
  const name = `select${methodName}`;
817
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
818
+ componentName,
819
+ methodName,
820
+ nativeRole: "select"
821
+ }));
737
822
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
738
- const selectorExpr = `this.selectorForTestId(${toTypeScriptPomPatternExpression(selector)})`;
823
+ const testIdExpr = toTypeScriptPomPatternExpression(selector);
739
824
  return [
740
825
  createAsyncMethod(
741
826
  name,
742
827
  createParameters(selectorParams),
743
828
  (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);");
829
+ writer.writeLine(`const testId = ${testIdExpr};`);
830
+ writer.writeLine(`const locator = this.locatorByTestId(testId, ${locatorDescription});`);
831
+ writer.writeLine("await this.animateCursorToElement(locator, false, 500, annotationText);");
832
+ writer.writeLine("await locator.selectOption(value);");
747
833
  }
748
834
  )
749
835
  ];
750
836
  }
751
- function generateVSelectMethod(methodName, selector, parameters) {
837
+ function generateVSelectMethod(componentName, methodName, selector, parameters) {
752
838
  const name = `select${methodName}`;
839
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
840
+ componentName,
841
+ methodName,
842
+ nativeRole: "vselect"
843
+ }));
753
844
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
754
845
  return [
755
846
  createAsyncMethod(
756
847
  name,
757
848
  createParameters(selectorParams),
758
849
  (writer) => {
759
- writer.writeLine(`await this.selectVSelectByTestId(${toTypeScriptPomPatternExpression(selector)}, value, timeOut, annotationText);`);
850
+ writer.writeLine(`await this.selectVSelectByTestId(${toTypeScriptPomPatternExpression(selector)}, value, timeOut, annotationText, ${locatorDescription});`);
760
851
  }
761
852
  )
762
853
  ];
763
854
  }
764
- function generateTypeMethod(methodName, selector, parameters) {
855
+ function generateTypeMethod(componentName, methodName, selector, parameters) {
765
856
  const name = `type${methodName}`;
857
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
858
+ componentName,
859
+ methodName,
860
+ nativeRole: "input"
861
+ }));
766
862
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
767
863
  return [
768
864
  createAsyncMethod(
769
865
  name,
770
866
  createParameters(selectorParams),
771
867
  (writer) => {
772
- writer.writeLine(`await this.fillInputByTestId(${toTypeScriptPomPatternExpression(selector)}, text, annotationText);`);
868
+ writer.writeLine(`await this.fillInputByTestId(${toTypeScriptPomPatternExpression(selector)}, text, annotationText, ${locatorDescription});`);
773
869
  }
774
870
  )
775
871
  ];
@@ -784,22 +880,27 @@ function isAllDigits(value) {
784
880
  }
785
881
  return true;
786
882
  }
787
- function generateGetElementByDataTestId(methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
883
+ function generateGetElementByDataTestId(componentName, methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
884
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
885
+ componentName,
886
+ methodName,
887
+ nativeRole
888
+ }));
788
889
  const roleSuffix = upperFirst$1(nativeRole || "Element");
789
890
  const baseName = upperFirst$1(methodName);
790
891
  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}`;
892
+ const hasRoleSuffix2 = baseName.endsWith(roleSuffix) || baseName.startsWith(roleSuffix) && isAllDigits(numericSuffix);
893
+ const propertyName = hasRoleSuffix2 ? `${baseName}` : `${baseName}${roleSuffix}`;
793
894
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
794
895
  const indexedVariable = getIndexedPomPatternVariable(selector);
795
896
  if (indexedVariable) {
796
897
  const keyType = getPomParameter(selectorParams, indexedVariable)?.typeExpression || "string";
797
- const keyedPropertyName = getterNameOverride ?? removeByKeySegment(propertyName);
898
+ const keyedPropertyName = getterNameOverride ?? removeByKeySegment$1(propertyName);
798
899
  return [
799
900
  createClassGetter({
800
901
  name: keyedPropertyName,
801
902
  statements: [
802
- `return this.keyedLocators((${indexedVariable}: ${keyType}) => this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)}));`
903
+ `return this.keyedLocators((${indexedVariable}: ${keyType}) => this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)}, ${locatorDescription}));`
803
904
  ]
804
905
  })
805
906
  ];
@@ -812,20 +913,25 @@ function generateGetElementByDataTestId(methodName, nativeRole, selector, altern
812
913
  return [
813
914
  createClassGetter({
814
915
  name: finalPropertyName,
815
- statements: [`return ${locatorExpr};`]
916
+ statements: [`return this.describeLocator(${locatorExpr}, ${locatorDescription});`]
816
917
  })
817
918
  ];
818
919
  }
819
920
  return [
820
921
  createClassGetter({
821
922
  name: finalPropertyName,
822
- statements: [`return this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)});`]
923
+ statements: [`return this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)}, ${locatorDescription});`]
823
924
  })
824
925
  ];
825
926
  }
826
927
  function generateNavigationMethod(args) {
827
- const { targetPageObjectModelClass: target, baseMethodName, selector, alternateSelectors, parameters } = args;
928
+ const { componentName, targetPageObjectModelClass: target, baseMethodName, selector, alternateSelectors, parameters } = args;
828
929
  const methodName = baseMethodName ? `goTo${upperFirst$1(baseMethodName)}` : `goTo${target.endsWith("Page") ? target.slice(0, -"Page".length) : target}`;
930
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
931
+ componentName,
932
+ methodName: baseMethodName,
933
+ nativeRole: "button"
934
+ }));
829
935
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
830
936
  const methodParameters = createParameters(selectorParams);
831
937
  const alternates = uniquePomStringPatterns(selector, alternateSelectors).slice(1);
@@ -841,7 +947,7 @@ function generateNavigationMethod(args) {
841
947
  writer.writeLine(`const candidates = [${candidatesExpr}] as const;`);
842
948
  writer.writeLine("let lastError: unknown;");
843
949
  writer.write("for (const testId of candidates) ").block(() => {
844
- writer.writeLine("const locator = this.locatorByTestId(testId);");
950
+ writer.writeLine(`const locator = this.locatorByTestId(testId, ${locatorDescription});`);
845
951
  writer.write("try ").block(() => {
846
952
  writer.write("if (await locator.count()) ").block(() => {
847
953
  writer.writeLine("await this.clickLocator(locator);");
@@ -866,7 +972,8 @@ function generateNavigationMethod(args) {
866
972
  returnType: `Fluent<${target}>`,
867
973
  statements: (writer) => {
868
974
  writer.write("return this.fluent(async () => ").block(() => {
869
- writer.writeLine(`await this.clickByTestId(${toTypeScriptPomPatternExpression(selector)});`);
975
+ writer.writeLine(`const locator = this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)}, ${locatorDescription});`);
976
+ writer.writeLine("await this.clickLocator(locator);");
870
977
  writer.writeLine(`return new ${target}(this.page);`);
871
978
  });
872
979
  writer.writeLine(");");
@@ -874,9 +981,10 @@ function generateNavigationMethod(args) {
874
981
  })
875
982
  ];
876
983
  }
877
- function generateViewObjectModelMembers(targetPageObjectModelClass, methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
984
+ function generateViewObjectModelMembers(componentName, targetPageObjectModelClass, methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
878
985
  const baseMethodName = nativeRole === "radio" ? methodName || "Radio" : methodName;
879
986
  const members = generateGetElementByDataTestId(
987
+ componentName,
880
988
  baseMethodName,
881
989
  nativeRole,
882
990
  selector,
@@ -888,6 +996,7 @@ function generateViewObjectModelMembers(targetPageObjectModelClass, methodName,
888
996
  return [
889
997
  ...members,
890
998
  ...generateNavigationMethod({
999
+ componentName,
891
1000
  targetPageObjectModelClass,
892
1001
  baseMethodName,
893
1002
  selector,
@@ -897,18 +1006,18 @@ function generateViewObjectModelMembers(targetPageObjectModelClass, methodName,
897
1006
  ];
898
1007
  }
899
1008
  if (nativeRole === "select") {
900
- return [...members, ...generateSelectMethod(baseMethodName, selector, parameters)];
1009
+ return [...members, ...generateSelectMethod(componentName, baseMethodName, selector, parameters)];
901
1010
  }
902
1011
  if (nativeRole === "vselect") {
903
- return [...members, ...generateVSelectMethod(baseMethodName, selector, parameters)];
1012
+ return [...members, ...generateVSelectMethod(componentName, baseMethodName, selector, parameters)];
904
1013
  }
905
1014
  if (nativeRole === "input") {
906
- return [...members, ...generateTypeMethod(baseMethodName, selector, parameters)];
1015
+ return [...members, ...generateTypeMethod(componentName, baseMethodName, selector, parameters)];
907
1016
  }
908
1017
  if (nativeRole === "radio") {
909
- return [...members, ...generateRadioMethod(baseMethodName || "Radio", selector, parameters)];
1018
+ return [...members, ...generateRadioMethod(componentName, baseMethodName || "Radio", selector, parameters)];
910
1019
  }
911
- return [...members, ...generateClickMethod(baseMethodName, selector, alternateSelectors, parameters)];
1020
+ return [...members, ...generateClickMethod(componentName, baseMethodName, selector, alternateSelectors, parameters)];
912
1021
  }
913
1022
  function isSimpleExpressionNode(value) {
914
1023
  return value !== null && "type" in value && value.type === compilerCore.NodeTypes.SIMPLE_EXPRESSION;
@@ -2788,7 +2897,7 @@ Fix: either (1) include ${bestKeyPreservePlaceholder} in your :${attrLabel} temp
2788
2897
  }
2789
2898
  return value.slice(0, idx) + value.slice(idx + "ByKey".length);
2790
2899
  };
2791
- const hasRoleSuffix = (baseName, roleSuffix) => {
2900
+ const hasRoleSuffix2 = (baseName, roleSuffix) => {
2792
2901
  if (baseName.endsWith(roleSuffix)) {
2793
2902
  return true;
2794
2903
  }
@@ -2798,13 +2907,13 @@ Fix: either (1) include ${bestKeyPreservePlaceholder} in your :${attrLabel} temp
2798
2907
  const getPrimaryGetterName = (primaryMethodName) => {
2799
2908
  const roleSuffix = upperFirst(normalizedRole || "Element");
2800
2909
  const baseName = upperFirst(primaryMethodName);
2801
- const propertyName = hasRoleSuffix(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
2910
+ const propertyName = hasRoleSuffix2(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
2802
2911
  return selectorIsParameterized ? removeByKeySegment2(propertyName) : propertyName;
2803
2912
  };
2804
2913
  const getPrimaryGetterNameCandidates = (primaryMethodName) => {
2805
2914
  const roleSuffix = upperFirst(normalizedRole || "Element");
2806
2915
  const baseName = upperFirst(primaryMethodName);
2807
- const propertyName = hasRoleSuffix(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
2916
+ const propertyName = hasRoleSuffix2(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
2808
2917
  if (!selectorIsParameterized) {
2809
2918
  return { primary: propertyName };
2810
2919
  }
@@ -2931,7 +3040,7 @@ Fix: either (1) include ${bestKeyPreservePlaceholder} in your :${attrLabel} temp
2931
3040
  if (conflicts && nameCollisionBehavior === "error") {
2932
3041
  const roleSuffix = upperFirst(normalizedRole || "Element");
2933
3042
  const baseNameUpper = upperFirst(baseWithSuffix);
2934
- if (!hasRoleSuffix(baseNameUpper, roleSuffix)) {
3043
+ if (!hasRoleSuffix2(baseNameUpper, roleSuffix)) {
2935
3044
  const baseWithRoleSuffix = `${baseWithSuffix}${roleSuffix}`;
2936
3045
  const candidateWithRoleSuffix = selectorIsParameterized ? `${baseWithRoleSuffix}ByKey` : baseWithRoleSuffix;
2937
3046
  const actionNameWithRoleSuffix = getPrimaryActionMethodName(candidateWithRoleSuffix);
@@ -4483,7 +4592,7 @@ function generateGoToSelfMethod(componentName) {
4483
4592
  function getSelectorPatterns(selector) {
4484
4593
  return selector.kind === "testId" ? [selector.testId] : [selector.rootTestId, selector.label];
4485
4594
  }
4486
- function generateExtraClickMethodMembers(spec) {
4595
+ function generateExtraClickMethodMembers(spec, componentName) {
4487
4596
  if (spec.kind !== "click") {
4488
4597
  return [];
4489
4598
  }
@@ -4498,16 +4607,13 @@ function generateExtraClickMethodMembers(spec) {
4498
4607
  const hasWait = signatureSpecs.some((param) => param.name === "wait");
4499
4608
  const annotationArg = hasAnnotationText ? "annotationText" : '""';
4500
4609
  const waitArg = hasWait ? "wait" : "true";
4610
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
4611
+ componentName,
4612
+ methodName: stripPomActionPrefix(spec.name),
4613
+ nativeRole: "button"
4614
+ }));
4501
4615
  if (spec.selector.kind === "testId") {
4502
4616
  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
4617
  return [
4512
4618
  createClassMethod({
4513
4619
  name: spec.name,
@@ -4520,7 +4626,7 @@ function generateExtraClickMethodMembers(spec) {
4520
4626
  for (const statement of testIdBinding.setupStatements) {
4521
4627
  writer.writeLine(statement);
4522
4628
  }
4523
- writer.writeLine(`await this.clickByTestId(${clickArgs.join(", ")});`);
4629
+ writer.writeLine(`await this.clickByTestId(${testIdBinding.expression}, ${annotationArg}, ${waitArg}, ${locatorDescription});`);
4524
4630
  }
4525
4631
  })
4526
4632
  ];
@@ -4542,16 +4648,17 @@ function generateExtraClickMethodMembers(spec) {
4542
4648
  for (const statement of labelBinding.setupStatements) {
4543
4649
  writer.writeLine(statement);
4544
4650
  }
4545
- writer.writeLine(`await this.clickWithinTestIdByLabel(${rootBinding.expression}, ${labelBinding.expression}, ${annotationArg}, ${waitArg});`);
4651
+ writer.writeLine(`await this.clickWithinTestIdByLabel(${rootBinding.expression}, ${labelBinding.expression}, ${annotationArg}, ${waitArg}, { description: ${locatorDescription} });`);
4546
4652
  }
4547
4653
  })
4548
4654
  ];
4549
4655
  }
4550
- function generateMethodMembersFromPom(primary, targetPageObjectModelClass) {
4656
+ function generateMethodMembersFromPom(componentName, primary, targetPageObjectModelClass) {
4551
4657
  if (primary.emitPrimary === false) {
4552
4658
  return [];
4553
4659
  }
4554
4660
  return generateViewObjectModelMembers(
4661
+ componentName,
4555
4662
  targetPageObjectModelClass,
4556
4663
  primary.methodName,
4557
4664
  primary.nativeRole,
@@ -4561,7 +4668,7 @@ function generateMethodMembersFromPom(primary, targetPageObjectModelClass) {
4561
4668
  primary.parameters
4562
4669
  );
4563
4670
  }
4564
- function generateMethodsContentForDependencies(dependencies) {
4671
+ function generateMethodsContentForDependencies(componentName, dependencies) {
4565
4672
  const entries = Array.from(dependencies.dataTestIdSet ?? []);
4566
4673
  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
4674
  const seenPrimaryKeys = /* @__PURE__ */ new Set();
@@ -4586,10 +4693,10 @@ function generateMethodsContentForDependencies(dependencies) {
4586
4693
  const extras = (dependencies.pomExtraMethods ?? []).slice().sort((a, b) => a.name.localeCompare(b.name));
4587
4694
  const members = [];
4588
4695
  for (const { pom, target } of primarySpecs) {
4589
- members.push(...generateMethodMembersFromPom(pom, target));
4696
+ members.push(...generateMethodMembersFromPom(componentName, pom, target));
4590
4697
  }
4591
4698
  for (const extra of extras) {
4592
- members.push(...generateExtraClickMethodMembers(extra));
4699
+ members.push(...generateExtraClickMethodMembers(extra, componentName));
4593
4700
  }
4594
4701
  return members;
4595
4702
  }
@@ -5443,7 +5550,7 @@ function prepareViewObjectModelClass(componentName, dependencies, componentHiera
5443
5550
  members.push(...generateRouteProperty(routeMeta));
5444
5551
  members.push(...generateGoToSelfMethod(className));
5445
5552
  }
5446
- members.push(...generateMethodsContentForDependencies(dependencies));
5553
+ members.push(...generateMethodsContentForDependencies(componentName, dependencies));
5447
5554
  return {
5448
5555
  className,
5449
5556
  componentRefsForInstances,
@@ -8108,8 +8215,174 @@ function createDevProcessorPlugin(options) {
8108
8215
  }
8109
8216
  };
8110
8217
  }
8111
- function generateTestIdsModule(componentTestIds) {
8112
- const manifestEntries = Array.from(componentTestIds.entries()).sort((a, b) => a[0].localeCompare(b[0]));
8218
+ function supportsInlineTextAccessibleName(role) {
8219
+ return role === "button" || role === "radio";
8220
+ }
8221
+ function buildAccessibilityAudit(metadata, inferredRole) {
8222
+ if (!metadata || !inferredRole) {
8223
+ return void 0;
8224
+ }
8225
+ const role = normalizePomRoleLabel(inferredRole).toLowerCase();
8226
+ const dynamicProps = new Set(metadata.dynamicProps ?? []);
8227
+ const hasDynamicAccessibleNameSignal = dynamicProps.has("aria-label") || dynamicProps.has("title") || !!metadata.hasDynamicText;
8228
+ let accessibleNameSource;
8229
+ const reasons = [];
8230
+ if (metadata.staticAriaLabel) {
8231
+ accessibleNameSource = "aria-label";
8232
+ } else if (supportsInlineTextAccessibleName(role) && metadata.staticTextContent) {
8233
+ accessibleNameSource = "text";
8234
+ } else if (metadata.staticTitle) {
8235
+ accessibleNameSource = "title";
8236
+ } else if (hasDynamicAccessibleNameSignal) {
8237
+ accessibleNameSource = "dynamic";
8238
+ } else if (role === "input" || role === "select") {
8239
+ accessibleNameSource = "unknown";
8240
+ reasons.push("No inline accessible-name signal was found; the element may rely on external markup such as a separate <label>.");
8241
+ } else {
8242
+ accessibleNameSource = "missing";
8243
+ reasons.push("No compile-time accessible-name signal was found.");
8244
+ }
8245
+ return {
8246
+ needsReview: accessibleNameSource === "unknown" || accessibleNameSource === "missing",
8247
+ accessibleNameSource,
8248
+ reasons,
8249
+ ...metadata.staticAriaLabel ? { staticAriaLabel: metadata.staticAriaLabel } : {},
8250
+ ...metadata.staticRole ? { staticRole: metadata.staticRole } : {},
8251
+ ...metadata.staticTitle ? { staticTitle: metadata.staticTitle } : {},
8252
+ ...metadata.staticTextContent ? { staticTextContent: metadata.staticTextContent } : {}
8253
+ };
8254
+ }
8255
+ function collectAccessibilityReviewWarnings(manifest) {
8256
+ const warnings = [];
8257
+ for (const [componentName, component] of Object.entries(manifest)) {
8258
+ for (const entry of component.entries) {
8259
+ if (!entry.accessibility?.needsReview) {
8260
+ continue;
8261
+ }
8262
+ const entryLabel = entry.generatedPropertyName ?? entry.testId;
8263
+ const reasonText = entry.accessibility.reasons.join(" ");
8264
+ warnings.push(
8265
+ `[vue-pom-generator] Accessibility review suggested for ${componentName}.${entryLabel} (role=${entry.inferredRole ?? "unknown"}, testId=${JSON.stringify(entry.testId)}): ${reasonText}`
8266
+ );
8267
+ }
8268
+ }
8269
+ return warnings;
8270
+ }
8271
+ function removeByKeySegment(value) {
8272
+ const idx = value.indexOf("ByKey");
8273
+ if (idx < 0) {
8274
+ return value;
8275
+ }
8276
+ return value.slice(0, idx) + value.slice(idx + "ByKey".length);
8277
+ }
8278
+ function hasRoleSuffix(baseName, roleSuffix) {
8279
+ if (baseName.endsWith(roleSuffix)) {
8280
+ return true;
8281
+ }
8282
+ const re = new RegExp(`^${roleSuffix}\\d+$`);
8283
+ return re.test(baseName);
8284
+ }
8285
+ function getGeneratedPropertyName(pom) {
8286
+ if (pom.getterNameOverride) {
8287
+ return pom.getterNameOverride;
8288
+ }
8289
+ const roleSuffix = upperFirst(pom.nativeRole || "Element");
8290
+ const baseName = upperFirst(pom.methodName);
8291
+ const propertyName = hasRoleSuffix(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
8292
+ return pom.selector.patternKind === "parameterized" ? removeByKeySegment(propertyName) : propertyName;
8293
+ }
8294
+ function getGeneratedActionName(entry, pom) {
8295
+ const methodNameUpper = upperFirst(pom.methodName);
8296
+ const radioMethodNameUpper = upperFirst(pom.methodName || "Radio");
8297
+ const isNavigation = !!entry.targetPageObjectModelClass;
8298
+ if (isNavigation) {
8299
+ return `goTo${methodNameUpper}`;
8300
+ }
8301
+ switch (pom.nativeRole) {
8302
+ case "input":
8303
+ return `type${methodNameUpper}`;
8304
+ case "select":
8305
+ case "vselect":
8306
+ return `select${methodNameUpper}`;
8307
+ case "radio":
8308
+ return `select${radioMethodNameUpper}`;
8309
+ default:
8310
+ return `click${methodNameUpper}`;
8311
+ }
8312
+ }
8313
+ function matchesPrimarySelector(extraMethod, pom) {
8314
+ if (extraMethod.selector.kind !== "testId") {
8315
+ return false;
8316
+ }
8317
+ return extraMethod.selector.testId.formatted === pom.selector.formatted && extraMethod.selector.testId.patternKind === pom.selector.patternKind;
8318
+ }
8319
+ function getManifestEntry(componentName, entry, componentMetadata, extraMethods) {
8320
+ const testId = entry.selectorValue.formatted;
8321
+ const metadata = componentMetadata?.get(testId);
8322
+ const pom = entry.pom;
8323
+ const generatedActionName = pom ? getGeneratedActionName(entry, pom) : null;
8324
+ const extraActionNames = pom ? extraMethods.filter((extraMethod) => matchesPrimarySelector(extraMethod, pom)).map((extraMethod) => extraMethod.name).sort((a, b) => a.localeCompare(b)) : [];
8325
+ const generatedActionNames = Array.from(/* @__PURE__ */ new Set([
8326
+ ...generatedActionName ? [generatedActionName] : [],
8327
+ ...extraActionNames.filter((name) => name !== generatedActionName)
8328
+ ]));
8329
+ const accessibility = buildAccessibilityAudit(metadata, pom?.nativeRole ?? null);
8330
+ return {
8331
+ testId,
8332
+ selectorPatternKind: entry.selectorValue.patternKind,
8333
+ semanticName: metadata?.semanticName ?? (pom ? humanizePomMethodName(pom.methodName) : testId),
8334
+ locatorDescription: pom ? buildPomLocatorDescription({
8335
+ componentName,
8336
+ methodName: pom.methodName,
8337
+ nativeRole: pom.nativeRole
8338
+ }) : componentName,
8339
+ inferredRole: pom?.nativeRole ?? null,
8340
+ ...accessibility ? { accessibility } : {},
8341
+ generatedPropertyName: pom ? getGeneratedPropertyName(pom) : null,
8342
+ generatedActionName,
8343
+ generatedActionNames,
8344
+ emitPrimary: pom?.emitPrimary !== false,
8345
+ ...entry.targetPageObjectModelClass ? { targetPageObjectModelClass: entry.targetPageObjectModelClass } : {},
8346
+ ...metadata?.tag ? { sourceTag: metadata.tag } : {},
8347
+ ...metadata ? { sourceTagType: metadata.tagType } : {},
8348
+ ...metadata?.patchFlag !== void 0 ? { patchFlag: metadata.patchFlag } : {},
8349
+ ...metadata?.dynamicProps?.length ? { dynamicProps: metadata.dynamicProps } : {},
8350
+ ...metadata?.hasClickHandler !== void 0 ? { hasClickHandler: metadata.hasClickHandler } : {},
8351
+ ...metadata?.hasDynamicClass !== void 0 ? { hasDynamicClass: metadata.hasDynamicClass } : {},
8352
+ ...metadata?.hasDynamicStyle !== void 0 ? { hasDynamicStyle: metadata.hasDynamicStyle } : {},
8353
+ ...metadata?.hasDynamicText !== void 0 ? { hasDynamicText: metadata.hasDynamicText } : {}
8354
+ };
8355
+ }
8356
+ function buildPomManifest(componentHierarchyMap, elementMetadata) {
8357
+ const manifestEntries = Array.from(componentHierarchyMap.entries()).sort((a, b) => a[0].localeCompare(b[0])).map(([componentName, dependencies]) => {
8358
+ 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 ?? []));
8359
+ if (!entries.length) {
8360
+ return null;
8361
+ }
8362
+ return [componentName, {
8363
+ componentName,
8364
+ className: componentName,
8365
+ sourceFile: dependencies.filePath,
8366
+ kind: dependencies.isView ? "view" : "component",
8367
+ testIds: Array.from(new Set(entries.map((entry) => entry.testId))),
8368
+ entries
8369
+ }];
8370
+ }).filter((entry) => entry !== null);
8371
+ return Object.fromEntries(manifestEntries);
8372
+ }
8373
+ function buildTestIdManifest(pomManifest) {
8374
+ return Object.fromEntries(
8375
+ Object.entries(pomManifest).map(([componentName, component]) => [componentName, Array.from(new Set(component.testIds)).sort((a, b) => a.localeCompare(b))])
8376
+ );
8377
+ }
8378
+ function writeConstJson(value) {
8379
+ return (writer) => {
8380
+ writer.write(`${JSON.stringify(value, null, 2)} as const`);
8381
+ };
8382
+ }
8383
+ function generateTestIdsModule(componentHierarchyMap, elementMetadata) {
8384
+ const pomManifest = buildPomManifest(componentHierarchyMap, elementMetadata);
8385
+ const testIdManifest = buildTestIdManifest(pomManifest);
8113
8386
  return renderSourceFile("virtual-testids.ts", (sourceFile) => {
8114
8387
  sourceFile.addStatements("// Virtual module: test id manifest");
8115
8388
  sourceFile.addVariableStatement({
@@ -8117,16 +8390,15 @@ function generateTestIdsModule(componentTestIds) {
8117
8390
  isExported: true,
8118
8391
  declarations: [{
8119
8392
  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
- }
8393
+ initializer: writeConstJson(testIdManifest)
8394
+ }]
8395
+ });
8396
+ sourceFile.addVariableStatement({
8397
+ declarationKind: tsMorph.VariableDeclarationKind.Const,
8398
+ isExported: true,
8399
+ declarations: [{
8400
+ name: "pomManifest",
8401
+ initializer: writeConstJson(pomManifest)
8130
8402
  }]
8131
8403
  });
8132
8404
  sourceFile.addTypeAlias({
@@ -8139,27 +8411,67 @@ function generateTestIdsModule(componentTestIds) {
8139
8411
  name: "ComponentName",
8140
8412
  type: "keyof TestIdManifest"
8141
8413
  });
8414
+ sourceFile.addTypeAlias({
8415
+ isExported: true,
8416
+ name: "PomManifest",
8417
+ type: "typeof pomManifest"
8418
+ });
8419
+ sourceFile.addTypeAlias({
8420
+ isExported: true,
8421
+ name: "PomManifestComponentName",
8422
+ type: "keyof PomManifest"
8423
+ });
8142
8424
  });
8143
8425
  }
8144
- const VIRTUAL_ID = "virtual:testids";
8145
- const RESOLVED_ID = `\0${VIRTUAL_ID}`;
8146
- function createTestIdsVirtualModulesPlugin(componentTestIds) {
8426
+ function generatePomManifestModule(componentHierarchyMap, elementMetadata) {
8427
+ const pomManifest = buildPomManifest(componentHierarchyMap, elementMetadata);
8428
+ return renderSourceFile("virtual-pom-manifest.ts", (sourceFile) => {
8429
+ sourceFile.addStatements("// Virtual module: richer POM discoverability manifest");
8430
+ sourceFile.addVariableStatement({
8431
+ declarationKind: tsMorph.VariableDeclarationKind.Const,
8432
+ isExported: true,
8433
+ declarations: [{
8434
+ name: "pomManifest",
8435
+ initializer: writeConstJson(pomManifest)
8436
+ }]
8437
+ });
8438
+ sourceFile.addTypeAlias({
8439
+ isExported: true,
8440
+ name: "PomManifest",
8441
+ type: "typeof pomManifest"
8442
+ });
8443
+ sourceFile.addTypeAlias({
8444
+ isExported: true,
8445
+ name: "PomManifestComponentName",
8446
+ type: "keyof PomManifest"
8447
+ });
8448
+ });
8449
+ }
8450
+ const TEST_IDS_VIRTUAL_ID = "virtual:testids";
8451
+ const TEST_IDS_RESOLVED_ID = `\0${TEST_IDS_VIRTUAL_ID}`;
8452
+ const POM_MANIFEST_VIRTUAL_ID = "virtual:pom-manifest";
8453
+ const POM_MANIFEST_RESOLVED_ID = `\0${POM_MANIFEST_VIRTUAL_ID}`;
8454
+ function createTestIdsVirtualModulesPlugin(componentHierarchyMap, elementMetadata) {
8147
8455
  return {
8148
8456
  name: "vue-pom-generator:virtual-testids",
8149
8457
  resolveId(id) {
8150
- if (id === VIRTUAL_ID)
8151
- return RESOLVED_ID;
8458
+ if (id === TEST_IDS_VIRTUAL_ID)
8459
+ return TEST_IDS_RESOLVED_ID;
8460
+ if (id === POM_MANIFEST_VIRTUAL_ID)
8461
+ return POM_MANIFEST_RESOLVED_ID;
8152
8462
  },
8153
8463
  load(id) {
8154
- if (id === RESOLVED_ID)
8155
- return generateTestIdsModule(componentTestIds);
8464
+ if (id === TEST_IDS_RESOLVED_ID)
8465
+ return generateTestIdsModule(componentHierarchyMap, elementMetadata);
8466
+ if (id === POM_MANIFEST_RESOLVED_ID)
8467
+ return generatePomManifestModule(componentHierarchyMap, elementMetadata);
8156
8468
  }
8157
8469
  };
8158
8470
  }
8159
8471
  function createSupportPlugins(options) {
8160
8472
  const {
8161
- componentTestIds,
8162
8473
  componentHierarchyMap,
8474
+ elementMetadata,
8163
8475
  vueFilesPathMap,
8164
8476
  nativeWrappers,
8165
8477
  excludedComponents,
@@ -8245,7 +8557,7 @@ function createSupportPlugins(options) {
8245
8557
  getResolvedRouterEntry: resolveRouterEntry2,
8246
8558
  loggerRef
8247
8559
  });
8248
- const virtualModules = createTestIdsVirtualModulesPlugin(componentTestIds);
8560
+ const virtualModules = createTestIdsVirtualModulesPlugin(componentHierarchyMap, elementMetadata);
8249
8561
  return [tsProcessor, devProcessor, virtualModules];
8250
8562
  }
8251
8563
  function findDataTestIdProp(element, attributeName = "data-testid") {
@@ -8301,6 +8613,33 @@ function parseDynamicProps(dynamicProps) {
8301
8613
  }
8302
8614
  return void 0;
8303
8615
  }
8616
+ function findStaticAttributeValue(element, attributeName) {
8617
+ const attribute = element.props.find(
8618
+ (prop) => prop.type === compilerCore.NodeTypes.ATTRIBUTE && prop.name === attributeName
8619
+ );
8620
+ const value = attribute?.value?.content?.trim();
8621
+ return value ? value : void 0;
8622
+ }
8623
+ function collectStaticTextContent(children) {
8624
+ const parts = [];
8625
+ const visit = (nodes) => {
8626
+ for (const node of nodes) {
8627
+ if (node.type === compilerCore.NodeTypes.TEXT) {
8628
+ const text2 = node.content.replace(/\s+/g, " ").trim();
8629
+ if (text2) {
8630
+ parts.push(text2);
8631
+ }
8632
+ continue;
8633
+ }
8634
+ if (node.type === compilerCore.NodeTypes.ELEMENT) {
8635
+ visit(node.children);
8636
+ }
8637
+ }
8638
+ };
8639
+ visit(children);
8640
+ const text = parts.join(" ").replace(/\s+/g, " ").trim();
8641
+ return text || void 0;
8642
+ }
8304
8643
  function tryCreateElementMetadata(args) {
8305
8644
  const { element, semanticNameMap } = args;
8306
8645
  const testIdAttribute = (args.testIdAttribute ?? "data-testid").trim() || "data-testid";
@@ -8324,13 +8663,9 @@ function tryCreateElementMetadata(args) {
8324
8663
  dynamicPropsList = [content];
8325
8664
  }
8326
8665
  }
8327
- const semanticName = semanticNameMap.get(testId);
8328
- if (!semanticName) {
8329
- return null;
8330
- }
8331
8666
  const metadata = {
8332
8667
  testId,
8333
- semanticName,
8668
+ semanticName: semanticNameMap.get(testId),
8334
8669
  tag: element.tag,
8335
8670
  tagType: element.tagType,
8336
8671
  patchFlag,
@@ -8339,7 +8674,11 @@ function tryCreateElementMetadata(args) {
8339
8674
  hasClickHandler: patchFlag ? Boolean(patchFlag & 32) : void 0,
8340
8675
  hasDynamicClass: patchFlag ? Boolean(patchFlag & 2) : void 0,
8341
8676
  hasDynamicStyle: patchFlag ? Boolean(patchFlag & 4) : void 0,
8342
- hasDynamicText: patchFlag ? Boolean(patchFlag & 1) : void 0
8677
+ hasDynamicText: patchFlag ? Boolean(patchFlag & 1) : void 0,
8678
+ staticAriaLabel: findStaticAttributeValue(element, "aria-label"),
8679
+ staticRole: findStaticAttributeValue(element, "role"),
8680
+ staticTitle: findStaticAttributeValue(element, "title"),
8681
+ staticTextContent: collectStaticTextContent(element.children)
8343
8682
  };
8344
8683
  return metadata;
8345
8684
  }
@@ -8391,6 +8730,7 @@ function extractMetadataAfterTransform(ast, componentName, elementMetadata, sema
8391
8730
  if (componentMetadata.size > 0) {
8392
8731
  elementMetadata.set(componentName, componentMetadata);
8393
8732
  }
8733
+ return componentMetadata;
8394
8734
  }
8395
8735
  function createVuePluginWithTestIds(options) {
8396
8736
  const {
@@ -8405,11 +8745,13 @@ function createVuePluginWithTestIds(options) {
8405
8745
  excludedComponents,
8406
8746
  getViewsDirAbs,
8407
8747
  testIdAttribute,
8748
+ accessibilityAudit,
8408
8749
  loggerRef,
8409
8750
  getSourceDirs,
8410
8751
  getWrapperSearchRoots,
8411
8752
  getProjectRoot
8412
8753
  } = options;
8754
+ const lastAccessibilityWarningSignatureByComponent = /* @__PURE__ */ new Map();
8413
8755
  const getComponentNameFromPath = (filename) => {
8414
8756
  return resolveComponentNameFromPath({
8415
8757
  filename,
@@ -8482,13 +8824,37 @@ function createVuePluginWithTestIds(options) {
8482
8824
  )
8483
8825
  );
8484
8826
  return () => {
8485
- extractMetadataAfterTransform(
8827
+ const componentMetadata = extractMetadataAfterTransform(
8486
8828
  node,
8487
8829
  componentName,
8488
8830
  elementMetadata,
8489
8831
  semanticNameMap,
8490
8832
  testIdAttribute
8491
8833
  );
8834
+ if (!accessibilityAudit) {
8835
+ return;
8836
+ }
8837
+ const dependencies = componentHierarchyMap.get(componentName);
8838
+ if (!dependencies || componentMetadata.size === 0) {
8839
+ return;
8840
+ }
8841
+ const manifest = buildPomManifest(
8842
+ /* @__PURE__ */ new Map([[componentName, dependencies]]),
8843
+ /* @__PURE__ */ new Map([[componentName, componentMetadata]])
8844
+ );
8845
+ const warnings = collectAccessibilityReviewWarnings(manifest);
8846
+ const signature = warnings.join("\n");
8847
+ if (!signature) {
8848
+ lastAccessibilityWarningSignatureByComponent.delete(componentName);
8849
+ return;
8850
+ }
8851
+ if (lastAccessibilityWarningSignatureByComponent.get(componentName) === signature) {
8852
+ return;
8853
+ }
8854
+ lastAccessibilityWarningSignatureByComponent.set(componentName, signature);
8855
+ for (const warning of warnings) {
8856
+ loggerRef.current.warn(warning);
8857
+ }
8492
8858
  };
8493
8859
  }
8494
8860
  let transform = perFileTransform.get(componentName);
@@ -8735,7 +9101,6 @@ function getSharedGeneratorState(key) {
8735
9101
  let state = sharedGeneratorStateRegistry.get(key);
8736
9102
  if (!state) {
8737
9103
  state = {
8738
- componentTestIds: /* @__PURE__ */ new Map(),
8739
9104
  elementMetadata: /* @__PURE__ */ new Map(),
8740
9105
  semanticNameMap: /* @__PURE__ */ new Map(),
8741
9106
  componentHierarchyMap: /* @__PURE__ */ new Map(),
@@ -8854,6 +9219,7 @@ function createVuePomGeneratorPlugins(options = {}) {
8854
9219
  nameCollisionBehavior: generationOptions?.nameCollisionBehavior,
8855
9220
  existingIdBehavior: resolvedInjectionOptions.existingIdBehavior,
8856
9221
  testIdAttribute,
9222
+ accessibilityAudit: generationOptions?.accessibilityAudit,
8857
9223
  routerAwarePoms: typeof routerEntry === "string" && routerEntry.trim().length > 0 || routerType === "nuxt",
8858
9224
  routerEntry,
8859
9225
  routerType,
@@ -8934,7 +9300,7 @@ function createVuePomGeneratorPlugins(options = {}) {
8934
9300
  };
8935
9301
  const getViewsDirAbs = () => resolveFromProjectRoot(projectRootRef.current, getViewsDir());
8936
9302
  const getWrapperSearchRootsAbs = () => getWrapperSearchRoots().map((root) => resolveFromProjectRoot(projectRootRef.current, root));
8937
- const { componentTestIds, elementMetadata, semanticNameMap, componentHierarchyMap, vueFilesPathMap } = sharedState;
9303
+ const { elementMetadata, semanticNameMap, componentHierarchyMap, vueFilesPathMap } = sharedState;
8938
9304
  const { metadataCollectorPlugin, internalVuePlugin, templateCompilerOptions } = createVuePluginWithTestIds({
8939
9305
  vueOptions,
8940
9306
  existingIdBehavior: resolvedGenerationOptions.existingIdBehavior,
@@ -8947,6 +9313,7 @@ function createVuePomGeneratorPlugins(options = {}) {
8947
9313
  excludedComponents,
8948
9314
  getViewsDirAbs,
8949
9315
  testIdAttribute,
9316
+ accessibilityAudit: resolvedGenerationOptions.accessibilityAudit,
8950
9317
  loggerRef,
8951
9318
  getSourceDirs,
8952
9319
  getWrapperSearchRoots: getWrapperSearchRootsAbs,
@@ -8954,8 +9321,8 @@ function createVuePomGeneratorPlugins(options = {}) {
8954
9321
  });
8955
9322
  templateCompilerOptionsForResolvedPlugin = templateCompilerOptions;
8956
9323
  const supportPlugins = createSupportPlugins({
8957
- componentTestIds,
8958
9324
  componentHierarchyMap,
9325
+ elementMetadata,
8959
9326
  vueFilesPathMap,
8960
9327
  nativeWrappers,
8961
9328
  excludedComponents,
@@ -8982,7 +9349,7 @@ function createVuePomGeneratorPlugins(options = {}) {
8982
9349
  ...supportPlugins
8983
9350
  ];
8984
9351
  if (!generationEnabled) {
8985
- const virtualModules = createTestIdsVirtualModulesPlugin(componentTestIds);
9352
+ const virtualModules = createTestIdsVirtualModulesPlugin(componentHierarchyMap, elementMetadata);
8986
9353
  return [
8987
9354
  configPlugin,
8988
9355
  metadataCollectorPlugin,