testaro 14.2.6 → 14.4.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 +53 -33
- package/actSpecs.js +1 -1
- package/call.js +1 -0
- package/package.json +1 -1
- package/run.js +35 -23
- package/standardize.js +133 -16
- package/testaro/allHidden.js +23 -3
- package/testaro/attVal.js +6 -0
- package/testaro/autocomplete.js +6 -2
- package/testaro/bulk.js +5 -1
- package/testaro/docType.js +2 -0
- package/testaro/dupAtt.js +10 -4
- package/testaro/embAc.js +7 -1
- package/testaro/filter.js +6 -1
- package/testaro/focAll.js +3 -0
- package/testaro/focInd.js +37 -13
- package/testaro/focOp.js +36 -15
- package/testaro/focVis.js +10 -2
- package/testaro/hover.js +8 -2
- package/testaro/labClash.js +36 -12
- package/testaro/linkTo.js +13 -5
- package/testaro/linkUl.js +21 -9
- package/testaro/menuNav.js +17 -6
- package/testaro/miniText.js +11 -2
- package/testaro/motion.js +13 -9
- package/testaro/nonTable.js +12 -4
- package/testaro/radioSet.js +15 -6
- package/testaro/role.js +19 -10
- package/testaro/styleDiff.js +14 -10
- package/testaro/tabNav.js +16 -4
- package/testaro/titledEl.js +7 -1
- package/testaro/zIndex.js +7 -4
- package/tests/ibm.js +1 -1
package/testaro/autocomplete.js
CHANGED
|
@@ -20,7 +20,7 @@ const addFailure = async (withItems, input, inputText, autocomplete, data) => {
|
|
|
20
20
|
// If itemization is required:
|
|
21
21
|
if (withItems) {
|
|
22
22
|
// Add the item to the data.
|
|
23
|
-
data.items.push([autocomplete, inputText.slice(0, 100)]);
|
|
23
|
+
data.items.push([autocomplete, input.id || '', inputText.slice(0, 100)]);
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
};
|
|
@@ -77,12 +77,14 @@ exports.reporter = async (page, withItems) => {
|
|
|
77
77
|
issueID: `autocomplete-${item[0]}`,
|
|
78
78
|
what: `Input is missing the required autocomplete attribute with value ${item[0]}`,
|
|
79
79
|
ordinalSeverity: 2,
|
|
80
|
+
tagName: 'INPUT',
|
|
81
|
+
id: item[1] || '',
|
|
80
82
|
location: {
|
|
81
83
|
doc: '',
|
|
82
84
|
type: '',
|
|
83
85
|
spec: ''
|
|
84
86
|
},
|
|
85
|
-
excerpt: item[
|
|
87
|
+
excerpt: item[2]
|
|
86
88
|
});
|
|
87
89
|
});
|
|
88
90
|
}
|
|
@@ -91,6 +93,8 @@ exports.reporter = async (page, withItems) => {
|
|
|
91
93
|
issueID: 'autocomplete',
|
|
92
94
|
what: 'Inputs are missing required autocomplete attributes',
|
|
93
95
|
ordinalSeverity: 2,
|
|
96
|
+
tagName: '',
|
|
97
|
+
id: '',
|
|
94
98
|
location: {
|
|
95
99
|
doc: '',
|
|
96
100
|
type: '',
|
package/testaro/bulk.js
CHANGED
|
@@ -18,13 +18,17 @@ exports.reporter = async page => {
|
|
|
18
18
|
});
|
|
19
19
|
const visibleElements = await page.$$('body :visible');
|
|
20
20
|
data.visibleElements = visibleElements.length;
|
|
21
|
+
const count = Math.round(data.visibleElements / 400);
|
|
21
22
|
return {
|
|
22
23
|
data,
|
|
23
|
-
totals: [
|
|
24
|
+
totals: [count, 0, 0, 0],
|
|
24
25
|
standardInstances: data.visibleElements < 200 ? [] : [{
|
|
25
26
|
issueID: 'bulk',
|
|
26
27
|
what: 'Page contains a large number of visible elements',
|
|
28
|
+
count,
|
|
27
29
|
ordinalSeverity: 0,
|
|
30
|
+
tagName: 'HTML',
|
|
31
|
+
id: '',
|
|
28
32
|
location: {
|
|
29
33
|
doc: '',
|
|
30
34
|
type: '',
|
package/testaro/docType.js
CHANGED
package/testaro/dupAtt.js
CHANGED
|
@@ -53,11 +53,12 @@ exports.reporter = async (page, withItems) => {
|
|
|
53
53
|
// Extract the opening tags of its elements.
|
|
54
54
|
let elements = rawPage.match(/<[a-zA-Z][^<>]+>/g);
|
|
55
55
|
// Delete their enclosing angle brackets and the values of any attributes in them.
|
|
56
|
-
|
|
56
|
+
const elementsWithVals = elements.map(el => el.replace(/^< *| *>$/g, ''));
|
|
57
|
+
const elementsWithoutVals = elementsWithVals.map(el => el.replace(/^ *|=[^ ]*$/g, ''));
|
|
57
58
|
// For each element:
|
|
58
|
-
|
|
59
|
+
elementsWithoutVals.forEach((elementWithoutVals, index) => {
|
|
59
60
|
// Identify its tag name and attributes.
|
|
60
|
-
const terms =
|
|
61
|
+
const terms = elementWithoutVals.split(' ');
|
|
61
62
|
// If it has 2 or more attributes:
|
|
62
63
|
if (terms.length > 2) {
|
|
63
64
|
// If any is duplicated:
|
|
@@ -65,11 +66,14 @@ exports.reporter = async (page, withItems) => {
|
|
|
65
66
|
const attributes = terms.slice(1);
|
|
66
67
|
const attSet = new Set(attributes);
|
|
67
68
|
if (attSet.size < attributes.length) {
|
|
68
|
-
// Add
|
|
69
|
+
// Add it to the data.
|
|
69
70
|
data.total++;
|
|
70
71
|
if (withItems) {
|
|
71
72
|
data.items.push({
|
|
72
73
|
tagName,
|
|
74
|
+
id: terms.includes('id')
|
|
75
|
+
? elementsWithVals[index].replace(/^.+id=/, '').replace(/ .*$/, '')
|
|
76
|
+
: '',
|
|
73
77
|
attributes
|
|
74
78
|
});
|
|
75
79
|
}
|
|
@@ -83,6 +87,8 @@ exports.reporter = async (page, withItems) => {
|
|
|
83
87
|
issueID: 'dupAtt',
|
|
84
88
|
what: `Element ${item.tagName} has 2 attributes with the same name`,
|
|
85
89
|
ordinalSeverity: 2,
|
|
90
|
+
tagName: item.tagName,
|
|
91
|
+
id: item.id,
|
|
86
92
|
location: {
|
|
87
93
|
doc: '',
|
|
88
94
|
type: '',
|
package/testaro/embAc.js
CHANGED
|
@@ -32,6 +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
36
|
excerpt: compact(container.outerHTML)
|
|
36
37
|
});
|
|
37
38
|
}
|
|
@@ -45,8 +46,10 @@ exports.reporter = async (page, withItems) => await page.$$eval(
|
|
|
45
46
|
items.forEach(item => {
|
|
46
47
|
standardInstances.push({
|
|
47
48
|
issueID: `embAc-${item.embeddedElement}`,
|
|
48
|
-
what:
|
|
49
|
+
what: `${item.embeddedElement} element is embedded in a link or button`,
|
|
49
50
|
ordinalSeverity: 2,
|
|
51
|
+
tagName: item.embeddedElement,
|
|
52
|
+
id: item.id,
|
|
50
53
|
location: {
|
|
51
54
|
doc: '',
|
|
52
55
|
type: '',
|
|
@@ -60,7 +63,10 @@ exports.reporter = async (page, withItems) => await page.$$eval(
|
|
|
60
63
|
standardInstances.push({
|
|
61
64
|
issueID: 'embAc',
|
|
62
65
|
what: 'Interactive elements are contained by links or buttons',
|
|
66
|
+
count: total,
|
|
63
67
|
ordinalSeverity: 2,
|
|
68
|
+
tagName: '',
|
|
69
|
+
id: '',
|
|
64
70
|
location: {
|
|
65
71
|
doc: '',
|
|
66
72
|
type: '',
|
package/testaro/filter.js
CHANGED
|
@@ -40,6 +40,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
40
40
|
filterData.forEach(filterDatum => {
|
|
41
41
|
data.items.push({
|
|
42
42
|
tagName: filterDatum.element.tagName,
|
|
43
|
+
id: filterDatum.element.id,
|
|
43
44
|
text: compact(filterDatum.element.textContent) || compact(filterDatum.element.outerHTML),
|
|
44
45
|
impact: filterDatum.impact
|
|
45
46
|
});
|
|
@@ -53,8 +54,10 @@ exports.reporter = async (page, withItems) => {
|
|
|
53
54
|
data.items.forEach(item => {
|
|
54
55
|
standardInstances.push({
|
|
55
56
|
issueID: 'filterStyle',
|
|
56
|
-
what:
|
|
57
|
+
what: `${item.tagName} element has a filter style that impacts ${item.impact} elements`,
|
|
57
58
|
ordinalSeverity: 2,
|
|
59
|
+
tagName: item.toUpperCase(),
|
|
60
|
+
id: item.id,
|
|
58
61
|
location: {
|
|
59
62
|
doc: '',
|
|
60
63
|
type: '',
|
|
@@ -69,6 +72,8 @@ exports.reporter = async (page, withItems) => {
|
|
|
69
72
|
issueID: 'filterStyle',
|
|
70
73
|
what: 'Elements have filter styles impacting other elements',
|
|
71
74
|
ordinalSeverity: 2,
|
|
75
|
+
tagName: '',
|
|
76
|
+
id: '',
|
|
72
77
|
location: {
|
|
73
78
|
doc: '',
|
|
74
79
|
type: '',
|
package/testaro/focAll.js
CHANGED
|
@@ -67,7 +67,10 @@ exports.reporter = async page => {
|
|
|
67
67
|
standardInstances: data.discrepancy ? [{
|
|
68
68
|
issueID: 'focAll',
|
|
69
69
|
what: 'Some focusable elements are not Tab-focusable or vice versa',
|
|
70
|
+
count: Math.abs(data.discrepancy),
|
|
70
71
|
ordinalSeverity: 2,
|
|
72
|
+
tagName: '',
|
|
73
|
+
id: '',
|
|
71
74
|
location: {
|
|
72
75
|
doc: '',
|
|
73
76
|
type: '',
|
package/testaro/focInd.js
CHANGED
|
@@ -67,6 +67,7 @@ exports.reporter = async (page, withItems, revealAll = false, allowedDelay = 250
|
|
|
67
67
|
if (withItems) {
|
|
68
68
|
const elementData = {
|
|
69
69
|
tagName,
|
|
70
|
+
id: element.id,
|
|
70
71
|
text: (element.textContent.trim() || element.outerHTML.trim()).replace(/\s+/g, ' ')
|
|
71
72
|
.slice(0, 100)
|
|
72
73
|
};
|
|
@@ -174,8 +175,10 @@ exports.reporter = async (page, withItems, revealAll = false, allowedDelay = 250
|
|
|
174
175
|
const qualifier = issueName === 'nonOutlinePresent' ? 'a non-outline' : 'no';
|
|
175
176
|
standardInstances.push({
|
|
176
177
|
issueID: `focInd-${issueName}`,
|
|
177
|
-
what:
|
|
178
|
+
what: `${item.tagName} element has ${qualifier} focus indicator`,
|
|
178
179
|
ordinalSeverity: issueName === 'nonOutlinePresent' ? 2 : 3,
|
|
180
|
+
tagName: item.tagName,
|
|
181
|
+
id: item.id,
|
|
179
182
|
location: {
|
|
180
183
|
doc: '',
|
|
181
184
|
type: '',
|
|
@@ -186,18 +189,39 @@ exports.reporter = async (page, withItems, revealAll = false, allowedDelay = 250
|
|
|
186
189
|
});
|
|
187
190
|
});
|
|
188
191
|
}
|
|
189
|
-
else
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
192
|
+
else {
|
|
193
|
+
if (types.indicatorMissing.total) {
|
|
194
|
+
standardInstances.push({
|
|
195
|
+
issueID: 'focInd-missing',
|
|
196
|
+
what: 'Elements have missing focus indicators',
|
|
197
|
+
count: types.indicatorMissing.total,
|
|
198
|
+
ordinalSeverity: 3,
|
|
199
|
+
tagName: '',
|
|
200
|
+
id: '',
|
|
201
|
+
location: {
|
|
202
|
+
doc: '',
|
|
203
|
+
type: '',
|
|
204
|
+
spec: ''
|
|
205
|
+
},
|
|
206
|
+
excerpt: ''
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
if (types.nonOutlinePresent.total) {
|
|
210
|
+
standardInstances.push({
|
|
211
|
+
issueID: 'focInd-nonoutline',
|
|
212
|
+
what: 'Elements have non-outline focus indicators',
|
|
213
|
+
count: types.nonOutlinePresent.total,
|
|
214
|
+
ordinalSeverity: 2,
|
|
215
|
+
tagName: '',
|
|
216
|
+
id: '',
|
|
217
|
+
location: {
|
|
218
|
+
doc: '',
|
|
219
|
+
type: '',
|
|
220
|
+
spec: ''
|
|
221
|
+
},
|
|
222
|
+
excerpt: ''
|
|
223
|
+
});
|
|
224
|
+
}
|
|
201
225
|
}
|
|
202
226
|
// Reload the page.
|
|
203
227
|
try {
|
package/testaro/focOp.js
CHANGED
|
@@ -158,12 +158,12 @@ exports.reporter = async (page, withItems) => {
|
|
|
158
158
|
: 'is operable but not focusable';
|
|
159
159
|
const ordinalSeverity = issue === 'onlyFocusable' ? 2 : 3;
|
|
160
160
|
data.items[issue].forEach(item => {
|
|
161
|
-
const itemID = item.id ? ` (ID ${item.id})` : '';
|
|
162
|
-
const which = `${item.tagName}${itemID}`;
|
|
163
161
|
standardInstances.push({
|
|
164
162
|
issueID,
|
|
165
|
-
what:
|
|
163
|
+
what: `${item.tagName} element ${gripe}`,
|
|
166
164
|
ordinalSeverity,
|
|
165
|
+
tagName: item.tagName,
|
|
166
|
+
id: item.id,
|
|
167
167
|
location: {
|
|
168
168
|
doc: '',
|
|
169
169
|
type: '',
|
|
@@ -174,18 +174,39 @@ exports.reporter = async (page, withItems) => {
|
|
|
174
174
|
});
|
|
175
175
|
});
|
|
176
176
|
}
|
|
177
|
-
else
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
177
|
+
else {
|
|
178
|
+
if (totals[2]) {
|
|
179
|
+
standardInstances.push({
|
|
180
|
+
issueID: 'focOp-onlyFocusable',
|
|
181
|
+
what: 'Focusable elements are inoperable',
|
|
182
|
+
count: totals[2],
|
|
183
|
+
ordinalSeverity: 2,
|
|
184
|
+
tagName: '',
|
|
185
|
+
id: '',
|
|
186
|
+
location: {
|
|
187
|
+
doc: '',
|
|
188
|
+
type: '',
|
|
189
|
+
spec: ''
|
|
190
|
+
},
|
|
191
|
+
excerpt: ''
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (totals[3]) {
|
|
195
|
+
standardInstances.push({
|
|
196
|
+
issueID: 'focOp-onlyOperable',
|
|
197
|
+
what: 'Operable elements are nonfocusable',
|
|
198
|
+
count: totals[3],
|
|
199
|
+
ordinalSeverity: 3,
|
|
200
|
+
tagName: '',
|
|
201
|
+
id: '',
|
|
202
|
+
location: {
|
|
203
|
+
doc: '',
|
|
204
|
+
type: '',
|
|
205
|
+
spec: ''
|
|
206
|
+
},
|
|
207
|
+
excerpt: ''
|
|
208
|
+
});
|
|
209
|
+
}
|
|
189
210
|
}
|
|
190
211
|
// Reload the page.
|
|
191
212
|
try {
|
package/testaro/focVis.js
CHANGED
|
@@ -14,7 +14,10 @@ exports.reporter = async (page, withItems) => {
|
|
|
14
14
|
links.forEach(link => {
|
|
15
15
|
link.focus();
|
|
16
16
|
if (link.offsetTop + link.offsetHeight <= 0 || link.offsetLeft + link.offsetWidth <= 0) {
|
|
17
|
-
badLinks.push(
|
|
17
|
+
badLinks.push({
|
|
18
|
+
id: link.id,
|
|
19
|
+
text: compact(link.textContent)
|
|
20
|
+
});
|
|
18
21
|
}
|
|
19
22
|
});
|
|
20
23
|
return badLinks;
|
|
@@ -33,12 +36,14 @@ exports.reporter = async (page, withItems) => {
|
|
|
33
36
|
issueID: 'focVis',
|
|
34
37
|
what: 'Visible link is above or to the left of the display',
|
|
35
38
|
ordinalSeverity: 2,
|
|
39
|
+
tagName: 'A',
|
|
40
|
+
id: item.id,
|
|
36
41
|
location: {
|
|
37
42
|
doc: '',
|
|
38
43
|
type: '',
|
|
39
44
|
spec: ''
|
|
40
45
|
},
|
|
41
|
-
excerpt: item
|
|
46
|
+
excerpt: item.text
|
|
42
47
|
});
|
|
43
48
|
});
|
|
44
49
|
}
|
|
@@ -46,7 +51,10 @@ exports.reporter = async (page, withItems) => {
|
|
|
46
51
|
standardInstances.push({
|
|
47
52
|
issueID: 'focVis',
|
|
48
53
|
what: 'Visible links are above or to the left of the display',
|
|
54
|
+
count: data.total,
|
|
49
55
|
ordinalSeverity: 2,
|
|
56
|
+
tagName: 'A',
|
|
57
|
+
id: '',
|
|
50
58
|
location: {
|
|
51
59
|
doc: '',
|
|
52
60
|
type: '',
|
package/testaro/hover.js
CHANGED
|
@@ -269,6 +269,7 @@ const find = async (data, withItems, page, sample) => {
|
|
|
269
269
|
// Add them to the data.
|
|
270
270
|
data.items.impactTriggers.push({
|
|
271
271
|
tagName,
|
|
272
|
+
id: itemData.id,
|
|
272
273
|
text: await textOf(firstTrigger[0], 100),
|
|
273
274
|
additions,
|
|
274
275
|
removals,
|
|
@@ -404,17 +405,19 @@ exports.reporter = async (page, withItems, sampleSize = -1) => {
|
|
|
404
405
|
if (data.items) {
|
|
405
406
|
Object.keys(data.items).forEach(issue => {
|
|
406
407
|
data.items[issue].forEach(item => {
|
|
407
|
-
const itemID = item.id ? ` (ID ${item.id})` : '';
|
|
408
408
|
standardInstances.push({
|
|
409
409
|
issueID: `hover-${issue}`,
|
|
410
410
|
what: what[issue],
|
|
411
|
+
count: data.totals[issue],
|
|
411
412
|
ordinalSeverity: severity[issue],
|
|
413
|
+
tagName: item.tagName,
|
|
414
|
+
id: item.id,
|
|
412
415
|
location: {
|
|
413
416
|
doc: '',
|
|
414
417
|
type: '',
|
|
415
418
|
spec: ''
|
|
416
419
|
},
|
|
417
|
-
excerpt:
|
|
420
|
+
excerpt: item.text
|
|
418
421
|
});
|
|
419
422
|
});
|
|
420
423
|
});
|
|
@@ -423,7 +426,10 @@ exports.reporter = async (page, withItems, sampleSize = -1) => {
|
|
|
423
426
|
standardInstances.push({
|
|
424
427
|
issueID: 'hover',
|
|
425
428
|
what: 'Hovering has unexpected impacts',
|
|
429
|
+
count: Object.values(data.totals).reduce((total, current) => total + current),
|
|
426
430
|
ordinalSeverity: totals.reduce((max, current, index) => current ? index : max, 0),
|
|
431
|
+
tagName: '',
|
|
432
|
+
id: '',
|
|
427
433
|
location: {
|
|
428
434
|
doc: '',
|
|
429
435
|
type: '',
|
package/testaro/labClash.js
CHANGED
|
@@ -140,6 +140,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
140
140
|
data.items.mislabeled.push({
|
|
141
141
|
index,
|
|
142
142
|
tagName: labelee.tagName,
|
|
143
|
+
id: labelee.id,
|
|
143
144
|
type: labelee.type,
|
|
144
145
|
labelTypes,
|
|
145
146
|
texts
|
|
@@ -166,6 +167,8 @@ exports.reporter = async (page, withItems) => {
|
|
|
166
167
|
issueID: `labClash-${issue}`,
|
|
167
168
|
what: `Element ${item.tagName} ${diagnosis}`,
|
|
168
169
|
ordinalSeverity: issue === 'mislabeled' ? 2 : 3,
|
|
170
|
+
tagName: item.tagName,
|
|
171
|
+
id: item.id,
|
|
169
172
|
location: {
|
|
170
173
|
doc: '',
|
|
171
174
|
type: '',
|
|
@@ -176,18 +179,39 @@ exports.reporter = async (page, withItems) => {
|
|
|
176
179
|
});
|
|
177
180
|
});
|
|
178
181
|
}
|
|
179
|
-
else
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
182
|
+
else {
|
|
183
|
+
if (data.totals.unlabeled) {
|
|
184
|
+
standardInstances.push({
|
|
185
|
+
issueID: 'labClash-unlabeled',
|
|
186
|
+
what: 'Element labels are missing',
|
|
187
|
+
count: data.totals.unlabeled,
|
|
188
|
+
ordinalSeverity: 3,
|
|
189
|
+
tagName: '',
|
|
190
|
+
id: '',
|
|
191
|
+
location: {
|
|
192
|
+
doc: '',
|
|
193
|
+
type: '',
|
|
194
|
+
spec: ''
|
|
195
|
+
},
|
|
196
|
+
excerpt: ''
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
if (data.totals.mislabeled) {
|
|
200
|
+
standardInstances.push({
|
|
201
|
+
issueID: 'labClash-mislabeled',
|
|
202
|
+
what: 'Element labels are conflicting',
|
|
203
|
+
count: data.totals.mislabeled,
|
|
204
|
+
ordinalSeverity: 2,
|
|
205
|
+
tagName: '',
|
|
206
|
+
id: '',
|
|
207
|
+
location: {
|
|
208
|
+
doc: '',
|
|
209
|
+
type: '',
|
|
210
|
+
spec: ''
|
|
211
|
+
},
|
|
212
|
+
excerpt: ''
|
|
213
|
+
});
|
|
214
|
+
}
|
|
191
215
|
}
|
|
192
216
|
return {
|
|
193
217
|
data,
|
package/testaro/linkTo.js
CHANGED
|
@@ -5,34 +5,39 @@
|
|
|
5
5
|
*/
|
|
6
6
|
exports.reporter = async (page, withItems) => {
|
|
7
7
|
// Identify the visible links without href attributes.
|
|
8
|
-
const
|
|
8
|
+
const badLinkData = await page.$$eval(
|
|
9
9
|
'a:not([href]):visible',
|
|
10
10
|
badLinks => {
|
|
11
11
|
// FUNCTION DEFINITION START
|
|
12
12
|
// Returns a space-minimized copy of a string.
|
|
13
13
|
const compact = string => string.replace(/[\t\n]/g, '').replace(/\s{2,}/g, ' ').trim();
|
|
14
14
|
// FUNCTION DEFINITION END
|
|
15
|
-
return badLinks.map(link =>
|
|
15
|
+
return badLinks.map(link => ({
|
|
16
|
+
id: link.id,
|
|
17
|
+
text: compact(link.textContent)
|
|
18
|
+
}));
|
|
16
19
|
}
|
|
17
20
|
);
|
|
18
21
|
const data = {
|
|
19
|
-
total:
|
|
22
|
+
total: badLinkData.length
|
|
20
23
|
};
|
|
21
24
|
const totals = [0, 0, data.total, 0];
|
|
22
25
|
const standardInstances = [];
|
|
23
26
|
if (withItems) {
|
|
24
|
-
data.items =
|
|
27
|
+
data.items = badLinkData;
|
|
25
28
|
data.items.forEach(item => {
|
|
26
29
|
standardInstances.push({
|
|
27
30
|
issueID: 'linkTo',
|
|
28
31
|
what: 'Element a has no href attribute',
|
|
29
32
|
ordinalSeverity: 2,
|
|
33
|
+
tagName: 'A',
|
|
34
|
+
id: item.id,
|
|
30
35
|
location: {
|
|
31
36
|
doc: '',
|
|
32
37
|
type: '',
|
|
33
38
|
spec: ''
|
|
34
39
|
},
|
|
35
|
-
excerpt: item
|
|
40
|
+
excerpt: item.text
|
|
36
41
|
});
|
|
37
42
|
});
|
|
38
43
|
}
|
|
@@ -40,7 +45,10 @@ exports.reporter = async (page, withItems) => {
|
|
|
40
45
|
standardInstances.push({
|
|
41
46
|
issueID: 'linkTo',
|
|
42
47
|
what: 'Links are missing href attributes',
|
|
48
|
+
count: data.total,
|
|
43
49
|
ordinalSeverity: 2,
|
|
50
|
+
tagName: 'A',
|
|
51
|
+
id: '',
|
|
44
52
|
location: {
|
|
45
53
|
doc: '',
|
|
46
54
|
type: '',
|
package/testaro/linkUl.js
CHANGED
|
@@ -25,25 +25,32 @@ exports.reporter = async (page, withItems) => {
|
|
|
25
25
|
const adjacentLinks = linkTypes.adjacent;
|
|
26
26
|
const adjacentLinkCount = adjacentLinks.length;
|
|
27
27
|
let underlined = 0;
|
|
28
|
-
const
|
|
29
|
-
const
|
|
28
|
+
const ulAdjacentLinkData = [];
|
|
29
|
+
const nulAdjacentLinkData = [];
|
|
30
30
|
// For each of them:
|
|
31
31
|
adjacentLinks.forEach(link => {
|
|
32
32
|
// Identify the text of the link if itemization is required.
|
|
33
|
+
const id = link.id;
|
|
33
34
|
const text = withItems ? compact(link.textContent) || compact(link.outerHTML) : '';
|
|
34
35
|
// If it is underlined:
|
|
35
36
|
if (window.getComputedStyle(link).textDecorationLine === 'underline') {
|
|
36
37
|
// Increment the count of underlined inline links.
|
|
37
38
|
underlined++;
|
|
38
|
-
// If required, add its
|
|
39
|
+
// If required, add its data to the array of their data.
|
|
39
40
|
if (withItems) {
|
|
40
|
-
|
|
41
|
+
ulAdjacentLinkData.push({
|
|
42
|
+
id,
|
|
43
|
+
text
|
|
44
|
+
});
|
|
41
45
|
}
|
|
42
46
|
}
|
|
43
47
|
// Otherwise, if it is not underlined and itemization is required:
|
|
44
48
|
else if (withItems) {
|
|
45
49
|
// Add its text to the array of texts of non-underlined inline links.
|
|
46
|
-
|
|
50
|
+
nulAdjacentLinkData.push({
|
|
51
|
+
id,
|
|
52
|
+
text
|
|
53
|
+
});
|
|
47
54
|
}
|
|
48
55
|
});
|
|
49
56
|
// Get the percentage of underlined links among all inline links.
|
|
@@ -62,8 +69,8 @@ exports.reporter = async (page, withItems) => {
|
|
|
62
69
|
};
|
|
63
70
|
if (withItems) {
|
|
64
71
|
data.items = {
|
|
65
|
-
underlined:
|
|
66
|
-
notUnderlined:
|
|
72
|
+
underlined: ulAdjacentLinkData,
|
|
73
|
+
notUnderlined: nulAdjacentLinkData
|
|
67
74
|
};
|
|
68
75
|
}
|
|
69
76
|
const {adjacent} = data.totals;
|
|
@@ -73,14 +80,16 @@ exports.reporter = async (page, withItems) => {
|
|
|
73
80
|
data.items.notUnderlined.forEach(item => {
|
|
74
81
|
standardInstances.push({
|
|
75
82
|
issueID: 'linkUl',
|
|
76
|
-
what: '
|
|
83
|
+
what: 'Link is inline but has no underline',
|
|
77
84
|
ordinalSeverity: 1,
|
|
85
|
+
tagName: 'A',
|
|
86
|
+
id: item.id,
|
|
78
87
|
location: {
|
|
79
88
|
doc: '',
|
|
80
89
|
type: '',
|
|
81
90
|
spec: ''
|
|
82
91
|
},
|
|
83
|
-
excerpt: item
|
|
92
|
+
excerpt: item.text
|
|
84
93
|
});
|
|
85
94
|
});
|
|
86
95
|
}
|
|
@@ -88,7 +97,10 @@ exports.reporter = async (page, withItems) => {
|
|
|
88
97
|
standardInstances.push({
|
|
89
98
|
issueID: 'linkUl',
|
|
90
99
|
what: 'Inline links are missing underlines',
|
|
100
|
+
count: adjacent.total - adjacent.underlined,
|
|
91
101
|
ordinalSeverity: 1,
|
|
102
|
+
tagName: 'A',
|
|
103
|
+
id: '',
|
|
92
104
|
location: {
|
|
93
105
|
doc: '',
|
|
94
106
|
type: '',
|
package/testaro/menuNav.js
CHANGED
|
@@ -166,7 +166,12 @@ exports.reporter = async (page, withItems) => {
|
|
|
166
166
|
// If itemization is required:
|
|
167
167
|
if (withItems) {
|
|
168
168
|
// Initialize a report on the menu item.
|
|
169
|
-
|
|
169
|
+
const identifiers = await page.evaluate(element => ({
|
|
170
|
+
tagName: element.tagName,
|
|
171
|
+
id: element.id
|
|
172
|
+
}), currentItem);
|
|
173
|
+
itemData.tagName = identifiers.tagName;
|
|
174
|
+
itemData.id = identifiers.id;
|
|
170
175
|
itemData.text = await allText(page, currentItem);
|
|
171
176
|
itemData.navigationErrors = [];
|
|
172
177
|
}
|
|
@@ -203,14 +208,14 @@ exports.reporter = async (page, withItems) => {
|
|
|
203
208
|
isCorrect = await testKey(
|
|
204
209
|
menu, menuItems, currentItem, 'End', 'end', itemCount - 1, isCorrect, itemData
|
|
205
210
|
);
|
|
206
|
-
// Update the menu
|
|
211
|
+
// Update the menu status (Node 14 does not support the ES 2021 &&= operator).
|
|
207
212
|
menuIsCorrect = menuIsCorrect && isCorrect;
|
|
208
213
|
// Increment the data.
|
|
209
214
|
data.totals.menuItems[isCorrect ? 'correct' : 'incorrect']++;
|
|
210
215
|
if (withItems) {
|
|
211
216
|
data.menuItems[isCorrect ? 'correct' : 'incorrect'].push(itemData);
|
|
212
217
|
}
|
|
213
|
-
// Process the next
|
|
218
|
+
// Process the next menu item.
|
|
214
219
|
return await testMenuItems(menu, menuItems, index + 1, orientation, menuIsCorrect);
|
|
215
220
|
}
|
|
216
221
|
// Otherwise, i.e. if all menu items have been tested:
|
|
@@ -271,14 +276,17 @@ exports.reporter = async (page, withItems) => {
|
|
|
271
276
|
data.menuItems.incorrect.forEach(item => {
|
|
272
277
|
standardInstances.push({
|
|
273
278
|
issueID: 'menuNav',
|
|
274
|
-
what: `
|
|
279
|
+
what: `Menu item responds nonstandardly to ${item.navigationErrors.join(', ')}`,
|
|
280
|
+
count: item.navigationErrors.length,
|
|
275
281
|
ordinalSeverity: 1,
|
|
282
|
+
tagName: item.tagName,
|
|
283
|
+
id: item.id,
|
|
276
284
|
location: {
|
|
277
285
|
doc: '',
|
|
278
286
|
type: '',
|
|
279
287
|
spec: ''
|
|
280
288
|
},
|
|
281
|
-
excerpt:
|
|
289
|
+
excerpt: item.text
|
|
282
290
|
});
|
|
283
291
|
});
|
|
284
292
|
}
|
|
@@ -286,7 +294,10 @@ exports.reporter = async (page, withItems) => {
|
|
|
286
294
|
standardInstances.push({
|
|
287
295
|
issueID: 'menuNav',
|
|
288
296
|
what: 'Menus and menu items have nonstandard navigation',
|
|
289
|
-
|
|
297
|
+
count: data.totals.navigations.all.incorrect,
|
|
298
|
+
ordinalSeverity: 1,
|
|
299
|
+
tagName: '',
|
|
300
|
+
id: '',
|
|
290
301
|
location: {
|
|
291
302
|
doc: '',
|
|
292
303
|
type: '',
|