testaro 13.0.2 → 14.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/README.md +190 -94
- package/actSpecs.js +17 -158
- package/package.json +1 -1
- package/run.js +31 -31
- package/standardize.js +338 -0
- package/{tests → testaro}/allHidden.js +36 -1
- package/{tests → testaro}/attVal.js +36 -2
- package/{tests → testaro}/autocomplete.js +34 -1
- package/{tests → testaro}/bulk.js +15 -1
- package/{tests → testaro}/docType.js +15 -1
- package/{tests → testaro}/dupAtt.js +34 -1
- package/{tests → testaro}/elements.js +9 -3
- package/testaro/embAc.js +78 -0
- package/{tests → testaro}/filter.js +35 -1
- package/{tests → testaro}/focAll.js +20 -3
- package/{tests → testaro}/focInd.js +45 -3
- package/{tests → testaro}/focOp.js +61 -2
- package/{tests → testaro}/focVis.js +35 -1
- package/{tests → testaro}/hover.js +67 -5
- package/{tests → testaro}/labClash.js +54 -4
- package/{tests → testaro}/linkTo.js +33 -1
- package/{tests → testaro}/linkUl.js +43 -5
- package/{tests → testaro}/menuNav.js +42 -1
- package/{tests → testaro}/miniText.js +32 -1
- package/{tests → testaro}/motion.js +27 -4
- package/{tests → testaro}/nonTable.js +32 -1
- package/{tests → testaro}/radioSet.js +33 -2
- package/{tests → testaro}/role.js +38 -1
- package/{tests → testaro}/styleDiff.js +43 -2
- package/{tests → testaro}/tabNav.js +42 -1
- package/{tests → testaro}/textNodes.js +6 -2
- package/{tests → testaro}/title.js +8 -4
- package/{tests → testaro}/titledEl.js +32 -1
- package/{tests → testaro}/zIndex.js +40 -2
- package/tests/alfa.js +72 -75
- package/tests/continuum.js +6 -2
- package/tests/ibm.js +14 -42
- package/tests/testaro.js +73 -0
- package/validation/tests/jobs/allHidden.json +877 -174
- package/validation/tests/jobs/attVal.json +57 -19
- package/validation/tests/jobs/autocomplete.json +34 -8
- package/validation/tests/jobs/bulk.json +33 -7
- package/validation/tests/jobs/docType.json +23 -5
- package/validation/tests/jobs/dupAtt.json +47 -8
- package/validation/tests/jobs/elements.json +231 -70
- package/validation/tests/jobs/embAc.json +70 -15
- package/validation/tests/jobs/filter.json +56 -12
- package/validation/tests/jobs/focAll.json +64 -16
- package/validation/tests/jobs/focInd.json +107 -23
- package/validation/tests/jobs/focOp.json +93 -21
- package/validation/tests/jobs/focVis.json +16 -4
- package/validation/tests/jobs/hover.json +246 -56
- package/validation/tests/jobs/labClash.json +43 -11
- package/validation/tests/jobs/linkTo.json +16 -4
- package/validation/tests/jobs/linkUl.json +79 -19
- package/validation/tests/jobs/menuNav.json +313 -65
- package/validation/tests/jobs/miniText.json +21 -5
- package/validation/tests/jobs/motion.json +81 -23
- package/validation/tests/jobs/nonTable.json +26 -6
- package/validation/tests/jobs/radioSet.json +43 -11
- package/validation/tests/jobs/role.json +93 -19
- package/validation/tests/jobs/styleDiff.json +124 -28
- package/validation/tests/jobs/tabNav.json +313 -65
- package/validation/tests/jobs/textNodes.json +190 -49
- package/validation/tests/jobs/title.json +23 -5
- package/validation/tests/jobs/titledEl.json +26 -6
- package/validation/tests/jobs/zIndex.json +28 -8
- package/validation/tests/old/allHidden.json +314 -0
- package/validation/tests/old/attVal.json +60 -0
- package/validation/tests/old/autocomplete.json +51 -0
- package/validation/tests/old/bulk.json +48 -0
- package/validation/tests/old/docType.json +46 -0
- package/validation/tests/old/dupAtt.json +51 -0
- package/validation/tests/old/elements.json +140 -0
- package/validation/tests/old/embAc.json +54 -0
- package/validation/tests/old/filter.json +55 -0
- package/validation/tests/old/focAll.json +68 -0
- package/validation/tests/old/focInd.json +69 -0
- package/validation/tests/old/focOp.json +62 -0
- package/validation/tests/old/focVis.json +35 -0
- package/validation/tests/old/hover.json +118 -0
- package/validation/tests/old/labClash.json +52 -0
- package/validation/tests/old/linkTo.json +35 -0
- package/validation/tests/old/linkUl.json +71 -0
- package/validation/tests/old/menuNav.json +106 -0
- package/validation/tests/old/miniText.json +36 -0
- package/validation/tests/old/motion.json +62 -0
- package/validation/tests/old/nonTable.json +37 -0
- package/validation/tests/old/radioSet.json +52 -0
- package/validation/tests/old/role.json +60 -0
- package/validation/tests/old/styleDiff.json +71 -0
- package/validation/tests/old/tabNav.json +106 -0
- package/validation/tests/old/temp.js +28 -0
- package/validation/tests/old/textNodes.json +98 -0
- package/validation/tests/old/title.json +46 -0
- package/validation/tests/old/titledEl.json +37 -0
- package/validation/tests/old/zIndex.json +49 -0
- package/validation/tests/targets/attVal/good.html +1 -1
- package/validation/tests/targets/elements/index.html +1 -0
- package/tests/embAc.js +0 -36
|
@@ -63,7 +63,7 @@ const shootAll = async (page, delay, interval, count, toDo, buffers) => {
|
|
|
63
63
|
// Returns a number rounded to 2 decimal digits.
|
|
64
64
|
const round = (num, precision) => Number.parseFloat(num.toPrecision(precision));
|
|
65
65
|
// Reports motion in a page.
|
|
66
|
-
exports.reporter = async (page, delay, interval, count) => {
|
|
66
|
+
exports.reporter = async (page, withItems, delay = 2500, interval = 2500, count = 5) => {
|
|
67
67
|
// Make screenshots and get their image buffers.
|
|
68
68
|
const shots = await shootAll(page, delay, interval, count, count, []);
|
|
69
69
|
// If the shooting succeeded:
|
|
@@ -99,7 +99,7 @@ exports.reporter = async (page, delay, interval, count) => {
|
|
|
99
99
|
);
|
|
100
100
|
// Return the result.
|
|
101
101
|
return {
|
|
102
|
-
|
|
102
|
+
data: {
|
|
103
103
|
bytes,
|
|
104
104
|
localRatios,
|
|
105
105
|
meanLocalRatio,
|
|
@@ -109,14 +109,37 @@ exports.reporter = async (page, delay, interval, count) => {
|
|
|
109
109
|
meanPixelChange,
|
|
110
110
|
maxPixelChange,
|
|
111
111
|
changeFrequency
|
|
112
|
-
}
|
|
112
|
+
},
|
|
113
|
+
totals: [
|
|
114
|
+
0,
|
|
115
|
+
0,
|
|
116
|
+
2 * (meanLocalRatio - 1)
|
|
117
|
+
+ maxLocalRatio - 1
|
|
118
|
+
+ globalRatio - 1
|
|
119
|
+
+ meanPixelChange / 10000
|
|
120
|
+
+ maxPixelChange / 25000
|
|
121
|
+
+ 3 * changeFrequency
|
|
122
|
+
|| 0,
|
|
123
|
+
0
|
|
124
|
+
],
|
|
125
|
+
standardInstances: [{
|
|
126
|
+
issueID: 'motion',
|
|
127
|
+
what: 'Content moves or changes without user request',
|
|
128
|
+
ordinalSeverity: 2,
|
|
129
|
+
location: {
|
|
130
|
+
doc: '',
|
|
131
|
+
type: '',
|
|
132
|
+
spec: ''
|
|
133
|
+
},
|
|
134
|
+
excerpt: ''
|
|
135
|
+
}]
|
|
113
136
|
};
|
|
114
137
|
}
|
|
115
138
|
// Otherwise, i.e. if the shooting failed:
|
|
116
139
|
else {
|
|
117
140
|
// Return failure.
|
|
118
141
|
return {
|
|
119
|
-
|
|
142
|
+
data: {
|
|
120
143
|
prevented: true,
|
|
121
144
|
error: 'ERROR: screenshots failed'
|
|
122
145
|
}
|
|
@@ -60,8 +60,39 @@ exports.reporter = async (page, withItems) => {
|
|
|
60
60
|
const data = {
|
|
61
61
|
total: badTableTexts.length
|
|
62
62
|
};
|
|
63
|
+
const standardInstances = [];
|
|
63
64
|
if (withItems) {
|
|
64
65
|
data.items = badTableTexts;
|
|
66
|
+
data.items.forEach(text => {
|
|
67
|
+
standardInstances.push({
|
|
68
|
+
issueID: 'nonTable',
|
|
69
|
+
what: 'Table is misused to arrange content',
|
|
70
|
+
ordinalSeverity: 0,
|
|
71
|
+
location: {
|
|
72
|
+
doc: '',
|
|
73
|
+
type: '',
|
|
74
|
+
spec: ''
|
|
75
|
+
},
|
|
76
|
+
excerpt: text
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
else if (data.total) {
|
|
81
|
+
standardInstances.push({
|
|
82
|
+
issueID: 'nonTable',
|
|
83
|
+
what: 'Tables are misused to arrange content',
|
|
84
|
+
ordinalSeverity: 2,
|
|
85
|
+
location: {
|
|
86
|
+
doc: '',
|
|
87
|
+
type: '',
|
|
88
|
+
spec: ''
|
|
89
|
+
},
|
|
90
|
+
excerpt: ''
|
|
91
|
+
});
|
|
65
92
|
}
|
|
66
|
-
return {
|
|
93
|
+
return {
|
|
94
|
+
data,
|
|
95
|
+
totals: [0, 0, data.total, 0],
|
|
96
|
+
standardInstances
|
|
97
|
+
};
|
|
67
98
|
};
|
|
@@ -75,18 +75,49 @@ exports.reporter = async (page, withItems) => {
|
|
|
75
75
|
totals.inSet = setRadios.length;
|
|
76
76
|
totals.percent = totals.total ? Math.floor(100 * totals.inSet / totals.total) : 'N.A.';
|
|
77
77
|
// If itemization is required:
|
|
78
|
+
const standardInstances = [];
|
|
78
79
|
if (withItems) {
|
|
79
80
|
// Add it to the results.
|
|
80
81
|
const nonSetRadios = allRadios.filter(radio => ! setRadios.includes(radio));
|
|
81
82
|
const items = data.items;
|
|
82
83
|
items.inSet = setRadios.map(radio => textOf(radio));
|
|
83
84
|
items.notInSet = nonSetRadios.map(radio => textOf(radio));
|
|
85
|
+
items.notInSet.forEach(text => {
|
|
86
|
+
standardInstances.push({
|
|
87
|
+
issueID: 'radioSet',
|
|
88
|
+
what: 'Radio button and others with its name are not grouped in their own fieldset with a legend',
|
|
89
|
+
ordinalSeverity: 0,
|
|
90
|
+
location: {
|
|
91
|
+
doc: '',
|
|
92
|
+
type: '',
|
|
93
|
+
spec: ''
|
|
94
|
+
},
|
|
95
|
+
excerpt: text
|
|
96
|
+
});
|
|
97
|
+
});
|
|
84
98
|
}
|
|
85
|
-
|
|
99
|
+
else if (totals.total - totals.inSet > 0) {
|
|
100
|
+
standardInstances.push({
|
|
101
|
+
issueID: 'radioSet',
|
|
102
|
+
what: 'Radio buttons are not validly grouped in fieldsets with legends',
|
|
103
|
+
ordinalSeverity: 1,
|
|
104
|
+
location: {
|
|
105
|
+
doc: '',
|
|
106
|
+
type: '',
|
|
107
|
+
spec: ''
|
|
108
|
+
},
|
|
109
|
+
excerpt: ''
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
data,
|
|
114
|
+
totals: [0, totals.total - totals.inSet, 0, 0],
|
|
115
|
+
standardInstances
|
|
116
|
+
};
|
|
86
117
|
}
|
|
87
118
|
else {
|
|
88
119
|
return {
|
|
89
|
-
|
|
120
|
+
data: {
|
|
90
121
|
prevented: true,
|
|
91
122
|
error: 'ERROR identifying homogeneous field sets'
|
|
92
123
|
}
|
|
@@ -478,6 +478,43 @@ exports.reporter = async page => await page.$eval('body', body => {
|
|
|
478
478
|
}
|
|
479
479
|
}
|
|
480
480
|
});
|
|
481
|
+
const standardInstances = [];
|
|
482
|
+
Object.keys(data.tagNames).forEach(tagName => {
|
|
483
|
+
Object.keys(data.tagNames[tagName]).forEach(role => {
|
|
484
|
+
let count = data.tagNames[tagName][role].redundant;
|
|
485
|
+
if (count) {
|
|
486
|
+
standardInstances.push({
|
|
487
|
+
issueID: 'role',
|
|
488
|
+
what: `Elements ${tagName} have redundant explicit role ${role} (count: ${count})`,
|
|
489
|
+
ordinalSeverity: 1,
|
|
490
|
+
location: {
|
|
491
|
+
doc: '',
|
|
492
|
+
type: '',
|
|
493
|
+
spec: ''
|
|
494
|
+
},
|
|
495
|
+
excerpt: ''
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
count = data.tagNames[tagName][role].bad;
|
|
499
|
+
if (count) {
|
|
500
|
+
standardInstances.push({
|
|
501
|
+
issueID: 'role',
|
|
502
|
+
what: `Elements ${tagName} have invalid or native-replaceable explicit role ${role} (count: ${count})`,
|
|
503
|
+
ordinalSeverity: 3,
|
|
504
|
+
location: {
|
|
505
|
+
doc: '',
|
|
506
|
+
type: '',
|
|
507
|
+
spec: ''
|
|
508
|
+
},
|
|
509
|
+
excerpt: ''
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
});
|
|
481
514
|
// Return the result.
|
|
482
|
-
return {
|
|
515
|
+
return {
|
|
516
|
+
data,
|
|
517
|
+
totals: [0, data.redundantRoleElements, 0, data.badRoleElements],
|
|
518
|
+
standardInstances
|
|
519
|
+
};
|
|
483
520
|
});
|
|
@@ -107,7 +107,9 @@ exports.reporter = async (page, withItems) => {
|
|
|
107
107
|
if (! styleProps[typeName]) {
|
|
108
108
|
styleProps[typeName] = {};
|
|
109
109
|
}
|
|
110
|
-
const elementText = element.textContent.trim().
|
|
110
|
+
const elementText = (element.textContent.trim() || element.outerHTML.trim())
|
|
111
|
+
.replace(/\s+/g, ' ')
|
|
112
|
+
.slice(0, 100);
|
|
111
113
|
// For each style property being compared:
|
|
112
114
|
styles.forEach(styleName => {
|
|
113
115
|
if (! styleProps[typeName][styleName]) {
|
|
@@ -116,6 +118,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
116
118
|
if (! styleProps[typeName][styleName][style[styleName]]) {
|
|
117
119
|
styleProps[typeName][styleName][style[styleName]] = [];
|
|
118
120
|
}
|
|
121
|
+
// Add the element text to the style details.
|
|
119
122
|
styleProps[typeName][styleName][style[styleName]].push(elementText);
|
|
120
123
|
});
|
|
121
124
|
}
|
|
@@ -148,6 +151,44 @@ exports.reporter = async (page, withItems) => {
|
|
|
148
151
|
headingNames.forEach(headingName => {
|
|
149
152
|
tallyStyles(headingName, elements.headings[headingName], headingStyles, withItems);
|
|
150
153
|
});
|
|
151
|
-
|
|
154
|
+
// Report the standandardized data.
|
|
155
|
+
const totals = [0, 0, 0, 0];
|
|
156
|
+
const standardInstances = [];
|
|
157
|
+
const elementData = {
|
|
158
|
+
adjacentLink: [0, 'In-line links'],
|
|
159
|
+
listLink: [1, 'Links in columns'],
|
|
160
|
+
button: [2, 'Buttons'],
|
|
161
|
+
h1: [3, 'Level-1 headings'],
|
|
162
|
+
h2: [3, 'Level-2 headings'],
|
|
163
|
+
h3: [3, 'Level-3 headings'],
|
|
164
|
+
h4: [3, 'Level-4 headings'],
|
|
165
|
+
h5: [3, 'Level-5 headings'],
|
|
166
|
+
h6: [3, 'Level-6 headings'],
|
|
167
|
+
};
|
|
168
|
+
Object.keys(elementData).forEach(elementName => {
|
|
169
|
+
const elementTotal = data.totals[elementName];
|
|
170
|
+
if (elementTotal && elementTotal.subtotals) {
|
|
171
|
+
const currentData = elementData[elementName];
|
|
172
|
+
const severity = currentData[0];
|
|
173
|
+
const elementSubtotals = elementTotal.subtotals;
|
|
174
|
+
totals[severity] += elementSubtotals.length - 1;
|
|
175
|
+
standardInstances.push({
|
|
176
|
+
issueID: 'styleDiff',
|
|
177
|
+
what: `${currentData[1]} have ${elementSubtotals.length} different styles`,
|
|
178
|
+
ordinalSeverity: severity,
|
|
179
|
+
location: {
|
|
180
|
+
doc: '',
|
|
181
|
+
type: '',
|
|
182
|
+
spec: ''
|
|
183
|
+
},
|
|
184
|
+
excerpt: ''
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
return {
|
|
189
|
+
data,
|
|
190
|
+
totals,
|
|
191
|
+
standardInstances
|
|
192
|
+
};
|
|
152
193
|
}, [linkTypes, withItems]);
|
|
153
194
|
};
|
|
@@ -324,5 +324,46 @@ exports.reporter = async (page, withItems) => {
|
|
|
324
324
|
// FUNCTION DEFINITIONS END
|
|
325
325
|
await testTabLists(tabLists);
|
|
326
326
|
}
|
|
327
|
-
|
|
327
|
+
const totals = data.totals ? [
|
|
328
|
+
data.totals.navigations.all.incorrect,
|
|
329
|
+
data.totals.tabElements.incorrect,
|
|
330
|
+
data.totals.tabLists.incorrect,
|
|
331
|
+
0
|
|
332
|
+
] : [];
|
|
333
|
+
const standardInstances = [];
|
|
334
|
+
if (data.tabElements && data.tabElements.incorrect) {
|
|
335
|
+
data.tabElements.incorrect.forEach(item => {
|
|
336
|
+
standardInstances.push({
|
|
337
|
+
issueID: 'tabNav',
|
|
338
|
+
what: `Element ${item.tagName} has a tab role but has nonstandard navigation`,
|
|
339
|
+
ordinalSeverity: 1,
|
|
340
|
+
location: {
|
|
341
|
+
doc: '',
|
|
342
|
+
type: '',
|
|
343
|
+
spec: ''
|
|
344
|
+
},
|
|
345
|
+
excerpt: `${item.tagName}: ${item.text}`
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
else if (data.totals.navigations.all.incorrect) {
|
|
350
|
+
standardInstances.push({
|
|
351
|
+
issueID: 'tabNav',
|
|
352
|
+
what: 'Tablists have nonstandard navigation',
|
|
353
|
+
ordinalSeverity: 2,
|
|
354
|
+
location: {
|
|
355
|
+
doc: '',
|
|
356
|
+
type: '',
|
|
357
|
+
spec: ''
|
|
358
|
+
},
|
|
359
|
+
excerpt: ''
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
// Reload the page.
|
|
363
|
+
await page.reload({timeout: 15000});
|
|
364
|
+
return {
|
|
365
|
+
data,
|
|
366
|
+
totals,
|
|
367
|
+
standardInstances
|
|
368
|
+
};
|
|
328
369
|
};
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
1-3. Count of ancestry levels to provide data on (1 = text node, 2 = also parent,
|
|
7
7
|
3 = also grandparent)
|
|
8
8
|
*/
|
|
9
|
-
exports.reporter = async (page, detailLevel, text = '') => {
|
|
9
|
+
exports.reporter = async (page, withItems, detailLevel, text = '') => {
|
|
10
10
|
let data = {};
|
|
11
11
|
// Get the data on the text nodes.
|
|
12
12
|
try {
|
|
@@ -136,5 +136,9 @@ exports.reporter = async (page, detailLevel, text = '') => {
|
|
|
136
136
|
};
|
|
137
137
|
}
|
|
138
138
|
// Return the result.
|
|
139
|
-
return {
|
|
139
|
+
return {
|
|
140
|
+
data,
|
|
141
|
+
totals: [],
|
|
142
|
+
standardInstances: []
|
|
143
|
+
};
|
|
140
144
|
};
|
|
@@ -24,8 +24,39 @@ exports.reporter = async (page, withItems) => {
|
|
|
24
24
|
const data = {
|
|
25
25
|
total: badTitleElements.length
|
|
26
26
|
};
|
|
27
|
+
const standardInstances = [];
|
|
27
28
|
if (withItems) {
|
|
28
29
|
data.items = badTitleElements;
|
|
30
|
+
badTitleElements.forEach(element => {
|
|
31
|
+
standardInstances.push({
|
|
32
|
+
issueID: 'titledEl',
|
|
33
|
+
what: `Element ${element.tagName} has a title attribute`,
|
|
34
|
+
ordinalSeverity: 2,
|
|
35
|
+
location: {
|
|
36
|
+
doc: '',
|
|
37
|
+
type: '',
|
|
38
|
+
spec: ''
|
|
39
|
+
},
|
|
40
|
+
excerpt: `${element.tagName} (${element.text}): ${element.title}`
|
|
41
|
+
});
|
|
42
|
+
});
|
|
29
43
|
}
|
|
30
|
-
|
|
44
|
+
else if (data.total) {
|
|
45
|
+
standardInstances.push({
|
|
46
|
+
issueID: 'titledEl',
|
|
47
|
+
what: 'Ineligible elements have title attributes',
|
|
48
|
+
ordinalSeverity: 2,
|
|
49
|
+
location: {
|
|
50
|
+
doc: '',
|
|
51
|
+
type: '',
|
|
52
|
+
spec: ''
|
|
53
|
+
},
|
|
54
|
+
excerpt: ''
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
data,
|
|
59
|
+
totals: [0, 0, data.total, 0],
|
|
60
|
+
standardInstances
|
|
61
|
+
};
|
|
31
62
|
};
|
|
@@ -33,7 +33,10 @@ exports.reporter = async (page, withItems) => {
|
|
|
33
33
|
data.items.push({
|
|
34
34
|
tagName,
|
|
35
35
|
id: element.id || '',
|
|
36
|
-
text:
|
|
36
|
+
text:
|
|
37
|
+
(element.textContent.trim() || element.outerHTML.trim())
|
|
38
|
+
.replace(/\s+/g, ' ')
|
|
39
|
+
.slice(0, 100)
|
|
37
40
|
});
|
|
38
41
|
}
|
|
39
42
|
};
|
|
@@ -45,5 +48,40 @@ exports.reporter = async (page, withItems) => {
|
|
|
45
48
|
});
|
|
46
49
|
return data;
|
|
47
50
|
}, withItems);
|
|
48
|
-
|
|
51
|
+
const standardInstances = [];
|
|
52
|
+
if (data.items) {
|
|
53
|
+
data.items.forEach(item => {
|
|
54
|
+
const itemID = item.id ? ` (ID ${item.id})` : '';
|
|
55
|
+
const which = `${item.tagName}${itemID}`;
|
|
56
|
+
standardInstances.push({
|
|
57
|
+
issueID: 'zIndex',
|
|
58
|
+
what: `Element ${item.tagName} has a non-default Z index`,
|
|
59
|
+
ordinalSeverity: 0,
|
|
60
|
+
location: {
|
|
61
|
+
doc: '',
|
|
62
|
+
type: '',
|
|
63
|
+
spec: ''
|
|
64
|
+
},
|
|
65
|
+
excerpt: `${which}: ${item.text}`
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
else if (data.totals.total) {
|
|
70
|
+
standardInstances.push({
|
|
71
|
+
issueID: 'zIndex',
|
|
72
|
+
what: 'Elements have non-default Z indexes',
|
|
73
|
+
ordinalSeverity: 0,
|
|
74
|
+
location: {
|
|
75
|
+
doc: '',
|
|
76
|
+
type: '',
|
|
77
|
+
spec: ''
|
|
78
|
+
},
|
|
79
|
+
excerpt: ''
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
data,
|
|
84
|
+
totals: [data.totals.total, 0, 0, 0],
|
|
85
|
+
standardInstances
|
|
86
|
+
};
|
|
49
87
|
};
|
package/tests/alfa.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// IMPORTS
|
|
7
7
|
|
|
8
8
|
const {Audit} = require('@siteimprove/alfa-act');
|
|
9
|
-
const {
|
|
9
|
+
const {Playwright} = require('@siteimprove/alfa-playwright');
|
|
10
10
|
let alfaRules = require('@siteimprove/alfa-rules').default;
|
|
11
11
|
|
|
12
12
|
// FUNCTIONS
|
|
@@ -25,8 +25,16 @@ exports.reporter = async (page, rules) => {
|
|
|
25
25
|
const msgText = msg.text();
|
|
26
26
|
console.log(msgText);
|
|
27
27
|
});
|
|
28
|
+
// Initialize the result.
|
|
29
|
+
let data = {
|
|
30
|
+
totals: {
|
|
31
|
+
failures: 0,
|
|
32
|
+
warnings: 0
|
|
33
|
+
},
|
|
34
|
+
items: []
|
|
35
|
+
};
|
|
28
36
|
try {
|
|
29
|
-
const response = await rulePage.goto('https://alfa.siteimprove.com/rules', {timeout:
|
|
37
|
+
const response = await rulePage.goto('https://alfa.siteimprove.com/rules', {timeout: 15000});
|
|
30
38
|
let ruleData = {};
|
|
31
39
|
if (response.status() === 200) {
|
|
32
40
|
// Compile data on the rule IDs and summaries.
|
|
@@ -49,90 +57,79 @@ exports.reporter = async (page, rules) => {
|
|
|
49
57
|
});
|
|
50
58
|
await rulePage.close();
|
|
51
59
|
}
|
|
52
|
-
//
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
tagName: targetJ.name || '',
|
|
99
|
-
path: target.path(),
|
|
100
|
-
codeLines: codeLines.map(line => line.length > 300 ? `${line.slice(0, 300)}...` : line)
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
const etcTags = [];
|
|
104
|
-
tags.forEach(tag => {
|
|
105
|
-
if (tag.type === 'scope') {
|
|
106
|
-
outcomeData.rule.scope = tag.scope;
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
etcTags.push(tag);
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
if (etcTags.length) {
|
|
113
|
-
outcomeData.etcTags = etcTags;
|
|
114
|
-
}
|
|
115
|
-
if (outcomeData.verdict === 'failed') {
|
|
116
|
-
data.totals.failures++;
|
|
117
|
-
}
|
|
118
|
-
else if (outcomeData.verdict === 'cantTell') {
|
|
119
|
-
data.totals.warnings++;
|
|
120
|
-
}
|
|
121
|
-
data.items.push(outcomeData);
|
|
60
|
+
// Test the page content with the specified rules.
|
|
61
|
+
const doc = await page.evaluateHandle('document');
|
|
62
|
+
const alfaPage = await Playwright.toPage(doc);
|
|
63
|
+
const audit = Audit.of(alfaPage, alfaRules);
|
|
64
|
+
const outcomes = Array.from(await audit.evaluate());
|
|
65
|
+
// For each failure or warning:
|
|
66
|
+
outcomes.forEach((outcome, index) => {
|
|
67
|
+
const {target} = outcome;
|
|
68
|
+
if (target && ! target._members) {
|
|
69
|
+
const outcomeJ = outcome.toJSON();
|
|
70
|
+
const verdict = outcomeJ.outcome;
|
|
71
|
+
if (verdict !== 'passed') {
|
|
72
|
+
// Add to the result.
|
|
73
|
+
const {rule} = outcomeJ;
|
|
74
|
+
const {tags, uri, requirements} = rule;
|
|
75
|
+
const ruleID = uri.replace(/^.+-/, '');
|
|
76
|
+
const ruleSummary = ruleData[ruleID] || '';
|
|
77
|
+
const targetJ = outcomeJ.target;
|
|
78
|
+
const codeLines = target.toString().split('\n');
|
|
79
|
+
if (codeLines[0] === '#document') {
|
|
80
|
+
codeLines.splice(2, codeLines.length - 3, '...');
|
|
81
|
+
}
|
|
82
|
+
else if (codeLines[0].startsWith('<html')) {
|
|
83
|
+
codeLines.splice(1, codeLines.length - 2, '...');
|
|
84
|
+
}
|
|
85
|
+
const outcomeData = {
|
|
86
|
+
index,
|
|
87
|
+
verdict,
|
|
88
|
+
rule: {
|
|
89
|
+
ruleID,
|
|
90
|
+
ruleSummary,
|
|
91
|
+
scope: '',
|
|
92
|
+
uri,
|
|
93
|
+
requirements
|
|
94
|
+
},
|
|
95
|
+
target: {
|
|
96
|
+
type: targetJ.type,
|
|
97
|
+
tagName: targetJ.name || '',
|
|
98
|
+
path: target.path(),
|
|
99
|
+
codeLines: codeLines.map(line => line.length > 300 ? `${line.slice(0, 300)}...` : line)
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
const etcTags = [];
|
|
103
|
+
tags.forEach(tag => {
|
|
104
|
+
if (tag.type === 'scope') {
|
|
105
|
+
outcomeData.rule.scope = tag.scope;
|
|
122
106
|
}
|
|
107
|
+
else {
|
|
108
|
+
etcTags.push(tag);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
if (etcTags.length) {
|
|
112
|
+
outcomeData.etcTags = etcTags;
|
|
123
113
|
}
|
|
124
|
-
|
|
114
|
+
if (outcomeData.verdict === 'failed') {
|
|
115
|
+
data.totals.failures++;
|
|
116
|
+
}
|
|
117
|
+
else if (outcomeData.verdict === 'cantTell') {
|
|
118
|
+
data.totals.warnings++;
|
|
119
|
+
}
|
|
120
|
+
data.items.push(outcomeData);
|
|
121
|
+
}
|
|
125
122
|
}
|
|
126
123
|
});
|
|
127
|
-
return {result: data};
|
|
128
124
|
}
|
|
129
125
|
catch(error) {
|
|
130
126
|
console.log(`ERROR: navigation to URL timed out (${error})`);
|
|
131
|
-
|
|
127
|
+
data = {
|
|
132
128
|
result: {
|
|
133
129
|
prevented: true,
|
|
134
130
|
error: 'ERROR: navigation to URL timed out'
|
|
135
131
|
}
|
|
136
132
|
};
|
|
137
133
|
}
|
|
134
|
+
return {result: data};
|
|
138
135
|
};
|
package/tests/continuum.js
CHANGED
|
@@ -43,12 +43,16 @@ exports.reporter = async (page, rules) => {
|
|
|
43
43
|
}, 30000);
|
|
44
44
|
});
|
|
45
45
|
const bigResult = await Promise.race([bigResultPromise, deadlinePromise]);
|
|
46
|
+
// If the result compilation succeeded:
|
|
46
47
|
if (Array.isArray(bigResult)) {
|
|
48
|
+
// Return a compact version of it, removing useless and invariant properties.
|
|
47
49
|
return bigResult.map(bigItem => {
|
|
48
50
|
const item = bigItem._rawEngineJsonObject;
|
|
51
|
+
delete item.testResult;
|
|
52
|
+
delete item.fixType;
|
|
49
53
|
delete item.fingerprint.encoding;
|
|
50
|
-
if (item.element.length >
|
|
51
|
-
item.element = `${item.element.slice(0,
|
|
54
|
+
if (item.element.length > 240) {
|
|
55
|
+
item.element = `${item.element.slice(0, 200)} ... ${item.element.slice(-200)}`;
|
|
52
56
|
}
|
|
53
57
|
return item;
|
|
54
58
|
});
|