aria-ease 6.7.0 → 6.9.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.
Files changed (39) hide show
  1. package/README.md +77 -10
  2. package/bin/AccordionComponentStrategy-4ZEIQ2V6.js +42 -0
  3. package/bin/ComboboxComponentStrategy-OGRVZXAF.js +64 -0
  4. package/bin/MenuComponentStrategy-JAMTCSNF.js +81 -0
  5. package/bin/TabsComponentStrategy-3SQURPMX.js +29 -0
  6. package/bin/buildContracts-GBOY7UXG.js +437 -0
  7. package/bin/{chunk-VPBHLMAS.js → chunk-LMSKLN5O.js} +21 -0
  8. package/bin/chunk-PK5L2SAF.js +17 -0
  9. package/bin/{chunk-2TOYEY5L.js → chunk-XERMSYEH.js} +12 -3
  10. package/bin/cli.cjs +991 -128
  11. package/bin/cli.js +33 -2
  12. package/bin/{configLoader-XRF6VM4J.js → configLoader-Q6A4JLKW.js} +1 -1
  13. package/{dist/contractTestRunnerPlaywright-UAOFNS7Z.js → bin/contractTestRunnerPlaywright-ZZNWDUYP.js} +270 -219
  14. package/bin/{test-WRIJHN6H.js → test-OND56UUL.js} +97 -10
  15. package/dist/AccordionComponentStrategy-4ZEIQ2V6.js +42 -0
  16. package/dist/ComboboxComponentStrategy-OGRVZXAF.js +64 -0
  17. package/dist/MenuComponentStrategy-JAMTCSNF.js +81 -0
  18. package/dist/TabsComponentStrategy-3SQURPMX.js +29 -0
  19. package/dist/chunk-PK5L2SAF.js +17 -0
  20. package/dist/{chunk-2TOYEY5L.js → chunk-XERMSYEH.js} +12 -3
  21. package/dist/{configLoader-IT4PWCJB.js → configLoader-WTGJAP4Z.js} +21 -0
  22. package/{bin/contractTestRunnerPlaywright-UAOFNS7Z.js → dist/contractTestRunnerPlaywright-XBWJZMR3.js} +270 -219
  23. package/dist/index.cjs +800 -96
  24. package/dist/index.d.cts +136 -1
  25. package/dist/index.d.ts +136 -1
  26. package/dist/index.js +421 -16
  27. package/dist/src/utils/test/AccordionComponentStrategy-WRHZOEN6.js +38 -0
  28. package/dist/src/utils/test/ComboboxComponentStrategy-5AECQSRN.js +60 -0
  29. package/dist/src/utils/test/MenuComponentStrategy-VKZQYLBE.js +77 -0
  30. package/dist/src/utils/test/TabsComponentStrategy-BKG53SEV.js +26 -0
  31. package/dist/src/utils/test/aria-contracts/accordion/accordion.contract.json +5 -11
  32. package/dist/src/utils/test/aria-contracts/combobox/combobox.listbox.contract.json +1 -1
  33. package/dist/src/utils/test/aria-contracts/menu/menu.contract.json +1 -1
  34. package/dist/src/utils/test/{chunk-2TOYEY5L.js → chunk-XERMSYEH.js} +12 -3
  35. package/dist/src/utils/test/{configLoader-LD4RV2WQ.js → configLoader-YE2CYGDG.js} +21 -0
  36. package/dist/src/utils/test/{contractTestRunnerPlaywright-IRJOAEMT.js → contractTestRunnerPlaywright-LC5OAVXB.js} +262 -200
  37. package/dist/src/utils/test/index.cjs +472 -88
  38. package/dist/src/utils/test/index.js +97 -12
  39. package/package.json +7 -2
@@ -0,0 +1,437 @@
1
+ import "./chunk-I2KLQ2HA.js";
2
+
3
+ // src/utils/cli/buildContracts.ts
4
+ import path from "path";
5
+ import fs from "fs-extra";
6
+ import { glob } from "fs/promises";
7
+ import chalk from "chalk";
8
+
9
+ // src/utils/cli/contractValidator.ts
10
+ function validateContractSchema(contract) {
11
+ const errors = [];
12
+ if (!contract || typeof contract !== "object") {
13
+ return {
14
+ valid: false,
15
+ errors: [{ path: "$", message: "Contract must be an object" }]
16
+ };
17
+ }
18
+ const c = contract;
19
+ if (!c.selectors) {
20
+ errors.push({ path: "$.selectors", message: "selectors is required" });
21
+ } else if (typeof c.selectors !== "object" || c.selectors === null || Array.isArray(c.selectors)) {
22
+ errors.push({ path: "$.selectors", message: "selectors must be an object" });
23
+ } else {
24
+ const selectors = c.selectors;
25
+ Object.entries(selectors).forEach(([key, value]) => {
26
+ if (typeof value !== "string") {
27
+ errors.push({ path: `$.selectors['${key}']`, message: "All selectors must be strings" });
28
+ }
29
+ });
30
+ }
31
+ if (!Array.isArray(c.static)) {
32
+ errors.push({ path: "$.static", message: "static must be an array" });
33
+ } else {
34
+ c.static.forEach((item, idx) => {
35
+ if (typeof item !== "object" || item === null) {
36
+ errors.push({ path: `$.static[${idx}]`, message: "static item must be an object" });
37
+ return;
38
+ }
39
+ const staticItem = item;
40
+ if (!Array.isArray(staticItem.assertions)) {
41
+ errors.push({
42
+ path: `$.static[${idx}].assertions`,
43
+ message: "assertions must be an array"
44
+ });
45
+ return;
46
+ }
47
+ staticItem.assertions.forEach((assertion, assertIdx) => {
48
+ if (typeof assertion !== "object" || assertion === null) {
49
+ errors.push({
50
+ path: `$.static[${idx}].assertions[${assertIdx}]`,
51
+ message: "assertion must be an object"
52
+ });
53
+ return;
54
+ }
55
+ const a = assertion;
56
+ if (typeof a.target !== "string") {
57
+ errors.push({
58
+ path: `$.static[${idx}].assertions[${assertIdx}].target`,
59
+ message: "target is required and must be a string"
60
+ });
61
+ }
62
+ if (typeof a.attribute !== "string") {
63
+ errors.push({
64
+ path: `$.static[${idx}].assertions[${assertIdx}].attribute`,
65
+ message: "attribute is required and must be a string"
66
+ });
67
+ }
68
+ if (typeof a.failureMessage !== "string") {
69
+ errors.push({
70
+ path: `$.static[${idx}].assertions[${assertIdx}].failureMessage`,
71
+ message: "failureMessage is required and must be a string"
72
+ });
73
+ }
74
+ if (a.level !== void 0 && !["required", "recommended", "optional"].includes(a.level)) {
75
+ errors.push({
76
+ path: `$.static[${idx}].assertions[${assertIdx}].level`,
77
+ message: "level must be one of: required, recommended, optional"
78
+ });
79
+ }
80
+ });
81
+ });
82
+ }
83
+ if (!Array.isArray(c.dynamic)) {
84
+ errors.push({ path: "$.dynamic", message: "dynamic must be an array" });
85
+ } else {
86
+ c.dynamic.forEach((item, idx) => {
87
+ if (typeof item !== "object" || item === null) {
88
+ errors.push({ path: `$.dynamic[${idx}]`, message: "dynamic item must be an object" });
89
+ return;
90
+ }
91
+ const dynamicItem = item;
92
+ if (typeof dynamicItem.description !== "string") {
93
+ errors.push({
94
+ path: `$.dynamic[${idx}].description`,
95
+ message: "description is required and must be a string"
96
+ });
97
+ }
98
+ if (!Array.isArray(dynamicItem.action)) {
99
+ errors.push({
100
+ path: `$.dynamic[${idx}].action`,
101
+ message: "action is required and must be an array"
102
+ });
103
+ } else {
104
+ dynamicItem.action.forEach((action, actIdx) => {
105
+ if (typeof action !== "object" || action === null) {
106
+ errors.push({
107
+ path: `$.dynamic[${idx}].action[${actIdx}]`,
108
+ message: "action item must be an object"
109
+ });
110
+ return;
111
+ }
112
+ const a = action;
113
+ if (typeof a.type !== "string") {
114
+ errors.push({
115
+ path: `$.dynamic[${idx}].action[${actIdx}].type`,
116
+ message: "type is required and must be a string"
117
+ });
118
+ }
119
+ if (typeof a.target !== "string") {
120
+ errors.push({
121
+ path: `$.dynamic[${idx}].action[${actIdx}].target`,
122
+ message: "target is required and must be a string"
123
+ });
124
+ }
125
+ });
126
+ }
127
+ if (!Array.isArray(dynamicItem.assertions)) {
128
+ errors.push({
129
+ path: `$.dynamic[${idx}].assertions`,
130
+ message: "assertions is required and must be an array"
131
+ });
132
+ } else {
133
+ dynamicItem.assertions.forEach((assertion, assertIdx) => {
134
+ if (typeof assertion !== "object" || assertion === null) {
135
+ errors.push({
136
+ path: `$.dynamic[${idx}].assertions[${assertIdx}]`,
137
+ message: "assertion must be an object"
138
+ });
139
+ return;
140
+ }
141
+ const a = assertion;
142
+ if (typeof a.target !== "string") {
143
+ errors.push({
144
+ path: `$.dynamic[${idx}].assertions[${assertIdx}].target`,
145
+ message: "target is required and must be a string"
146
+ });
147
+ }
148
+ if (typeof a.assertion !== "string") {
149
+ errors.push({
150
+ path: `$.dynamic[${idx}].assertions[${assertIdx}].assertion`,
151
+ message: "assertion is required and must be a string"
152
+ });
153
+ }
154
+ if (a.level !== void 0 && !["required", "recommended", "optional"].includes(a.level)) {
155
+ errors.push({
156
+ path: `$.dynamic[${idx}].assertions[${assertIdx}].level`,
157
+ message: "level must be one of: required, recommended, optional"
158
+ });
159
+ }
160
+ });
161
+ }
162
+ });
163
+ }
164
+ if (c.relationships !== void 0) {
165
+ if (!Array.isArray(c.relationships)) {
166
+ errors.push({ path: "$.relationships", message: "relationships must be an array" });
167
+ } else {
168
+ c.relationships.forEach((rel, idx) => {
169
+ if (typeof rel !== "object" || rel === null) {
170
+ errors.push({ path: `$.relationships[${idx}]`, message: "relationship must be an object" });
171
+ return;
172
+ }
173
+ const r = rel;
174
+ const type = r.type;
175
+ if (!["aria-reference", "contains"].includes(type)) {
176
+ errors.push({
177
+ path: `$.relationships[${idx}].type`,
178
+ message: "type must be one of: aria-reference, contains"
179
+ });
180
+ }
181
+ if (type === "aria-reference") {
182
+ if (typeof r.from !== "string") {
183
+ errors.push({
184
+ path: `$.relationships[${idx}].from`,
185
+ message: "from is required for aria-reference and must be a string"
186
+ });
187
+ }
188
+ if (typeof r.attribute !== "string") {
189
+ errors.push({
190
+ path: `$.relationships[${idx}].attribute`,
191
+ message: "attribute is required for aria-reference and must be a string"
192
+ });
193
+ }
194
+ if (typeof r.to !== "string") {
195
+ errors.push({
196
+ path: `$.relationships[${idx}].to`,
197
+ message: "to is required for aria-reference and must be a string"
198
+ });
199
+ }
200
+ } else if (type === "contains") {
201
+ if (typeof r.parent !== "string") {
202
+ errors.push({
203
+ path: `$.relationships[${idx}].parent`,
204
+ message: "parent is required for contains and must be a string"
205
+ });
206
+ }
207
+ if (typeof r.child !== "string") {
208
+ errors.push({
209
+ path: `$.relationships[${idx}].child`,
210
+ message: "child is required for contains and must be a string"
211
+ });
212
+ }
213
+ }
214
+ });
215
+ }
216
+ }
217
+ return {
218
+ valid: errors.length === 0,
219
+ errors
220
+ };
221
+ }
222
+ function validateRelationshipReferences(contract, selectorKeys) {
223
+ const errors = [];
224
+ if (!contract || typeof contract !== "object") {
225
+ return errors;
226
+ }
227
+ const c = contract;
228
+ const relationships = c.relationships;
229
+ if (!Array.isArray(relationships)) {
230
+ return errors;
231
+ }
232
+ relationships.forEach((rel, idx) => {
233
+ const type = rel.type;
234
+ if (type === "aria-reference") {
235
+ const from = rel.from;
236
+ const to = rel.to;
237
+ if (from && !selectorKeys.has(from)) {
238
+ errors.push({
239
+ path: `$.relationships[${idx}].from`,
240
+ message: `Selector '${from}' not found in selectors`
241
+ });
242
+ }
243
+ if (to && !selectorKeys.has(to)) {
244
+ errors.push({
245
+ path: `$.relationships[${idx}].to`,
246
+ message: `Selector '${to}' not found in selectors`
247
+ });
248
+ }
249
+ } else if (type === "contains") {
250
+ const parent = rel.parent;
251
+ const child = rel.child;
252
+ if (parent && !selectorKeys.has(parent)) {
253
+ errors.push({
254
+ path: `$.relationships[${idx}].parent`,
255
+ message: `Selector '${parent}' not found in selectors`
256
+ });
257
+ }
258
+ if (child && !selectorKeys.has(child)) {
259
+ errors.push({
260
+ path: `$.relationships[${idx}].child`,
261
+ message: `Selector '${child}' not found in selectors`
262
+ });
263
+ }
264
+ }
265
+ });
266
+ return errors;
267
+ }
268
+ function validateTargetReferences(contract, selectorKeys) {
269
+ const errors = [];
270
+ if (!contract || typeof contract !== "object") {
271
+ return errors;
272
+ }
273
+ const c = contract;
274
+ const staticItems = c.static;
275
+ if (Array.isArray(staticItems)) {
276
+ staticItems.forEach((item, itemIdx) => {
277
+ const assertions = item.assertions;
278
+ if (Array.isArray(assertions)) {
279
+ assertions.forEach((assertion, assertIdx) => {
280
+ const target = assertion.target;
281
+ if (target && !selectorKeys.has(target)) {
282
+ errors.push({
283
+ path: `$.static[${itemIdx}].assertions[${assertIdx}].target`,
284
+ message: `Selector '${target}' not found in selectors`
285
+ });
286
+ }
287
+ });
288
+ }
289
+ });
290
+ }
291
+ const dynamicItems = c.dynamic;
292
+ if (Array.isArray(dynamicItems)) {
293
+ dynamicItems.forEach((item, itemIdx) => {
294
+ const actions = item.action;
295
+ if (Array.isArray(actions)) {
296
+ actions.forEach((action, actIdx) => {
297
+ const target = action.target;
298
+ if (target && target !== "document" && !selectorKeys.has(target)) {
299
+ errors.push({
300
+ path: `$.dynamic[${itemIdx}].action[${actIdx}].target`,
301
+ message: `Selector '${target}' not found in selectors (or use 'document')`
302
+ });
303
+ }
304
+ });
305
+ }
306
+ const assertions = item.assertions;
307
+ if (Array.isArray(assertions)) {
308
+ assertions.forEach((assertion, assertIdx) => {
309
+ const target = assertion.target;
310
+ if (target && target !== "relative" && !selectorKeys.has(target)) {
311
+ errors.push({
312
+ path: `$.dynamic[${itemIdx}].assertions[${assertIdx}].target`,
313
+ message: `Selector '${target}' not found in selectors (or use 'relative')`
314
+ });
315
+ }
316
+ });
317
+ }
318
+ });
319
+ }
320
+ return errors;
321
+ }
322
+
323
+ // src/utils/cli/buildContracts.ts
324
+ async function buildContracts(cwd, config) {
325
+ const errors = [];
326
+ const built = [];
327
+ if (!config.contracts || config.contracts.length === 0) {
328
+ console.log(chalk.yellow('\u2139\uFE0F No contracts configured. Add "contracts" array to ariaease.config.js'));
329
+ return { success: true, built, errors };
330
+ }
331
+ try {
332
+ console.log(chalk.cyanBright("\n\u{1F3D7}\uFE0F Building contracts...\n"));
333
+ for (const contractSource of config.contracts) {
334
+ const srcPattern = path.resolve(cwd, contractSource.src);
335
+ const outDir = contractSource.out ? path.resolve(cwd, contractSource.out) : void 0;
336
+ console.log(chalk.gray(` Pattern: ${contractSource.src}`));
337
+ if (outDir) {
338
+ console.log(chalk.gray(` Output: ${contractSource.out}
339
+ `));
340
+ } else {
341
+ console.log(chalk.gray(` Output: Same directory as source
342
+ `));
343
+ }
344
+ const contractFiles = [];
345
+ for await (const file of glob(srcPattern, { cwd })) {
346
+ contractFiles.push(file);
347
+ }
348
+ if (contractFiles.length === 0) {
349
+ console.log(chalk.yellow(`\u26A0\uFE0F No contract files found matching pattern: ${contractSource.src}`));
350
+ continue;
351
+ }
352
+ if (outDir) {
353
+ await fs.ensureDir(outDir);
354
+ }
355
+ for (const contractFile of contractFiles) {
356
+ try {
357
+ const absolutePath = contractFile.startsWith("/") ? contractFile : path.resolve(cwd, contractFile);
358
+ const module = await import(`file://${absolutePath}`);
359
+ let contract = module.default;
360
+ if (!contract) {
361
+ const namedExports = Object.entries(module).find(
362
+ ([, value]) => value && typeof value === "object" && "toJSON" in value
363
+ );
364
+ if (namedExports) {
365
+ contract = namedExports[1];
366
+ }
367
+ }
368
+ if (!contract || typeof contract.toJSON !== "function") {
369
+ errors.push(`${path.basename(contractFile)}: No contract with toJSON() method found`);
370
+ continue;
371
+ }
372
+ const json = contract.toJSON();
373
+ const schemaValidation = validateContractSchema(json);
374
+ if (!schemaValidation.valid) {
375
+ const errorLines = schemaValidation.errors.map((err) => ` ${chalk.red("\u2717")} ${err.path}: ${err.message}`).join("\n");
376
+ const errorMsg = `Schema validation failed:
377
+ ${errorLines}`;
378
+ errors.push(`${path.basename(contractFile)}: ${errorMsg}`);
379
+ console.log(chalk.red(`\u274C ${path.basename(contractFile)}`));
380
+ console.log(chalk.red(` ${errorMsg}`));
381
+ continue;
382
+ }
383
+ const selectorKeys = /* @__PURE__ */ new Set();
384
+ if (json && typeof json === "object" && "selectors" in json) {
385
+ const selectors = json.selectors;
386
+ Object.keys(selectors).forEach((key) => selectorKeys.add(key));
387
+ }
388
+ const refErrors = [
389
+ ...validateRelationshipReferences(json, selectorKeys),
390
+ ...validateTargetReferences(json, selectorKeys)
391
+ ];
392
+ if (refErrors.length > 0) {
393
+ const errorLines = refErrors.map((err) => ` ${chalk.red("\u2717")} ${err.path}: ${err.message}`).join("\n");
394
+ const errorMsg = `Reference validation failed:
395
+ ${errorLines}`;
396
+ errors.push(`${path.basename(contractFile)}: ${errorMsg}`);
397
+ console.log(chalk.red(`\u274C ${path.basename(contractFile)}`));
398
+ console.log(chalk.red(` ${errorMsg}`));
399
+ continue;
400
+ }
401
+ const contractName = path.basename(contractFile, path.extname(contractFile));
402
+ const outputFile = outDir ? path.join(outDir, `${contractName}.json`) : path.join(path.dirname(absolutePath), `${contractName}.json`);
403
+ await fs.writeFile(outputFile, JSON.stringify(json, null, 2) + "\n", "utf-8");
404
+ const relativePath = path.relative(cwd, outputFile);
405
+ built.push(relativePath);
406
+ console.log(chalk.green(`\u2705 ${path.basename(contractFile)} \u2192 ${path.basename(outputFile)}`));
407
+ } catch (error) {
408
+ const errorMsg = error instanceof Error ? error.message : String(error);
409
+ const filename = path.basename(contractFile);
410
+ errors.push(`${filename}: ${errorMsg}`);
411
+ console.log(chalk.red(`\u274C ${filename}`));
412
+ console.log(chalk.red(` Error: ${errorMsg}`));
413
+ }
414
+ }
415
+ }
416
+ console.log("");
417
+ if (errors.length === 0) {
418
+ console.log(chalk.green(`\u2705 Built ${built.length} contract${built.length !== 1 ? "s" : ""} successfully
419
+ `));
420
+ return { success: true, built, errors };
421
+ } else {
422
+ console.log(chalk.yellow(`\u26A0\uFE0F Built ${built.length} contracts with ${errors.length} error${errors.length !== 1 ? "s" : ""}
423
+ `));
424
+ return { success: false, built, errors };
425
+ }
426
+ } catch (error) {
427
+ const errorMsg = error instanceof Error ? error.message : String(error);
428
+ const msg = `Failed to build contracts: ${errorMsg}`;
429
+ errors.push(msg);
430
+ console.log(chalk.red(`\u274C ${msg}
431
+ `));
432
+ return { success: false, built, errors };
433
+ }
434
+ }
435
+ export {
436
+ buildContracts
437
+ };
@@ -54,6 +54,9 @@ function validateConfig(config) {
54
54
  if (comp.path !== void 0 && typeof comp.path !== "string") {
55
55
  errors.push(`test.components[${idx}].path must be a string when provided`);
56
56
  }
57
+ if (comp.strategyPath !== void 0 && typeof comp.strategyPath !== "string") {
58
+ errors.push(`test.components[${idx}].strategyPath must be a string when provided`);
59
+ }
57
60
  if (comp.strictness !== void 0 && !["minimal", "balanced", "strict", "paranoid"].includes(comp.strictness)) {
58
61
  errors.push(`test.components[${idx}].strictness must be one of: minimal, balanced, strict, paranoid`);
59
62
  }
@@ -68,6 +71,24 @@ function validateConfig(config) {
68
71
  }
69
72
  }
70
73
  }
74
+ if (cfg.contracts !== void 0) {
75
+ if (!Array.isArray(cfg.contracts)) {
76
+ errors.push("contracts must be an array");
77
+ } else {
78
+ cfg.contracts.forEach((contract, idx) => {
79
+ if (typeof contract !== "object" || contract === null) {
80
+ errors.push(`contracts[${idx}] must be an object`);
81
+ } else {
82
+ if (typeof contract.src !== "string") {
83
+ errors.push(`contracts[${idx}].src is required and must be a string`);
84
+ }
85
+ if (contract.out !== void 0 && typeof contract.out !== "string") {
86
+ errors.push(`contracts[${idx}].out must be a string`);
87
+ }
88
+ }
89
+ });
90
+ }
91
+ }
71
92
  return { valid: errors.length === 0, errors };
72
93
  }
73
94
  async function loadConfigFile(filePath) {
@@ -0,0 +1,17 @@
1
+ import {
2
+ __export,
3
+ __reExport
4
+ } from "./chunk-I2KLQ2HA.js";
5
+
6
+ // node_modules/@playwright/test/index.mjs
7
+ var test_exports = {};
8
+ __export(test_exports, {
9
+ default: () => default2
10
+ });
11
+ __reExport(test_exports, test_star);
12
+ import * as test_star from "playwright/test";
13
+ import { default as default2 } from "playwright/test";
14
+
15
+ export {
16
+ test_exports
17
+ };
@@ -30,11 +30,13 @@ var ContractReporter = class {
30
30
  skipped = 0;
31
31
  warnings = 0;
32
32
  isPlaywright = false;
33
+ isCustomContract = false;
33
34
  apgUrl = "https://www.w3.org/WAI/ARIA/apg/";
34
35
  hasPrintedStaticSection = false;
35
36
  hasPrintedDynamicSection = false;
36
- constructor(isPlaywright = false) {
37
+ constructor(isPlaywright = false, isCustomContract = false) {
37
38
  this.isPlaywright = isPlaywright;
39
+ this.isCustomContract = isCustomContract;
38
40
  }
39
41
  log(message) {
40
42
  process.stderr.write(message + "\n");
@@ -195,6 +197,13 @@ ${"\u2500".repeat(60)}`);
195
197
  const totalPasses = this.staticPasses + dynamicPasses;
196
198
  const totalFailures = this.staticFailures + dynamicFailures;
197
199
  const totalRun = totalPasses + totalFailures + this.warnings;
200
+ const getComponentMessage = () => {
201
+ const componentDisplayName = `${this.componentName.charAt(0).toUpperCase()}${this.componentName.slice(1)}`;
202
+ if (this.isCustomContract) {
203
+ return `${componentDisplayName} component validates against your custom accessibility policy \u2713`;
204
+ }
205
+ return `${componentDisplayName} component meets Aria-Ease baseline WAI-ARIA expectations \u2713`;
206
+ };
198
207
  if (failures.length > 0) {
199
208
  this.reportFailures(failures);
200
209
  }
@@ -206,7 +215,7 @@ ${"\u2550".repeat(60)}`);
206
215
  `);
207
216
  if (totalFailures === 0 && this.skipped === 0 && this.warnings === 0) {
208
217
  this.log(`\u2705 All ${totalRun} tests passed!`);
209
- this.log(` ${this.componentName.charAt(0).toUpperCase()}${this.componentName.slice(1)} component meets WAI-ARIA expectations for Roles, States, Properties, and Keyboard Interactions \u2713`);
218
+ this.log(` ${getComponentMessage()}`);
210
219
  } else if (totalFailures === 0) {
211
220
  this.log(`\u2705 ${totalPasses}/${totalRun} tests passed`);
212
221
  if (this.skipped > 0) {
@@ -215,7 +224,7 @@ ${"\u2550".repeat(60)}`);
215
224
  if (this.warnings > 0) {
216
225
  this.log(`\u26A0\uFE0F ${this.warnings} warning${this.warnings > 1 ? "s" : ""}`);
217
226
  }
218
- this.log(` ${this.componentName.charAt(0).toUpperCase()}${this.componentName.slice(1)} component meets WAI-ARIA expectations for Roles, States, Properties, and Keyboard Interactions \u2713`);
227
+ this.log(` ${getComponentMessage()}`);
219
228
  } else {
220
229
  this.log(`\u274C ${totalFailures} test${totalFailures > 1 ? "s" : ""} failed`);
221
230
  this.log(`\u2705 ${totalPasses} test${totalPasses > 1 ? "s" : ""} passed`);