@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.
- package/README.md +38 -7
- package/RELEASE_NOTES.md +28 -72
- package/class-generation/base-page.ts +43 -12
- package/class-generation/index.ts +19 -18
- package/dist/accessibility-audit.d.ts +20 -0
- package/dist/accessibility-audit.d.ts.map +1 -0
- package/dist/class-generation/base-page.d.ts +7 -4
- package/dist/class-generation/base-page.d.ts.map +1 -1
- package/dist/class-generation/index.d.ts.map +1 -1
- package/dist/compiler-metadata-utils.d.ts.map +1 -1
- package/dist/index.cjs +451 -84
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +451 -84
- package/dist/index.mjs.map +1 -1
- package/dist/manifest-generator.d.ts +37 -1
- package/dist/manifest-generator.d.ts.map +1 -1
- package/dist/metadata-collector.d.ts +5 -1
- package/dist/metadata-collector.d.ts.map +1 -1
- package/dist/method-generation.d.ts +2 -2
- package/dist/method-generation.d.ts.map +1 -1
- package/dist/plugin/create-vue-pom-generator-plugins.d.ts.map +1 -1
- package/dist/plugin/resolved-generation-options.d.ts +1 -0
- package/dist/plugin/resolved-generation-options.d.ts.map +1 -1
- package/dist/plugin/support/virtual-modules.d.ts +3 -1
- package/dist/plugin/support/virtual-modules.d.ts.map +1 -1
- package/dist/plugin/support-plugins.d.ts +2 -1
- package/dist/plugin/support-plugins.d.ts.map +1 -1
- package/dist/plugin/types.d.ts +7 -0
- package/dist/plugin/types.d.ts.map +1 -1
- package/dist/plugin/vue-plugin.d.ts +1 -0
- package/dist/plugin/vue-plugin.d.ts.map +1 -1
- package/dist/pom-discoverability.d.ts +10 -0
- package/dist/pom-discoverability.d.ts.map +1 -0
- package/dist/tests/accessibility-audit.test.d.ts +2 -0
- package/dist/tests/accessibility-audit.test.d.ts.map +1 -0
- package/dist/tests/fixtures/generated-tsc/base-page.full.d.ts +7 -4
- package/dist/tests/fixtures/generated-tsc/base-page.full.d.ts.map +1 -1
- package/dist/tests/resolved-generation-options.test.d.ts +2 -0
- package/dist/tests/resolved-generation-options.test.d.ts.map +1 -0
- 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(
|
|
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
|
|
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
|
|
745
|
-
writer.writeLine(
|
|
746
|
-
writer.writeLine("await this.
|
|
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
|
|
792
|
-
const propertyName =
|
|
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(
|
|
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(`
|
|
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
|
|
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 =
|
|
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 =
|
|
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 (!
|
|
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(${
|
|
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
|
|
8112
|
-
|
|
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: (
|
|
8121
|
-
|
|
8122
|
-
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8126
|
-
|
|
8127
|
-
|
|
8128
|
-
|
|
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
|
-
|
|
8145
|
-
const
|
|
8146
|
-
|
|
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 ===
|
|
8151
|
-
return
|
|
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 ===
|
|
8155
|
-
return generateTestIdsModule(
|
|
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(
|
|
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 {
|
|
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(
|
|
9352
|
+
const virtualModules = createTestIdsVirtualModulesPlugin(componentHierarchyMap, elementMetadata);
|
|
8986
9353
|
return [
|
|
8987
9354
|
configPlugin,
|
|
8988
9355
|
metadataCollectorPlugin,
|