@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.mjs CHANGED
@@ -219,6 +219,7 @@ function resolveGenerationSupportOptions(options) {
219
219
  nameCollisionBehavior: options.nameCollisionBehavior ?? "error",
220
220
  existingIdBehavior: options.existingIdBehavior ?? "error",
221
221
  testIdAttribute: (options.testIdAttribute ?? "data-testid").trim() || "data-testid",
222
+ accessibilityAudit: options.accessibilityAudit ?? false,
222
223
  routerAwarePoms: options.routerAwarePoms ?? false,
223
224
  routerEntry: options.routerEntry,
224
225
  routerType: options.routerType ?? "vue-router",
@@ -558,6 +559,75 @@ function uniquePomStringPatterns(primary, alternates) {
558
559
  }
559
560
  return out;
560
561
  }
562
+ function splitDiscoverabilityWords(value) {
563
+ 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();
564
+ if (!normalized) {
565
+ return [];
566
+ }
567
+ return normalized.split(/\s+/).map((word) => word.toLowerCase()).filter(Boolean);
568
+ }
569
+ function joinDiscoverabilityWords(words) {
570
+ return words.join(" ").replace(/\s+/g, " ").trim();
571
+ }
572
+ function toSentenceCase(value) {
573
+ return value.charAt(0).toUpperCase() + value.slice(1);
574
+ }
575
+ function stripComponentKindSuffix(componentName) {
576
+ for (const suffix of ["Page", "Component", "Layout"]) {
577
+ if (componentName.endsWith(suffix) && componentName.length > suffix.length) {
578
+ return componentName.slice(0, -suffix.length);
579
+ }
580
+ }
581
+ return componentName;
582
+ }
583
+ function removeLeadingWords(words, prefixWords) {
584
+ if (!prefixWords.length || words.length < prefixWords.length) {
585
+ return [...words];
586
+ }
587
+ for (let i = 0; i < prefixWords.length; i++) {
588
+ if (words[i] !== prefixWords[i]) {
589
+ return [...words];
590
+ }
591
+ }
592
+ return words.slice(prefixWords.length);
593
+ }
594
+ function removeTrailingRoleWord(words, roleWord) {
595
+ if (!words.length || words[words.length - 1] !== roleWord) {
596
+ return [...words];
597
+ }
598
+ return words.slice(0, -1);
599
+ }
600
+ function humanizePomMethodName(methodName) {
601
+ return joinDiscoverabilityWords(splitDiscoverabilityWords(methodName));
602
+ }
603
+ function stripPomActionPrefix(actionName) {
604
+ for (const prefix of ["click", "select", "type", "goTo"]) {
605
+ if (actionName.startsWith(prefix) && actionName.length > prefix.length) {
606
+ return actionName.slice(prefix.length);
607
+ }
608
+ }
609
+ return actionName;
610
+ }
611
+ function normalizePomRoleLabel(nativeRole) {
612
+ if (nativeRole === "vselect") {
613
+ return "select";
614
+ }
615
+ return nativeRole || "element";
616
+ }
617
+ function buildPomLocatorDescription(args) {
618
+ const componentWords = splitDiscoverabilityWords(args.componentName ? stripComponentKindSuffix(args.componentName) : "");
619
+ const roleWord = normalizePomRoleLabel(args.nativeRole).toLowerCase();
620
+ const semanticWords = removeLeadingWords(
621
+ removeTrailingRoleWord(splitDiscoverabilityWords(args.methodName), roleWord),
622
+ componentWords
623
+ );
624
+ const phrase = joinDiscoverabilityWords([
625
+ ...componentWords,
626
+ ...semanticWords,
627
+ roleWord
628
+ ]);
629
+ return toSentenceCase(phrase || "Generated element");
630
+ }
561
631
  function upperFirst$1(value) {
562
632
  if (!value) {
563
633
  return value;
@@ -574,7 +644,7 @@ function createInlineParameter(name, options = {}) {
574
644
  initializer: options.initializer
575
645
  };
576
646
  }
577
- function removeByKeySegment(value) {
647
+ function removeByKeySegment$1(value) {
578
648
  const idx = value.lastIndexOf("ByKey");
579
649
  if (idx < 0) {
580
650
  return value;
@@ -589,9 +659,14 @@ function createAsyncMethod(name, parameters, statements) {
589
659
  statements
590
660
  });
591
661
  }
592
- function generateClickMethod(methodName, selector, alternateSelectors, parameters) {
662
+ function generateClickMethod(componentName, methodName, selector, alternateSelectors, parameters) {
593
663
  const name = `click${methodName}`;
594
664
  const noWaitName = `${name}NoWait`;
665
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
666
+ componentName,
667
+ methodName,
668
+ nativeRole: "button"
669
+ }));
595
670
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
596
671
  const hasSelectorVariables = hasPomPatternVariables(selector);
597
672
  const baseParameters = createParameters(selectorParams);
@@ -614,7 +689,7 @@ function generateClickMethod(methodName, selector, alternateSelectors, parameter
614
689
  writer.writeLine(`const candidates = [${candidatesExpr}] as const;`);
615
690
  writer.writeLine("let lastError: unknown;");
616
691
  writer.write("for (const testId of candidates) ").block(() => {
617
- writer.writeLine("const locator = this.locatorByTestId(testId);");
692
+ writer.writeLine(`const locator = this.locatorByTestId(testId, ${locatorDescription});`);
618
693
  writer.write("try ").block(() => {
619
694
  writer.write("if (await locator.count()) ").block(() => {
620
695
  writer.writeLine("await this.clickLocator(locator, annotationText, wait);");
@@ -648,7 +723,7 @@ function generateClickMethod(methodName, selector, alternateSelectors, parameter
648
723
  createInlineParameter("annotationText", { type: "string", initializer: '""' })
649
724
  ],
650
725
  (writer) => {
651
- writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait);`);
726
+ writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait, ${locatorDescription});`);
652
727
  }
653
728
  ),
654
729
  createAsyncMethod(
@@ -668,7 +743,7 @@ function generateClickMethod(methodName, selector, alternateSelectors, parameter
668
743
  createInlineParameter("annotationText", { type: "string", initializer: '""' })
669
744
  ],
670
745
  (writer) => {
671
- writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait);`);
746
+ writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait, ${locatorDescription});`);
672
747
  }
673
748
  ),
674
749
  createAsyncMethod(
@@ -680,55 +755,76 @@ function generateClickMethod(methodName, selector, alternateSelectors, parameter
680
755
  )
681
756
  ];
682
757
  }
683
- function generateRadioMethod(methodName, selector, parameters) {
758
+ function generateRadioMethod(componentName, methodName, selector, parameters) {
684
759
  const name = `select${methodName}`;
760
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
761
+ componentName,
762
+ methodName,
763
+ nativeRole: "radio"
764
+ }));
685
765
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
686
766
  const methodParameters = createParameters(selectorParams);
687
767
  const testIdExpr = toTypeScriptPomPatternExpression(selector);
688
768
  return [
689
769
  createAsyncMethod(name, methodParameters, (writer) => {
690
- writer.writeLine(`await this.clickByTestId(${testIdExpr}, annotationText);`);
770
+ writer.writeLine(`await this.clickByTestId(${testIdExpr}, annotationText, true, ${locatorDescription});`);
691
771
  })
692
772
  ];
693
773
  }
694
- function generateSelectMethod(methodName, selector, parameters) {
774
+ function generateSelectMethod(componentName, methodName, selector, parameters) {
695
775
  const name = `select${methodName}`;
776
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
777
+ componentName,
778
+ methodName,
779
+ nativeRole: "select"
780
+ }));
696
781
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
697
- const selectorExpr = `this.selectorForTestId(${toTypeScriptPomPatternExpression(selector)})`;
782
+ const testIdExpr = toTypeScriptPomPatternExpression(selector);
698
783
  return [
699
784
  createAsyncMethod(
700
785
  name,
701
786
  createParameters(selectorParams),
702
787
  (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);");
788
+ writer.writeLine(`const testId = ${testIdExpr};`);
789
+ writer.writeLine(`const locator = this.locatorByTestId(testId, ${locatorDescription});`);
790
+ writer.writeLine("await this.animateCursorToElement(locator, false, 500, annotationText);");
791
+ writer.writeLine("await locator.selectOption(value);");
706
792
  }
707
793
  )
708
794
  ];
709
795
  }
710
- function generateVSelectMethod(methodName, selector, parameters) {
796
+ function generateVSelectMethod(componentName, methodName, selector, parameters) {
711
797
  const name = `select${methodName}`;
798
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
799
+ componentName,
800
+ methodName,
801
+ nativeRole: "vselect"
802
+ }));
712
803
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
713
804
  return [
714
805
  createAsyncMethod(
715
806
  name,
716
807
  createParameters(selectorParams),
717
808
  (writer) => {
718
- writer.writeLine(`await this.selectVSelectByTestId(${toTypeScriptPomPatternExpression(selector)}, value, timeOut, annotationText);`);
809
+ writer.writeLine(`await this.selectVSelectByTestId(${toTypeScriptPomPatternExpression(selector)}, value, timeOut, annotationText, ${locatorDescription});`);
719
810
  }
720
811
  )
721
812
  ];
722
813
  }
723
- function generateTypeMethod(methodName, selector, parameters) {
814
+ function generateTypeMethod(componentName, methodName, selector, parameters) {
724
815
  const name = `type${methodName}`;
816
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
817
+ componentName,
818
+ methodName,
819
+ nativeRole: "input"
820
+ }));
725
821
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
726
822
  return [
727
823
  createAsyncMethod(
728
824
  name,
729
825
  createParameters(selectorParams),
730
826
  (writer) => {
731
- writer.writeLine(`await this.fillInputByTestId(${toTypeScriptPomPatternExpression(selector)}, text, annotationText);`);
827
+ writer.writeLine(`await this.fillInputByTestId(${toTypeScriptPomPatternExpression(selector)}, text, annotationText, ${locatorDescription});`);
732
828
  }
733
829
  )
734
830
  ];
@@ -743,22 +839,27 @@ function isAllDigits(value) {
743
839
  }
744
840
  return true;
745
841
  }
746
- function generateGetElementByDataTestId(methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
842
+ function generateGetElementByDataTestId(componentName, methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
843
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
844
+ componentName,
845
+ methodName,
846
+ nativeRole
847
+ }));
747
848
  const roleSuffix = upperFirst$1(nativeRole || "Element");
748
849
  const baseName = upperFirst$1(methodName);
749
850
  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}`;
851
+ const hasRoleSuffix2 = baseName.endsWith(roleSuffix) || baseName.startsWith(roleSuffix) && isAllDigits(numericSuffix);
852
+ const propertyName = hasRoleSuffix2 ? `${baseName}` : `${baseName}${roleSuffix}`;
752
853
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
753
854
  const indexedVariable = getIndexedPomPatternVariable(selector);
754
855
  if (indexedVariable) {
755
856
  const keyType = getPomParameter(selectorParams, indexedVariable)?.typeExpression || "string";
756
- const keyedPropertyName = getterNameOverride ?? removeByKeySegment(propertyName);
857
+ const keyedPropertyName = getterNameOverride ?? removeByKeySegment$1(propertyName);
757
858
  return [
758
859
  createClassGetter({
759
860
  name: keyedPropertyName,
760
861
  statements: [
761
- `return this.keyedLocators((${indexedVariable}: ${keyType}) => this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)}));`
862
+ `return this.keyedLocators((${indexedVariable}: ${keyType}) => this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)}, ${locatorDescription}));`
762
863
  ]
763
864
  })
764
865
  ];
@@ -771,20 +872,25 @@ function generateGetElementByDataTestId(methodName, nativeRole, selector, altern
771
872
  return [
772
873
  createClassGetter({
773
874
  name: finalPropertyName,
774
- statements: [`return ${locatorExpr};`]
875
+ statements: [`return this.describeLocator(${locatorExpr}, ${locatorDescription});`]
775
876
  })
776
877
  ];
777
878
  }
778
879
  return [
779
880
  createClassGetter({
780
881
  name: finalPropertyName,
781
- statements: [`return this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)});`]
882
+ statements: [`return this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)}, ${locatorDescription});`]
782
883
  })
783
884
  ];
784
885
  }
785
886
  function generateNavigationMethod(args) {
786
- const { targetPageObjectModelClass: target, baseMethodName, selector, alternateSelectors, parameters } = args;
887
+ const { componentName, targetPageObjectModelClass: target, baseMethodName, selector, alternateSelectors, parameters } = args;
787
888
  const methodName = baseMethodName ? `goTo${upperFirst$1(baseMethodName)}` : `goTo${target.endsWith("Page") ? target.slice(0, -"Page".length) : target}`;
889
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
890
+ componentName,
891
+ methodName: baseMethodName,
892
+ nativeRole: "button"
893
+ }));
788
894
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
789
895
  const methodParameters = createParameters(selectorParams);
790
896
  const alternates = uniquePomStringPatterns(selector, alternateSelectors).slice(1);
@@ -800,7 +906,7 @@ function generateNavigationMethod(args) {
800
906
  writer.writeLine(`const candidates = [${candidatesExpr}] as const;`);
801
907
  writer.writeLine("let lastError: unknown;");
802
908
  writer.write("for (const testId of candidates) ").block(() => {
803
- writer.writeLine("const locator = this.locatorByTestId(testId);");
909
+ writer.writeLine(`const locator = this.locatorByTestId(testId, ${locatorDescription});`);
804
910
  writer.write("try ").block(() => {
805
911
  writer.write("if (await locator.count()) ").block(() => {
806
912
  writer.writeLine("await this.clickLocator(locator);");
@@ -825,7 +931,8 @@ function generateNavigationMethod(args) {
825
931
  returnType: `Fluent<${target}>`,
826
932
  statements: (writer) => {
827
933
  writer.write("return this.fluent(async () => ").block(() => {
828
- writer.writeLine(`await this.clickByTestId(${toTypeScriptPomPatternExpression(selector)});`);
934
+ writer.writeLine(`const locator = this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)}, ${locatorDescription});`);
935
+ writer.writeLine("await this.clickLocator(locator);");
829
936
  writer.writeLine(`return new ${target}(this.page);`);
830
937
  });
831
938
  writer.writeLine(");");
@@ -833,9 +940,10 @@ function generateNavigationMethod(args) {
833
940
  })
834
941
  ];
835
942
  }
836
- function generateViewObjectModelMembers(targetPageObjectModelClass, methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
943
+ function generateViewObjectModelMembers(componentName, targetPageObjectModelClass, methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
837
944
  const baseMethodName = nativeRole === "radio" ? methodName || "Radio" : methodName;
838
945
  const members = generateGetElementByDataTestId(
946
+ componentName,
839
947
  baseMethodName,
840
948
  nativeRole,
841
949
  selector,
@@ -847,6 +955,7 @@ function generateViewObjectModelMembers(targetPageObjectModelClass, methodName,
847
955
  return [
848
956
  ...members,
849
957
  ...generateNavigationMethod({
958
+ componentName,
850
959
  targetPageObjectModelClass,
851
960
  baseMethodName,
852
961
  selector,
@@ -856,18 +965,18 @@ function generateViewObjectModelMembers(targetPageObjectModelClass, methodName,
856
965
  ];
857
966
  }
858
967
  if (nativeRole === "select") {
859
- return [...members, ...generateSelectMethod(baseMethodName, selector, parameters)];
968
+ return [...members, ...generateSelectMethod(componentName, baseMethodName, selector, parameters)];
860
969
  }
861
970
  if (nativeRole === "vselect") {
862
- return [...members, ...generateVSelectMethod(baseMethodName, selector, parameters)];
971
+ return [...members, ...generateVSelectMethod(componentName, baseMethodName, selector, parameters)];
863
972
  }
864
973
  if (nativeRole === "input") {
865
- return [...members, ...generateTypeMethod(baseMethodName, selector, parameters)];
974
+ return [...members, ...generateTypeMethod(componentName, baseMethodName, selector, parameters)];
866
975
  }
867
976
  if (nativeRole === "radio") {
868
- return [...members, ...generateRadioMethod(baseMethodName || "Radio", selector, parameters)];
977
+ return [...members, ...generateRadioMethod(componentName, baseMethodName || "Radio", selector, parameters)];
869
978
  }
870
- return [...members, ...generateClickMethod(baseMethodName, selector, alternateSelectors, parameters)];
979
+ return [...members, ...generateClickMethod(componentName, baseMethodName, selector, alternateSelectors, parameters)];
871
980
  }
872
981
  function isSimpleExpressionNode(value) {
873
982
  return value !== null && "type" in value && value.type === NodeTypes.SIMPLE_EXPRESSION;
@@ -2747,7 +2856,7 @@ Fix: either (1) include ${bestKeyPreservePlaceholder} in your :${attrLabel} temp
2747
2856
  }
2748
2857
  return value.slice(0, idx) + value.slice(idx + "ByKey".length);
2749
2858
  };
2750
- const hasRoleSuffix = (baseName, roleSuffix) => {
2859
+ const hasRoleSuffix2 = (baseName, roleSuffix) => {
2751
2860
  if (baseName.endsWith(roleSuffix)) {
2752
2861
  return true;
2753
2862
  }
@@ -2757,13 +2866,13 @@ Fix: either (1) include ${bestKeyPreservePlaceholder} in your :${attrLabel} temp
2757
2866
  const getPrimaryGetterName = (primaryMethodName) => {
2758
2867
  const roleSuffix = upperFirst(normalizedRole || "Element");
2759
2868
  const baseName = upperFirst(primaryMethodName);
2760
- const propertyName = hasRoleSuffix(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
2869
+ const propertyName = hasRoleSuffix2(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
2761
2870
  return selectorIsParameterized ? removeByKeySegment2(propertyName) : propertyName;
2762
2871
  };
2763
2872
  const getPrimaryGetterNameCandidates = (primaryMethodName) => {
2764
2873
  const roleSuffix = upperFirst(normalizedRole || "Element");
2765
2874
  const baseName = upperFirst(primaryMethodName);
2766
- const propertyName = hasRoleSuffix(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
2875
+ const propertyName = hasRoleSuffix2(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
2767
2876
  if (!selectorIsParameterized) {
2768
2877
  return { primary: propertyName };
2769
2878
  }
@@ -2890,7 +2999,7 @@ Fix: either (1) include ${bestKeyPreservePlaceholder} in your :${attrLabel} temp
2890
2999
  if (conflicts && nameCollisionBehavior === "error") {
2891
3000
  const roleSuffix = upperFirst(normalizedRole || "Element");
2892
3001
  const baseNameUpper = upperFirst(baseWithSuffix);
2893
- if (!hasRoleSuffix(baseNameUpper, roleSuffix)) {
3002
+ if (!hasRoleSuffix2(baseNameUpper, roleSuffix)) {
2894
3003
  const baseWithRoleSuffix = `${baseWithSuffix}${roleSuffix}`;
2895
3004
  const candidateWithRoleSuffix = selectorIsParameterized ? `${baseWithRoleSuffix}ByKey` : baseWithRoleSuffix;
2896
3005
  const actionNameWithRoleSuffix = getPrimaryActionMethodName(candidateWithRoleSuffix);
@@ -4442,7 +4551,7 @@ function generateGoToSelfMethod(componentName) {
4442
4551
  function getSelectorPatterns(selector) {
4443
4552
  return selector.kind === "testId" ? [selector.testId] : [selector.rootTestId, selector.label];
4444
4553
  }
4445
- function generateExtraClickMethodMembers(spec) {
4554
+ function generateExtraClickMethodMembers(spec, componentName) {
4446
4555
  if (spec.kind !== "click") {
4447
4556
  return [];
4448
4557
  }
@@ -4457,16 +4566,13 @@ function generateExtraClickMethodMembers(spec) {
4457
4566
  const hasWait = signatureSpecs.some((param) => param.name === "wait");
4458
4567
  const annotationArg = hasAnnotationText ? "annotationText" : '""';
4459
4568
  const waitArg = hasWait ? "wait" : "true";
4569
+ const locatorDescription = JSON.stringify(buildPomLocatorDescription({
4570
+ componentName,
4571
+ methodName: stripPomActionPrefix(spec.name),
4572
+ nativeRole: "button"
4573
+ }));
4460
4574
  if (spec.selector.kind === "testId") {
4461
4575
  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
4576
  return [
4471
4577
  createClassMethod({
4472
4578
  name: spec.name,
@@ -4479,7 +4585,7 @@ function generateExtraClickMethodMembers(spec) {
4479
4585
  for (const statement of testIdBinding.setupStatements) {
4480
4586
  writer.writeLine(statement);
4481
4587
  }
4482
- writer.writeLine(`await this.clickByTestId(${clickArgs.join(", ")});`);
4588
+ writer.writeLine(`await this.clickByTestId(${testIdBinding.expression}, ${annotationArg}, ${waitArg}, ${locatorDescription});`);
4483
4589
  }
4484
4590
  })
4485
4591
  ];
@@ -4501,16 +4607,17 @@ function generateExtraClickMethodMembers(spec) {
4501
4607
  for (const statement of labelBinding.setupStatements) {
4502
4608
  writer.writeLine(statement);
4503
4609
  }
4504
- writer.writeLine(`await this.clickWithinTestIdByLabel(${rootBinding.expression}, ${labelBinding.expression}, ${annotationArg}, ${waitArg});`);
4610
+ writer.writeLine(`await this.clickWithinTestIdByLabel(${rootBinding.expression}, ${labelBinding.expression}, ${annotationArg}, ${waitArg}, { description: ${locatorDescription} });`);
4505
4611
  }
4506
4612
  })
4507
4613
  ];
4508
4614
  }
4509
- function generateMethodMembersFromPom(primary, targetPageObjectModelClass) {
4615
+ function generateMethodMembersFromPom(componentName, primary, targetPageObjectModelClass) {
4510
4616
  if (primary.emitPrimary === false) {
4511
4617
  return [];
4512
4618
  }
4513
4619
  return generateViewObjectModelMembers(
4620
+ componentName,
4514
4621
  targetPageObjectModelClass,
4515
4622
  primary.methodName,
4516
4623
  primary.nativeRole,
@@ -4520,7 +4627,7 @@ function generateMethodMembersFromPom(primary, targetPageObjectModelClass) {
4520
4627
  primary.parameters
4521
4628
  );
4522
4629
  }
4523
- function generateMethodsContentForDependencies(dependencies) {
4630
+ function generateMethodsContentForDependencies(componentName, dependencies) {
4524
4631
  const entries = Array.from(dependencies.dataTestIdSet ?? []);
4525
4632
  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
4633
  const seenPrimaryKeys = /* @__PURE__ */ new Set();
@@ -4545,10 +4652,10 @@ function generateMethodsContentForDependencies(dependencies) {
4545
4652
  const extras = (dependencies.pomExtraMethods ?? []).slice().sort((a, b) => a.name.localeCompare(b.name));
4546
4653
  const members = [];
4547
4654
  for (const { pom, target } of primarySpecs) {
4548
- members.push(...generateMethodMembersFromPom(pom, target));
4655
+ members.push(...generateMethodMembersFromPom(componentName, pom, target));
4549
4656
  }
4550
4657
  for (const extra of extras) {
4551
- members.push(...generateExtraClickMethodMembers(extra));
4658
+ members.push(...generateExtraClickMethodMembers(extra, componentName));
4552
4659
  }
4553
4660
  return members;
4554
4661
  }
@@ -5402,7 +5509,7 @@ function prepareViewObjectModelClass(componentName, dependencies, componentHiera
5402
5509
  members.push(...generateRouteProperty(routeMeta));
5403
5510
  members.push(...generateGoToSelfMethod(className));
5404
5511
  }
5405
- members.push(...generateMethodsContentForDependencies(dependencies));
5512
+ members.push(...generateMethodsContentForDependencies(componentName, dependencies));
5406
5513
  return {
5407
5514
  className,
5408
5515
  componentRefsForInstances,
@@ -8067,8 +8174,174 @@ function createDevProcessorPlugin(options) {
8067
8174
  }
8068
8175
  };
8069
8176
  }
8070
- function generateTestIdsModule(componentTestIds) {
8071
- const manifestEntries = Array.from(componentTestIds.entries()).sort((a, b) => a[0].localeCompare(b[0]));
8177
+ function supportsInlineTextAccessibleName(role) {
8178
+ return role === "button" || role === "radio";
8179
+ }
8180
+ function buildAccessibilityAudit(metadata, inferredRole) {
8181
+ if (!metadata || !inferredRole) {
8182
+ return void 0;
8183
+ }
8184
+ const role = normalizePomRoleLabel(inferredRole).toLowerCase();
8185
+ const dynamicProps = new Set(metadata.dynamicProps ?? []);
8186
+ const hasDynamicAccessibleNameSignal = dynamicProps.has("aria-label") || dynamicProps.has("title") || !!metadata.hasDynamicText;
8187
+ let accessibleNameSource;
8188
+ const reasons = [];
8189
+ if (metadata.staticAriaLabel) {
8190
+ accessibleNameSource = "aria-label";
8191
+ } else if (supportsInlineTextAccessibleName(role) && metadata.staticTextContent) {
8192
+ accessibleNameSource = "text";
8193
+ } else if (metadata.staticTitle) {
8194
+ accessibleNameSource = "title";
8195
+ } else if (hasDynamicAccessibleNameSignal) {
8196
+ accessibleNameSource = "dynamic";
8197
+ } else if (role === "input" || role === "select") {
8198
+ accessibleNameSource = "unknown";
8199
+ reasons.push("No inline accessible-name signal was found; the element may rely on external markup such as a separate <label>.");
8200
+ } else {
8201
+ accessibleNameSource = "missing";
8202
+ reasons.push("No compile-time accessible-name signal was found.");
8203
+ }
8204
+ return {
8205
+ needsReview: accessibleNameSource === "unknown" || accessibleNameSource === "missing",
8206
+ accessibleNameSource,
8207
+ reasons,
8208
+ ...metadata.staticAriaLabel ? { staticAriaLabel: metadata.staticAriaLabel } : {},
8209
+ ...metadata.staticRole ? { staticRole: metadata.staticRole } : {},
8210
+ ...metadata.staticTitle ? { staticTitle: metadata.staticTitle } : {},
8211
+ ...metadata.staticTextContent ? { staticTextContent: metadata.staticTextContent } : {}
8212
+ };
8213
+ }
8214
+ function collectAccessibilityReviewWarnings(manifest) {
8215
+ const warnings = [];
8216
+ for (const [componentName, component] of Object.entries(manifest)) {
8217
+ for (const entry of component.entries) {
8218
+ if (!entry.accessibility?.needsReview) {
8219
+ continue;
8220
+ }
8221
+ const entryLabel = entry.generatedPropertyName ?? entry.testId;
8222
+ const reasonText = entry.accessibility.reasons.join(" ");
8223
+ warnings.push(
8224
+ `[vue-pom-generator] Accessibility review suggested for ${componentName}.${entryLabel} (role=${entry.inferredRole ?? "unknown"}, testId=${JSON.stringify(entry.testId)}): ${reasonText}`
8225
+ );
8226
+ }
8227
+ }
8228
+ return warnings;
8229
+ }
8230
+ function removeByKeySegment(value) {
8231
+ const idx = value.indexOf("ByKey");
8232
+ if (idx < 0) {
8233
+ return value;
8234
+ }
8235
+ return value.slice(0, idx) + value.slice(idx + "ByKey".length);
8236
+ }
8237
+ function hasRoleSuffix(baseName, roleSuffix) {
8238
+ if (baseName.endsWith(roleSuffix)) {
8239
+ return true;
8240
+ }
8241
+ const re = new RegExp(`^${roleSuffix}\\d+$`);
8242
+ return re.test(baseName);
8243
+ }
8244
+ function getGeneratedPropertyName(pom) {
8245
+ if (pom.getterNameOverride) {
8246
+ return pom.getterNameOverride;
8247
+ }
8248
+ const roleSuffix = upperFirst(pom.nativeRole || "Element");
8249
+ const baseName = upperFirst(pom.methodName);
8250
+ const propertyName = hasRoleSuffix(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
8251
+ return pom.selector.patternKind === "parameterized" ? removeByKeySegment(propertyName) : propertyName;
8252
+ }
8253
+ function getGeneratedActionName(entry, pom) {
8254
+ const methodNameUpper = upperFirst(pom.methodName);
8255
+ const radioMethodNameUpper = upperFirst(pom.methodName || "Radio");
8256
+ const isNavigation = !!entry.targetPageObjectModelClass;
8257
+ if (isNavigation) {
8258
+ return `goTo${methodNameUpper}`;
8259
+ }
8260
+ switch (pom.nativeRole) {
8261
+ case "input":
8262
+ return `type${methodNameUpper}`;
8263
+ case "select":
8264
+ case "vselect":
8265
+ return `select${methodNameUpper}`;
8266
+ case "radio":
8267
+ return `select${radioMethodNameUpper}`;
8268
+ default:
8269
+ return `click${methodNameUpper}`;
8270
+ }
8271
+ }
8272
+ function matchesPrimarySelector(extraMethod, pom) {
8273
+ if (extraMethod.selector.kind !== "testId") {
8274
+ return false;
8275
+ }
8276
+ return extraMethod.selector.testId.formatted === pom.selector.formatted && extraMethod.selector.testId.patternKind === pom.selector.patternKind;
8277
+ }
8278
+ function getManifestEntry(componentName, entry, componentMetadata, extraMethods) {
8279
+ const testId = entry.selectorValue.formatted;
8280
+ const metadata = componentMetadata?.get(testId);
8281
+ const pom = entry.pom;
8282
+ const generatedActionName = pom ? getGeneratedActionName(entry, pom) : null;
8283
+ const extraActionNames = pom ? extraMethods.filter((extraMethod) => matchesPrimarySelector(extraMethod, pom)).map((extraMethod) => extraMethod.name).sort((a, b) => a.localeCompare(b)) : [];
8284
+ const generatedActionNames = Array.from(/* @__PURE__ */ new Set([
8285
+ ...generatedActionName ? [generatedActionName] : [],
8286
+ ...extraActionNames.filter((name) => name !== generatedActionName)
8287
+ ]));
8288
+ const accessibility = buildAccessibilityAudit(metadata, pom?.nativeRole ?? null);
8289
+ return {
8290
+ testId,
8291
+ selectorPatternKind: entry.selectorValue.patternKind,
8292
+ semanticName: metadata?.semanticName ?? (pom ? humanizePomMethodName(pom.methodName) : testId),
8293
+ locatorDescription: pom ? buildPomLocatorDescription({
8294
+ componentName,
8295
+ methodName: pom.methodName,
8296
+ nativeRole: pom.nativeRole
8297
+ }) : componentName,
8298
+ inferredRole: pom?.nativeRole ?? null,
8299
+ ...accessibility ? { accessibility } : {},
8300
+ generatedPropertyName: pom ? getGeneratedPropertyName(pom) : null,
8301
+ generatedActionName,
8302
+ generatedActionNames,
8303
+ emitPrimary: pom?.emitPrimary !== false,
8304
+ ...entry.targetPageObjectModelClass ? { targetPageObjectModelClass: entry.targetPageObjectModelClass } : {},
8305
+ ...metadata?.tag ? { sourceTag: metadata.tag } : {},
8306
+ ...metadata ? { sourceTagType: metadata.tagType } : {},
8307
+ ...metadata?.patchFlag !== void 0 ? { patchFlag: metadata.patchFlag } : {},
8308
+ ...metadata?.dynamicProps?.length ? { dynamicProps: metadata.dynamicProps } : {},
8309
+ ...metadata?.hasClickHandler !== void 0 ? { hasClickHandler: metadata.hasClickHandler } : {},
8310
+ ...metadata?.hasDynamicClass !== void 0 ? { hasDynamicClass: metadata.hasDynamicClass } : {},
8311
+ ...metadata?.hasDynamicStyle !== void 0 ? { hasDynamicStyle: metadata.hasDynamicStyle } : {},
8312
+ ...metadata?.hasDynamicText !== void 0 ? { hasDynamicText: metadata.hasDynamicText } : {}
8313
+ };
8314
+ }
8315
+ function buildPomManifest(componentHierarchyMap, elementMetadata) {
8316
+ const manifestEntries = Array.from(componentHierarchyMap.entries()).sort((a, b) => a[0].localeCompare(b[0])).map(([componentName, dependencies]) => {
8317
+ 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 ?? []));
8318
+ if (!entries.length) {
8319
+ return null;
8320
+ }
8321
+ return [componentName, {
8322
+ componentName,
8323
+ className: componentName,
8324
+ sourceFile: dependencies.filePath,
8325
+ kind: dependencies.isView ? "view" : "component",
8326
+ testIds: Array.from(new Set(entries.map((entry) => entry.testId))),
8327
+ entries
8328
+ }];
8329
+ }).filter((entry) => entry !== null);
8330
+ return Object.fromEntries(manifestEntries);
8331
+ }
8332
+ function buildTestIdManifest(pomManifest) {
8333
+ return Object.fromEntries(
8334
+ Object.entries(pomManifest).map(([componentName, component]) => [componentName, Array.from(new Set(component.testIds)).sort((a, b) => a.localeCompare(b))])
8335
+ );
8336
+ }
8337
+ function writeConstJson(value) {
8338
+ return (writer) => {
8339
+ writer.write(`${JSON.stringify(value, null, 2)} as const`);
8340
+ };
8341
+ }
8342
+ function generateTestIdsModule(componentHierarchyMap, elementMetadata) {
8343
+ const pomManifest = buildPomManifest(componentHierarchyMap, elementMetadata);
8344
+ const testIdManifest = buildTestIdManifest(pomManifest);
8072
8345
  return renderSourceFile("virtual-testids.ts", (sourceFile) => {
8073
8346
  sourceFile.addStatements("// Virtual module: test id manifest");
8074
8347
  sourceFile.addVariableStatement({
@@ -8076,16 +8349,15 @@ function generateTestIdsModule(componentTestIds) {
8076
8349
  isExported: true,
8077
8350
  declarations: [{
8078
8351
  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
- }
8352
+ initializer: writeConstJson(testIdManifest)
8353
+ }]
8354
+ });
8355
+ sourceFile.addVariableStatement({
8356
+ declarationKind: VariableDeclarationKind.Const,
8357
+ isExported: true,
8358
+ declarations: [{
8359
+ name: "pomManifest",
8360
+ initializer: writeConstJson(pomManifest)
8089
8361
  }]
8090
8362
  });
8091
8363
  sourceFile.addTypeAlias({
@@ -8098,27 +8370,67 @@ function generateTestIdsModule(componentTestIds) {
8098
8370
  name: "ComponentName",
8099
8371
  type: "keyof TestIdManifest"
8100
8372
  });
8373
+ sourceFile.addTypeAlias({
8374
+ isExported: true,
8375
+ name: "PomManifest",
8376
+ type: "typeof pomManifest"
8377
+ });
8378
+ sourceFile.addTypeAlias({
8379
+ isExported: true,
8380
+ name: "PomManifestComponentName",
8381
+ type: "keyof PomManifest"
8382
+ });
8101
8383
  });
8102
8384
  }
8103
- const VIRTUAL_ID = "virtual:testids";
8104
- const RESOLVED_ID = `\0${VIRTUAL_ID}`;
8105
- function createTestIdsVirtualModulesPlugin(componentTestIds) {
8385
+ function generatePomManifestModule(componentHierarchyMap, elementMetadata) {
8386
+ const pomManifest = buildPomManifest(componentHierarchyMap, elementMetadata);
8387
+ return renderSourceFile("virtual-pom-manifest.ts", (sourceFile) => {
8388
+ sourceFile.addStatements("// Virtual module: richer POM discoverability manifest");
8389
+ sourceFile.addVariableStatement({
8390
+ declarationKind: VariableDeclarationKind.Const,
8391
+ isExported: true,
8392
+ declarations: [{
8393
+ name: "pomManifest",
8394
+ initializer: writeConstJson(pomManifest)
8395
+ }]
8396
+ });
8397
+ sourceFile.addTypeAlias({
8398
+ isExported: true,
8399
+ name: "PomManifest",
8400
+ type: "typeof pomManifest"
8401
+ });
8402
+ sourceFile.addTypeAlias({
8403
+ isExported: true,
8404
+ name: "PomManifestComponentName",
8405
+ type: "keyof PomManifest"
8406
+ });
8407
+ });
8408
+ }
8409
+ const TEST_IDS_VIRTUAL_ID = "virtual:testids";
8410
+ const TEST_IDS_RESOLVED_ID = `\0${TEST_IDS_VIRTUAL_ID}`;
8411
+ const POM_MANIFEST_VIRTUAL_ID = "virtual:pom-manifest";
8412
+ const POM_MANIFEST_RESOLVED_ID = `\0${POM_MANIFEST_VIRTUAL_ID}`;
8413
+ function createTestIdsVirtualModulesPlugin(componentHierarchyMap, elementMetadata) {
8106
8414
  return {
8107
8415
  name: "vue-pom-generator:virtual-testids",
8108
8416
  resolveId(id) {
8109
- if (id === VIRTUAL_ID)
8110
- return RESOLVED_ID;
8417
+ if (id === TEST_IDS_VIRTUAL_ID)
8418
+ return TEST_IDS_RESOLVED_ID;
8419
+ if (id === POM_MANIFEST_VIRTUAL_ID)
8420
+ return POM_MANIFEST_RESOLVED_ID;
8111
8421
  },
8112
8422
  load(id) {
8113
- if (id === RESOLVED_ID)
8114
- return generateTestIdsModule(componentTestIds);
8423
+ if (id === TEST_IDS_RESOLVED_ID)
8424
+ return generateTestIdsModule(componentHierarchyMap, elementMetadata);
8425
+ if (id === POM_MANIFEST_RESOLVED_ID)
8426
+ return generatePomManifestModule(componentHierarchyMap, elementMetadata);
8115
8427
  }
8116
8428
  };
8117
8429
  }
8118
8430
  function createSupportPlugins(options) {
8119
8431
  const {
8120
- componentTestIds,
8121
8432
  componentHierarchyMap,
8433
+ elementMetadata,
8122
8434
  vueFilesPathMap,
8123
8435
  nativeWrappers,
8124
8436
  excludedComponents,
@@ -8204,7 +8516,7 @@ function createSupportPlugins(options) {
8204
8516
  getResolvedRouterEntry: resolveRouterEntry2,
8205
8517
  loggerRef
8206
8518
  });
8207
- const virtualModules = createTestIdsVirtualModulesPlugin(componentTestIds);
8519
+ const virtualModules = createTestIdsVirtualModulesPlugin(componentHierarchyMap, elementMetadata);
8208
8520
  return [tsProcessor, devProcessor, virtualModules];
8209
8521
  }
8210
8522
  function findDataTestIdProp(element, attributeName = "data-testid") {
@@ -8260,6 +8572,33 @@ function parseDynamicProps(dynamicProps) {
8260
8572
  }
8261
8573
  return void 0;
8262
8574
  }
8575
+ function findStaticAttributeValue(element, attributeName) {
8576
+ const attribute = element.props.find(
8577
+ (prop) => prop.type === NodeTypes.ATTRIBUTE && prop.name === attributeName
8578
+ );
8579
+ const value = attribute?.value?.content?.trim();
8580
+ return value ? value : void 0;
8581
+ }
8582
+ function collectStaticTextContent(children) {
8583
+ const parts = [];
8584
+ const visit = (nodes) => {
8585
+ for (const node of nodes) {
8586
+ if (node.type === NodeTypes.TEXT) {
8587
+ const text2 = node.content.replace(/\s+/g, " ").trim();
8588
+ if (text2) {
8589
+ parts.push(text2);
8590
+ }
8591
+ continue;
8592
+ }
8593
+ if (node.type === NodeTypes.ELEMENT) {
8594
+ visit(node.children);
8595
+ }
8596
+ }
8597
+ };
8598
+ visit(children);
8599
+ const text = parts.join(" ").replace(/\s+/g, " ").trim();
8600
+ return text || void 0;
8601
+ }
8263
8602
  function tryCreateElementMetadata(args) {
8264
8603
  const { element, semanticNameMap } = args;
8265
8604
  const testIdAttribute = (args.testIdAttribute ?? "data-testid").trim() || "data-testid";
@@ -8283,13 +8622,9 @@ function tryCreateElementMetadata(args) {
8283
8622
  dynamicPropsList = [content];
8284
8623
  }
8285
8624
  }
8286
- const semanticName = semanticNameMap.get(testId);
8287
- if (!semanticName) {
8288
- return null;
8289
- }
8290
8625
  const metadata = {
8291
8626
  testId,
8292
- semanticName,
8627
+ semanticName: semanticNameMap.get(testId),
8293
8628
  tag: element.tag,
8294
8629
  tagType: element.tagType,
8295
8630
  patchFlag,
@@ -8298,7 +8633,11 @@ function tryCreateElementMetadata(args) {
8298
8633
  hasClickHandler: patchFlag ? Boolean(patchFlag & 32) : void 0,
8299
8634
  hasDynamicClass: patchFlag ? Boolean(patchFlag & 2) : void 0,
8300
8635
  hasDynamicStyle: patchFlag ? Boolean(patchFlag & 4) : void 0,
8301
- hasDynamicText: patchFlag ? Boolean(patchFlag & 1) : void 0
8636
+ hasDynamicText: patchFlag ? Boolean(patchFlag & 1) : void 0,
8637
+ staticAriaLabel: findStaticAttributeValue(element, "aria-label"),
8638
+ staticRole: findStaticAttributeValue(element, "role"),
8639
+ staticTitle: findStaticAttributeValue(element, "title"),
8640
+ staticTextContent: collectStaticTextContent(element.children)
8302
8641
  };
8303
8642
  return metadata;
8304
8643
  }
@@ -8350,6 +8689,7 @@ function extractMetadataAfterTransform(ast, componentName, elementMetadata, sema
8350
8689
  if (componentMetadata.size > 0) {
8351
8690
  elementMetadata.set(componentName, componentMetadata);
8352
8691
  }
8692
+ return componentMetadata;
8353
8693
  }
8354
8694
  function createVuePluginWithTestIds(options) {
8355
8695
  const {
@@ -8364,11 +8704,13 @@ function createVuePluginWithTestIds(options) {
8364
8704
  excludedComponents,
8365
8705
  getViewsDirAbs,
8366
8706
  testIdAttribute,
8707
+ accessibilityAudit,
8367
8708
  loggerRef,
8368
8709
  getSourceDirs,
8369
8710
  getWrapperSearchRoots,
8370
8711
  getProjectRoot
8371
8712
  } = options;
8713
+ const lastAccessibilityWarningSignatureByComponent = /* @__PURE__ */ new Map();
8372
8714
  const getComponentNameFromPath = (filename) => {
8373
8715
  return resolveComponentNameFromPath({
8374
8716
  filename,
@@ -8441,13 +8783,37 @@ function createVuePluginWithTestIds(options) {
8441
8783
  )
8442
8784
  );
8443
8785
  return () => {
8444
- extractMetadataAfterTransform(
8786
+ const componentMetadata = extractMetadataAfterTransform(
8445
8787
  node,
8446
8788
  componentName,
8447
8789
  elementMetadata,
8448
8790
  semanticNameMap,
8449
8791
  testIdAttribute
8450
8792
  );
8793
+ if (!accessibilityAudit) {
8794
+ return;
8795
+ }
8796
+ const dependencies = componentHierarchyMap.get(componentName);
8797
+ if (!dependencies || componentMetadata.size === 0) {
8798
+ return;
8799
+ }
8800
+ const manifest = buildPomManifest(
8801
+ /* @__PURE__ */ new Map([[componentName, dependencies]]),
8802
+ /* @__PURE__ */ new Map([[componentName, componentMetadata]])
8803
+ );
8804
+ const warnings = collectAccessibilityReviewWarnings(manifest);
8805
+ const signature = warnings.join("\n");
8806
+ if (!signature) {
8807
+ lastAccessibilityWarningSignatureByComponent.delete(componentName);
8808
+ return;
8809
+ }
8810
+ if (lastAccessibilityWarningSignatureByComponent.get(componentName) === signature) {
8811
+ return;
8812
+ }
8813
+ lastAccessibilityWarningSignatureByComponent.set(componentName, signature);
8814
+ for (const warning of warnings) {
8815
+ loggerRef.current.warn(warning);
8816
+ }
8451
8817
  };
8452
8818
  }
8453
8819
  let transform = perFileTransform.get(componentName);
@@ -8694,7 +9060,6 @@ function getSharedGeneratorState(key) {
8694
9060
  let state = sharedGeneratorStateRegistry.get(key);
8695
9061
  if (!state) {
8696
9062
  state = {
8697
- componentTestIds: /* @__PURE__ */ new Map(),
8698
9063
  elementMetadata: /* @__PURE__ */ new Map(),
8699
9064
  semanticNameMap: /* @__PURE__ */ new Map(),
8700
9065
  componentHierarchyMap: /* @__PURE__ */ new Map(),
@@ -8813,6 +9178,7 @@ function createVuePomGeneratorPlugins(options = {}) {
8813
9178
  nameCollisionBehavior: generationOptions?.nameCollisionBehavior,
8814
9179
  existingIdBehavior: resolvedInjectionOptions.existingIdBehavior,
8815
9180
  testIdAttribute,
9181
+ accessibilityAudit: generationOptions?.accessibilityAudit,
8816
9182
  routerAwarePoms: typeof routerEntry === "string" && routerEntry.trim().length > 0 || routerType === "nuxt",
8817
9183
  routerEntry,
8818
9184
  routerType,
@@ -8893,7 +9259,7 @@ function createVuePomGeneratorPlugins(options = {}) {
8893
9259
  };
8894
9260
  const getViewsDirAbs = () => resolveFromProjectRoot(projectRootRef.current, getViewsDir());
8895
9261
  const getWrapperSearchRootsAbs = () => getWrapperSearchRoots().map((root) => resolveFromProjectRoot(projectRootRef.current, root));
8896
- const { componentTestIds, elementMetadata, semanticNameMap, componentHierarchyMap, vueFilesPathMap } = sharedState;
9262
+ const { elementMetadata, semanticNameMap, componentHierarchyMap, vueFilesPathMap } = sharedState;
8897
9263
  const { metadataCollectorPlugin, internalVuePlugin, templateCompilerOptions } = createVuePluginWithTestIds({
8898
9264
  vueOptions,
8899
9265
  existingIdBehavior: resolvedGenerationOptions.existingIdBehavior,
@@ -8906,6 +9272,7 @@ function createVuePomGeneratorPlugins(options = {}) {
8906
9272
  excludedComponents,
8907
9273
  getViewsDirAbs,
8908
9274
  testIdAttribute,
9275
+ accessibilityAudit: resolvedGenerationOptions.accessibilityAudit,
8909
9276
  loggerRef,
8910
9277
  getSourceDirs,
8911
9278
  getWrapperSearchRoots: getWrapperSearchRootsAbs,
@@ -8913,8 +9280,8 @@ function createVuePomGeneratorPlugins(options = {}) {
8913
9280
  });
8914
9281
  templateCompilerOptionsForResolvedPlugin = templateCompilerOptions;
8915
9282
  const supportPlugins = createSupportPlugins({
8916
- componentTestIds,
8917
9283
  componentHierarchyMap,
9284
+ elementMetadata,
8918
9285
  vueFilesPathMap,
8919
9286
  nativeWrappers,
8920
9287
  excludedComponents,
@@ -8941,7 +9308,7 @@ function createVuePomGeneratorPlugins(options = {}) {
8941
9308
  ...supportPlugins
8942
9309
  ];
8943
9310
  if (!generationEnabled) {
8944
- const virtualModules = createTestIdsVirtualModulesPlugin(componentTestIds);
9311
+ const virtualModules = createTestIdsVirtualModulesPlugin(componentHierarchyMap, elementMetadata);
8945
9312
  return [
8946
9313
  configPlugin,
8947
9314
  metadataCollectorPlugin,