@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.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(
|
|
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
|
|
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
|
|
704
|
-
writer.writeLine(
|
|
705
|
-
writer.writeLine("await this.
|
|
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
|
|
751
|
-
const propertyName =
|
|
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(
|
|
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(`
|
|
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
|
|
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 =
|
|
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 =
|
|
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 (!
|
|
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(${
|
|
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
|
|
8071
|
-
|
|
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: (
|
|
8080
|
-
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
|
|
8086
|
-
|
|
8087
|
-
|
|
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
|
-
|
|
8104
|
-
const
|
|
8105
|
-
|
|
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 ===
|
|
8110
|
-
return
|
|
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 ===
|
|
8114
|
-
return generateTestIdsModule(
|
|
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(
|
|
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 {
|
|
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(
|
|
9311
|
+
const virtualModules = createTestIdsVirtualModulesPlugin(componentHierarchyMap, elementMetadata);
|
|
8945
9312
|
return [
|
|
8946
9313
|
configPlugin,
|
|
8947
9314
|
metadataCollectorPlugin,
|