testaro 65.0.4 → 65.1.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/package.json +1 -1
- package/procs/standardize.js +24 -20
- package/run.js +1 -1
- package/tests/alfa.js +56 -34
package/package.json
CHANGED
package/procs/standardize.js
CHANGED
|
@@ -393,66 +393,70 @@ const convert = (toolName, data, result, standardResult) => {
|
|
|
393
393
|
// alfa
|
|
394
394
|
else if (toolName === 'alfa' && result.totals) {
|
|
395
395
|
result.items.forEach(item => {
|
|
396
|
-
const {
|
|
396
|
+
const {outcome, rule, violator} = item;
|
|
397
|
+
const {ruleID, ruleSummary} = rule;
|
|
398
|
+
const {codeLines, path, pathID, tagName, text} = violator;
|
|
397
399
|
const code = Array.isArray(codeLines) ? codeLines.join(' ') : '';
|
|
398
400
|
const identifiers = getIdentifiers(code);
|
|
399
|
-
const tagNameArray =
|
|
400
|
-
&& item.target.path
|
|
401
|
-
&& item.target.path.match(/^.*\/([a-z]+)\[\d+\]/);
|
|
401
|
+
const tagNameArray = path && path.match(/^.*\/([a-z]+)\[\d+\]/);
|
|
402
402
|
if (tagNameArray && tagNameArray.length === 2) {
|
|
403
403
|
identifiers[0] = tagNameArray[1].toUpperCase();
|
|
404
404
|
}
|
|
405
|
-
const {rule, target} = item;
|
|
406
405
|
let instance;
|
|
407
|
-
if (
|
|
406
|
+
if (outcome === 'failed') {
|
|
408
407
|
instance = {
|
|
409
|
-
ruleID
|
|
410
|
-
what:
|
|
408
|
+
ruleID,
|
|
409
|
+
what: ruleSummary,
|
|
411
410
|
ordinalSeverity: 3,
|
|
412
|
-
tagName: identifiers[0],
|
|
411
|
+
tagName: tagName || identifiers[0],
|
|
413
412
|
id: identifiers[1],
|
|
414
413
|
location: {
|
|
415
414
|
doc: 'dom',
|
|
416
415
|
type: 'xpath',
|
|
417
|
-
spec:
|
|
416
|
+
spec: path
|
|
418
417
|
},
|
|
419
418
|
excerpt: cap(code),
|
|
419
|
+
text,
|
|
420
420
|
boxID: '',
|
|
421
|
-
pathID
|
|
421
|
+
pathID
|
|
422
422
|
};
|
|
423
423
|
standardResult.instances.push(instance);
|
|
424
424
|
}
|
|
425
425
|
else if (item.verdict === 'cantTell') {
|
|
426
|
-
if (['r66', 'r69'].includes(
|
|
426
|
+
if (['r66', 'r69'].includes(ruleID)) {
|
|
427
427
|
instance = {
|
|
428
428
|
ruleID: 'cantTellTextContrast',
|
|
429
|
-
what: `cannot test for rule ${
|
|
429
|
+
what: `cannot test for rule ${ruleID}: ${ruleSummary}`,
|
|
430
430
|
ordinalSeverity: 0,
|
|
431
|
-
tagName: identifiers[0],
|
|
431
|
+
tagName: tagName || identifiers[0],
|
|
432
432
|
id: identifiers[1],
|
|
433
433
|
location: {
|
|
434
434
|
doc: 'dom',
|
|
435
435
|
type: 'xpath',
|
|
436
|
-
spec:
|
|
436
|
+
spec: path
|
|
437
437
|
},
|
|
438
|
-
excerpt: cap(code)
|
|
438
|
+
excerpt: cap(code),
|
|
439
|
+
text,
|
|
440
|
+
boxID: '',
|
|
441
|
+
pathID
|
|
439
442
|
};
|
|
440
443
|
}
|
|
441
444
|
else {
|
|
442
445
|
instance = {
|
|
443
446
|
ruleID: 'cantTell',
|
|
444
|
-
what: `cannot test for rule ${
|
|
447
|
+
what: `cannot test for rule ${ruleID}: ${ruleSummary}`,
|
|
445
448
|
ordinalSeverity: 0,
|
|
446
|
-
tagName: identifiers[0],
|
|
449
|
+
tagName: tagName || identifiers[0],
|
|
447
450
|
id: identifiers[1],
|
|
448
451
|
location: {
|
|
449
452
|
doc: 'dom',
|
|
450
453
|
type: 'xpath',
|
|
451
|
-
spec:
|
|
454
|
+
spec: path
|
|
452
455
|
},
|
|
453
456
|
excerpt: cap(code),
|
|
457
|
+
text,
|
|
454
458
|
boxID: '',
|
|
455
|
-
pathID
|
|
459
|
+
pathID
|
|
456
460
|
};
|
|
457
461
|
}
|
|
458
462
|
standardResult.instances.push(instance);
|
package/run.js
CHANGED
|
@@ -1765,7 +1765,7 @@ const doActs = async (report, opts = {}) => {
|
|
|
1765
1765
|
.replace(/ data-testaro-id="[^" ]* /g, ' ');
|
|
1766
1766
|
}
|
|
1767
1767
|
pathID = instance.pathID;
|
|
1768
|
-
// If the instance has no text property:
|
|
1768
|
+
// If the instance has no or an empty text property:
|
|
1769
1769
|
if (! instance.text) {
|
|
1770
1770
|
const {excerpt} = instance;
|
|
1771
1771
|
// If the instance has a markup-free non-empty excerpt:
|
package/tests/alfa.js
CHANGED
|
@@ -17,12 +17,13 @@
|
|
|
17
17
|
|
|
18
18
|
let alfaRules = require('@siteimprove/alfa-rules').default;
|
|
19
19
|
const {Audit} = require('@siteimprove/alfa-act');
|
|
20
|
+
const {getNormalizedXPath} = require('../procs/identify');
|
|
20
21
|
const {Playwright} = require('@siteimprove/alfa-playwright');
|
|
21
22
|
|
|
22
23
|
// FUNCTIONS
|
|
23
24
|
|
|
24
25
|
// Simplifies the spacing of a string.
|
|
25
|
-
const tidy = string => string.replace(/\
|
|
26
|
+
const tidy = string => string.replace(/\s+/g, ' ');
|
|
26
27
|
|
|
27
28
|
// Conducts and reports the alfa tests.
|
|
28
29
|
exports.reporter = async (page, report, actIndex) => {
|
|
@@ -47,35 +48,53 @@ exports.reporter = async (page, report, actIndex) => {
|
|
|
47
48
|
const doc = await page.evaluateHandle('document');
|
|
48
49
|
const alfaPage = await Playwright.toPage(doc);
|
|
49
50
|
const audit = Audit.of(alfaPage, alfaRules);
|
|
50
|
-
// Get the
|
|
51
|
-
const
|
|
52
|
-
// For each
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
51
|
+
// Get the evaluation.
|
|
52
|
+
const evaluation = Array.from(await audit.evaluate());
|
|
53
|
+
// For each of its components:
|
|
54
|
+
for (const index in evaluation) {
|
|
55
|
+
const component = evaluation[index];
|
|
56
|
+
const violatorClass = component.target;
|
|
57
|
+
// If it has a non-collection violator:
|
|
58
|
+
if (violatorClass && ! violatorClass._members) {
|
|
59
|
+
// Get the path.
|
|
60
|
+
const path = violatorClass.path();
|
|
61
|
+
// Get the normalized path, omitting any final text() selector.
|
|
62
|
+
const pathID = getNormalizedXPath(path.replace(/\/text\(\).*$/, ''));
|
|
63
|
+
// Get the code lines of the violator.
|
|
64
|
+
const codeLines = violatorClass.toString().split('\n');
|
|
65
|
+
// Convert the component to a finding object.
|
|
66
|
+
const finding = component.toJSON();
|
|
67
|
+
const {expectations, outcome, rule} = finding;
|
|
68
|
+
// If the outcome of the finding is a failure or warning:
|
|
69
|
+
if (outcome !== 'passed') {
|
|
70
|
+
let text = '';
|
|
71
|
+
// Get a locator for the violator.
|
|
72
|
+
const violatorLoc = page.locator(`xpath=${pathID}`);
|
|
73
|
+
try {
|
|
74
|
+
// Get the inner text of the violator.
|
|
75
|
+
text = await violatorLoc.innerText({timeout: 50});
|
|
76
|
+
}
|
|
77
|
+
catch(error) {}
|
|
65
78
|
const {tags, uri, requirements} = rule;
|
|
66
79
|
const ruleID = uri.replace(/^.+-/, '');
|
|
67
80
|
let ruleSummary = tidy(expectations?.[0]?.[1]?.error?.message || '');
|
|
68
|
-
const
|
|
69
|
-
const
|
|
81
|
+
const violator = finding.target;
|
|
82
|
+
const {name, type} = violator;
|
|
70
83
|
if (codeLines[0] === '#document') {
|
|
71
84
|
codeLines.splice(2, codeLines.length - 3, '...');
|
|
72
85
|
}
|
|
73
86
|
else if (codeLines[0].startsWith('<html')) {
|
|
74
87
|
codeLines.splice(1, codeLines.length - 2, '...');
|
|
75
88
|
}
|
|
76
|
-
|
|
89
|
+
let tagName = name?.toUpperCase();
|
|
90
|
+
if (pathID && (tagName?.startsWith('TEXT') || ! tagName)) {
|
|
91
|
+
const standardTagName = pathID.split('/').pop().replace(/\[.+/, '').toUpperCase() || '';
|
|
92
|
+
tagName = standardTagName;
|
|
93
|
+
}
|
|
94
|
+
// Get data on the finding.
|
|
95
|
+
const findingData = {
|
|
77
96
|
index,
|
|
78
|
-
|
|
97
|
+
outcome,
|
|
79
98
|
rule: {
|
|
80
99
|
ruleID,
|
|
81
100
|
ruleSummary,
|
|
@@ -83,46 +102,49 @@ exports.reporter = async (page, report, actIndex) => {
|
|
|
83
102
|
uri,
|
|
84
103
|
requirements
|
|
85
104
|
},
|
|
86
|
-
|
|
87
|
-
type
|
|
88
|
-
|
|
89
|
-
|
|
105
|
+
violator: {
|
|
106
|
+
type,
|
|
107
|
+
name,
|
|
108
|
+
tagName,
|
|
109
|
+
path,
|
|
90
110
|
codeLines: codeLines.map(
|
|
91
111
|
line => line.length > 300 ? `${line.slice(0, 300)}...` : line
|
|
92
|
-
)
|
|
112
|
+
),
|
|
113
|
+
text,
|
|
114
|
+
pathID
|
|
93
115
|
}
|
|
94
116
|
};
|
|
95
117
|
// If the rule summary is missing:
|
|
96
|
-
if (
|
|
118
|
+
if (findingData.rule.ruleSummary === '') {
|
|
97
119
|
// If a first requirement title exists:
|
|
98
|
-
const {requirements} =
|
|
120
|
+
const {requirements} = findingData.rule;
|
|
99
121
|
if (requirements && requirements.length && requirements[0].title) {
|
|
100
122
|
// Make it the rule summary.
|
|
101
|
-
|
|
123
|
+
findingData.rule.ruleSummary = requirements[0].title;
|
|
102
124
|
}
|
|
103
125
|
}
|
|
104
126
|
const etcTags = [];
|
|
105
127
|
tags.forEach(tag => {
|
|
106
128
|
if (tag.type === 'scope') {
|
|
107
|
-
|
|
129
|
+
findingData.rule.scope = tag.scope;
|
|
108
130
|
}
|
|
109
131
|
else {
|
|
110
132
|
etcTags.push(tag);
|
|
111
133
|
}
|
|
112
134
|
});
|
|
113
135
|
if (etcTags.length) {
|
|
114
|
-
|
|
136
|
+
findingData.etcTags = etcTags;
|
|
115
137
|
}
|
|
116
|
-
if (
|
|
138
|
+
if (findingData.outcome === 'failed') {
|
|
117
139
|
result.totals.failures++;
|
|
118
140
|
}
|
|
119
|
-
else if (
|
|
141
|
+
else if (findingData.outcome === 'cantTell') {
|
|
120
142
|
result.totals.warnings++;
|
|
121
143
|
}
|
|
122
|
-
result.items.push(
|
|
144
|
+
result.items.push(findingData);
|
|
123
145
|
}
|
|
124
146
|
}
|
|
125
|
-
}
|
|
147
|
+
};
|
|
126
148
|
}
|
|
127
149
|
catch(error) {
|
|
128
150
|
console.log(`ERROR: Navigation to URL timed out (${error})`);
|