testaro 4.6.0 → 4.7.1
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/commands.js +3 -2
- package/package.json +1 -1
- package/procs/linksByType.js +40 -76
- package/run.js +1 -1
- package/tests/focInd.js +88 -35
- package/tests/linkUl.js +23 -20
- package/tests/styleDiff.js +5 -5
- package/tests/tabNav.js +55 -56
- package/validation/tests/scripts/focInd.json +11 -7
- package/validation/tests/scripts/linkUl.json +16 -16
- package/validation/tests/scripts/styleDiff.json +10 -10
- package/validation/tests/targets/focInd/bad.html +29 -8
- package/validation/tests/targets/linkUl/bad.html +23 -4
- package/validation/tests/targets/linkUl/good.html +5 -5
- package/validation/tests/targets/linkUl/na.html +3 -3
package/commands.js
CHANGED
|
@@ -166,8 +166,9 @@ exports.commands = {
|
|
|
166
166
|
focInd: [
|
|
167
167
|
'Perform a focInd test',
|
|
168
168
|
{
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
revealAll: [true, 'boolean', '', 'whether to make all elements visible first'],
|
|
170
|
+
allowedDelay: [true, 'number', '', 'milliseconds to wait for an outline'],
|
|
171
|
+
withItems: [true, 'boolean']
|
|
171
172
|
}
|
|
172
173
|
],
|
|
173
174
|
focOp: [
|
package/package.json
CHANGED
package/procs/linksByType.js
CHANGED
|
@@ -1,90 +1,54 @@
|
|
|
1
1
|
// Returns an object classifying the links in a page by layout.
|
|
2
2
|
exports.linksByType = async page => await page.evaluateHandle(() => {
|
|
3
3
|
// FUNCTION DEFINITIONS START
|
|
4
|
-
// Returns whether an element has fluid display.
|
|
5
|
-
const hasFluidDisplay = element => {
|
|
6
|
-
const display = window.getComputedStyle(element).display;
|
|
7
|
-
return display.startsWith('inline') || display.startsWith('flex');
|
|
8
|
-
};
|
|
9
|
-
// Returns whether an element and its children have fluid display.
|
|
10
|
-
const isFluid = element => {
|
|
11
|
-
if (hasFluidDisplay(element)) {
|
|
12
|
-
return Array.from(element.children).every(child => hasFluidDisplay(child));
|
|
13
|
-
}
|
|
14
|
-
else {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
// Returns whether all siblings of an element have fluid display.
|
|
19
|
-
const hasFluidSiblings = element => {
|
|
20
|
-
const preSib = element.previousElementSibling;
|
|
21
|
-
if (preSib) {
|
|
22
|
-
const postSib = element.nextElementSibling;
|
|
23
|
-
if (postSib) {
|
|
24
|
-
return isFluid(preSib) && isFluid(postSib);
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
return isFluid(preSib);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
const postSib = element.nextElementSibling;
|
|
32
|
-
if (postSib) {
|
|
33
|
-
return isFluid(postSib);
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
4
|
// Removes spacing characters from a text.
|
|
41
5
|
const despace = text => text.replace(/\s/g, '');
|
|
42
|
-
// Returns whether
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
6
|
+
// Returns whether a list is a list entirely of links.
|
|
7
|
+
const isLinkList = list => {
|
|
8
|
+
const listItems = Array.from(list.children);
|
|
9
|
+
if (listItems.length > 1) {
|
|
10
|
+
return listItems.length > 1 && listItems.every(item => {
|
|
11
|
+
if (item.tagName === 'LI') {
|
|
12
|
+
const {children} = item;
|
|
13
|
+
if (children.length === 1) {
|
|
14
|
+
const link = children[0];
|
|
15
|
+
if (link.tagName === 'A') {
|
|
16
|
+
const itemText = despace(item.textContent);
|
|
17
|
+
const linkText = despace(link.textContent);
|
|
18
|
+
return itemText.length === linkText.length;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
62
32
|
}
|
|
63
|
-
// Otherwise, i.e. if the element has no text:
|
|
64
33
|
else {
|
|
65
|
-
// Return no.
|
|
66
34
|
return false;
|
|
67
35
|
}
|
|
68
36
|
};
|
|
69
37
|
// FUNCTION DEFINITIONS END
|
|
70
|
-
//
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
inline: [],
|
|
77
|
-
block: []
|
|
78
|
-
};
|
|
79
|
-
// Populate it.
|
|
80
|
-
links.forEach(link => {
|
|
81
|
-
if (isFluid(link) && hasFluidSiblings(link) && hasAdjacentText(link)) {
|
|
82
|
-
linkTypes.inline.push(link);
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
linkTypes.block.push(link);
|
|
38
|
+
// Identify the list links in the page.
|
|
39
|
+
const lists = Array.from(document.body.querySelectorAll('ul, ol'));
|
|
40
|
+
const listLinks = [];
|
|
41
|
+
lists.forEach(list => {
|
|
42
|
+
if (isLinkList(list)) {
|
|
43
|
+
listLinks.push(... Array.from(list.querySelectorAll('a')));
|
|
86
44
|
}
|
|
87
45
|
});
|
|
88
|
-
//
|
|
89
|
-
|
|
46
|
+
// Identify the adjacent links in the page.
|
|
47
|
+
const allLinks = Array.from(document.body.querySelectorAll('a'));
|
|
48
|
+
const adjacentLinks = allLinks.filter(link => ! listLinks.includes(link));
|
|
49
|
+
// Return the data.
|
|
50
|
+
return {
|
|
51
|
+
adjacent: adjacentLinks,
|
|
52
|
+
list: listLinks
|
|
53
|
+
};
|
|
90
54
|
});
|
package/run.js
CHANGED
|
@@ -36,7 +36,7 @@ const tests = {
|
|
|
36
36
|
hover: 'hover-caused content additions',
|
|
37
37
|
ibm: 'IBM Accessibility Checker',
|
|
38
38
|
labClash: 'labeling inconsistencies',
|
|
39
|
-
linkUl: '
|
|
39
|
+
linkUl: 'adjacent-link underlining',
|
|
40
40
|
menuNav: 'keyboard navigation between focusable menu items',
|
|
41
41
|
motion: 'motion',
|
|
42
42
|
radioSet: 'fieldset grouping of radio buttons',
|
package/tests/focInd.js
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
/*
|
|
2
2
|
focInd
|
|
3
3
|
This test reports focusable elements without focus indicators, with non-outline focus
|
|
4
|
-
indicators, and with outline focus indicators.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
indicators, and with outline focus indicators. An outline is recognized if it has non-zero
|
|
5
|
+
line thickness and non-transparent color. The test is based on the assumption that outlines are
|
|
6
|
+
the standard and thus most familiar focus indicator. Other focus indicators are assumed better
|
|
7
|
+
than none, but more likely to be misunderstood. For example, underlines may be mistaken for
|
|
8
|
+
selection indicators. Some pages delay the appearance of focus indicators. This test waits for
|
|
9
|
+
focus indicators to appear if specified and, if there is a delay, reports on its magnitude.
|
|
9
10
|
|
|
10
11
|
Bug: This test fails to recognize outlines when run with firefox.
|
|
11
12
|
*/
|
|
12
|
-
exports.reporter = async (page,
|
|
13
|
+
exports.reporter = async (page, revealAll, allowedDelay, withItems) => {
|
|
13
14
|
// If required, make all elements visible.
|
|
14
15
|
if (revealAll) {
|
|
15
16
|
await require('../procs/allVis').allVis(page);
|
|
16
17
|
}
|
|
17
18
|
// Get data on the focusable visible elements with and without indicators.
|
|
18
|
-
const data = await page.$$eval('body *:visible', (elements,
|
|
19
|
+
const data = await page.$$eval('body *:visible', async (elements, args) => {
|
|
20
|
+
const allowedDelay = args[0];
|
|
21
|
+
const withItems = args[1];
|
|
19
22
|
// Initialize the data.
|
|
20
23
|
const data = {
|
|
21
24
|
totals: {
|
|
@@ -31,6 +34,7 @@ exports.reporter = async (page, withItems, revealAll) => {
|
|
|
31
34
|
},
|
|
32
35
|
outlinePresent: {
|
|
33
36
|
total: 0,
|
|
37
|
+
meanDelay: 0,
|
|
34
38
|
tagNames: {}
|
|
35
39
|
}
|
|
36
40
|
}
|
|
@@ -43,9 +47,13 @@ exports.reporter = async (page, withItems, revealAll) => {
|
|
|
43
47
|
outlinePresent: []
|
|
44
48
|
};
|
|
45
49
|
}
|
|
46
|
-
|
|
50
|
+
// Adds facts about an element to the result.
|
|
51
|
+
const addElementFacts = (element, status, delay = null) => {
|
|
47
52
|
const type = data.totals.types[status];
|
|
48
53
|
type.total++;
|
|
54
|
+
if (status === 'outlinePresent') {
|
|
55
|
+
type.meanDelay = Math.round(((type.total - 1) * type.meanDelay + delay) / type.total);
|
|
56
|
+
}
|
|
49
57
|
const tagName = element.tagName;
|
|
50
58
|
if (type.tagNames[tagName]) {
|
|
51
59
|
type.tagNames[tagName]++;
|
|
@@ -54,46 +62,91 @@ exports.reporter = async (page, withItems, revealAll) => {
|
|
|
54
62
|
type.tagNames[tagName] = 1;
|
|
55
63
|
}
|
|
56
64
|
if (withItems) {
|
|
57
|
-
|
|
65
|
+
const elementData = {
|
|
58
66
|
tagName,
|
|
59
67
|
text: element.textContent.trim().replace(/\s{2,}/g, ' ').slice(0, 100)
|
|
60
|
-
}
|
|
68
|
+
};
|
|
69
|
+
if (status === 'outlinePresent') {
|
|
70
|
+
elementData.delay = delay;
|
|
71
|
+
}
|
|
72
|
+
data.items[status].push(elementData);
|
|
61
73
|
}
|
|
62
74
|
};
|
|
63
|
-
|
|
75
|
+
// For each visible element descendant of the body:
|
|
76
|
+
for(const element of elements) {
|
|
77
|
+
// If it is Tab-focusable:
|
|
64
78
|
if (element.tabIndex === 0) {
|
|
79
|
+
// Increment the total of focusable elements.
|
|
65
80
|
data.totals.total++;
|
|
66
|
-
|
|
81
|
+
// Get a live style declaration of its properties.
|
|
82
|
+
const styleDec = window.getComputedStyle(element);
|
|
83
|
+
// Freeze a copy to preserve the style properties when not focused.
|
|
84
|
+
const styleBlurred = Object.assign({}, styleDec);
|
|
85
|
+
// Focus it, potentially changing the properties in its style declaration.
|
|
67
86
|
element.focus({preventScroll: true});
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
87
|
+
let hasOutline = false;
|
|
88
|
+
// If it has no outline when not focused:
|
|
89
|
+
if (styleBlurred.outlineWidth === '0px') {
|
|
90
|
+
// If an outline appeared immediately on focus:
|
|
91
|
+
if (styleDec.outlineWidth !== '0px' && styleDec.outlineColor !== 'rgba(0, 0, 0, 0)') {
|
|
92
|
+
// Add facts about the element to the result.
|
|
93
|
+
addElementFacts(element, 'outlinePresent', 0);
|
|
94
|
+
hasOutline = true;
|
|
95
|
+
}
|
|
96
|
+
// Otherwise, if a wait for an outline is allowed:
|
|
97
|
+
else if (allowedDelay) {
|
|
98
|
+
// Determine how long an outline takes to appear or whether it times out.
|
|
99
|
+
const outlineDelay = new Promise(resolve => {
|
|
100
|
+
const focusTime = Date.now();
|
|
101
|
+
const deadline = focusTime + allowedDelay;
|
|
102
|
+
const interval = setInterval(() => {
|
|
103
|
+
if (
|
|
104
|
+
styleDec.outlineWidth !== '0px' && styleDec.outlineColor !== 'rgba(0, 0, 0, 0)'
|
|
105
|
+
) {
|
|
106
|
+
resolve(Date.now() - focusTime);
|
|
107
|
+
clearInterval(interval);
|
|
108
|
+
}
|
|
109
|
+
else if (Date.now() > deadline) {
|
|
110
|
+
resolve(null);
|
|
111
|
+
clearInterval(interval);
|
|
112
|
+
}
|
|
113
|
+
}, 100);
|
|
114
|
+
});
|
|
115
|
+
// If it appeared before the wait limit:
|
|
116
|
+
const delay = await outlineDelay;
|
|
117
|
+
if (delay) {
|
|
118
|
+
// Add facts about the element to the result.
|
|
119
|
+
addElementFacts(element, 'outlinePresent', delay);
|
|
120
|
+
hasOutline = true;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
74
123
|
}
|
|
75
|
-
|
|
76
|
-
|
|
124
|
+
// If no allowed outline appeared:
|
|
125
|
+
if (! hasOutline) {
|
|
126
|
+
// Returns whether a style property differs between focused and not focused.
|
|
127
|
+
const diff = prop => styleDec[prop] !== styleBlurred[prop];
|
|
128
|
+
// Determine whether the element has another allowed focus indicator.
|
|
129
|
+
const hasDiffOutline = styleDec.outlineWidth !== '0px'
|
|
130
|
+
&& styleDec.outlineColor !== 'rgba(0, 0, 0, 0)'
|
|
131
|
+
&& (diff('outlineStyle') || diff('outlineWidth'));
|
|
132
|
+
const hasDiffBorder = styleDec.borderWidth !== '0px'
|
|
133
|
+
&& styleDec.borderColor !== 'rgba(0, 0, 0, 0)'
|
|
134
|
+
&& (diff('borderStyle') || diff('borderWidth'));
|
|
77
135
|
const hasIndicator
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|| diff('fontSize')
|
|
87
|
-
|| diff('fontStyle')
|
|
88
|
-
|| diff('textDecorationLine')
|
|
89
|
-
|| diff('textDecorationStyle')
|
|
90
|
-
|| diff('textDecorationThickness');
|
|
136
|
+
= hasDiffOutline
|
|
137
|
+
|| hasDiffBorder
|
|
138
|
+
|| diff('fontSize')
|
|
139
|
+
|| diff('fontStyle')
|
|
140
|
+
|| diff('textDecorationLine')
|
|
141
|
+
|| diff('textDecorationStyle')
|
|
142
|
+
|| diff('textDecorationThickness');
|
|
143
|
+
// Add the determination to the result.
|
|
91
144
|
const status = hasIndicator ? 'nonOutlinePresent' : 'indicatorMissing';
|
|
92
145
|
addElementFacts(element, status);
|
|
93
146
|
}
|
|
94
147
|
}
|
|
95
|
-
}
|
|
148
|
+
};
|
|
96
149
|
return data;
|
|
97
|
-
}, withItems);
|
|
150
|
+
}, [allowedDelay, withItems]);
|
|
98
151
|
return {result: data};
|
|
99
152
|
};
|
package/tests/linkUl.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/*
|
|
2
2
|
linkUl
|
|
3
|
-
This test reports failures to underline
|
|
4
|
-
traditional style properties that identify links.
|
|
5
|
-
recognized without underlines, but
|
|
6
|
-
visually from surrounding text if not underlined. Underlining
|
|
7
|
-
provides an indicator valuable only to mouse users, and even they must traverse the text
|
|
8
|
-
a mouse merely to discover which passages are links.
|
|
3
|
+
This test reports failures to underline links that are adjacent to nonlink text. Underlining
|
|
4
|
+
and color are the traditional style properties that identify links. Lists of links containing
|
|
5
|
+
only links can be recognized without underlines, but other links are difficult or impossible to
|
|
6
|
+
distinguish visually from surrounding text if not underlined. Underlining adjacent links only on
|
|
7
|
+
hover provides an indicator valuable only to mouse users, and even they must traverse the text
|
|
8
|
+
with a mouse merely to discover which passages are links. This tests treats links as adjacent
|
|
9
|
+
unless they are in an ordered or unordered list of at least 2 links with no other text.
|
|
9
10
|
*/
|
|
10
11
|
exports.reporter = async (page, withItems) => {
|
|
11
12
|
// Identify the links in the page, by type.
|
|
@@ -17,14 +18,14 @@ exports.reporter = async (page, withItems) => {
|
|
|
17
18
|
// Returns a space-minimized copy of a string.
|
|
18
19
|
const compact = string => string.replace(/[\t\n]/g, '').replace(/\s{2,}/g, ' ').trim();
|
|
19
20
|
// FUNCTION DEFINITION END
|
|
20
|
-
// Identify the
|
|
21
|
-
const
|
|
22
|
-
const
|
|
21
|
+
// Identify the adjacent links.
|
|
22
|
+
const adjacentLinks = linkTypes.adjacent;
|
|
23
|
+
const adjacentLinkCount = adjacentLinks.length;
|
|
23
24
|
let underlined = 0;
|
|
24
|
-
const
|
|
25
|
-
const
|
|
25
|
+
const ulAdjacentLinkTexts = [];
|
|
26
|
+
const nulAdjacentLinkTexts = [];
|
|
26
27
|
// For each of them:
|
|
27
|
-
|
|
28
|
+
adjacentLinks.forEach(link => {
|
|
28
29
|
// Identify the text of the link if itemization is required.
|
|
29
30
|
const text = withItems ? compact(link.textContent) : '';
|
|
30
31
|
// If it is underlined:
|
|
@@ -33,22 +34,24 @@ exports.reporter = async (page, withItems) => {
|
|
|
33
34
|
underlined++;
|
|
34
35
|
// If required, add its text to the array of their texts.
|
|
35
36
|
if (withItems) {
|
|
36
|
-
|
|
37
|
+
ulAdjacentLinkTexts.push(text);
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
// Otherwise, if it is not underlined and itemization is required:
|
|
40
41
|
else if (withItems) {
|
|
41
42
|
// Add its text to the array of texts of non-underlined inline links.
|
|
42
|
-
|
|
43
|
+
nulAdjacentLinkTexts.push(text);
|
|
43
44
|
}
|
|
44
45
|
});
|
|
45
46
|
// Get the percentage of underlined links among all inline links.
|
|
46
|
-
const underlinedPercent =
|
|
47
|
+
const underlinedPercent = adjacentLinkCount
|
|
48
|
+
? Math.floor(100 * underlined / adjacentLinkCount)
|
|
49
|
+
: 'N/A';
|
|
47
50
|
const data = {
|
|
48
51
|
totals: {
|
|
49
|
-
links:
|
|
50
|
-
|
|
51
|
-
total:
|
|
52
|
+
links: adjacentLinks.length + linkTypes.list.length,
|
|
53
|
+
adjacent: {
|
|
54
|
+
total: adjacentLinkCount,
|
|
52
55
|
underlined,
|
|
53
56
|
underlinedPercent
|
|
54
57
|
}
|
|
@@ -56,8 +59,8 @@ exports.reporter = async (page, withItems) => {
|
|
|
56
59
|
};
|
|
57
60
|
if (withItems) {
|
|
58
61
|
data.items = {
|
|
59
|
-
underlined:
|
|
60
|
-
notUnderlined:
|
|
62
|
+
underlined: ulAdjacentLinkTexts,
|
|
63
|
+
notUnderlined: nulAdjacentLinkTexts
|
|
61
64
|
};
|
|
62
65
|
}
|
|
63
66
|
return {result: data};
|
package/tests/styleDiff.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/*
|
|
2
2
|
styleDiff
|
|
3
3
|
This test reports style differences among links, buttons, and headings. It assumes
|
|
4
|
-
that an accessible page employs few or only one style for
|
|
5
|
-
for
|
|
4
|
+
that an accessible page employs few or only one style for adjacent links, and likewise
|
|
5
|
+
for list links, buttons, and headings at each level. The test considers only
|
|
6
6
|
particular style properties, listed in the 'mainStyles' and 'headingStyles' arrays.
|
|
7
7
|
*/
|
|
8
8
|
exports.reporter = async (page, withItems) => {
|
|
9
|
-
// Get an object with arrays of
|
|
9
|
+
// Get an object with arrays of list and adjacent links as properties.
|
|
10
10
|
const linkTypes = await require('../procs/linksByType').linksByType(page);
|
|
11
11
|
return await page.$eval('body', (body, args) => {
|
|
12
12
|
const withItems = args[0];
|
|
@@ -51,8 +51,8 @@ exports.reporter = async (page, withItems) => {
|
|
|
51
51
|
const elementClasses = {
|
|
52
52
|
headings: {},
|
|
53
53
|
other: {
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
aAdjacent: linkTypes.adjacent,
|
|
55
|
+
aList: linkTypes.list
|
|
56
56
|
}
|
|
57
57
|
};
|
|
58
58
|
// For each heading tag name:
|
package/tests/tabNav.js
CHANGED
|
@@ -4,70 +4,69 @@
|
|
|
4
4
|
Standards are based on https://www.w3.org/TR/wai-aria-practices-1.1/#tabpanel.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
//
|
|
7
|
+
// FUNCTIONS
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
},
|
|
17
|
-
specific: {
|
|
18
|
-
tab: {
|
|
19
|
-
total: 0,
|
|
20
|
-
correct: 0,
|
|
21
|
-
incorrect: 0
|
|
22
|
-
},
|
|
23
|
-
left: {
|
|
24
|
-
total: 0,
|
|
25
|
-
correct: 0,
|
|
26
|
-
incorrect: 0
|
|
27
|
-
},
|
|
28
|
-
right: {
|
|
29
|
-
total: 0,
|
|
30
|
-
correct: 0,
|
|
31
|
-
incorrect: 0
|
|
32
|
-
},
|
|
33
|
-
up: {
|
|
34
|
-
total: 0,
|
|
35
|
-
correct: 0,
|
|
36
|
-
incorrect: 0
|
|
37
|
-
},
|
|
38
|
-
down: {
|
|
39
|
-
total: 0,
|
|
40
|
-
correct: 0,
|
|
41
|
-
incorrect: 0
|
|
42
|
-
},
|
|
43
|
-
home: {
|
|
9
|
+
// Tests tab-list navigation and reports results.
|
|
10
|
+
exports.reporter = async (page, withItems) => {
|
|
11
|
+
// Initialize the results.
|
|
12
|
+
const data = {
|
|
13
|
+
totals: {
|
|
14
|
+
navigations: {
|
|
15
|
+
all: {
|
|
44
16
|
total: 0,
|
|
45
17
|
correct: 0,
|
|
46
18
|
incorrect: 0
|
|
47
19
|
},
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
20
|
+
specific: {
|
|
21
|
+
tab: {
|
|
22
|
+
total: 0,
|
|
23
|
+
correct: 0,
|
|
24
|
+
incorrect: 0
|
|
25
|
+
},
|
|
26
|
+
left: {
|
|
27
|
+
total: 0,
|
|
28
|
+
correct: 0,
|
|
29
|
+
incorrect: 0
|
|
30
|
+
},
|
|
31
|
+
right: {
|
|
32
|
+
total: 0,
|
|
33
|
+
correct: 0,
|
|
34
|
+
incorrect: 0
|
|
35
|
+
},
|
|
36
|
+
up: {
|
|
37
|
+
total: 0,
|
|
38
|
+
correct: 0,
|
|
39
|
+
incorrect: 0
|
|
40
|
+
},
|
|
41
|
+
down: {
|
|
42
|
+
total: 0,
|
|
43
|
+
correct: 0,
|
|
44
|
+
incorrect: 0
|
|
45
|
+
},
|
|
46
|
+
home: {
|
|
47
|
+
total: 0,
|
|
48
|
+
correct: 0,
|
|
49
|
+
incorrect: 0
|
|
50
|
+
},
|
|
51
|
+
end: {
|
|
52
|
+
total: 0,
|
|
53
|
+
correct: 0,
|
|
54
|
+
incorrect: 0
|
|
55
|
+
}
|
|
52
56
|
}
|
|
57
|
+
},
|
|
58
|
+
tabElements: {
|
|
59
|
+
total: 0,
|
|
60
|
+
correct: 0,
|
|
61
|
+
incorrect: 0
|
|
62
|
+
},
|
|
63
|
+
tabLists: {
|
|
64
|
+
total: 0,
|
|
65
|
+
correct: 0,
|
|
66
|
+
incorrect: 0
|
|
53
67
|
}
|
|
54
|
-
},
|
|
55
|
-
tabElements: {
|
|
56
|
-
total: 0,
|
|
57
|
-
correct: 0,
|
|
58
|
-
incorrect: 0
|
|
59
|
-
},
|
|
60
|
-
tabLists: {
|
|
61
|
-
total: 0,
|
|
62
|
-
correct: 0,
|
|
63
|
-
incorrect: 0
|
|
64
68
|
}
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
// FUNCTIONS
|
|
69
|
-
|
|
70
|
-
exports.reporter = async (page, withItems) => {
|
|
69
|
+
};
|
|
71
70
|
if (withItems) {
|
|
72
71
|
data.tabElements = {
|
|
73
72
|
incorrect: [],
|
|
@@ -15,9 +15,10 @@
|
|
|
15
15
|
{
|
|
16
16
|
"type": "test",
|
|
17
17
|
"which": "focInd",
|
|
18
|
-
"what": "focus indication",
|
|
19
|
-
"withItems": false,
|
|
20
18
|
"revealAll": false,
|
|
19
|
+
"allowedDelay": 0,
|
|
20
|
+
"withItems": false,
|
|
21
|
+
"what": "focus indication",
|
|
21
22
|
"expect": [
|
|
22
23
|
["totals.total", "=", 4],
|
|
23
24
|
["totals.types.indicatorMissing.total", "=", 0],
|
|
@@ -36,14 +37,17 @@
|
|
|
36
37
|
{
|
|
37
38
|
"type": "test",
|
|
38
39
|
"which": "focInd",
|
|
39
|
-
"what": "focus indication",
|
|
40
|
-
"withItems": false,
|
|
41
40
|
"revealAll": false,
|
|
41
|
+
"allowedDelay": 150,
|
|
42
|
+
"withItems": true,
|
|
43
|
+
"what": "focus indication",
|
|
42
44
|
"expect": [
|
|
43
|
-
["totals.total", "=",
|
|
44
|
-
["totals.types.indicatorMissing.total", "=",
|
|
45
|
+
["totals.total", "=", 7],
|
|
46
|
+
["totals.types.indicatorMissing.total", "=", 2],
|
|
47
|
+
["totals.types.indicatorMissing.tagNames.A", "=", 1],
|
|
45
48
|
["totals.types.indicatorMissing.tagNames.INPUT", "=", 1],
|
|
46
|
-
["totals.types.nonOutlinePresent.total", "=",
|
|
49
|
+
["totals.types.nonOutlinePresent.total", "=", 2],
|
|
50
|
+
["totals.types.nonOutlinePresent.tagNames.A", "=", 1],
|
|
47
51
|
["totals.types.nonOutlinePresent.tagNames.BUTTON", "=", 1],
|
|
48
52
|
["totals.types.outlinePresent.total", "=", 3],
|
|
49
53
|
["totals.types.outlinePresent.tagNames.A", "=", 1],
|
|
@@ -10,52 +10,52 @@
|
|
|
10
10
|
{
|
|
11
11
|
"type": "url",
|
|
12
12
|
"which": "__targets__/linkUl/good.html",
|
|
13
|
-
"what": "page with underlined
|
|
13
|
+
"what": "page with underlined adjacent links"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"type": "test",
|
|
17
17
|
"which": "linkUl",
|
|
18
|
-
"what": "link underlining",
|
|
18
|
+
"what": "adjacent-link underlining",
|
|
19
19
|
"withItems": false,
|
|
20
20
|
"expect": [
|
|
21
21
|
["totals.links", "=", 6],
|
|
22
|
-
["totals.
|
|
23
|
-
["totals.
|
|
24
|
-
["totals.
|
|
22
|
+
["totals.adjacent.total", "=", 2],
|
|
23
|
+
["totals.adjacent.underlined", "=", 2],
|
|
24
|
+
["totals.adjacent.underlinedPercent", "=", 100]
|
|
25
25
|
]
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
"type": "url",
|
|
29
29
|
"which": "__targets__/linkUl/bad.html",
|
|
30
|
-
"what": "page
|
|
30
|
+
"what": "page with nonunderlined adjacent links"
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
33
|
"type": "test",
|
|
34
34
|
"which": "linkUl",
|
|
35
|
-
"what": "link underlining",
|
|
35
|
+
"what": "adjacent-link underlining",
|
|
36
36
|
"withItems": false,
|
|
37
37
|
"expect": [
|
|
38
|
-
["totals.links", "=",
|
|
39
|
-
["totals.
|
|
40
|
-
["totals.
|
|
41
|
-
["totals.
|
|
38
|
+
["totals.links", "=", 6],
|
|
39
|
+
["totals.adjacent.total", "=", 6],
|
|
40
|
+
["totals.adjacent.underlined", "=", 0],
|
|
41
|
+
["totals.adjacent.underlinedPercent", "=", 0]
|
|
42
42
|
]
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
"type": "url",
|
|
46
46
|
"which": "__targets__/linkUl/na.html",
|
|
47
|
-
"what": "page without
|
|
47
|
+
"what": "page without adjacent links"
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
"type": "test",
|
|
51
51
|
"which": "linkUl",
|
|
52
|
-
"what": "link underlining",
|
|
52
|
+
"what": "adjacent-link underlining",
|
|
53
53
|
"withItems": false,
|
|
54
54
|
"expect": [
|
|
55
55
|
["totals.links", "=", 2],
|
|
56
|
-
["totals.
|
|
57
|
-
["totals.
|
|
58
|
-
["totals.
|
|
56
|
+
["totals.adjacent.total", "=", 0],
|
|
57
|
+
["totals.adjacent.underlined", "=", 0],
|
|
58
|
+
["totals.adjacent.underlinedPercent", "=", "N/A"]
|
|
59
59
|
]
|
|
60
60
|
}
|
|
61
61
|
]
|
|
@@ -18,13 +18,13 @@
|
|
|
18
18
|
"what": "styleDiff",
|
|
19
19
|
"withItems": false,
|
|
20
20
|
"expect": [
|
|
21
|
-
["totals.
|
|
22
|
-
["totals.
|
|
21
|
+
["totals.aAdjacent.total", "=", 2],
|
|
22
|
+
["totals.aList.total", "=", 2],
|
|
23
23
|
["totals.button.total", "=", 2],
|
|
24
24
|
["totals.h1.total", "=", 1],
|
|
25
25
|
["totals.h2.total", "=", 4],
|
|
26
|
-
["totals.
|
|
27
|
-
["totals.
|
|
26
|
+
["totals.aAdjacent.subtotals"],
|
|
27
|
+
["totals.aList.subtotals"],
|
|
28
28
|
["totals.button.subtotals"],
|
|
29
29
|
["totals.h1.subtotals"],
|
|
30
30
|
["totals.h2.subtotals"]
|
|
@@ -41,15 +41,15 @@
|
|
|
41
41
|
"what": "styleDiff",
|
|
42
42
|
"withItems": false,
|
|
43
43
|
"expect": [
|
|
44
|
-
["totals.
|
|
45
|
-
["totals.
|
|
44
|
+
["totals.aAdjacent.total", "=", 2],
|
|
45
|
+
["totals.aList.total", "=", 2],
|
|
46
46
|
["totals.button.total", "=", 2],
|
|
47
47
|
["totals.h1.total", "=", 1],
|
|
48
48
|
["totals.h2.total", "=", 4],
|
|
49
|
-
["totals.
|
|
50
|
-
["totals.
|
|
51
|
-
["totals.
|
|
52
|
-
["totals.
|
|
49
|
+
["totals.aAdjacent.subtotals.0", "=", 1],
|
|
50
|
+
["totals.aAdjacent.subtotals.1", "=", 1],
|
|
51
|
+
["totals.aList.subtotals.0", "=", 1],
|
|
52
|
+
["totals.aList.subtotals.1", "=", 1],
|
|
53
53
|
["totals.button.subtotals.0", "=", 1],
|
|
54
54
|
["totals.button.subtotals.1", "=", 1],
|
|
55
55
|
["totals.h1.subtotals"],
|
|
@@ -6,26 +6,47 @@
|
|
|
6
6
|
<meta name="description" content="tester">
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
8
8
|
<style>
|
|
9
|
-
|
|
10
|
-
outline: none;
|
|
11
|
-
}
|
|
12
|
-
input[type=checkbox]:focus {
|
|
9
|
+
#button:focus, #checkbox:focus {
|
|
13
10
|
outline: 3px blue solid;
|
|
14
11
|
outline-offset: 2px;
|
|
15
12
|
}
|
|
13
|
+
#slowLink:focus, #sluggishLink:focus {
|
|
14
|
+
border: 2px green solid;
|
|
15
|
+
outline: none;
|
|
16
|
+
}
|
|
17
|
+
#checkbox {
|
|
18
|
+
transition-property: outline;
|
|
19
|
+
transition-delay: 35ms;
|
|
20
|
+
transition-duration: 40ms;
|
|
21
|
+
}
|
|
22
|
+
#slowLink {
|
|
23
|
+
transition-property: border;
|
|
24
|
+
transition-delay: 125ms;
|
|
25
|
+
}
|
|
26
|
+
#sluggishLink {
|
|
27
|
+
transition-property: border;
|
|
28
|
+
transition-delay: 525ms;
|
|
29
|
+
}
|
|
16
30
|
#special:focus {
|
|
17
31
|
outline: none;
|
|
18
32
|
text-decoration-line: underline overline;
|
|
19
|
-
text-decoration-color:
|
|
33
|
+
text-decoration-color: darkorange;
|
|
34
|
+
}
|
|
35
|
+
#textInput:focus {
|
|
36
|
+
outline: none;
|
|
20
37
|
}
|
|
21
38
|
</style>
|
|
22
39
|
</head>
|
|
23
40
|
<body>
|
|
24
41
|
<main>
|
|
25
42
|
<h1>Page with mixed focus indication</h1>
|
|
26
|
-
<p>This paragraph contains a link to <a href="https://en.wikipedia.org">information</a>,
|
|
27
|
-
<p>This paragraph contains a
|
|
28
|
-
<p>This paragraph contains a
|
|
43
|
+
<p>This paragraph contains a link to <a href="https://en.wikipedia.org">English information</a>, which exhibits a default focus indicator.</p>
|
|
44
|
+
<p>This paragraph contains a <button id="button" type="button">button</button> with a custom outline as its focus indicator.</p>
|
|
45
|
+
<p>This paragraph contains a <label>text input <input id="textInput" type="text"></label> with the focus indicator suppressed.</p>
|
|
46
|
+
<p>This paragraph contains a checkbox with an enhanced focus-indication outline that finishes appearing 75 ms after the checkbox becomes focused. <label><input id="checkbox" type="checkbox"> I am a bad page.</label></p>
|
|
47
|
+
<p>This paragraph contains a link to <a id="slowLink" href="https://fr.wikipedia.org">French information</a> with a custom border as its only focus indicator. The border appears 125 ms after the link becomes focused.</p>
|
|
48
|
+
<p>This paragraph contains a link to <a id="sluggishLink" href="https://de.wikipedia.org">German information</a> with a custom border as its only focus indicator. The border appears 525 ms after the link becomes focused.</p>
|
|
49
|
+
<p>This paragraph contains a button with a focus indicator consisting solely of an underline and an overline. <button id="special" type="button">special button</button></p>
|
|
29
50
|
</main>
|
|
30
51
|
</body>
|
|
31
52
|
</html>
|
|
@@ -2,15 +2,34 @@
|
|
|
2
2
|
<html lang="en-US">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
|
-
<title>Page without underlined
|
|
5
|
+
<title>Page without underlined adjacent links</title>
|
|
6
6
|
<meta name="description" content="tester">
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
8
|
+
<style>
|
|
9
|
+
.nude > li > a {
|
|
10
|
+
text-decoration: none;
|
|
11
|
+
}
|
|
12
|
+
</style>
|
|
8
13
|
</head>
|
|
9
14
|
<body>
|
|
10
15
|
<main>
|
|
11
|
-
<h1>Page without underlined
|
|
12
|
-
<p>This paragraph contains an
|
|
13
|
-
<p>This paragraph contains an
|
|
16
|
+
<h1>Page without underlined adjacent links</h1>
|
|
17
|
+
<p>This paragraph contains an adjacent link to <a style="text-decoration: overline" href="https://en.wikipedia.org">English information</a> that has an overline instead of an underline.</p>
|
|
18
|
+
<p>This paragraph contains an adjacent link to <a style="text-decoration: none" href="https://fr.wikipedia.org">French information</a> without any underline.</p>
|
|
19
|
+
<p>This paragraph introduces a list of nonunderlined links, but it contains only 1 link, so that link fails to be classified as a list link.</p>
|
|
20
|
+
<ul class="nude"</ul>
|
|
21
|
+
<li><a href="https://eus.wikipedia.org">Basque information</a></li>
|
|
22
|
+
</ul>
|
|
23
|
+
<p>This paragraph introduces a list of nonunderlined links, but it contains an item that is not a link, so its link fails to be classified as a list link.</p>
|
|
24
|
+
<ul class="nude">
|
|
25
|
+
<li><span>Spanish information is not available</span></li>
|
|
26
|
+
<li><a href="https://eo.wikipedia.org">Esperanto information</a></li>
|
|
27
|
+
</ul>
|
|
28
|
+
<p>This paragraph introduces a list of nonunderlined links, but it contains additional text, so its links fail to be classified as a list link.</p>
|
|
29
|
+
<ul class="nude">
|
|
30
|
+
<li><a href="https://fr.wikipedia.org">French information</a> is available, as well as</li>
|
|
31
|
+
<li><a href="https://tr.wikipedia.org">Turkish information</a></li>
|
|
32
|
+
</ul>
|
|
14
33
|
</main>
|
|
15
34
|
</body>
|
|
16
35
|
</html>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<html lang="en-US">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
|
-
<title>Page with underlined
|
|
5
|
+
<title>Page with underlined adjacent links</title>
|
|
6
6
|
<meta name="description" content="tester">
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
8
8
|
<style>
|
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<main>
|
|
16
|
-
<h1>Page with underlined
|
|
17
|
-
<p>This page contains
|
|
18
|
-
<p>The following links, however, are not
|
|
16
|
+
<h1>Page with underlined adjacent links</h1>
|
|
17
|
+
<p>This page contains adjacent links to <a href="https://en.wikipedia.org">English information</a> and <a style="text-decoration: underline wavy 2px" href="https://fr.wikipedia.org">French information</a>. The latter is weird, but still an underline.</p>
|
|
18
|
+
<p>The following links, however, are not adjacent, so can be (and are) devoid of underlines.</p>
|
|
19
19
|
<ul class="nude">
|
|
20
20
|
<li><a href="https://sp.wikipedia.org">Spanish information</a></li>
|
|
21
21
|
<li><a href="https://eo.wikipedia.org">Esperanto information</a></li>
|
|
22
22
|
</ul>
|
|
23
|
-
<p>The following links are not
|
|
23
|
+
<p>The following links are not adjacent, so are permitted to be devoid of underlines, but still have them.</p>
|
|
24
24
|
<ul>
|
|
25
25
|
<li><a href="https://eus.wikipedia.org">Basque information</a></li>
|
|
26
26
|
<li><a href="https://tr.wikipedia.org">Turkish information</a></li>
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
</head>
|
|
9
9
|
<body>
|
|
10
10
|
<main>
|
|
11
|
-
<h1>Page without
|
|
12
|
-
<p>This page contains no
|
|
13
|
-
<p>The following links are
|
|
11
|
+
<h1>Page without adjacent links</h1>
|
|
12
|
+
<p>This page contains list links but no adjacent links.</p>
|
|
13
|
+
<p>The following links are list links.</p>
|
|
14
14
|
<ul>
|
|
15
15
|
<li><a href="https://eus.wikipedia.org">Basque information</a></li>
|
|
16
16
|
<li><a href="https://tr.wikipedia.org">Turkish information</a></li>
|