aria-ease 6.6.0 → 6.8.0
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 +81 -16
- package/bin/{chunk-LKN5PRYD.js → chunk-2TOYEY5L.js} +87 -35
- package/bin/chunk-VPBHLMAS.js +127 -0
- package/bin/cli.cjs +380 -231
- package/bin/cli.js +8 -123
- package/bin/configLoader-XRF6VM4J.js +7 -0
- package/{dist/contractTestRunnerPlaywright-PC6JOYYV.js → bin/contractTestRunnerPlaywright-UAOFNS7Z.js} +98 -59
- package/bin/{test-LP723IXM.js → test-WRIJHN6H.js} +65 -24
- package/dist/{chunk-LKN5PRYD.js → chunk-2TOYEY5L.js} +87 -35
- package/dist/configLoader-IT4PWCJB.js +128 -0
- package/{bin/contractTestRunnerPlaywright-PC6JOYYV.js → dist/contractTestRunnerPlaywright-UAOFNS7Z.js} +98 -59
- package/dist/index.cjs +404 -125
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +83 -29
- package/dist/src/menu/index.cjs +18 -5
- package/dist/src/menu/index.js +18 -5
- package/dist/src/utils/test/aria-contracts/accordion/accordion.contract.json +13 -19
- package/dist/src/utils/test/aria-contracts/combobox/combobox.listbox.contract.json +5 -5
- package/dist/src/utils/test/aria-contracts/menu/menu.contract.json +45 -20
- package/dist/src/utils/test/aria-contracts/tabs/tabs.contract.json +3 -3
- package/dist/src/utils/test/{chunk-LKN5PRYD.js → chunk-2TOYEY5L.js} +85 -36
- package/dist/src/utils/test/configLoader-LD4RV2WQ.js +126 -0
- package/dist/src/utils/test/{contractTestRunnerPlaywright-RGKMGXND.js → contractTestRunnerPlaywright-IRJOAEMT.js} +94 -58
- package/dist/src/utils/test/index.cjs +380 -119
- package/dist/src/utils/test/index.d.cts +7 -1
- package/dist/src/utils/test/index.d.ts +7 -1
- package/dist/src/utils/test/index.js +61 -23
- package/package.json +1 -1
package/bin/cli.cjs
CHANGED
|
@@ -31,6 +31,140 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
31
31
|
mod
|
|
32
32
|
));
|
|
33
33
|
|
|
34
|
+
// src/utils/cli/configLoader.ts
|
|
35
|
+
var configLoader_exports = {};
|
|
36
|
+
__export(configLoader_exports, {
|
|
37
|
+
loadConfig: () => loadConfig
|
|
38
|
+
});
|
|
39
|
+
function validateConfig(config) {
|
|
40
|
+
const errors = [];
|
|
41
|
+
if (!config || typeof config !== "object") {
|
|
42
|
+
errors.push("Config must be an object");
|
|
43
|
+
return { valid: false, errors };
|
|
44
|
+
}
|
|
45
|
+
const cfg = config;
|
|
46
|
+
if (cfg.audit !== void 0) {
|
|
47
|
+
if (typeof cfg.audit !== "object" || cfg.audit === null) {
|
|
48
|
+
errors.push("audit must be an object");
|
|
49
|
+
} else {
|
|
50
|
+
if (cfg.audit.urls !== void 0) {
|
|
51
|
+
if (!Array.isArray(cfg.audit.urls)) {
|
|
52
|
+
errors.push("audit.urls must be an array");
|
|
53
|
+
} else if (cfg.audit.urls.some((url) => typeof url !== "string")) {
|
|
54
|
+
errors.push("audit.urls must contain only strings");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (cfg.audit.output !== void 0) {
|
|
58
|
+
if (typeof cfg.audit.output !== "object") {
|
|
59
|
+
errors.push("audit.output must be an object");
|
|
60
|
+
} else {
|
|
61
|
+
const output = cfg.audit.output;
|
|
62
|
+
if (output.format !== void 0) {
|
|
63
|
+
if (!["json", "csv", "html", "all"].includes(output.format)) {
|
|
64
|
+
errors.push("audit.output.format must be one of: json, csv, html, all");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (output.out !== void 0 && typeof output.out !== "string") {
|
|
68
|
+
errors.push("audit.output.out must be a string");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (cfg.test !== void 0) {
|
|
75
|
+
if (typeof cfg.test !== "object" || cfg.test === null) {
|
|
76
|
+
errors.push("test must be an object");
|
|
77
|
+
} else {
|
|
78
|
+
if (cfg.test.components !== void 0) {
|
|
79
|
+
if (!Array.isArray(cfg.test.components)) {
|
|
80
|
+
errors.push("test.components must be an array");
|
|
81
|
+
} else {
|
|
82
|
+
cfg.test.components.forEach((comp, idx) => {
|
|
83
|
+
if (typeof comp !== "object" || comp === null) {
|
|
84
|
+
errors.push(`test.components[${idx}] must be an object`);
|
|
85
|
+
} else {
|
|
86
|
+
if (typeof comp.name !== "string") {
|
|
87
|
+
errors.push(`test.components[${idx}].name must be a string`);
|
|
88
|
+
}
|
|
89
|
+
if (comp.path !== void 0 && typeof comp.path !== "string") {
|
|
90
|
+
errors.push(`test.components[${idx}].path must be a string when provided`);
|
|
91
|
+
}
|
|
92
|
+
if (comp.strictness !== void 0 && !["minimal", "balanced", "strict", "paranoid"].includes(comp.strictness)) {
|
|
93
|
+
errors.push(`test.components[${idx}].strictness must be one of: minimal, balanced, strict, paranoid`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (cfg.test.strictness !== void 0) {
|
|
100
|
+
if (!["minimal", "balanced", "strict", "paranoid"].includes(cfg.test.strictness)) {
|
|
101
|
+
errors.push("test.strictness must be one of: minimal, balanced, strict, paranoid");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return { valid: errors.length === 0, errors };
|
|
107
|
+
}
|
|
108
|
+
async function loadConfigFile(filePath) {
|
|
109
|
+
try {
|
|
110
|
+
const ext = import_path.default.extname(filePath);
|
|
111
|
+
if (ext === ".json") {
|
|
112
|
+
const content = await import_fs_extra.default.readFile(filePath, "utf-8");
|
|
113
|
+
return JSON.parse(content);
|
|
114
|
+
} else if ([".js", ".mjs", ".cjs", ".ts"].includes(ext)) {
|
|
115
|
+
const imported = await import(filePath);
|
|
116
|
+
return imported.default || imported;
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
} catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async function loadConfig(cwd = process.cwd()) {
|
|
124
|
+
const configNames = [
|
|
125
|
+
"ariaease.config.js",
|
|
126
|
+
"ariaease.config.mjs",
|
|
127
|
+
"ariaease.config.cjs",
|
|
128
|
+
"ariaease.config.json",
|
|
129
|
+
"ariaease.config.ts"
|
|
130
|
+
];
|
|
131
|
+
let loadedConfig = null;
|
|
132
|
+
let foundPath = null;
|
|
133
|
+
const errors = [];
|
|
134
|
+
for (const name of configNames) {
|
|
135
|
+
const configPath = import_path.default.resolve(cwd, name);
|
|
136
|
+
if (await import_fs_extra.default.pathExists(configPath)) {
|
|
137
|
+
foundPath = configPath;
|
|
138
|
+
loadedConfig = await loadConfigFile(configPath);
|
|
139
|
+
if (loadedConfig === null) {
|
|
140
|
+
errors.push(`Found config at ${name} but failed to load it. Check for syntax errors.`);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const validation = validateConfig(loadedConfig);
|
|
144
|
+
if (!validation.valid) {
|
|
145
|
+
errors.push(`Config validation failed in ${name}:`);
|
|
146
|
+
errors.push(...validation.errors.map((err) => ` - ${err}`));
|
|
147
|
+
loadedConfig = null;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
config: loadedConfig || {},
|
|
155
|
+
configPath: loadedConfig ? foundPath : null,
|
|
156
|
+
errors
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
var import_path, import_fs_extra;
|
|
160
|
+
var init_configLoader = __esm({
|
|
161
|
+
"src/utils/cli/configLoader.ts"() {
|
|
162
|
+
"use strict";
|
|
163
|
+
import_path = __toESM(require("path"), 1);
|
|
164
|
+
import_fs_extra = __toESM(require("fs-extra"), 1);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
34
168
|
// src/utils/cli/badgeHelper.ts
|
|
35
169
|
var badgeHelper_exports = {};
|
|
36
170
|
__export(badgeHelper_exports, {
|
|
@@ -432,10 +566,11 @@ var init_ContractReporter = __esm({
|
|
|
432
566
|
componentName = "";
|
|
433
567
|
staticPasses = 0;
|
|
434
568
|
staticFailures = 0;
|
|
569
|
+
staticWarnings = 0;
|
|
435
570
|
dynamicResults = [];
|
|
436
571
|
totalTests = 0;
|
|
437
572
|
skipped = 0;
|
|
438
|
-
|
|
573
|
+
warnings = 0;
|
|
439
574
|
isPlaywright = false;
|
|
440
575
|
apgUrl = "https://www.w3.org/WAI/ARIA/apg/";
|
|
441
576
|
hasPrintedStaticSection = false;
|
|
@@ -462,23 +597,27 @@ ${"\u2550".repeat(60)}`);
|
|
|
462
597
|
this.log(`${"\u2550".repeat(60)}
|
|
463
598
|
`);
|
|
464
599
|
}
|
|
465
|
-
reportStatic(passes, failures) {
|
|
600
|
+
reportStatic(passes, failures, warnings = 0) {
|
|
466
601
|
this.staticPasses = passes;
|
|
467
602
|
this.staticFailures = failures;
|
|
603
|
+
this.staticWarnings = warnings;
|
|
468
604
|
}
|
|
469
605
|
/**
|
|
470
606
|
* Report individual static test pass
|
|
471
607
|
*/
|
|
472
|
-
reportStaticTest(description,
|
|
608
|
+
reportStaticTest(description, status, failureMessage, level) {
|
|
473
609
|
if (!this.hasPrintedStaticSection) {
|
|
474
610
|
this.log(`${"\u2500".repeat(60)}`);
|
|
475
611
|
this.log(`\u{1F9EA} Static Assertions`);
|
|
476
612
|
this.log(`${"\u2500".repeat(60)}`);
|
|
477
613
|
this.hasPrintedStaticSection = true;
|
|
478
614
|
}
|
|
479
|
-
const icon =
|
|
615
|
+
const icon = status === "pass" ? "\u2713" : status === "warn" ? "\u26A0" : status === "skip" ? "\u25CB" : "\u2717";
|
|
480
616
|
this.log(` ${icon} ${description}`);
|
|
481
|
-
if (
|
|
617
|
+
if (level) {
|
|
618
|
+
this.log(` \u21B3 level=${level}`);
|
|
619
|
+
}
|
|
620
|
+
if ((status === "fail" || status === "warn" || status === "skip") && failureMessage) {
|
|
482
621
|
this.log(` \u21B3 ${failureMessage}`);
|
|
483
622
|
}
|
|
484
623
|
}
|
|
@@ -497,23 +636,26 @@ ${"\u2550".repeat(60)}`);
|
|
|
497
636
|
description: test.description,
|
|
498
637
|
status,
|
|
499
638
|
failureMessage,
|
|
500
|
-
|
|
639
|
+
level: test.level
|
|
501
640
|
};
|
|
502
641
|
if (status === "skip") {
|
|
503
642
|
result.skipReason = "Requires real browser (addEventListener events)";
|
|
504
643
|
}
|
|
505
644
|
this.dynamicResults.push(result);
|
|
506
|
-
const icons = { pass: "\u2713", fail: "\u2717",
|
|
507
|
-
const
|
|
508
|
-
this.log(` ${icons[status]} ${
|
|
645
|
+
const icons = { pass: "\u2713", fail: "\u2717", warn: "\u26A0", skip: "\u25CB" };
|
|
646
|
+
const levelPrefix = test.level ? `[${test.level.toUpperCase()}] ` : "";
|
|
647
|
+
this.log(` ${icons[status]} ${levelPrefix}${test.description}`);
|
|
509
648
|
if (status === "skip" && !this.isPlaywright) {
|
|
510
649
|
this.log(` \u21B3 Skipped in jsdom (runs in Playwright)`);
|
|
511
650
|
}
|
|
512
|
-
if (status === "fail" && failureMessage
|
|
651
|
+
if (status === "fail" && failureMessage) {
|
|
513
652
|
this.log(` \u21B3 ${failureMessage}`);
|
|
514
653
|
}
|
|
515
|
-
if (status === "
|
|
516
|
-
this.log(` \u21B3
|
|
654
|
+
if (status === "warn" && failureMessage) {
|
|
655
|
+
this.log(` \u21B3 ${failureMessage}`);
|
|
656
|
+
}
|
|
657
|
+
if (status === "skip" && failureMessage) {
|
|
658
|
+
this.log(` \u21B3 ${failureMessage}`);
|
|
517
659
|
}
|
|
518
660
|
}
|
|
519
661
|
/**
|
|
@@ -537,29 +679,29 @@ ${"\u2500".repeat(60)}`);
|
|
|
537
679
|
this.log("");
|
|
538
680
|
});
|
|
539
681
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
reportOptionalSuggestions() {
|
|
544
|
-
const suggestions = this.dynamicResults.filter((r) => r.status === "optional-fail");
|
|
545
|
-
if (suggestions.length === 0) return;
|
|
682
|
+
reportWarnings() {
|
|
683
|
+
const warnings = this.dynamicResults.filter((r) => r.status === "warn");
|
|
684
|
+
if (warnings.length === 0 && this.staticWarnings === 0) return;
|
|
546
685
|
this.log(`
|
|
547
686
|
${"\u2500".repeat(60)}`);
|
|
548
|
-
this.log(`\
|
|
687
|
+
this.log(`\u26A0\uFE0F Warnings (${this.staticWarnings + warnings.length}):
|
|
549
688
|
`);
|
|
550
|
-
this.log(`These
|
|
551
|
-
this.log(`for improved user experience and keyboard interaction:
|
|
689
|
+
this.log(`These checks are failing but treated as warnings under the active strictness mode.
|
|
552
690
|
`);
|
|
553
|
-
|
|
691
|
+
warnings.forEach((test, index) => {
|
|
554
692
|
this.log(`${index + 1}. ${test.description}`);
|
|
555
693
|
if (test.failureMessage) {
|
|
556
694
|
this.log(` \u21B3 ${test.failureMessage}`);
|
|
557
695
|
}
|
|
696
|
+
if (test.level) {
|
|
697
|
+
this.log(` \u21B3 level=${test.level}`);
|
|
698
|
+
}
|
|
558
699
|
});
|
|
559
|
-
this.
|
|
560
|
-
|
|
561
|
-
|
|
700
|
+
if (this.apgUrl) {
|
|
701
|
+
this.log(`
|
|
702
|
+
Reference: ${this.apgUrl}
|
|
562
703
|
`);
|
|
704
|
+
}
|
|
563
705
|
}
|
|
564
706
|
/**
|
|
565
707
|
* Report skipped tests with helpful context
|
|
@@ -589,41 +731,42 @@ ${"\u2500".repeat(60)}`);
|
|
|
589
731
|
const duration = Date.now() - this.startTime;
|
|
590
732
|
const dynamicPasses = this.dynamicResults.filter((r) => r.status === "pass").length;
|
|
591
733
|
const dynamicFailures = this.dynamicResults.filter((r) => r.status === "fail").length;
|
|
734
|
+
const dynamicWarnings = this.dynamicResults.filter((r) => r.status === "warn").length;
|
|
592
735
|
this.skipped = this.dynamicResults.filter((r) => r.status === "skip").length;
|
|
593
|
-
this.
|
|
736
|
+
this.warnings = this.staticWarnings + dynamicWarnings;
|
|
594
737
|
const totalPasses = this.staticPasses + dynamicPasses;
|
|
595
738
|
const totalFailures = this.staticFailures + dynamicFailures;
|
|
596
|
-
const totalRun = totalPasses + totalFailures;
|
|
739
|
+
const totalRun = totalPasses + totalFailures + this.warnings;
|
|
597
740
|
if (failures.length > 0) {
|
|
598
741
|
this.reportFailures(failures);
|
|
599
742
|
}
|
|
600
|
-
this.
|
|
743
|
+
this.reportWarnings();
|
|
601
744
|
this.reportSkipped();
|
|
602
745
|
this.log(`
|
|
603
746
|
${"\u2550".repeat(60)}`);
|
|
604
747
|
this.log(`\u{1F4CA} Summary
|
|
605
748
|
`);
|
|
606
|
-
if (totalFailures === 0 && this.skipped === 0 && this.
|
|
749
|
+
if (totalFailures === 0 && this.skipped === 0 && this.warnings === 0) {
|
|
607
750
|
this.log(`\u2705 All ${totalRun} tests passed!`);
|
|
608
751
|
this.log(` ${this.componentName.charAt(0).toUpperCase()}${this.componentName.slice(1)} component meets WAI-ARIA expectations for Roles, States, Properties, and Keyboard Interactions \u2713`);
|
|
609
752
|
} else if (totalFailures === 0) {
|
|
610
|
-
this.log(`\u2705 ${totalPasses}/${totalRun}
|
|
753
|
+
this.log(`\u2705 ${totalPasses}/${totalRun} tests passed`);
|
|
611
754
|
if (this.skipped > 0) {
|
|
612
755
|
this.log(`\u25CB ${this.skipped} tests skipped`);
|
|
613
756
|
}
|
|
614
|
-
if (this.
|
|
615
|
-
this.log(`\
|
|
757
|
+
if (this.warnings > 0) {
|
|
758
|
+
this.log(`\u26A0\uFE0F ${this.warnings} warning${this.warnings > 1 ? "s" : ""}`);
|
|
616
759
|
}
|
|
617
760
|
this.log(` ${this.componentName.charAt(0).toUpperCase()}${this.componentName.slice(1)} component meets WAI-ARIA expectations for Roles, States, Properties, and Keyboard Interactions \u2713`);
|
|
618
761
|
} else {
|
|
619
762
|
this.log(`\u274C ${totalFailures} test${totalFailures > 1 ? "s" : ""} failed`);
|
|
620
763
|
this.log(`\u2705 ${totalPasses} test${totalPasses > 1 ? "s" : ""} passed`);
|
|
764
|
+
if (this.warnings > 0) {
|
|
765
|
+
this.log(`\u26A0\uFE0F ${this.warnings} warning${this.warnings > 1 ? "s" : ""}`);
|
|
766
|
+
}
|
|
621
767
|
if (this.skipped > 0) {
|
|
622
768
|
this.log(`\u25CB ${this.skipped} test${this.skipped > 1 ? "s" : ""} skipped`);
|
|
623
769
|
}
|
|
624
|
-
if (this.optionalSuggestions > 0) {
|
|
625
|
-
this.log(`\u{1F4A1} ${this.optionalSuggestions} optional enhancement${this.optionalSuggestions > 1 ? "s" : ""} suggested`);
|
|
626
|
-
}
|
|
627
770
|
}
|
|
628
771
|
this.log(`\u23F1\uFE0F Duration: ${duration}ms`);
|
|
629
772
|
this.log(`${"\u2550".repeat(60)}
|
|
@@ -660,9 +803,56 @@ ${"\u2550".repeat(60)}`);
|
|
|
660
803
|
}
|
|
661
804
|
});
|
|
662
805
|
|
|
806
|
+
// src/utils/test/src/strictness.ts
|
|
807
|
+
function normalizeLevel(level) {
|
|
808
|
+
if (level === "required" || level === "recommended" || level === "optional") {
|
|
809
|
+
return level;
|
|
810
|
+
}
|
|
811
|
+
return FALLBACK_LEVEL;
|
|
812
|
+
}
|
|
813
|
+
function normalizeStrictness(strictness) {
|
|
814
|
+
if (strictness === "minimal" || strictness === "balanced" || strictness === "strict" || strictness === "paranoid") {
|
|
815
|
+
return strictness;
|
|
816
|
+
}
|
|
817
|
+
return "balanced";
|
|
818
|
+
}
|
|
819
|
+
function resolveEnforcement(level, strictness) {
|
|
820
|
+
const matrix = {
|
|
821
|
+
minimal: {
|
|
822
|
+
required: "error",
|
|
823
|
+
recommended: "ignore",
|
|
824
|
+
optional: "ignore"
|
|
825
|
+
},
|
|
826
|
+
balanced: {
|
|
827
|
+
required: "error",
|
|
828
|
+
recommended: "warning",
|
|
829
|
+
optional: "ignore"
|
|
830
|
+
},
|
|
831
|
+
strict: {
|
|
832
|
+
required: "error",
|
|
833
|
+
recommended: "error",
|
|
834
|
+
optional: "warning"
|
|
835
|
+
},
|
|
836
|
+
paranoid: {
|
|
837
|
+
required: "error",
|
|
838
|
+
recommended: "error",
|
|
839
|
+
optional: "error"
|
|
840
|
+
}
|
|
841
|
+
};
|
|
842
|
+
return matrix[strictness][level];
|
|
843
|
+
}
|
|
844
|
+
var FALLBACK_LEVEL;
|
|
845
|
+
var init_strictness = __esm({
|
|
846
|
+
"src/utils/test/src/strictness.ts"() {
|
|
847
|
+
"use strict";
|
|
848
|
+
FALLBACK_LEVEL = "required";
|
|
849
|
+
}
|
|
850
|
+
});
|
|
851
|
+
|
|
663
852
|
// src/utils/test/src/contractTestRunner.ts
|
|
664
|
-
async function runContractTests(componentName, component) {
|
|
853
|
+
async function runContractTests(componentName, component, strictness) {
|
|
665
854
|
const reporter = new ContractReporter(false);
|
|
855
|
+
const strictnessMode = normalizeStrictness(strictness);
|
|
666
856
|
const contractTyped = contract_default;
|
|
667
857
|
const contractPath = contractTyped[componentName]?.path;
|
|
668
858
|
if (!contractPath) {
|
|
@@ -676,19 +866,42 @@ async function runContractTests(componentName, component) {
|
|
|
676
866
|
const failures = [];
|
|
677
867
|
const passes = [];
|
|
678
868
|
const skipped = [];
|
|
679
|
-
const
|
|
869
|
+
const warnings = [];
|
|
870
|
+
const classifyFailure = (message, levelRaw) => {
|
|
871
|
+
const level = normalizeLevel(levelRaw);
|
|
872
|
+
const enforcement = resolveEnforcement(level, strictnessMode);
|
|
873
|
+
if (enforcement === "error") {
|
|
874
|
+
failures.push(message);
|
|
875
|
+
return { status: "fail", level, detail: message };
|
|
876
|
+
}
|
|
877
|
+
if (enforcement === "warning") {
|
|
878
|
+
warnings.push(message);
|
|
879
|
+
return { status: "warn", level, detail: message };
|
|
880
|
+
}
|
|
881
|
+
const ignoredMessage = `${message} (ignored by strictness=${strictnessMode}, level=${level})`;
|
|
882
|
+
skipped.push(ignoredMessage);
|
|
883
|
+
return { status: "skip", level, detail: ignoredMessage };
|
|
884
|
+
};
|
|
885
|
+
let staticPassed = 0;
|
|
886
|
+
let staticFailed = 0;
|
|
887
|
+
let staticWarnings = 0;
|
|
680
888
|
for (const test of componentContract.static[0].assertions) {
|
|
681
889
|
if (test.target !== "relative") {
|
|
890
|
+
const staticLevel = normalizeLevel(test.level);
|
|
682
891
|
const selector = componentContract.selectors[test.target];
|
|
683
892
|
if (!selector) {
|
|
684
|
-
|
|
685
|
-
|
|
893
|
+
const outcome = classifyFailure(`Selector for target ${test.target} not found.`, test.level);
|
|
894
|
+
if (outcome.status === "fail") staticFailed += 1;
|
|
895
|
+
if (outcome.status === "warn") staticWarnings += 1;
|
|
896
|
+
reporter.reportStaticTest(`${test.target} has required ARIA attributes`, outcome.status, outcome.detail, outcome.level);
|
|
686
897
|
continue;
|
|
687
898
|
}
|
|
688
899
|
const target = component.querySelector(selector);
|
|
689
900
|
if (!target) {
|
|
690
|
-
|
|
691
|
-
|
|
901
|
+
const outcome = classifyFailure(`Target ${test.target} not found.`, test.level);
|
|
902
|
+
if (outcome.status === "fail") staticFailed += 1;
|
|
903
|
+
if (outcome.status === "warn") staticWarnings += 1;
|
|
904
|
+
reporter.reportStaticTest(`${test.target} has required ARIA attributes`, outcome.status, outcome.detail, outcome.level);
|
|
692
905
|
continue;
|
|
693
906
|
}
|
|
694
907
|
const attributeValue = target.getAttribute(test.attribute);
|
|
@@ -696,31 +909,34 @@ async function runContractTests(componentName, component) {
|
|
|
696
909
|
const attributes = test.attribute.split(" | ");
|
|
697
910
|
const hasAnyAttribute = attributes.some((attr) => target.hasAttribute(attr));
|
|
698
911
|
if (!hasAnyAttribute) {
|
|
699
|
-
|
|
700
|
-
|
|
912
|
+
const outcome = classifyFailure(test.failureMessage + ` None of the attributes "${test.attribute}" are present.`, test.level);
|
|
913
|
+
if (outcome.status === "fail") staticFailed += 1;
|
|
914
|
+
if (outcome.status === "warn") staticWarnings += 1;
|
|
915
|
+
reporter.reportStaticTest(`${test.target} has ${test.attribute}`, outcome.status, outcome.detail, outcome.level);
|
|
701
916
|
} else {
|
|
702
917
|
passes.push(`At least one of the attributes "${test.attribute}" exists on the element.`);
|
|
703
|
-
|
|
918
|
+
staticPassed += 1;
|
|
919
|
+
reporter.reportStaticTest(`${test.target} has ${test.attribute}`, "pass", void 0, staticLevel);
|
|
704
920
|
}
|
|
705
921
|
} else if (!attributeValue || !test.expectedValue.split(" | ").includes(attributeValue)) {
|
|
706
|
-
|
|
707
|
-
|
|
922
|
+
const outcome = classifyFailure(test.failureMessage + ` Attribute value does not match expected value. Expected: ${test.expectedValue}, Found: ${attributeValue}`, test.level);
|
|
923
|
+
if (outcome.status === "fail") staticFailed += 1;
|
|
924
|
+
if (outcome.status === "warn") staticWarnings += 1;
|
|
925
|
+
reporter.reportStaticTest(`${test.target} has ${test.attribute}="${test.expectedValue}"`, outcome.status, outcome.detail, outcome.level);
|
|
708
926
|
} else {
|
|
709
927
|
passes.push(`Attribute value matches expected value. Expected: ${test.expectedValue}, Found: ${attributeValue}`);
|
|
710
|
-
|
|
928
|
+
staticPassed += 1;
|
|
929
|
+
reporter.reportStaticTest(`${test.target} has ${test.attribute}="${attributeValue}"`, "pass", void 0, staticLevel);
|
|
711
930
|
}
|
|
712
931
|
}
|
|
713
932
|
}
|
|
714
933
|
for (const dynamicTest of componentContract.dynamic) {
|
|
715
934
|
skipped.push(dynamicTest.description);
|
|
716
|
-
reporter.reportTest(dynamicTest, "skip");
|
|
935
|
+
reporter.reportTest({ description: dynamicTest.description, level: dynamicTest.level }, "skip");
|
|
717
936
|
}
|
|
718
|
-
|
|
719
|
-
const staticFailed = failures.length - failuresBeforeStatic;
|
|
720
|
-
const staticPassed = Math.max(0, staticTotal - staticFailed);
|
|
721
|
-
reporter.reportStatic(staticPassed, staticFailed);
|
|
937
|
+
reporter.reportStatic(staticPassed, staticFailed, staticWarnings);
|
|
722
938
|
reporter.summary(failures);
|
|
723
|
-
return { passes, failures, skipped };
|
|
939
|
+
return { passes, failures, skipped, warnings };
|
|
724
940
|
}
|
|
725
941
|
var import_promises, import_meta;
|
|
726
942
|
var init_contractTestRunner = __esm({
|
|
@@ -729,6 +945,7 @@ var init_contractTestRunner = __esm({
|
|
|
729
945
|
init_contract();
|
|
730
946
|
import_promises = __toESM(require("fs/promises"), 1);
|
|
731
947
|
init_ContractReporter();
|
|
948
|
+
init_strictness();
|
|
732
949
|
import_meta = {};
|
|
733
950
|
}
|
|
734
951
|
});
|
|
@@ -1118,9 +1335,6 @@ var init_ActionExecutor = __esm({
|
|
|
1118
1335
|
this.selectors = selectors;
|
|
1119
1336
|
this.timeoutMs = timeoutMs;
|
|
1120
1337
|
}
|
|
1121
|
-
isOptionalMenuTarget(target) {
|
|
1122
|
-
return ["submenu", "submenuTrigger", "submenuItems"].includes(target);
|
|
1123
|
-
}
|
|
1124
1338
|
/**
|
|
1125
1339
|
* Check if error is due to browser/page being closed
|
|
1126
1340
|
*/
|
|
@@ -1252,10 +1466,9 @@ var init_ActionExecutor = __esm({
|
|
|
1252
1466
|
const locator = this.page.locator(selector).first();
|
|
1253
1467
|
const elementCount = await locator.count();
|
|
1254
1468
|
if (elementCount === 0) {
|
|
1255
|
-
const optionalMenuTarget = this.isOptionalMenuTarget(target);
|
|
1256
1469
|
return {
|
|
1257
1470
|
success: false,
|
|
1258
|
-
error:
|
|
1471
|
+
error: `${target} element not found.`,
|
|
1259
1472
|
shouldBreak: true
|
|
1260
1473
|
// Signal to skip this test
|
|
1261
1474
|
};
|
|
@@ -1569,10 +1782,11 @@ var contractTestRunnerPlaywright_exports = {};
|
|
|
1569
1782
|
__export(contractTestRunnerPlaywright_exports, {
|
|
1570
1783
|
runContractTestsPlaywright: () => runContractTestsPlaywright
|
|
1571
1784
|
});
|
|
1572
|
-
async function runContractTestsPlaywright(componentName, url) {
|
|
1785
|
+
async function runContractTestsPlaywright(componentName, url, strictness) {
|
|
1573
1786
|
const reporter = new ContractReporter(true);
|
|
1574
1787
|
const actionTimeoutMs = 400;
|
|
1575
1788
|
const assertionTimeoutMs = 400;
|
|
1789
|
+
const strictnessMode = normalizeStrictness(strictness);
|
|
1576
1790
|
const contractTyped = contract_default;
|
|
1577
1791
|
const contractPath = contractTyped[componentName]?.path;
|
|
1578
1792
|
const resolvedPath = new URL(contractPath, import_meta3.url).pathname;
|
|
@@ -1581,9 +1795,25 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
1581
1795
|
const totalTests = componentContract.static[0].assertions.length + componentContract.dynamic.length;
|
|
1582
1796
|
const apgUrl = componentContract.meta?.source?.apg;
|
|
1583
1797
|
const failures = [];
|
|
1798
|
+
const warnings = [];
|
|
1584
1799
|
const passes = [];
|
|
1585
1800
|
const skipped = [];
|
|
1586
1801
|
let page = null;
|
|
1802
|
+
const classifyFailure = (message, levelRaw) => {
|
|
1803
|
+
const level = normalizeLevel(levelRaw);
|
|
1804
|
+
const enforcement = resolveEnforcement(level, strictnessMode);
|
|
1805
|
+
if (enforcement === "error") {
|
|
1806
|
+
failures.push(message);
|
|
1807
|
+
return { status: "fail", level, detail: message };
|
|
1808
|
+
}
|
|
1809
|
+
if (enforcement === "warning") {
|
|
1810
|
+
warnings.push(message);
|
|
1811
|
+
return { status: "warn", level, detail: message };
|
|
1812
|
+
}
|
|
1813
|
+
const ignoredMessage = `${message} (ignored by strictness=${strictnessMode}, level=${level})`;
|
|
1814
|
+
skipped.push(ignoredMessage);
|
|
1815
|
+
return { status: "skip", level, detail: ignoredMessage };
|
|
1816
|
+
};
|
|
1587
1817
|
try {
|
|
1588
1818
|
page = await createTestPage();
|
|
1589
1819
|
if (url) {
|
|
@@ -1629,35 +1859,37 @@ This usually means:
|
|
|
1629
1859
|
});
|
|
1630
1860
|
}
|
|
1631
1861
|
const hasSubmenuCapability = componentName === "menu" && !!componentContract.selectors.submenuTrigger ? await page.locator(componentContract.selectors.submenuTrigger).count() > 0 : false;
|
|
1862
|
+
let staticPassed = 0;
|
|
1632
1863
|
let staticFailed = 0;
|
|
1864
|
+
let staticWarnings = 0;
|
|
1633
1865
|
const staticAssertionRunner = new AssertionRunner(page, componentContract.selectors, assertionTimeoutMs);
|
|
1634
1866
|
for (const test of componentContract.static[0]?.assertions || []) {
|
|
1635
1867
|
if (test.target === "relative") continue;
|
|
1636
1868
|
const staticDescription = `${test.target}${test.attribute ? ` (${test.attribute})` : ""}`;
|
|
1869
|
+
const staticLevel = normalizeLevel(test.level);
|
|
1637
1870
|
if (componentName === "menu" && test.target === "submenuTrigger" && !hasSubmenuCapability) {
|
|
1638
|
-
|
|
1639
|
-
|
|
1871
|
+
const skipMessage = `Skipping submenu static assertion for ${test.target}: no submenu capability detected in rendered component.`;
|
|
1872
|
+
skipped.push(skipMessage);
|
|
1873
|
+
reporter.reportStaticTest(staticDescription, "skip", skipMessage, staticLevel);
|
|
1640
1874
|
continue;
|
|
1641
1875
|
}
|
|
1642
1876
|
const targetSelector = componentContract.selectors[test.target];
|
|
1643
1877
|
if (!targetSelector) {
|
|
1644
1878
|
const failure = `Selector for target ${test.target} not found.`;
|
|
1645
|
-
|
|
1646
|
-
staticFailed += 1;
|
|
1647
|
-
|
|
1879
|
+
const outcome = classifyFailure(failure, test.level);
|
|
1880
|
+
if (outcome.status === "fail") staticFailed += 1;
|
|
1881
|
+
if (outcome.status === "warn") staticWarnings += 1;
|
|
1882
|
+
reporter.reportStaticTest(staticDescription, outcome.status, outcome.detail, outcome.level);
|
|
1648
1883
|
continue;
|
|
1649
1884
|
}
|
|
1650
1885
|
const target = page.locator(targetSelector).first();
|
|
1651
1886
|
const exists = await target.count() > 0;
|
|
1652
1887
|
if (!exists) {
|
|
1653
|
-
if (test.isOptional === true) {
|
|
1654
|
-
reporter.reportStaticTest(staticDescription, true);
|
|
1655
|
-
continue;
|
|
1656
|
-
}
|
|
1657
1888
|
const failure = `Target ${test.target} not found.`;
|
|
1658
|
-
|
|
1659
|
-
staticFailed += 1;
|
|
1660
|
-
|
|
1889
|
+
const outcome = classifyFailure(failure, test.level);
|
|
1890
|
+
if (outcome.status === "fail") staticFailed += 1;
|
|
1891
|
+
if (outcome.status === "warn") staticWarnings += 1;
|
|
1892
|
+
reporter.reportStaticTest(staticDescription, outcome.status, outcome.detail, outcome.level);
|
|
1661
1893
|
continue;
|
|
1662
1894
|
}
|
|
1663
1895
|
const isRedundantCheck = (selector, attrName, expectedVal) => {
|
|
@@ -1692,19 +1924,23 @@ This usually means:
|
|
|
1692
1924
|
}
|
|
1693
1925
|
if (!hasAny && !allRedundant) {
|
|
1694
1926
|
const failure = test.failureMessage + ` None of the attributes "${test.attribute}" are present.`;
|
|
1695
|
-
|
|
1696
|
-
staticFailed += 1;
|
|
1697
|
-
|
|
1927
|
+
const outcome = classifyFailure(failure, test.level);
|
|
1928
|
+
if (outcome.status === "fail") staticFailed += 1;
|
|
1929
|
+
if (outcome.status === "warn") staticWarnings += 1;
|
|
1930
|
+
reporter.reportStaticTest(staticDescription, outcome.status, outcome.detail, outcome.level);
|
|
1698
1931
|
} else if (!allRedundant && hasAny) {
|
|
1699
1932
|
passes.push(`At least one of the attributes "${test.attribute}" exists on the element.`);
|
|
1700
|
-
|
|
1933
|
+
staticPassed += 1;
|
|
1934
|
+
reporter.reportStaticTest(staticDescription, "pass", void 0, staticLevel);
|
|
1701
1935
|
} else {
|
|
1702
|
-
|
|
1936
|
+
staticPassed += 1;
|
|
1937
|
+
reporter.reportStaticTest(staticDescription, "pass", void 0, staticLevel);
|
|
1703
1938
|
}
|
|
1704
1939
|
} else {
|
|
1705
1940
|
if (isRedundantCheck(targetSelector, test.attribute, test.expectedValue)) {
|
|
1706
1941
|
passes.push(`${test.attribute}="${test.expectedValue}" on ${test.target} verified by selector (already present in: ${targetSelector}).`);
|
|
1707
|
-
|
|
1942
|
+
staticPassed += 1;
|
|
1943
|
+
reporter.reportStaticTest(staticDescription, "pass", void 0, staticLevel);
|
|
1708
1944
|
} else {
|
|
1709
1945
|
const result = await staticAssertionRunner.validateAttribute(
|
|
1710
1946
|
target,
|
|
@@ -1716,11 +1952,13 @@ This usually means:
|
|
|
1716
1952
|
);
|
|
1717
1953
|
if (result.success && result.passMessage) {
|
|
1718
1954
|
passes.push(result.passMessage);
|
|
1719
|
-
|
|
1955
|
+
staticPassed += 1;
|
|
1956
|
+
reporter.reportStaticTest(staticDescription, "pass", void 0, staticLevel);
|
|
1720
1957
|
} else if (!result.success && result.failMessage) {
|
|
1721
|
-
|
|
1722
|
-
staticFailed += 1;
|
|
1723
|
-
|
|
1958
|
+
const outcome = classifyFailure(result.failMessage, test.level);
|
|
1959
|
+
if (outcome.status === "fail") staticFailed += 1;
|
|
1960
|
+
if (outcome.status === "warn") staticWarnings += 1;
|
|
1961
|
+
reporter.reportStaticTest(staticDescription, outcome.status, outcome.detail, outcome.level);
|
|
1724
1962
|
}
|
|
1725
1963
|
}
|
|
1726
1964
|
}
|
|
@@ -1735,6 +1973,9 @@ This usually means:
|
|
|
1735
1973
|
}
|
|
1736
1974
|
const { action, assertions } = dynamicTest;
|
|
1737
1975
|
const failuresBeforeTest = failures.length;
|
|
1976
|
+
const warningsBeforeTest = warnings.length;
|
|
1977
|
+
const skippedBeforeTest = skipped.length;
|
|
1978
|
+
const dynamicLevel = normalizeLevel(dynamicTest.level);
|
|
1738
1979
|
try {
|
|
1739
1980
|
await strategy.resetState(page);
|
|
1740
1981
|
} catch (error) {
|
|
@@ -1744,13 +1985,15 @@ This usually means:
|
|
|
1744
1985
|
}
|
|
1745
1986
|
const shouldSkipTest = await strategy.shouldSkipTest(dynamicTest, page);
|
|
1746
1987
|
if (shouldSkipTest) {
|
|
1747
|
-
|
|
1988
|
+
const skipMessage = `Skipping test - component-specific conditions not met`;
|
|
1989
|
+
skipped.push(skipMessage);
|
|
1990
|
+
reporter.reportTest({ description: dynamicTest.description, level: dynamicLevel }, "skip", skipMessage);
|
|
1748
1991
|
continue;
|
|
1749
1992
|
}
|
|
1750
1993
|
const actionExecutor = new ActionExecutor(page, componentContract.selectors, actionTimeoutMs);
|
|
1751
1994
|
const assertionRunner = new AssertionRunner(page, componentContract.selectors, assertionTimeoutMs);
|
|
1752
|
-
let shouldSkipCurrentTest = false;
|
|
1753
1995
|
let shouldAbortCurrentTest = false;
|
|
1996
|
+
let actionOutcome = null;
|
|
1754
1997
|
for (const act of action) {
|
|
1755
1998
|
if (!page || page.isClosed()) {
|
|
1756
1999
|
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
@@ -1772,27 +2015,20 @@ This usually means:
|
|
|
1772
2015
|
continue;
|
|
1773
2016
|
}
|
|
1774
2017
|
if (!result.success) {
|
|
1775
|
-
if (result.shouldBreak) {
|
|
1776
|
-
if (result.error?.includes("optional submenu test")) {
|
|
1777
|
-
reporter.reportTest(dynamicTest, "skip", result.error);
|
|
1778
|
-
shouldSkipCurrentTest = true;
|
|
1779
|
-
} else if (result.error) {
|
|
1780
|
-
failures.push(result.error);
|
|
1781
|
-
shouldAbortCurrentTest = true;
|
|
1782
|
-
}
|
|
1783
|
-
break;
|
|
1784
|
-
}
|
|
1785
2018
|
if (result.error) {
|
|
1786
|
-
|
|
2019
|
+
const outcome = classifyFailure(result.error, dynamicTest.level);
|
|
2020
|
+
actionOutcome = { status: outcome.status, detail: outcome.detail };
|
|
1787
2021
|
}
|
|
1788
|
-
|
|
2022
|
+
shouldAbortCurrentTest = true;
|
|
2023
|
+
break;
|
|
1789
2024
|
}
|
|
1790
2025
|
}
|
|
1791
|
-
if (shouldSkipCurrentTest) {
|
|
1792
|
-
continue;
|
|
1793
|
-
}
|
|
1794
2026
|
if (shouldAbortCurrentTest) {
|
|
1795
|
-
reporter.reportTest(
|
|
2027
|
+
reporter.reportTest(
|
|
2028
|
+
{ description: dynamicTest.description, level: dynamicLevel },
|
|
2029
|
+
actionOutcome?.status || "fail",
|
|
2030
|
+
actionOutcome?.detail || failures[failures.length - 1]
|
|
2031
|
+
);
|
|
1796
2032
|
continue;
|
|
1797
2033
|
}
|
|
1798
2034
|
for (const assertion of assertions) {
|
|
@@ -1800,22 +2036,39 @@ This usually means:
|
|
|
1800
2036
|
if (result.success && result.passMessage) {
|
|
1801
2037
|
passes.push(result.passMessage);
|
|
1802
2038
|
} else if (!result.success && result.failMessage) {
|
|
1803
|
-
|
|
2039
|
+
const assertionLevel = normalizeLevel(assertion.level || dynamicTest.level);
|
|
2040
|
+
const outcome = classifyFailure(result.failMessage, assertionLevel);
|
|
2041
|
+
if (outcome.status === "skip") {
|
|
2042
|
+
continue;
|
|
2043
|
+
}
|
|
1804
2044
|
}
|
|
1805
2045
|
}
|
|
1806
2046
|
const failuresAfterTest = failures.length;
|
|
1807
|
-
const
|
|
1808
|
-
const
|
|
1809
|
-
if (
|
|
1810
|
-
|
|
1811
|
-
|
|
2047
|
+
const warningsAfterTest = warnings.length;
|
|
2048
|
+
const skippedAfterTest = skipped.length;
|
|
2049
|
+
if (failuresAfterTest > failuresBeforeTest) {
|
|
2050
|
+
reporter.reportTest(
|
|
2051
|
+
{ description: dynamicTest.description, level: dynamicLevel },
|
|
2052
|
+
"fail",
|
|
2053
|
+
failures[failures.length - 1]
|
|
2054
|
+
);
|
|
2055
|
+
} else if (warningsAfterTest > warningsBeforeTest) {
|
|
2056
|
+
reporter.reportTest(
|
|
2057
|
+
{ description: dynamicTest.description, level: dynamicLevel },
|
|
2058
|
+
"warn",
|
|
2059
|
+
warnings[warnings.length - 1]
|
|
2060
|
+
);
|
|
2061
|
+
} else if (skippedAfterTest > skippedBeforeTest) {
|
|
2062
|
+
reporter.reportTest(
|
|
2063
|
+
{ description: dynamicTest.description, level: dynamicLevel },
|
|
2064
|
+
"skip",
|
|
2065
|
+
skipped[skipped.length - 1]
|
|
2066
|
+
);
|
|
1812
2067
|
} else {
|
|
1813
|
-
reporter.reportTest(dynamicTest,
|
|
2068
|
+
reporter.reportTest({ description: dynamicTest.description, level: dynamicLevel }, "pass");
|
|
1814
2069
|
}
|
|
1815
2070
|
}
|
|
1816
|
-
|
|
1817
|
-
const staticPassed = Math.max(0, staticTotal - staticFailed);
|
|
1818
|
-
reporter.reportStatic(staticPassed, staticFailed);
|
|
2071
|
+
reporter.reportStatic(staticPassed, staticFailed, staticWarnings);
|
|
1819
2072
|
reporter.summary(failures);
|
|
1820
2073
|
} catch (error) {
|
|
1821
2074
|
if (error instanceof Error) {
|
|
@@ -1840,7 +2093,7 @@ Make sure your dev server is running at ${url}`);
|
|
|
1840
2093
|
} finally {
|
|
1841
2094
|
if (page) await page.close();
|
|
1842
2095
|
}
|
|
1843
|
-
return { passes, failures, skipped };
|
|
2096
|
+
return { passes, failures, skipped, warnings };
|
|
1844
2097
|
}
|
|
1845
2098
|
var import_fs2, import_meta3;
|
|
1846
2099
|
var init_contractTestRunnerPlaywright = __esm({
|
|
@@ -1853,12 +2106,13 @@ var init_contractTestRunnerPlaywright = __esm({
|
|
|
1853
2106
|
init_ContractReporter();
|
|
1854
2107
|
init_ActionExecutor();
|
|
1855
2108
|
init_AssertionRunner();
|
|
2109
|
+
init_strictness();
|
|
1856
2110
|
import_meta3 = {};
|
|
1857
2111
|
}
|
|
1858
2112
|
});
|
|
1859
2113
|
|
|
1860
2114
|
// src/utils/test/src/test.ts
|
|
1861
|
-
async function testUiComponent(componentName, component, url) {
|
|
2115
|
+
async function testUiComponent(componentName, component, url, options = {}) {
|
|
1862
2116
|
if (!componentName || typeof componentName !== "string") {
|
|
1863
2117
|
throw new Error("\u274C testUiComponent requires a valid componentName (string)");
|
|
1864
2118
|
}
|
|
@@ -1895,6 +2149,17 @@ Error: ${error instanceof Error ? error.message : String(error)}`
|
|
|
1895
2149
|
}
|
|
1896
2150
|
return null;
|
|
1897
2151
|
}
|
|
2152
|
+
let strictness = normalizeStrictness(options.strictness);
|
|
2153
|
+
if (options.strictness === void 0 && typeof window === "undefined") {
|
|
2154
|
+
try {
|
|
2155
|
+
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_configLoader(), configLoader_exports));
|
|
2156
|
+
const { config } = await loadConfig2(process.cwd());
|
|
2157
|
+
const componentStrictness = config.test?.components?.find((comp) => comp?.name === componentName)?.strictness;
|
|
2158
|
+
strictness = normalizeStrictness(componentStrictness ?? config.test?.strictness);
|
|
2159
|
+
} catch {
|
|
2160
|
+
strictness = "balanced";
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
1898
2163
|
let contract;
|
|
1899
2164
|
try {
|
|
1900
2165
|
if (url) {
|
|
@@ -1902,7 +2167,7 @@ Error: ${error instanceof Error ? error.message : String(error)}`
|
|
|
1902
2167
|
if (devServerUrl) {
|
|
1903
2168
|
console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
|
|
1904
2169
|
const { runContractTestsPlaywright: runContractTestsPlaywright2 } = await Promise.resolve().then(() => (init_contractTestRunnerPlaywright(), contractTestRunnerPlaywright_exports));
|
|
1905
|
-
contract = await runContractTestsPlaywright2(componentName, devServerUrl);
|
|
2170
|
+
contract = await runContractTestsPlaywright2(componentName, devServerUrl, strictness);
|
|
1906
2171
|
} else {
|
|
1907
2172
|
throw new Error(
|
|
1908
2173
|
`\u274C Dev server not running at ${url}
|
|
@@ -1911,7 +2176,7 @@ Please start your dev server and try again.`
|
|
|
1911
2176
|
}
|
|
1912
2177
|
} else if (component) {
|
|
1913
2178
|
console.log(`\u{1F3AD} Running component contract tests in JSDOM mode`);
|
|
1914
|
-
contract = await runContractTests(componentName, component);
|
|
2179
|
+
contract = await runContractTests(componentName, component, strictness);
|
|
1915
2180
|
} else {
|
|
1916
2181
|
throw new Error("\u274C Either component or URL must be provided");
|
|
1917
2182
|
}
|
|
@@ -1965,6 +2230,7 @@ var init_test2 = __esm({
|
|
|
1965
2230
|
import_jest_axe = require("jest-axe");
|
|
1966
2231
|
init_contractTestRunner();
|
|
1967
2232
|
init_playwrightTestHarness();
|
|
2233
|
+
init_strictness();
|
|
1968
2234
|
runTest = async () => {
|
|
1969
2235
|
return {
|
|
1970
2236
|
passes: [],
|
|
@@ -2032,124 +2298,7 @@ var import_commander = require("commander");
|
|
|
2032
2298
|
var import_chalk2 = __toESM(require("chalk"), 1);
|
|
2033
2299
|
var import_path3 = __toESM(require("path"), 1);
|
|
2034
2300
|
var import_fs_extra3 = __toESM(require("fs-extra"), 1);
|
|
2035
|
-
|
|
2036
|
-
// src/utils/cli/configLoader.ts
|
|
2037
|
-
var import_path = __toESM(require("path"), 1);
|
|
2038
|
-
var import_fs_extra = __toESM(require("fs-extra"), 1);
|
|
2039
|
-
function validateConfig(config) {
|
|
2040
|
-
const errors = [];
|
|
2041
|
-
if (!config || typeof config !== "object") {
|
|
2042
|
-
errors.push("Config must be an object");
|
|
2043
|
-
return { valid: false, errors };
|
|
2044
|
-
}
|
|
2045
|
-
const cfg = config;
|
|
2046
|
-
if (cfg.audit !== void 0) {
|
|
2047
|
-
if (typeof cfg.audit !== "object" || cfg.audit === null) {
|
|
2048
|
-
errors.push("audit must be an object");
|
|
2049
|
-
} else {
|
|
2050
|
-
if (cfg.audit.urls !== void 0) {
|
|
2051
|
-
if (!Array.isArray(cfg.audit.urls)) {
|
|
2052
|
-
errors.push("audit.urls must be an array");
|
|
2053
|
-
} else if (cfg.audit.urls.some((url) => typeof url !== "string")) {
|
|
2054
|
-
errors.push("audit.urls must contain only strings");
|
|
2055
|
-
}
|
|
2056
|
-
}
|
|
2057
|
-
if (cfg.audit.output !== void 0) {
|
|
2058
|
-
if (typeof cfg.audit.output !== "object") {
|
|
2059
|
-
errors.push("audit.output must be an object");
|
|
2060
|
-
} else {
|
|
2061
|
-
const output = cfg.audit.output;
|
|
2062
|
-
if (output.format !== void 0) {
|
|
2063
|
-
if (!["json", "csv", "html", "all"].includes(output.format)) {
|
|
2064
|
-
errors.push("audit.output.format must be one of: json, csv, html, all");
|
|
2065
|
-
}
|
|
2066
|
-
}
|
|
2067
|
-
if (output.out !== void 0 && typeof output.out !== "string") {
|
|
2068
|
-
errors.push("audit.output.out must be a string");
|
|
2069
|
-
}
|
|
2070
|
-
}
|
|
2071
|
-
}
|
|
2072
|
-
}
|
|
2073
|
-
}
|
|
2074
|
-
if (cfg.test !== void 0) {
|
|
2075
|
-
if (typeof cfg.test !== "object" || cfg.test === null) {
|
|
2076
|
-
errors.push("test must be an object");
|
|
2077
|
-
} else {
|
|
2078
|
-
if (cfg.test.components !== void 0) {
|
|
2079
|
-
if (!Array.isArray(cfg.test.components)) {
|
|
2080
|
-
errors.push("test.components must be an array");
|
|
2081
|
-
} else {
|
|
2082
|
-
cfg.test.components.forEach((comp, idx) => {
|
|
2083
|
-
if (typeof comp !== "object" || comp === null) {
|
|
2084
|
-
errors.push(`test.components[${idx}] must be an object`);
|
|
2085
|
-
} else {
|
|
2086
|
-
if (typeof comp.name !== "string") {
|
|
2087
|
-
errors.push(`test.components[${idx}].name must be a string`);
|
|
2088
|
-
}
|
|
2089
|
-
if (typeof comp.path !== "string") {
|
|
2090
|
-
errors.push(`test.components[${idx}].path must be a string`);
|
|
2091
|
-
}
|
|
2092
|
-
}
|
|
2093
|
-
});
|
|
2094
|
-
}
|
|
2095
|
-
}
|
|
2096
|
-
}
|
|
2097
|
-
}
|
|
2098
|
-
return { valid: errors.length === 0, errors };
|
|
2099
|
-
}
|
|
2100
|
-
async function loadConfigFile(filePath) {
|
|
2101
|
-
try {
|
|
2102
|
-
const ext = import_path.default.extname(filePath);
|
|
2103
|
-
if (ext === ".json") {
|
|
2104
|
-
const content = await import_fs_extra.default.readFile(filePath, "utf-8");
|
|
2105
|
-
return JSON.parse(content);
|
|
2106
|
-
} else if ([".js", ".mjs", ".cjs", ".ts"].includes(ext)) {
|
|
2107
|
-
const imported = await import(filePath);
|
|
2108
|
-
return imported.default || imported;
|
|
2109
|
-
}
|
|
2110
|
-
return null;
|
|
2111
|
-
} catch {
|
|
2112
|
-
return null;
|
|
2113
|
-
}
|
|
2114
|
-
}
|
|
2115
|
-
async function loadConfig(cwd = process.cwd()) {
|
|
2116
|
-
const configNames = [
|
|
2117
|
-
"ariaease.config.js",
|
|
2118
|
-
"ariaease.config.mjs",
|
|
2119
|
-
"ariaease.config.cjs",
|
|
2120
|
-
"ariaease.config.json",
|
|
2121
|
-
"ariaease.config.ts"
|
|
2122
|
-
];
|
|
2123
|
-
let loadedConfig = null;
|
|
2124
|
-
let foundPath = null;
|
|
2125
|
-
const errors = [];
|
|
2126
|
-
for (const name of configNames) {
|
|
2127
|
-
const configPath = import_path.default.resolve(cwd, name);
|
|
2128
|
-
if (await import_fs_extra.default.pathExists(configPath)) {
|
|
2129
|
-
foundPath = configPath;
|
|
2130
|
-
loadedConfig = await loadConfigFile(configPath);
|
|
2131
|
-
if (loadedConfig === null) {
|
|
2132
|
-
errors.push(`Found config at ${name} but failed to load it. Check for syntax errors.`);
|
|
2133
|
-
continue;
|
|
2134
|
-
}
|
|
2135
|
-
const validation = validateConfig(loadedConfig);
|
|
2136
|
-
if (!validation.valid) {
|
|
2137
|
-
errors.push(`Config validation failed in ${name}:`);
|
|
2138
|
-
errors.push(...validation.errors.map((err) => ` - ${err}`));
|
|
2139
|
-
loadedConfig = null;
|
|
2140
|
-
continue;
|
|
2141
|
-
}
|
|
2142
|
-
break;
|
|
2143
|
-
}
|
|
2144
|
-
}
|
|
2145
|
-
return {
|
|
2146
|
-
config: loadedConfig || {},
|
|
2147
|
-
configPath: loadedConfig ? foundPath : null,
|
|
2148
|
-
errors
|
|
2149
|
-
};
|
|
2150
|
-
}
|
|
2151
|
-
|
|
2152
|
-
// src/utils/cli/cli.ts
|
|
2301
|
+
init_configLoader();
|
|
2153
2302
|
init_badgeHelper();
|
|
2154
2303
|
var program = new import_commander.Command();
|
|
2155
2304
|
program.name("aria-ease").description("Run accessibility tests and audits").version("2.2.3");
|