testaro 4.5.0 → 4.5.3
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 +9 -37
- package/commands.js +2 -1
- package/package.json +1 -1
- package/run.js +10 -6
- package/tests/aatt.js +66 -29
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Testaro includes some of its own accessibility tests. In addition, it performs t
|
|
|
23
23
|
- [alfa](https://alfa.siteimprove.com/) (Siteimprove alfa)
|
|
24
24
|
- [Automated Accessibility Testing Tool](https://www.npmjs.com/package/aatt) (Paypal AATT, running HTML CodeSniffer)
|
|
25
25
|
- [axe-playwright](https://www.npmjs.com/package/axe-playwright) (Deque Axe-core)
|
|
26
|
-
- [Tenon](https://tenon.io/documentation/what-tenon-tests.php)
|
|
26
|
+
- [Tenon](https://tenon.io/documentation/what-tenon-tests.php) (Level Access)
|
|
27
27
|
- [WAVE API](https://wave.webaim.org/api/) (WebAIM WAVE)
|
|
28
28
|
|
|
29
29
|
As of this version, the counts of tests in the packages referenced above were:
|
|
@@ -560,6 +560,8 @@ The `tests` executor makes use of the scripts in the `validation/tests/scripts`
|
|
|
560
560
|
|
|
561
561
|
You can define additional Testaro commands and functionality. Contributions are welcome.
|
|
562
562
|
|
|
563
|
+
Please report any issues, including feature requests, at the [repository](https://github.com/jrpool/testaro/issues).
|
|
564
|
+
|
|
563
565
|
## Accessibility principles
|
|
564
566
|
|
|
565
567
|
The rationales motivating the Testaro-defined tests can be found in comments within the files of those tests, in the `tests` directory. Unavoidably, each test is opinionated. Testaro itself, however, can accommodate other tests representing different opinions. Testaro is intended to be neutral with respect to questions such as the criteria for accessibility, the severities of accessibility issues, whether accessibility is binary or graded, and the distinction between usability and accessibility.
|
|
@@ -594,53 +596,23 @@ The files in the `temp` directory are presumed ephemeral and are not tracked by
|
|
|
594
596
|
|
|
595
597
|
## Related packages
|
|
596
598
|
|
|
597
|
-
[Testilo](https://www.npmjs.com/package/testilo) is an application that
|
|
599
|
+
[Testilo](https://www.npmjs.com/package/testilo) is an application that:
|
|
600
|
+
- produces scores and adds them to the JSON report files of Testaro
|
|
601
|
+
- produces human-oriented HTML digests from scored reports
|
|
602
|
+
- produces human-oriented HTML reports comparing the scores of hosts
|
|
598
603
|
|
|
599
604
|
Testaro is derived from [Autotest](https://github.com/jrpool/autotest).
|
|
600
605
|
|
|
601
606
|
Testaro omits some functionalities of Autotest, such as:
|
|
602
607
|
- tests producing results intended to be human-inspected
|
|
603
|
-
- scoring
|
|
608
|
+
- scoring (performed now by Testilo)
|
|
604
609
|
- file operations for score aggregation, report revision, and HTML reports
|
|
605
610
|
- a web user interface
|
|
606
611
|
|
|
607
612
|
## Origin
|
|
608
613
|
|
|
609
|
-
Work on the custom tests in this package began in 2017, and work on the multi-package federation that Testaro implements began in early 2018. These two aspects were combined into the [Autotest](https://github.com/jrpool/autotest) package in early 2021 and into
|
|
614
|
+
Work on the custom tests in this package began in 2017, and work on the multi-package federation that Testaro implements began in early 2018. These two aspects were combined into the [Autotest](https://github.com/jrpool/autotest) package in early 2021 and into the more single-purpose packages, Testaro and Testilo, in January 2022.
|
|
610
615
|
|
|
611
616
|
## Etymology
|
|
612
617
|
|
|
613
618
|
“Testaro” means “collection of tests” in Esperanto.
|
|
614
|
-
|
|
615
|
-
## Future work
|
|
616
|
-
|
|
617
|
-
### Improvements
|
|
618
|
-
|
|
619
|
-
Further development is contemplated, is taking place, or is welcomed, on:
|
|
620
|
-
- addition of Tenon to the set of packages
|
|
621
|
-
- links with href="#"
|
|
622
|
-
- links and buttons styled non-distinguishably
|
|
623
|
-
- first focused element not first focusable element in DOM
|
|
624
|
-
- never-visible skip links
|
|
625
|
-
- buttons with no text content
|
|
626
|
-
- modal dialogs
|
|
627
|
-
- autocomplete attributes
|
|
628
|
-
- inclusion of other test packages, such as:
|
|
629
|
-
- FAE (https://github.com/opena11y/evaluation-library)
|
|
630
|
-
|
|
631
|
-
## Corrections
|
|
632
|
-
|
|
633
|
-
Issues found or reported with the current version that need diagnosis and correction include:
|
|
634
|
-
|
|
635
|
-
### hover
|
|
636
|
-
|
|
637
|
-
There seem to be a couple of problems with the hover test:
|
|
638
|
-
- The score for unhoverability is documented as 2 times the count of unhoverables, but is reported as 1 time that count.
|
|
639
|
-
- The list of unhoverables in the report is empty.
|
|
640
|
-
Observed after inquiry by Tobias Christian Jensen of Siteimprove on 2022-05-09.
|
|
641
|
-
|
|
642
|
-
### axe
|
|
643
|
-
|
|
644
|
-
Configuration to include best practices and experimental tests.
|
|
645
|
-
|
|
646
|
-
Investigation of tags, including wcag2a, wcag2aa, wcag21a, wcag21aa, best-practice, wcag***, ACT, cat.*.
|
package/commands.js
CHANGED
|
@@ -146,7 +146,8 @@ exports.commands = {
|
|
|
146
146
|
aatt: [
|
|
147
147
|
'Perform an AATT test with HTML CodeSniffer',
|
|
148
148
|
{
|
|
149
|
-
waitLong: [false, 'boolean', '', 'whether to wait
|
|
149
|
+
waitLong: [false, 'boolean', '', 'whether to wait 12 instead of 6 seconds for a result'],
|
|
150
|
+
tryLimit: [false, 'number', '', 'times to try the test before giving up; default 4']
|
|
150
151
|
}
|
|
151
152
|
],
|
|
152
153
|
axe: [
|
package/package.json
CHANGED
package/run.js
CHANGED
|
@@ -291,7 +291,7 @@ const textOf = async (page, element) => {
|
|
|
291
291
|
if (['A', 'BUTTON', 'INPUT', 'SELECT'].includes(tagName)) {
|
|
292
292
|
// Return its visible labels, descriptions, and legend if the first input in a fieldset.
|
|
293
293
|
totalText = await page.evaluate(element => {
|
|
294
|
-
const {tagName} = element;
|
|
294
|
+
const {tagName, ariaLabel} = element;
|
|
295
295
|
let ownText = '';
|
|
296
296
|
if (['A', 'BUTTON'].includes(tagName)) {
|
|
297
297
|
ownText = element.textContent;
|
|
@@ -302,6 +302,9 @@ const textOf = async (page, element) => {
|
|
|
302
302
|
// HTML link elements have no labels property.
|
|
303
303
|
const labels = tagName !== 'A' ? Array.from(element.labels) : [];
|
|
304
304
|
const labelTexts = labels.map(label => label.textContent);
|
|
305
|
+
if (ariaLabel) {
|
|
306
|
+
labelTexts.push(ariaLabel);
|
|
307
|
+
}
|
|
305
308
|
const refIDs = new Set([
|
|
306
309
|
element.getAttribute('aria-labelledby') || '',
|
|
307
310
|
element.getAttribute('aria-describedby') || ''
|
|
@@ -616,7 +619,7 @@ const doActs = async (report, actIndex, page) => {
|
|
|
616
619
|
if (truth[1]) {
|
|
617
620
|
// If the performance of commands is to stop:
|
|
618
621
|
if (act.jump === 0) {
|
|
619
|
-
//
|
|
622
|
+
// Stop.
|
|
620
623
|
actIndex = -2;
|
|
621
624
|
}
|
|
622
625
|
// Otherwise, if there is a numerical jump:
|
|
@@ -897,7 +900,7 @@ const doActs = async (report, actIndex, page) => {
|
|
|
897
900
|
// Otherwise, if the act is a move:
|
|
898
901
|
else if (moves[act.type]) {
|
|
899
902
|
const selector = typeof moves[act.type] === 'string' ? moves[act.type] : act.what;
|
|
900
|
-
//
|
|
903
|
+
// Try up to 5 times, every 2 seconds, to identify the element to perform the move on.
|
|
901
904
|
let matchResult = {success: false};
|
|
902
905
|
let tries = 0;
|
|
903
906
|
while (tries++ < 5 && ! matchResult.success) {
|
|
@@ -1019,9 +1022,10 @@ const doActs = async (report, actIndex, page) => {
|
|
|
1019
1022
|
}
|
|
1020
1023
|
// Otherwise, i.e. if no match was found:
|
|
1021
1024
|
else {
|
|
1022
|
-
|
|
1023
|
-
act.result =
|
|
1024
|
-
console.log(
|
|
1025
|
+
// Stop.
|
|
1026
|
+
act.result = matchResult;
|
|
1027
|
+
console.log('ERROR: Specified element not found');
|
|
1028
|
+
actIndex = -2;
|
|
1025
1029
|
}
|
|
1026
1030
|
}
|
|
1027
1031
|
// Otherwise, if the act is a keypress:
|
package/tests/aatt.js
CHANGED
|
@@ -7,31 +7,59 @@
|
|
|
7
7
|
const {evaluate} = require('aatt');
|
|
8
8
|
|
|
9
9
|
// FUNCTIONS
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
source
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
//
|
|
34
|
-
|
|
10
|
+
// Recursively test a page with HTML CodeSniffer for WCAC 2.1 AAA.
|
|
11
|
+
const retest = async (page, waitLong, triesLeft) => {
|
|
12
|
+
// If the limit on tries has not been exhausted:
|
|
13
|
+
if (triesLeft) {
|
|
14
|
+
// Set the limit in seconds on the wait for the result.
|
|
15
|
+
const timeLimit = waitLong ? 12 : 6;
|
|
16
|
+
// Get the HTML of the document body.
|
|
17
|
+
const source = await page.content();
|
|
18
|
+
// Return the result of a test with the HTML CodeSniffer WCAG 2.1 AA ruleset as a string.
|
|
19
|
+
const report = evaluate({
|
|
20
|
+
source,
|
|
21
|
+
output: 'json',
|
|
22
|
+
engine: 'htmlcs',
|
|
23
|
+
level: 'WCAG2AAA'
|
|
24
|
+
});
|
|
25
|
+
// Wait for it until the time limit expires.
|
|
26
|
+
let timeoutID;
|
|
27
|
+
const wait = new Promise(resolve => {
|
|
28
|
+
timeoutID = setTimeout(() => {
|
|
29
|
+
resolve('');
|
|
30
|
+
}, 1000 * timeLimit);
|
|
31
|
+
});
|
|
32
|
+
const result = await Promise.race([report, wait]);
|
|
33
|
+
// If it arrived within the time limit:
|
|
34
|
+
if (result) {
|
|
35
|
+
clearTimeout(timeoutID);
|
|
36
|
+
// Return the result as JSON.
|
|
37
|
+
const reportJSON = result.replace(/^.+?Object.?\s+|\s+done\s*$/sg, '');
|
|
38
|
+
return {
|
|
39
|
+
triesLeft,
|
|
40
|
+
reportJSON
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
console.log(`ERROR: Test aatt timed out at ${timeLimit} seconds; tries left: ${triesLeft - 1}`);
|
|
45
|
+
return retest(page, waitLong, --triesLeft);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Otherwise, i.e. if the limit on tries has been exhausted:
|
|
49
|
+
else {
|
|
50
|
+
// Return this.
|
|
51
|
+
return {
|
|
52
|
+
triesLeft,
|
|
53
|
+
reportJSON: ''
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
exports.reporter = async (page, waitLong, tryLimit = 4) => {
|
|
58
|
+
// Try the test up to the limit on tries.
|
|
59
|
+
const result = await retest(page, waitLong, tryLimit);
|
|
60
|
+
const {triesLeft, reportJSON} = result;
|
|
61
|
+
// If any try succeeded:
|
|
62
|
+
if (reportJSON) {
|
|
35
63
|
try {
|
|
36
64
|
// Convert the JSON string to an array.
|
|
37
65
|
const issueArray = JSON.parse(reportJSON);
|
|
@@ -49,10 +77,18 @@ exports.reporter = async (page, waitLong) => {
|
|
|
49
77
|
.join('+');
|
|
50
78
|
}
|
|
51
79
|
});
|
|
52
|
-
return {
|
|
80
|
+
return {
|
|
81
|
+
result: {
|
|
82
|
+
report: nonNotices,
|
|
83
|
+
triesLeft
|
|
84
|
+
}
|
|
85
|
+
};
|
|
53
86
|
}
|
|
54
87
|
catch (error) {
|
|
55
88
|
console.log(`ERROR processing AATT report (${error.message})`);
|
|
89
|
+
console.log(
|
|
90
|
+
`JSON report starts with ${reportJSON.slice(0, 50)} and ends with ${reportJSON.slice(-50)}`
|
|
91
|
+
);
|
|
56
92
|
return {
|
|
57
93
|
result: {
|
|
58
94
|
prevented: true,
|
|
@@ -61,14 +97,15 @@ exports.reporter = async (page, waitLong) => {
|
|
|
61
97
|
};
|
|
62
98
|
}
|
|
63
99
|
}
|
|
64
|
-
// Otherwise, i.e. if the
|
|
100
|
+
// Otherwise, i.e. if the limit on tries was exhausted:
|
|
65
101
|
else {
|
|
66
102
|
// Report the failure.
|
|
67
|
-
|
|
103
|
+
const error = 'ERROR: Getting AATT report took too long';
|
|
104
|
+
console.log(error);
|
|
68
105
|
return {
|
|
69
106
|
result: {
|
|
70
107
|
prevented: true,
|
|
71
|
-
error
|
|
108
|
+
error
|
|
72
109
|
}
|
|
73
110
|
};
|
|
74
111
|
}
|