testaro 14.4.0 → 14.5.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 +17 -4
- package/actSpecs.js +5 -43
- package/package.json +1 -1
- package/run.js +12 -16
- package/standardize.js +50 -71
- package/testaro/embAc.js +2 -2
- package/tests/alfa.js +2 -1
- package/tests/axe.js +3 -2
- package/tests/continuum.js +2 -1
- package/tests/htmlcs.js +2 -1
- package/tests/ibm.js +2 -1
- package/tests/nuVal.js +7 -6
- package/tests/qualWeb.js +3 -2
- package/tests/tenon.js +15 -1
- package/tests/testaro.js +39 -20
- package/tests/wave.js +19 -1
package/README.md
CHANGED
|
@@ -348,9 +348,9 @@ This act causes Testaro to alter the `display` and `visibility` style properties
|
|
|
348
348
|
|
|
349
349
|
###### Introduction
|
|
350
350
|
|
|
351
|
-
An act of type `test` performs
|
|
351
|
+
An act of type `test` performs the tests of a tool and reports a result. The result may indicate that a page passes or fails requirements. Typically, accessibility tests report successes and failures. But a test in Testaro is defined less restrictively, so it can report any result. As one example, the Testaro `elements` test reports facts about certain elements on a page, without asserting that those facts are successes or failures.
|
|
352
352
|
|
|
353
|
-
The `which` property of a `test` act identifies
|
|
353
|
+
The `which` property of a `test` act identifies a tool, such as `alfa`.
|
|
354
354
|
|
|
355
355
|
###### Configuration
|
|
356
356
|
|
|
@@ -360,20 +360,25 @@ Every test in Testaro must have:
|
|
|
360
360
|
|
|
361
361
|
The `actSpecs.js` file (described in detail below) contains a specification for any `test` act, namely:
|
|
362
362
|
|
|
363
|
-
```
|
|
363
|
+
```javascript
|
|
364
364
|
test: [
|
|
365
365
|
'Perform a test',
|
|
366
366
|
{
|
|
367
367
|
which: [true, 'string', 'isTest', 'test name'],
|
|
368
|
+
rules: [false, 'array', 'areStrings', 'rule IDs or specifications, if not all']
|
|
368
369
|
what: [false, 'string', 'hasLength', 'comment']
|
|
369
370
|
}
|
|
370
371
|
],
|
|
371
372
|
```
|
|
372
373
|
|
|
373
|
-
That means that a test act (i.e. an act with a `type` property having the value `'test'`) must have a string-valued `which` property naming a tool and may optionally have a string-valued `what` property describing the tool.
|
|
374
|
+
That means that a test act (i.e. an act with a `type` property having the value `'test'`) must have a string-valued `which` property naming a tool and may optionally have an array-valued `rules` property restricting the tests to be reported and/or a string-valued `what` property describing the tool and/or the tests.
|
|
374
375
|
|
|
375
376
|
If a particular test act either must have or may have any other properties, those properties are specified in the `tests` property in `actSpecs.js`.
|
|
376
377
|
|
|
378
|
+
When you include a `rules` property, you limit the tests of the tool that are performed or reported. For some tools (`alfa`, `axe`, `continuum`, `htmlcs`, `qualWeb`, and `testaro`), only the specified tests are performed. Other tools (`ibm`, `nuVal`, `tenon`, and `wave`) do not allow such a limitation, so, for those tools, all tests are performed but results are reported from only the specified tests.
|
|
379
|
+
|
|
380
|
+
The `nuVal` and `testaro` tools require specific formats for the `rules` property. Those formats are described below in the sections about those tools.
|
|
381
|
+
|
|
377
382
|
###### Examples
|
|
378
383
|
|
|
379
384
|
An example of a `test` act is:
|
|
@@ -521,6 +526,12 @@ These changes were proposed as pull requests 1333 and 1334 (https://github.com/I
|
|
|
521
526
|
|
|
522
527
|
The `ibm` tool is one of two tools (`testaro` is the other) with a `withItems` property. If you set `withItems` to `false`, the result includes the counts of “violations” and “recommendations”, but no information about the rules that gave rise to them.
|
|
523
528
|
|
|
529
|
+
###### Nu Html Checker
|
|
530
|
+
|
|
531
|
+
The `nuVal` tool performs the tests of the Nu Html Checker.
|
|
532
|
+
|
|
533
|
+
Its `rules` argument is **not** an array of rule IDs, but instead is an array of rule _specifications_. A rule specification for `nuVal` is a string with the format `type:substring`, where `type` is replaced with a message type (namely `info` or `error`) and `substring` is replaced with any substring of a message. This `rules` format arises from the fact that `nuVal` generates customized messages and does not accompany them with rule identifiers. Thus, by choosing a type and a substring, you are deciding that any message of that type that includes that substring will be deemed a `nuVal` rule.
|
|
534
|
+
|
|
524
535
|
###### QualWeb
|
|
525
536
|
|
|
526
537
|
The `qualWeb` tool performs the ACT rules, WCAG Techniques, and best-practices tests of QualWeb. Only failures and warnings are included in the report. The EARL report of QualWeb is not generated, because it is equivalent to the report of the ACT rules tests.
|
|
@@ -571,6 +582,8 @@ If a `tenon` test act is included in a job, environment variables named `TENON_U
|
|
|
571
582
|
|
|
572
583
|
If you do not specify rules when using the `testaro` tool, Testaro will test for the rules listed in the `evalRules` object of the `tests/testaro.js` file.
|
|
573
584
|
|
|
585
|
+
The `rules` property for `testaro` is an array whose first item is either `'y'` or `'n'` and whose remaining items are rule IDs. If `'y'`, then only the specified rules’ tests are performed. If `'n'`, then all the evaluative tests are performed, except for the specified rules.
|
|
586
|
+
|
|
574
587
|
It has been found that the `motion` test of the `testaro` tool measures motion only when the `webkit` browser type is in use. If you want to use `testaro` with different browser types for different tests, you can include 2 or 3 `testaro` test acts in your job, specifying different browser types and different rules.
|
|
575
588
|
|
|
576
589
|
The `testaro` tool (like the `ibm` tool) has a `withItems` property. If you set it to `false`, the `standardResult` object of `testaro` will contain an `instances` property with summaries that identify issues and instance counts. If you set it to `true`, some of the instances will be itemized.
|
package/actSpecs.js
CHANGED
|
@@ -122,10 +122,10 @@ exports.actSpecs = {
|
|
|
122
122
|
}
|
|
123
123
|
],
|
|
124
124
|
test: [
|
|
125
|
-
'Perform
|
|
125
|
+
'Perform tests of a tool',
|
|
126
126
|
{
|
|
127
|
-
which: [true, 'string', 'isTest', '
|
|
128
|
-
|
|
127
|
+
which: [true, 'string', 'isTest', 'tool name'],
|
|
128
|
+
rules: [false, 'array', 'areStrings', 'rule IDs or specifications, if not all']
|
|
129
129
|
}
|
|
130
130
|
],
|
|
131
131
|
text: [
|
|
@@ -153,34 +153,10 @@ exports.actSpecs = {
|
|
|
153
153
|
]
|
|
154
154
|
},
|
|
155
155
|
tests: {
|
|
156
|
-
alfa: [
|
|
157
|
-
'Perform alfa tests',
|
|
158
|
-
{
|
|
159
|
-
rules: [false, 'array', 'areStrings', 'rule names (e.g., r25), if not all']
|
|
160
|
-
}
|
|
161
|
-
],
|
|
162
156
|
axe: [
|
|
163
157
|
'Perform Axe tests',
|
|
164
158
|
{
|
|
165
|
-
detailLevel: [true, 'number', '', '0 = least, 4 = most']
|
|
166
|
-
rules: [true, 'array', 'areStrings', 'rule names, or empty if all']
|
|
167
|
-
}
|
|
168
|
-
],
|
|
169
|
-
continuum: [
|
|
170
|
-
'Perform Continuum tests',
|
|
171
|
-
{
|
|
172
|
-
rules: [false, 'array', 'areNumbers', 'rule numbers (e.g., 25), if not all']
|
|
173
|
-
}
|
|
174
|
-
],
|
|
175
|
-
htmlcs: [
|
|
176
|
-
'Perform HTML CodeSniffer tests',
|
|
177
|
-
{
|
|
178
|
-
rules: [
|
|
179
|
-
false,
|
|
180
|
-
'array',
|
|
181
|
-
'areStrings',
|
|
182
|
-
'rule names (e.g., Principle1.Guideline1_4.1_4_9), if not all'
|
|
183
|
-
]
|
|
159
|
+
detailLevel: [true, 'number', '', '0 = least, 4 = most']
|
|
184
160
|
}
|
|
185
161
|
],
|
|
186
162
|
ibm: [
|
|
@@ -189,26 +165,13 @@ exports.actSpecs = {
|
|
|
189
165
|
withItems: [true, 'boolean', '', 'itemize'],
|
|
190
166
|
withNewContent: [
|
|
191
167
|
true, 'boolean', '', 'true: use a URL; false: use page content'
|
|
192
|
-
],
|
|
193
|
-
rules: [false, 'array', 'areStrings', 'rule names (e.g., RPT_Elem_UniqueId), if not all']
|
|
194
|
-
}
|
|
195
|
-
],
|
|
196
|
-
nuVal: [
|
|
197
|
-
'Perform Nu Html Checker tests',
|
|
198
|
-
{
|
|
199
|
-
messages: [
|
|
200
|
-
false,
|
|
201
|
-
'array',
|
|
202
|
-
'areStrings',
|
|
203
|
-
'message specifications (type and start of message, e.g., error:Bad value), if not all'
|
|
204
168
|
]
|
|
205
169
|
}
|
|
206
170
|
],
|
|
207
171
|
qualWeb: [
|
|
208
172
|
'Perform QualWeb tests',
|
|
209
173
|
{
|
|
210
|
-
withNewContent: [true, 'boolean', '', 'whether to use a URL instead of page content']
|
|
211
|
-
rules: [false, 'array', 'areStrings', 'QualWeb or ACT IDs of ACT rules to include, if not all']
|
|
174
|
+
withNewContent: [true, 'boolean', '', 'whether to use a URL instead of page content']
|
|
212
175
|
}
|
|
213
176
|
],
|
|
214
177
|
tenon: [
|
|
@@ -221,7 +184,6 @@ exports.actSpecs = {
|
|
|
221
184
|
'Perform Testaro tests',
|
|
222
185
|
{
|
|
223
186
|
withItems: [true, 'boolean', '', 'itemize'],
|
|
224
|
-
rules: [false, 'array', 'areStrings', 'IDs of rules to include if array starts with y or exclude if with n, if not all evaluative rules'],
|
|
225
187
|
args: [false, 'object', 'areArrays', 'extra args (object with rule properties and arrays of argument values as values ({focInd: [false, 250], hover: [-1], motion: [2500, 2500, 5]} by default'],
|
|
226
188
|
}
|
|
227
189
|
],
|
package/package.json
CHANGED
package/run.js
CHANGED
|
@@ -36,15 +36,12 @@ const tests = {
|
|
|
36
36
|
alfa: 'alfa',
|
|
37
37
|
axe: 'Axe',
|
|
38
38
|
continuum: 'Level Access Continuum, community edition',
|
|
39
|
-
elements: 'data on specified elements',
|
|
40
39
|
htmlcs: 'HTML CodeSniffer WCAG 2.1 AA ruleset',
|
|
41
40
|
ibm: 'IBM Accessibility Checker',
|
|
42
41
|
nuVal: 'Nu Html Checker',
|
|
43
42
|
qualWeb: 'QualWeb',
|
|
44
43
|
tenon: 'Tenon',
|
|
45
44
|
testaro: 'Testaro',
|
|
46
|
-
textNodes: 'data on specified text nodes',
|
|
47
|
-
title: 'page title',
|
|
48
45
|
wave: 'WAVE',
|
|
49
46
|
};
|
|
50
47
|
// Browser types available in PlayWright.
|
|
@@ -164,6 +161,7 @@ const hasSubtype = (variable, subtype) => {
|
|
|
164
161
|
return isState(variable);
|
|
165
162
|
}
|
|
166
163
|
else {
|
|
164
|
+
console.log(`ERROR: ${subtype} not a known subtype`);
|
|
167
165
|
return false;
|
|
168
166
|
}
|
|
169
167
|
}
|
|
@@ -977,21 +975,18 @@ const doActs = async (report, actIndex, page) => {
|
|
|
977
975
|
tenonData.requestIDs[id] = responseID || '';
|
|
978
976
|
}
|
|
979
977
|
}
|
|
980
|
-
// Otherwise, if the act
|
|
978
|
+
// Otherwise, if the act performs the tests of a tool:
|
|
981
979
|
else if (act.type === 'test') {
|
|
982
980
|
// Add a description of the test to the act.
|
|
983
981
|
act.what = tests[act.which];
|
|
984
|
-
// Initialize the
|
|
985
|
-
const
|
|
986
|
-
//
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
// Add their values to the arguments.
|
|
993
|
-
args.push(...argProperties.map(propName => act[propName]));
|
|
994
|
-
}
|
|
982
|
+
// Initialize the options argument.
|
|
983
|
+
const options = {};
|
|
984
|
+
// Add any specified arguments to it.
|
|
985
|
+
Object.keys(act).forEach(key => {
|
|
986
|
+
if (! ['type', 'which'].includes(key)) {
|
|
987
|
+
options[key] = act[key];
|
|
988
|
+
}
|
|
989
|
+
});
|
|
995
990
|
// Conduct, report, and time the test.
|
|
996
991
|
const startTime = Date.now();
|
|
997
992
|
let testReport = {
|
|
@@ -1000,7 +995,8 @@ const doActs = async (report, actIndex, page) => {
|
|
|
1000
995
|
}
|
|
1001
996
|
};
|
|
1002
997
|
try {
|
|
1003
|
-
|
|
998
|
+
const args = [act.which === 'tenon' ? tenonData : page, options];
|
|
999
|
+
testReport = await require(`./tests/${act.which}`).reporter(... args);
|
|
1004
1000
|
const expectations = act.expect;
|
|
1005
1001
|
// If the test has expectations:
|
|
1006
1002
|
if (expectations) {
|
package/standardize.js
CHANGED
|
@@ -18,6 +18,22 @@ const cap = rawString => {
|
|
|
18
18
|
return '';
|
|
19
19
|
}
|
|
20
20
|
};
|
|
21
|
+
// Returns the tag name and the value of an id attribute from a substring of HTML code.
|
|
22
|
+
const getIdentifiers = code => {
|
|
23
|
+
let tagName = '';
|
|
24
|
+
let id = '';
|
|
25
|
+
if (code && typeof code === 'string' && code.length && /<.+/s.test(code)) {
|
|
26
|
+
const startTag = code.replace(/^[^<]*<|>.*/sg, '').trim();
|
|
27
|
+
if (startTag && startTag.length) {
|
|
28
|
+
tagName = startTag.replace(/\s.+$/s, '').toUpperCase();
|
|
29
|
+
const idArray = startTag.match(/\sid="([^"<>]+)"/);
|
|
30
|
+
if (idArray && idArray.length === 2) {
|
|
31
|
+
id = idArray[1];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return [tagName, id];
|
|
36
|
+
};
|
|
21
37
|
// Converts issue instances at an axe certainty level.
|
|
22
38
|
const doAxe = (result, standardResult, certainty) => {
|
|
23
39
|
if (result.details && result.details[certainty]) {
|
|
@@ -35,23 +51,13 @@ const doAxe = (result, standardResult, certainty) => {
|
|
|
35
51
|
critical: 1
|
|
36
52
|
};
|
|
37
53
|
const ordinalSeverity = severityWeights[node.impact] + (certainty === 'violations' ? 2 : 0);
|
|
38
|
-
const
|
|
39
|
-
let id = '';
|
|
40
|
-
if (node.target && node.target.length && node.target[0].startsWith('#')) {
|
|
41
|
-
id = node.target[0].slice(1);
|
|
42
|
-
}
|
|
43
|
-
else if (node.html) {
|
|
44
|
-
const idArray = node.html.match(/\sid="([^"]+)"/);
|
|
45
|
-
if (idArray && idArray.length === 2) {
|
|
46
|
-
id = idArray[1];
|
|
47
|
-
}
|
|
48
|
-
}
|
|
54
|
+
const identifiers = getIdentifiers(node.html);
|
|
49
55
|
const instance = {
|
|
50
56
|
issueID: rule.id,
|
|
51
57
|
what: Array.from(whatSet.values()).join('; '),
|
|
52
58
|
ordinalSeverity,
|
|
53
|
-
tagName,
|
|
54
|
-
id,
|
|
59
|
+
tagName: identifiers[0],
|
|
60
|
+
id: identifiers[1],
|
|
55
61
|
location: {
|
|
56
62
|
doc: 'dom',
|
|
57
63
|
type: 'selector',
|
|
@@ -96,18 +102,13 @@ const doNuVal = (result, standardResult, docType) => {
|
|
|
96
102
|
const items = result[docType] && result[docType].messages;
|
|
97
103
|
if (items && items.length) {
|
|
98
104
|
items.forEach(item => {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const tagNameLCArray = item.extract.match(
|
|
105
|
+
const identifiers = getIdentifiers(item.extract);
|
|
106
|
+
if (! identifiers[0] && item.message) {
|
|
107
|
+
const tagNameLCArray = item.message.match(
|
|
103
108
|
/^Element ([^ ]+)|^An (img) element| (meta|script) element| element (script)| tag (script)/
|
|
104
109
|
);
|
|
105
110
|
if (tagNameLCArray && tagNameLCArray.length > 1) {
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
const idArray = item.extract.match(/^.+\sid="([^"]+)"/);
|
|
109
|
-
if (idArray && idArray.length === 2) {
|
|
110
|
-
id = idArray[1];
|
|
111
|
+
identifiers[0] = tagNameLCArray[1].toUpperCase();
|
|
111
112
|
}
|
|
112
113
|
}
|
|
113
114
|
// Include the message twice, because in scoring it is likely to be replaced by a pattern.
|
|
@@ -115,8 +116,8 @@ const doNuVal = (result, standardResult, docType) => {
|
|
|
115
116
|
issueID: item.message,
|
|
116
117
|
what: item.message,
|
|
117
118
|
ordinalSeverity: -1,
|
|
118
|
-
tagName,
|
|
119
|
-
id,
|
|
119
|
+
tagName: identifiers[0],
|
|
120
|
+
id: identifiers[1],
|
|
120
121
|
location: {
|
|
121
122
|
doc: docType === 'pageContent' ? 'dom' : 'source',
|
|
122
123
|
type: 'line',
|
|
@@ -158,24 +159,13 @@ const doQualWeb = (result, standardResult, ruleClassName) => {
|
|
|
158
159
|
ruleResult.results.forEach(item => {
|
|
159
160
|
item.elements.forEach(element => {
|
|
160
161
|
const {htmlCode} = element;
|
|
161
|
-
|
|
162
|
-
let id = '';
|
|
163
|
-
if (htmlCode) {
|
|
164
|
-
const tagNameArray = htmlCode.match(/^<([^ >]+)/);
|
|
165
|
-
if (tagNameArray && tagNameArray.length === 2) {
|
|
166
|
-
tagName = tagNameArray[1].toUpperCase();
|
|
167
|
-
}
|
|
168
|
-
const idArray = htmlCode.match(/\sid="([^"]+)"/);
|
|
169
|
-
if (idArray && idArray.length === 2) {
|
|
170
|
-
id = idArray[1];
|
|
171
|
-
}
|
|
172
|
-
}
|
|
162
|
+
const identifiers = getIdentifiers(htmlCode);
|
|
173
163
|
const instance = {
|
|
174
164
|
issueID: rule,
|
|
175
165
|
what: ruleResult.description,
|
|
176
166
|
ordinalSeverity: severities[ruleClassName][item.verdict],
|
|
177
|
-
tagName,
|
|
178
|
-
id,
|
|
167
|
+
tagName: identifiers[0],
|
|
168
|
+
id: identifiers[1],
|
|
179
169
|
location: {
|
|
180
170
|
doc: 'dom',
|
|
181
171
|
type: 'selector',
|
|
@@ -235,26 +225,27 @@ const convert = (toolName, result, standardResult) => {
|
|
|
235
225
|
standardResult.totals = [result.totals.warnings, 0, 0, result.totals.failures];
|
|
236
226
|
result.items.forEach(item => {
|
|
237
227
|
const {codeLines} = item.target;
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
228
|
+
const code = Array.isArray(codeLines) ? codeLines.join(' ') : '';
|
|
229
|
+
const identifiers = getIdentifiers(code);
|
|
230
|
+
let tagName = item.target && item.target.tagName || '';
|
|
231
|
+
if (! tagName) {
|
|
232
|
+
const tagNameArray = item.target.path.match(/\/([a-z]+)\[\d+\]\/text\(\)\[\d+\]$/);
|
|
233
|
+
if (tagNameArray && tagNameArray.length === 2) {
|
|
234
|
+
tagName = tagNameArray[1];
|
|
244
235
|
}
|
|
245
236
|
}
|
|
246
237
|
const instance = {
|
|
247
238
|
issueID: item.rule.ruleID,
|
|
248
239
|
what: item.rule.ruleSummary,
|
|
249
240
|
ordinalSeverity: ['cantTell', '', '', 'failed'].indexOf(item.verdict),
|
|
250
|
-
tagName:
|
|
251
|
-
id,
|
|
241
|
+
tagName: tagName.toUpperCase() || identifiers[0],
|
|
242
|
+
id: identifiers[1],
|
|
252
243
|
location: {
|
|
253
244
|
doc: 'dom',
|
|
254
245
|
type: 'xpath',
|
|
255
246
|
spec: item.target.path
|
|
256
247
|
},
|
|
257
|
-
excerpt:
|
|
248
|
+
excerpt: cap(code)
|
|
258
249
|
};
|
|
259
250
|
standardResult.instances.push(instance);
|
|
260
251
|
});
|
|
@@ -321,26 +312,19 @@ const convert = (toolName, result, standardResult) => {
|
|
|
321
312
|
else if (toolName === 'ibm' && result.totals) {
|
|
322
313
|
standardResult.totals = [0, result.totals.recommendation, 0, result.totals.violation];
|
|
323
314
|
result.items.forEach(item => {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
if (item.path && item.path.dom) {
|
|
315
|
+
const identifiers = getIdentifiers(item.snippet);
|
|
316
|
+
if (! identifiers[0] && item.path && item.path.dom) {
|
|
327
317
|
const tagNameArray = item.path.dom.match(/^.+\/([^/[]+)/s);
|
|
328
318
|
if (tagNameArray && tagNameArray.length === 2) {
|
|
329
|
-
|
|
330
|
-
}
|
|
331
|
-
if (item.snippet) {
|
|
332
|
-
const idArray = item.snippet.match(/^.+\sid="([^"]+)"/s);
|
|
333
|
-
if (idArray && idArray.length === 2) {
|
|
334
|
-
id = idArray[1];
|
|
335
|
-
}
|
|
319
|
+
identifiers[0] = tagNameArray[1].toUpperCase();
|
|
336
320
|
}
|
|
337
321
|
}
|
|
338
322
|
const instance = {
|
|
339
323
|
issueID: item.ruleId,
|
|
340
324
|
what: item.message,
|
|
341
325
|
ordinalSeverity: ['', 'recommendation', '', 'violation'].indexOf(item.level),
|
|
342
|
-
tagName,
|
|
343
|
-
id,
|
|
326
|
+
tagName: identifiers[0],
|
|
327
|
+
id: identifiers[1],
|
|
344
328
|
location: {
|
|
345
329
|
doc: 'dom',
|
|
346
330
|
type: 'xpath',
|
|
@@ -391,18 +375,13 @@ const convert = (toolName, result, standardResult) => {
|
|
|
391
375
|
// tenon
|
|
392
376
|
else if (toolName === 'tenon' && result.data && result.data.resultSet) {
|
|
393
377
|
result.data.resultSet.forEach(item => {
|
|
394
|
-
|
|
395
|
-
|
|
378
|
+
const identifiers = getIdentifiers(
|
|
379
|
+
item.errorSnippet.replace(/</g, '<').replace(/>/g, '>')
|
|
380
|
+
);
|
|
381
|
+
if (! identifiers[0] && item.xpath) {
|
|
396
382
|
const tagNameArray = item.xpath.match(/^.+\/([^/[]+)/);
|
|
397
383
|
if (tagNameArray && tagNameArray.length === 2) {
|
|
398
|
-
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
let id = '';
|
|
402
|
-
if (item.errorSnippet) {
|
|
403
|
-
const idArray = item.errorSnippet.match(/^.+\sid="([^"]+)"/);
|
|
404
|
-
if (idArray && idArray.length === 2) {
|
|
405
|
-
id = idArray[1];
|
|
384
|
+
identifiers[0] = tagNameArray[1].toUpperCase();
|
|
406
385
|
}
|
|
407
386
|
}
|
|
408
387
|
const instance = {
|
|
@@ -411,8 +390,8 @@ const convert = (toolName, result, standardResult) => {
|
|
|
411
390
|
ordinalSeverity: Math.min(
|
|
412
391
|
3, Math.max(0, Math.round((item.certainty || 0) * (item.priority || 0) / 3333))
|
|
413
392
|
),
|
|
414
|
-
tagName,
|
|
415
|
-
id,
|
|
393
|
+
tagName: identifiers[0],
|
|
394
|
+
id: identifiers[1],
|
|
416
395
|
location: {
|
|
417
396
|
doc: 'dom',
|
|
418
397
|
type: 'xpath',
|
package/testaro/embAc.js
CHANGED
|
@@ -32,7 +32,7 @@ exports.reporter = async (page, withItems) => await page.$$eval(
|
|
|
32
32
|
}
|
|
33
33
|
items.push({
|
|
34
34
|
embeddedElement: bad.tagName,
|
|
35
|
-
embeddedID: bad.id,
|
|
35
|
+
embeddedID: bad.id || '',
|
|
36
36
|
excerpt: compact(container.outerHTML)
|
|
37
37
|
});
|
|
38
38
|
}
|
|
@@ -49,7 +49,7 @@ exports.reporter = async (page, withItems) => await page.$$eval(
|
|
|
49
49
|
what: `${item.embeddedElement} element is embedded in a link or button`,
|
|
50
50
|
ordinalSeverity: 2,
|
|
51
51
|
tagName: item.embeddedElement,
|
|
52
|
-
id: item.
|
|
52
|
+
id: item.embeddedID,
|
|
53
53
|
location: {
|
|
54
54
|
doc: '',
|
|
55
55
|
type: '',
|
package/tests/alfa.js
CHANGED
|
@@ -12,7 +12,8 @@ let alfaRules = require('@siteimprove/alfa-rules').default;
|
|
|
12
12
|
// FUNCTIONS
|
|
13
13
|
|
|
14
14
|
// Conducts and reports an alfa test.
|
|
15
|
-
exports.reporter = async (page,
|
|
15
|
+
exports.reporter = async (page, options) => {
|
|
16
|
+
const {rules} = options;
|
|
16
17
|
// If only some rules are to be employed:
|
|
17
18
|
if (rules && rules.length) {
|
|
18
19
|
// Remove the other rules.
|
package/tests/axe.js
CHANGED
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
const {injectAxe, getAxeResults} = require('axe-playwright');
|
|
23
23
|
// FUNCTIONS
|
|
24
24
|
// Conducts and reports an Axe test.
|
|
25
|
-
exports.reporter = async (page,
|
|
25
|
+
exports.reporter = async (page, options) => {
|
|
26
|
+
const {detailLevel, rules} = options;
|
|
26
27
|
// Initialize the report.
|
|
27
28
|
let data = {};
|
|
28
29
|
// Inject axe-core into the page.
|
|
@@ -38,7 +39,7 @@ exports.reporter = async (page, detailLevel, rules = []) => {
|
|
|
38
39
|
const axeOptions = {
|
|
39
40
|
resultTypes: ['violations', 'incomplete']
|
|
40
41
|
};
|
|
41
|
-
if (rules.length) {
|
|
42
|
+
if (rules && rules.length) {
|
|
42
43
|
axeOptions.runOnly = rules;
|
|
43
44
|
}
|
|
44
45
|
else {
|
package/tests/continuum.js
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
// FUNCTIONS
|
|
8
8
|
// Runs Continuum on the page.
|
|
9
|
-
exports.reporter = async (page,
|
|
9
|
+
exports.reporter = async (page, options) => {
|
|
10
|
+
const {rules} = options;
|
|
10
11
|
let result = {};
|
|
11
12
|
// Inject the continuum scripts into the page, exposing the continuum object.
|
|
12
13
|
for (const fileName of ['continuum.conf', 'AccessEngine.community', 'Continuum.community']) {
|
package/tests/htmlcs.js
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
// FUNCTIONS
|
|
7
7
|
// Runs HTML CodeSniffer on the page.
|
|
8
|
-
exports.reporter = async (page,
|
|
8
|
+
exports.reporter = async (page, options) => {
|
|
9
|
+
const {rules} = options;
|
|
9
10
|
const result = {};
|
|
10
11
|
// Add the HTMLCS script to the page.
|
|
11
12
|
await page.addScriptTag({
|
package/tests/ibm.js
CHANGED
|
@@ -122,7 +122,8 @@ const doTest = async (content, withItems, timeLimit, rules) => {
|
|
|
122
122
|
}
|
|
123
123
|
};
|
|
124
124
|
// Returns results of an IBM test.
|
|
125
|
-
exports.reporter = async (page,
|
|
125
|
+
exports.reporter = async (page, options) => {
|
|
126
|
+
const {withItems, withNewContent, rules} = options;
|
|
126
127
|
const contentType = withNewContent ? 'new' : 'existing';
|
|
127
128
|
console.log(`>>>>>> Content type: ${contentType}`);
|
|
128
129
|
let result;
|
package/tests/nuVal.js
CHANGED
|
@@ -18,7 +18,8 @@ const fs = require('fs/promises');
|
|
|
18
18
|
|
|
19
19
|
// ########## FUNCTIONS
|
|
20
20
|
|
|
21
|
-
exports.reporter = async (page,
|
|
21
|
+
exports.reporter = async (page, options) => {
|
|
22
|
+
const {rules} = options;
|
|
22
23
|
// Get the browser-parsed page.
|
|
23
24
|
const pageContent = await page.content();
|
|
24
25
|
// Get the page source.
|
|
@@ -63,12 +64,12 @@ exports.reporter = async (page, messages) => {
|
|
|
63
64
|
// Delete left and right quotation marks and their erratic invalid replacements.
|
|
64
65
|
data[page[0]] = nuDataClean;
|
|
65
66
|
// If there is a report and restrictions on the report messages were specified:
|
|
66
|
-
if (! data[page[0]].error &&
|
|
67
|
+
if (! data[page[0]].error && rules && Array.isArray(rules) && rules.length) {
|
|
67
68
|
// Remove all messages except those specified.
|
|
68
|
-
const
|
|
69
|
-
data[page[0]].messages = data[page[0]].messages.filter(message =>
|
|
70
|
-
|
|
71
|
-
&& message.message.
|
|
69
|
+
const ruleSpecs = rules.map(ruleSpec => ruleSpec.split(':', 2));
|
|
70
|
+
data[page[0]].messages = data[page[0]].messages.filter(message => ruleSpecs.some(
|
|
71
|
+
ruleSpec => message.type === ruleSpec[0]
|
|
72
|
+
&& message.message.includes(ruleSpec[1])
|
|
72
73
|
));
|
|
73
74
|
}
|
|
74
75
|
}
|
package/tests/qualWeb.js
CHANGED
|
@@ -11,11 +11,12 @@ const clusterOptions = {
|
|
|
11
11
|
};
|
|
12
12
|
// FUNCTIONS
|
|
13
13
|
// Conducts and reports a QualWeb test.
|
|
14
|
-
exports.reporter = async (page,
|
|
14
|
+
exports.reporter = async (page, options) => {
|
|
15
|
+
const {withNewContent, rules} = options;
|
|
15
16
|
// Initialize the report.
|
|
16
17
|
// Start the QualWeb core engine.
|
|
17
18
|
await qualWeb.start(clusterOptions);
|
|
18
|
-
// Specify the
|
|
19
|
+
// Specify the test options.
|
|
19
20
|
const qualWebOptions = {
|
|
20
21
|
log: {
|
|
21
22
|
console: true
|
package/tests/tenon.js
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
const https = require('https');
|
|
6
6
|
// Wait until a time limit in seconds expires.
|
|
7
7
|
const wait = timeLimit => new Promise(resolve => setTimeout(resolve, 1000 * timeLimit));
|
|
8
|
-
exports.reporter = async (tenonData,
|
|
8
|
+
exports.reporter = async (tenonData, options) => {
|
|
9
|
+
const {id, rules} = options;
|
|
9
10
|
if (tenonData && tenonData.accessToken && tenonData.requestIDs && tenonData.requestIDs[id]) {
|
|
10
11
|
// Shared request options.
|
|
11
12
|
const requestOptions = {
|
|
@@ -59,6 +60,19 @@ exports.reporter = async (tenonData, id) => {
|
|
|
59
60
|
});
|
|
60
61
|
resultRequest.end();
|
|
61
62
|
});
|
|
63
|
+
// If any rules were specified and any test results exist:
|
|
64
|
+
if (
|
|
65
|
+
rules
|
|
66
|
+
&& rules.length
|
|
67
|
+
&& testResult.data
|
|
68
|
+
&& testResult.data.resultSet
|
|
69
|
+
&& testResult.data.resultSet.length
|
|
70
|
+
) {
|
|
71
|
+
// Delete the results of tests of rules not specified.
|
|
72
|
+
const {resultSet} = testResult.data;
|
|
73
|
+
testResult.data.resultSet = resultSet
|
|
74
|
+
.filter(result => ! rules.includes(result.tID.toString()));
|
|
75
|
+
}
|
|
62
76
|
return testResult;
|
|
63
77
|
};
|
|
64
78
|
// Get the test status (not reliable: may say 200 instead of 202).
|
package/tests/testaro.js
CHANGED
|
@@ -42,31 +42,50 @@ const etcRules = {
|
|
|
42
42
|
// FUNCTIONS
|
|
43
43
|
|
|
44
44
|
// Conducts and reports a Testaro test.
|
|
45
|
-
exports.reporter = async (
|
|
46
|
-
|
|
47
|
-
)
|
|
45
|
+
exports.reporter = async (page, options) => {
|
|
46
|
+
const {withItems, args} = options;
|
|
47
|
+
const argRules = args ? Object.keys(args) : null;
|
|
48
|
+
const rules = options.rules || ['y', ... Object.keys(evalRules)];
|
|
48
49
|
// Initialize the data.
|
|
49
50
|
const data = {
|
|
50
51
|
rules: {}
|
|
51
52
|
};
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
//
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
53
|
+
// If the rule specification is valid:
|
|
54
|
+
if (
|
|
55
|
+
rules.length > 1
|
|
56
|
+
&& ['y', 'n'].includes(rules[0])
|
|
57
|
+
&& rules.slice(1).every(rule => evalRules[rule] || etcRules[rule])
|
|
58
|
+
) {
|
|
59
|
+
// For each rule invoked:
|
|
60
|
+
const realRules = rules[0] === 'y'
|
|
61
|
+
? rules.slice(1)
|
|
62
|
+
: Object.keys(evalRules).filter(ruleID => ! rules.slice(1).includes(ruleID));
|
|
63
|
+
for (const rule of realRules) {
|
|
64
|
+
// Initialize an argument array.
|
|
65
|
+
const ruleArgs = [page, withItems];
|
|
66
|
+
// If the rule has extra arguments:
|
|
67
|
+
if (argRules && argRules.includes(rule)) {
|
|
68
|
+
// Add them to the argument array.
|
|
69
|
+
ruleArgs.push(... args[rule]);
|
|
70
|
+
}
|
|
71
|
+
// Test the page.
|
|
72
|
+
const what = evalRules[rule] || etcRules[rule];
|
|
73
|
+
if (! data.rules[rule]) {
|
|
74
|
+
data.rules[rule] = {};
|
|
75
|
+
}
|
|
76
|
+
data.rules[rule].what = what;
|
|
77
|
+
console.log(`>>>>>> ${rule} (${what})`);
|
|
78
|
+
const report = await require(`../testaro/${rule}`).reporter(... ruleArgs);
|
|
79
|
+
Object.keys(report).forEach(key => {
|
|
80
|
+
data.rules[rule][key] = report[key];
|
|
81
|
+
});
|
|
64
82
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
83
|
+
}
|
|
84
|
+
// Otherwise, i.e. if the rule specification is invalid:
|
|
85
|
+
else {
|
|
86
|
+
console.log('ERROR: Testaro rule specification invalid');
|
|
87
|
+
data.prevented = true;
|
|
88
|
+
data.error = 'ERROR: Rule specification invalid';
|
|
70
89
|
}
|
|
71
90
|
return {result: data};
|
|
72
91
|
};
|
package/tests/wave.js
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
const fs = require('fs/promises');
|
|
8
8
|
const https = require('https');
|
|
9
|
-
exports.reporter = async (page,
|
|
9
|
+
exports.reporter = async (page, options) => {
|
|
10
|
+
const {reportType, rules} = options;
|
|
10
11
|
const waveKey = process.env.WAVE_KEY;
|
|
11
12
|
// Get the data from a WAVE test.
|
|
12
13
|
const data = await new Promise(resolve => {
|
|
@@ -30,6 +31,23 @@ exports.reporter = async (page, reportType) => {
|
|
|
30
31
|
delete categories.feature;
|
|
31
32
|
delete categories.structure;
|
|
32
33
|
delete categories.aria;
|
|
34
|
+
// If rules were specified:
|
|
35
|
+
if (rules && rules.length) {
|
|
36
|
+
// Delete the results of tests for other rules.
|
|
37
|
+
['error', 'contrast', 'alert'].forEach(category => {
|
|
38
|
+
if (
|
|
39
|
+
categories[category]
|
|
40
|
+
&& categories[category].items
|
|
41
|
+
&& categories[category].items.length
|
|
42
|
+
) {
|
|
43
|
+
Object.keys(categories[category].items).forEach(ruleID => {
|
|
44
|
+
if (! rules.includes(ruleID)) {
|
|
45
|
+
delete categories[category].items[ruleID];
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
33
51
|
// Add WCAG information from the WAVE documentation.
|
|
34
52
|
const waveDocJSON = await fs.readFile('procs/wavedoc.json');
|
|
35
53
|
const waveDoc = JSON.parse(waveDocJSON);
|