testaro 5.6.4 → 5.7.2
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 +11 -3
- package/commands.js +30 -0
- package/package.json +1 -1
- package/run.js +7 -0
- package/samples/scripts/tp14.json +1 -1
- package/samples/scripts/tp15.json +173 -0
- package/tests/docType.js +12 -0
- package/tests/focVis.js +29 -0
- package/tests/linkTo.js +25 -0
- package/tests/miniText.js +48 -0
- package/tests/nonTable.js +55 -0
- package/tests/nuVal.js +64 -0
- package/tests/titledEl.js +29 -0
- package/tests/wave.js +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Federated accessibility test automation
|
|
|
6
6
|
|
|
7
7
|
Testaro is a collection of collections of web accessibility tests.
|
|
8
8
|
|
|
9
|
-
The purpose of Testaro is to provide programmatic access to
|
|
9
|
+
The purpose of Testaro is to provide programmatic access to 1228 accessibility tests defined in several test packages and in Testaro itself.
|
|
10
10
|
|
|
11
11
|
## System requirements
|
|
12
12
|
|
|
@@ -26,6 +26,9 @@ Testaro includes some of its own accessibility tests. In addition, it performs t
|
|
|
26
26
|
- [axe-playwright](https://www.npmjs.com/package/axe-playwright) (Deque Axe-core)
|
|
27
27
|
- [Tenon](https://tenon.io/documentation/what-tenon-tests.php) (Level Access)
|
|
28
28
|
- [WAVE API](https://wave.webaim.org/api/) (WebAIM WAVE)
|
|
29
|
+
- [Nu Html Checker](https://github.com/validator/validator)
|
|
30
|
+
|
|
31
|
+
Some of the Testaro tests are derived from tests performed by the [BBC Accessibility Standards Checker](https://github.com/bbc/bbc-a11y).
|
|
29
32
|
|
|
30
33
|
As of this version, the counts of tests in the packages referenced above were:
|
|
31
34
|
- Alfa: 103
|
|
@@ -35,9 +38,10 @@ As of this version, the counts of tests in the packages referenced above were:
|
|
|
35
38
|
- HTML CodeSniffer: 98
|
|
36
39
|
- Tenon: 180
|
|
37
40
|
- WAVE: 110
|
|
41
|
+
- Nu Html Checker: 147
|
|
38
42
|
- subtotal: 612
|
|
39
|
-
- Testaro tests:
|
|
40
|
-
- grand total:
|
|
43
|
+
- Testaro tests: 22
|
|
44
|
+
- grand total: 1228
|
|
41
45
|
|
|
42
46
|
## Code organization
|
|
43
47
|
|
|
@@ -330,6 +334,10 @@ The changes in `htmlcs/HTMLCS.js` are:
|
|
|
330
334
|
> );
|
|
331
335
|
```
|
|
332
336
|
|
|
337
|
+
###### BBC Accessibility Standards Checker
|
|
338
|
+
|
|
339
|
+
The BBC Accessibility Standards Checker has obsolete dependencies with security vulnerabilities. Therefore, it is not used as a dependency of Testaro. Instead, 6 of its tests were reimplemented, in some case with revisions, as Testaro tests. They were drawn from the 18 automated tests of the Checker. The other 12 tests were found too duplicative of other tests to justify reimplementation.
|
|
340
|
+
|
|
333
341
|
##### Branching
|
|
334
342
|
|
|
335
343
|
An example of a **branching** command is:
|
package/commands.js
CHANGED
|
@@ -170,6 +170,12 @@ exports.commands = {
|
|
|
170
170
|
withItems: [true, 'boolean']
|
|
171
171
|
}
|
|
172
172
|
],
|
|
173
|
+
focVis: [
|
|
174
|
+
'Perform a focVis test',
|
|
175
|
+
{
|
|
176
|
+
withItems: [true, 'boolean']
|
|
177
|
+
}
|
|
178
|
+
],
|
|
173
179
|
hover: [
|
|
174
180
|
'Perform a hover test',
|
|
175
181
|
{
|
|
@@ -196,6 +202,12 @@ exports.commands = {
|
|
|
196
202
|
withItems: [true, 'boolean']
|
|
197
203
|
}
|
|
198
204
|
],
|
|
205
|
+
linkTo: [
|
|
206
|
+
'Perform a linkTo test',
|
|
207
|
+
{
|
|
208
|
+
withItems: [true, 'boolean']
|
|
209
|
+
}
|
|
210
|
+
],
|
|
199
211
|
linkUl: [
|
|
200
212
|
'Perform a linkUl test',
|
|
201
213
|
{
|
|
@@ -208,6 +220,12 @@ exports.commands = {
|
|
|
208
220
|
withItems: [true, 'boolean']
|
|
209
221
|
}
|
|
210
222
|
],
|
|
223
|
+
miniText: [
|
|
224
|
+
'Perform a miniText test',
|
|
225
|
+
{
|
|
226
|
+
withItems: [true, 'boolean']
|
|
227
|
+
}
|
|
228
|
+
],
|
|
211
229
|
motion: [
|
|
212
230
|
'Perform a motion test',
|
|
213
231
|
{
|
|
@@ -216,6 +234,12 @@ exports.commands = {
|
|
|
216
234
|
count: [true, 'number', '', 'count of screen shots to make']
|
|
217
235
|
}
|
|
218
236
|
],
|
|
237
|
+
nonTable: [
|
|
238
|
+
'Perform a nonTable test',
|
|
239
|
+
{
|
|
240
|
+
withItems: [true, 'boolean']
|
|
241
|
+
}
|
|
242
|
+
],
|
|
219
243
|
radioSet: [
|
|
220
244
|
'Perform a radioSet test',
|
|
221
245
|
{
|
|
@@ -240,6 +264,12 @@ exports.commands = {
|
|
|
240
264
|
id: [true, 'string', 'hasLength', 'ID of the requested test instance']
|
|
241
265
|
}
|
|
242
266
|
],
|
|
267
|
+
titledEl: [
|
|
268
|
+
'Perform a titledEl test',
|
|
269
|
+
{
|
|
270
|
+
withItems: [true, 'boolean']
|
|
271
|
+
}
|
|
272
|
+
],
|
|
243
273
|
wave: [
|
|
244
274
|
'Perform a WebAIM WAVE test',
|
|
245
275
|
{
|
package/package.json
CHANGED
package/run.js
CHANGED
|
@@ -33,22 +33,29 @@ const tests = {
|
|
|
33
33
|
axe: 'Axe',
|
|
34
34
|
bulk: 'count of visible elements',
|
|
35
35
|
continuum: 'Level Access Continuum, community edition',
|
|
36
|
+
docType: 'document without a doctype property',
|
|
36
37
|
embAc: 'active elements embedded in links or buttons',
|
|
37
38
|
focAll: 'focusable and Tab-focused elements',
|
|
38
39
|
focInd: 'focus indicators',
|
|
39
40
|
focOp: 'focusability and operability',
|
|
41
|
+
focVis: 'links that are invisible when focused',
|
|
40
42
|
hover: 'hover-caused content changes',
|
|
41
43
|
htmlcs: 'HTML CodeSniffer WCAG 2.1 AA ruleset',
|
|
42
44
|
ibm: 'IBM Accessibility Checker',
|
|
43
45
|
labClash: 'labeling inconsistencies',
|
|
46
|
+
linkTo: 'links without destinations',
|
|
44
47
|
linkUl: 'adjacent-link underlining',
|
|
45
48
|
menuNav: 'keyboard navigation between focusable menu items',
|
|
49
|
+
miniText: 'text smaller than 11 pixels',
|
|
46
50
|
motion: 'motion',
|
|
51
|
+
nonTable: 'table elements used for layout',
|
|
52
|
+
nuVal: 'failures to pass the Nu Html Checker',
|
|
47
53
|
radioSet: 'fieldset grouping of radio buttons',
|
|
48
54
|
role: 'roles',
|
|
49
55
|
styleDiff: 'style inconsistencies',
|
|
50
56
|
tabNav: 'keyboard navigation between tab elements',
|
|
51
57
|
tenon: 'Tenon',
|
|
58
|
+
titledEl: 'title attributes on inappropriate elements',
|
|
52
59
|
wave: 'WAVE',
|
|
53
60
|
zIndex: 'z indexes'
|
|
54
61
|
};
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "tp15",
|
|
3
|
+
"what": "Alfa, Axe, Continuum, HTML CodeSniffer, IBM, Nu Html Checker, Tenon, WAVE, and 22 custom tests",
|
|
4
|
+
"strict": true,
|
|
5
|
+
"timeLimit": 500,
|
|
6
|
+
"commands": [
|
|
7
|
+
{
|
|
8
|
+
"type": "launch",
|
|
9
|
+
"which": "webkit",
|
|
10
|
+
"what": "Webkit browser"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"type": "url",
|
|
14
|
+
"which": "https://*",
|
|
15
|
+
"what": "any page"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"type": "tenonRequest",
|
|
19
|
+
"id": "a",
|
|
20
|
+
"withNewContent": true,
|
|
21
|
+
"what": "Tenon API version 2 test request"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"type": "test",
|
|
25
|
+
"which": "motion",
|
|
26
|
+
"what": "spontaneous change of content; requires webkit",
|
|
27
|
+
"delay": 2500,
|
|
28
|
+
"interval": 2500,
|
|
29
|
+
"count": 5
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"type": "launch",
|
|
33
|
+
"which": "chromium",
|
|
34
|
+
"what": "Chromium browser"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"type": "url",
|
|
38
|
+
"which": "https://*",
|
|
39
|
+
"what": "any page"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"type": "test",
|
|
43
|
+
"which": "bulk",
|
|
44
|
+
"what": "count of visible elements"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"type": "test",
|
|
48
|
+
"which": "embAc",
|
|
49
|
+
"withItems": true,
|
|
50
|
+
"what": "active elements incorrectly embedded in each other"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"type": "test",
|
|
54
|
+
"which": "focAll",
|
|
55
|
+
"what": "Tab-focusability"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"type": "test",
|
|
59
|
+
"which": "focInd",
|
|
60
|
+
"revealAll": false,
|
|
61
|
+
"allowedDelay": 250,
|
|
62
|
+
"withItems": true,
|
|
63
|
+
"what": "focus indicators"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"type": "test",
|
|
67
|
+
"which": "focOp",
|
|
68
|
+
"withItems": true,
|
|
69
|
+
"what": "focusability and operability of elements"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"type": "test",
|
|
73
|
+
"which": "hover",
|
|
74
|
+
"headSize": 40,
|
|
75
|
+
"headSampleSize": 20,
|
|
76
|
+
"tailSampleSize": 15,
|
|
77
|
+
"withItems": true,
|
|
78
|
+
"what": "hover impacts"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"type": "test",
|
|
82
|
+
"which": "labClash",
|
|
83
|
+
"withItems": true,
|
|
84
|
+
"what": "unlabeled and mislabeled form controls"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"type": "test",
|
|
88
|
+
"which": "linkUl",
|
|
89
|
+
"withItems": true,
|
|
90
|
+
"what": "underlining of inline links"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"type": "test",
|
|
94
|
+
"which": "menuNav",
|
|
95
|
+
"withItems": true,
|
|
96
|
+
"what": "keyboard navigation within true-focus menus"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"type": "test",
|
|
100
|
+
"which": "radioSet",
|
|
101
|
+
"withItems": true,
|
|
102
|
+
"what": "grouping of radio buttons in fieldsets"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"type": "test",
|
|
106
|
+
"which": "role",
|
|
107
|
+
"what": "validity and necessity of role assignments"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"type": "test",
|
|
111
|
+
"which": "styleDiff",
|
|
112
|
+
"withItems": true,
|
|
113
|
+
"what": "style consistency of headings, buttons, and links"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"type": "test",
|
|
117
|
+
"which": "tabNav",
|
|
118
|
+
"withItems": true,
|
|
119
|
+
"what": "keyboard navigation within tab lists"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"type": "test",
|
|
123
|
+
"which": "zIndex",
|
|
124
|
+
"withItems": true,
|
|
125
|
+
"what": "elements with non-auto z indexes"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"type": "test",
|
|
129
|
+
"which": "alfa",
|
|
130
|
+
"what": "Siteimprove alfa"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"type": "test",
|
|
134
|
+
"which": "axe",
|
|
135
|
+
"detailLevel": 2,
|
|
136
|
+
"rules": [],
|
|
137
|
+
"what": "Axe core, all rules"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"type": "test",
|
|
141
|
+
"which": "continuum",
|
|
142
|
+
"what": "Continuum"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"type": "test",
|
|
146
|
+
"which": "htmlcs",
|
|
147
|
+
"what": "HTML CodeSniffer"
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"type": "test",
|
|
151
|
+
"which": "ibm",
|
|
152
|
+
"withItems": true,
|
|
153
|
+
"what": "IBM Accessibility Checker, with page content and again with URL"
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"type": "test",
|
|
157
|
+
"which": "nuVal",
|
|
158
|
+
"what": "Nu Html Checker"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"type": "test",
|
|
162
|
+
"which": "wave",
|
|
163
|
+
"reportType": 4,
|
|
164
|
+
"what": "WAVE, report-type 4"
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"type": "test",
|
|
168
|
+
"which": "tenon",
|
|
169
|
+
"id": "a",
|
|
170
|
+
"what": "Tenon API version 2 result retrieval"
|
|
171
|
+
}
|
|
172
|
+
]
|
|
173
|
+
}
|
package/tests/docType.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
docType
|
|
3
|
+
Derived from the bbc-a11y allDocumentsMustHaveAW3cRecommendedDoctype test.
|
|
4
|
+
This test reports a failure to equip the page document with a W3C-recommended doctype.
|
|
5
|
+
*/
|
|
6
|
+
exports.reporter = async page => {
|
|
7
|
+
// Identify the visible links without href attributes.
|
|
8
|
+
const docType = await page.evaluate(() => document.doctype);
|
|
9
|
+
return {result: {
|
|
10
|
+
docHasType: Boolean(docType)
|
|
11
|
+
}};
|
|
12
|
+
};
|
package/tests/focVis.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/*
|
|
2
|
+
focVis
|
|
3
|
+
Derived from the bbc-a11y elementsMustBeVisibleOnFocus test.
|
|
4
|
+
This test reports links that are off the display when focused.
|
|
5
|
+
*/
|
|
6
|
+
exports.reporter = async (page, withItems) => {
|
|
7
|
+
// Identify the initially visible links.
|
|
8
|
+
const badLinks = await page.$$eval('a:visible', links => {
|
|
9
|
+
// FUNCTION DEFINITION START
|
|
10
|
+
// Returns a space-minimized copy of a string.
|
|
11
|
+
const compact = string => string.replace(/[\t\n]/g, '').replace(/\s{2,}/g, ' ').trim();
|
|
12
|
+
// FUNCTION DEFINITION END
|
|
13
|
+
const badLinks = [];
|
|
14
|
+
links.forEach(link => {
|
|
15
|
+
link.focus();
|
|
16
|
+
if (link.offsetTop + link.offsetHeight <= 0 || link.offsetLeft + link.offsetWidth <= 0) {
|
|
17
|
+
badLinks.push(compact(link.textContent));
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
return badLinks;
|
|
21
|
+
});
|
|
22
|
+
const data = {
|
|
23
|
+
totals: badLinks.length
|
|
24
|
+
};
|
|
25
|
+
if (withItems) {
|
|
26
|
+
data.items = badLinks;
|
|
27
|
+
}
|
|
28
|
+
return {result: data};
|
|
29
|
+
};
|
package/tests/linkTo.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/*
|
|
2
|
+
linkTo
|
|
3
|
+
Derived from the bbc-a11y anchorsMustHaveHrefs test.
|
|
4
|
+
This test reports failures to equip links with destinations.
|
|
5
|
+
*/
|
|
6
|
+
exports.reporter = async (page, withItems) => {
|
|
7
|
+
// Identify the visible links without href attributes.
|
|
8
|
+
const badLinkTexts = await page.$$eval(
|
|
9
|
+
'a:not([href]):visible',
|
|
10
|
+
badLinks => {
|
|
11
|
+
// FUNCTION DEFINITION START
|
|
12
|
+
// Returns a space-minimized copy of a string.
|
|
13
|
+
const compact = string => string.replace(/[\t\n]/g, '').replace(/\s{2,}/g, ' ').trim();
|
|
14
|
+
// FUNCTION DEFINITION END
|
|
15
|
+
return badLinks.map(link => compact(link.textContent));
|
|
16
|
+
}
|
|
17
|
+
);
|
|
18
|
+
const data = {
|
|
19
|
+
totals: badLinkTexts.length
|
|
20
|
+
};
|
|
21
|
+
if (withItems) {
|
|
22
|
+
data.items = badLinkTexts;
|
|
23
|
+
}
|
|
24
|
+
return {result: data};
|
|
25
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/*
|
|
2
|
+
miniText
|
|
3
|
+
Derived from the bbc-a11y textCannotBeTooSmall test.
|
|
4
|
+
This test reports text nodes smaller than 11 pixels.
|
|
5
|
+
*/
|
|
6
|
+
exports.reporter = async (page, withItems) => {
|
|
7
|
+
// Identify the text nodes smaller than 11 pixels.
|
|
8
|
+
const miniTexts = await page.$$eval(
|
|
9
|
+
'body *:not(script, style):visible',
|
|
10
|
+
elements => {
|
|
11
|
+
// FUNCTION DEFINITION START
|
|
12
|
+
// Returns a space-minimized copy of a string.
|
|
13
|
+
const compact = string => string.replace(/[\t\n]/g, '').replace(/\s{2,}/g, ' ').trim();
|
|
14
|
+
// FUNCTION DEFINITION END
|
|
15
|
+
const textParents = new Set();
|
|
16
|
+
const miniTexts = [];
|
|
17
|
+
elements.forEach(element => {
|
|
18
|
+
element.childNodes.forEach(node => {
|
|
19
|
+
if (node.nodeType === 3) {
|
|
20
|
+
const nodeText = compact(node.textContent);
|
|
21
|
+
if (nodeText) {
|
|
22
|
+
textParents.add(element);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
textParents.forEach(textParent => {
|
|
28
|
+
const {fontSize} = window.getComputedStyle(textParent);
|
|
29
|
+
const pixels = Number.parseInt(fontSize);
|
|
30
|
+
if (pixels < 11) {
|
|
31
|
+
textParent.childNodes.forEach(node => {
|
|
32
|
+
if (node.nodeType === 3 && compact(node.textContent)) {
|
|
33
|
+
miniTexts.push(compact(node.textContent));
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return miniTexts;
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
const data = {
|
|
42
|
+
totals: miniTexts.length
|
|
43
|
+
};
|
|
44
|
+
if (withItems) {
|
|
45
|
+
data.items = miniTexts;
|
|
46
|
+
}
|
|
47
|
+
return {result: data};
|
|
48
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/*
|
|
2
|
+
nonTable
|
|
3
|
+
Derived from the bbc-a11y useTablesForData test. Crude heuristics omitted.
|
|
4
|
+
This test reports tables used for layout.
|
|
5
|
+
*/
|
|
6
|
+
exports.reporter = async (page, withItems) => {
|
|
7
|
+
// Identify the visible links without href attributes.
|
|
8
|
+
const badTableTexts = await page.$$eval('table', tables => {
|
|
9
|
+
const badTableTexts = [];
|
|
10
|
+
// FUNCTION DEFINITIONS START
|
|
11
|
+
// Returns a space-minimized copy of a string.
|
|
12
|
+
const compact = string => string.replace(/[\t\n]/g, '').replace(/\s{2,}/g, ' ').trim();
|
|
13
|
+
const addBad = table => {
|
|
14
|
+
badTableTexts.push(compact(table.outerHTML).slice(0, 100));
|
|
15
|
+
};
|
|
16
|
+
// FUNCTION DEFINITIONS END
|
|
17
|
+
tables.forEach(table => {
|
|
18
|
+
const role = table.getAttribute('role');
|
|
19
|
+
if (
|
|
20
|
+
table.caption
|
|
21
|
+
|| ['grid', 'treegrid'].includes(role)
|
|
22
|
+
|| table.querySelector('col, colgroup, tfoot, thead, th')
|
|
23
|
+
) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
else if (table.querySelector('table')) {
|
|
27
|
+
addBad(table);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
else if (
|
|
31
|
+
table.querySelectorAll('tr').length === 1
|
|
32
|
+
|| Math.max(
|
|
33
|
+
... Array
|
|
34
|
+
.from(table.querySelectorAll('tr'))
|
|
35
|
+
.map(row => Array.from(row.querySelectorAll('td')).length)
|
|
36
|
+
) === 1
|
|
37
|
+
) {
|
|
38
|
+
addBad(table);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
else if (table.querySelector('object, embed, applet, audio, video')) {
|
|
42
|
+
addBad(table);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
return badTableTexts;
|
|
47
|
+
});
|
|
48
|
+
const data = {
|
|
49
|
+
totals: badTableTexts.length
|
|
50
|
+
};
|
|
51
|
+
if (withItems) {
|
|
52
|
+
data.items = badTableTexts;
|
|
53
|
+
}
|
|
54
|
+
return {result: data};
|
|
55
|
+
};
|
package/tests/nuVal.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/*
|
|
2
|
+
nuVal
|
|
3
|
+
This test subjects a page to the Nu Html Checker.
|
|
4
|
+
*/
|
|
5
|
+
const https = require('https');
|
|
6
|
+
exports.reporter = async page => {
|
|
7
|
+
const pageContent = await page.content();
|
|
8
|
+
// Get the data from a Nu validation.
|
|
9
|
+
const data = await new Promise((resolve, reject) => {
|
|
10
|
+
try {
|
|
11
|
+
const request = https.request(
|
|
12
|
+
{
|
|
13
|
+
// Alternatives (more timeout-prone): host=validator.nu; path=/?parser=html@out=json
|
|
14
|
+
host: 'validator.w3.org',
|
|
15
|
+
path: '/nu/?parser=html&out=json',
|
|
16
|
+
method: 'POST',
|
|
17
|
+
headers: {
|
|
18
|
+
'User-Agent': 'Mozilla/5.0',
|
|
19
|
+
'Content-Type': 'text/html; charset=utf-8'
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
response => {
|
|
23
|
+
let report = '';
|
|
24
|
+
response.on('data', chunk => {
|
|
25
|
+
report += chunk;
|
|
26
|
+
});
|
|
27
|
+
// When the data arrive:
|
|
28
|
+
response.on('end', async () => {
|
|
29
|
+
try {
|
|
30
|
+
// Delete unnecessary properties.
|
|
31
|
+
const result = JSON.parse(report);
|
|
32
|
+
return resolve(result);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
console.log(`Validation failed (${error.message})`);
|
|
36
|
+
return resolve({
|
|
37
|
+
prevented: true,
|
|
38
|
+
error: error.message,
|
|
39
|
+
report
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
request.write(pageContent);
|
|
46
|
+
request.end();
|
|
47
|
+
request.on('error', error => {
|
|
48
|
+
console.log(error.message);
|
|
49
|
+
return reject({
|
|
50
|
+
prevented: true,
|
|
51
|
+
error: error.message
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch(error) {
|
|
56
|
+
console.log(error.message);
|
|
57
|
+
return reject({
|
|
58
|
+
prevented: true,
|
|
59
|
+
error: error.message
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
return {result: data};
|
|
64
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/*
|
|
2
|
+
titledEl
|
|
3
|
+
Derived from the bbc-a11y titleAttributesOnlyOnInputs test.
|
|
4
|
+
This test reports title attributes on inappropriate elements.
|
|
5
|
+
*/
|
|
6
|
+
exports.reporter = async (page, withItems) => {
|
|
7
|
+
// Identify the inappropriate elements with title attributes.
|
|
8
|
+
const badTitleElements = await page.$$eval(
|
|
9
|
+
'[title]:not(input, button, textarea, select, iframe):visible',
|
|
10
|
+
badTitleElements => {
|
|
11
|
+
// FUNCTION DEFINITION START
|
|
12
|
+
// Returns a space-minimized copy of a string.
|
|
13
|
+
const compact = string => string.replace(/[\t\n]/g, '').replace(/\s{2,}/g, ' ').trim();
|
|
14
|
+
// FUNCTION DEFINITION END
|
|
15
|
+
return badTitleElements.map(element => ({
|
|
16
|
+
tagName: element.tagName,
|
|
17
|
+
text: compact(element.textContent),
|
|
18
|
+
title: compact(element.title)
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
const data = {
|
|
23
|
+
totals: badTitleElements.length
|
|
24
|
+
};
|
|
25
|
+
if (withItems) {
|
|
26
|
+
data.items = badTitleElements;
|
|
27
|
+
}
|
|
28
|
+
return {result: data};
|
|
29
|
+
};
|