testaro 16.1.0 → 16.3.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/sample.js +30 -0
- package/procs/visChange.js +38 -8
- package/testaro/hovInd.js +297 -0
- package/testaro/hover.js +109 -387
- package/testaro/motion.js +7 -7
- package/tests/testaro.js +1 -0
- package/validation/tests/jobs/hovInd.json +179 -0
- package/validation/tests/jobs/hover.json +89 -143
- package/validation/tests/targets/hovInd/index.html +43 -0
- package/validation/tests/targets/hover/bad.html +10 -9
- package/testaro-old/allCaps.js +0 -94
- package/testaro-old/allSlanted.js +0 -86
- package/validation/tests/targets/hover/styleBad.html +0 -35
package/testaro/hover.js
CHANGED
|
@@ -1,427 +1,149 @@
|
|
|
1
1
|
/*
|
|
2
2
|
hover
|
|
3
|
-
This test reports unexpected impacts of hovering
|
|
4
|
-
|
|
3
|
+
This test reports unexpected impacts of hovering on the visible page. Impacts are measured by
|
|
4
|
+
pixel changes outside the hovered element and by unhoverability.
|
|
5
|
+
|
|
5
6
|
The elements that are subjected to hovering (called “triggers”) are the Playwright-visible
|
|
6
7
|
elements that have 'A', 'BUTTON', or (if not with role=menuitem) 'LI' tag names or have
|
|
7
8
|
'onmouseenter' or 'onmouseover' attributes.
|
|
8
9
|
|
|
9
|
-
When a trigger is hovered over, the test examines the impacts on descendants of the great
|
|
10
|
-
grandparents of triggers with tag names 'A' and 'BUTTON', grandparents of triggers with tag
|
|
11
|
-
name 'LI', and otherwise the descendants of the triggers themselves. Four impacts are counted:
|
|
12
|
-
(1) an element is added or becomes visible, (2) an element is removed or becomes invisible, (3)
|
|
13
|
-
the opacity of an element changes, and (4) the element is a descendant of an element whose opacity
|
|
14
|
-
changes. The test checks up to 4 times for hovering impacts at intervals of 0.3 second.
|
|
15
|
-
|
|
16
10
|
Despite the delay, the test can make the execution time practical by randomly sampling triggers
|
|
17
11
|
instead of hovering over all of them. When sampling is performed, the results may vary from one
|
|
18
12
|
execution to another. Because hover impacts typically occur near the beginning of a page with
|
|
19
13
|
navigation menus, the probability of the inclusion of a trigger in a sample decreases with the
|
|
20
14
|
index of the trigger.
|
|
21
15
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
16
|
+
Pixel changes: If no pixel changes occur immediately after an element is hovered over, the page
|
|
17
|
+
is examined once more, after 0.5 second. The greater the fraction of changed pixels, the greater
|
|
18
|
+
the ordinal severity.
|
|
19
|
+
|
|
20
|
+
Unhoverability: An element is reported as unhoverable when it fails the Playwright actionability
|
|
21
|
+
checks for hovering, i.e. fails to be attached to the DOM, visible, stable (not or no longer
|
|
22
|
+
animating), and able to receive events. All triggers satisfy the first two conditions, so only the
|
|
23
|
+
last two might fail. Playwright defines the ability to receive events as being the target of an
|
|
24
|
+
action on the location where the center of the element is, rather than some other element with a
|
|
25
|
+
higher zIndex value in the same location being the target.
|
|
28
26
|
|
|
29
|
-
|
|
27
|
+
WARNING: This test uses the Playwright page.screenshot method, which is not implemented for the
|
|
28
|
+
firefox browser type.
|
|
30
29
|
*/
|
|
31
30
|
|
|
32
|
-
//
|
|
31
|
+
// IMPORTS
|
|
33
32
|
|
|
34
|
-
|
|
33
|
+
// Module to get locator data.
|
|
34
|
+
const {getLocatorData} = require('../procs/getLocatorData');
|
|
35
|
+
// Module to draw a sample.
|
|
36
|
+
const {getSample} = require('../procs/sample');
|
|
37
|
+
// Module to get pixel changes between two times.
|
|
38
|
+
const {visChange} = require('../procs/visChange');
|
|
35
39
|
|
|
36
40
|
// FUNCTIONS
|
|
37
41
|
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
// Performs the hover test and reports results.
|
|
43
|
+
exports.reporter = async (page, withItems, sampleSize = 20) => {
|
|
44
|
+
// Initialize the result.
|
|
45
|
+
const data = {};
|
|
46
|
+
const totals = [0, 0, 0, 0];
|
|
47
|
+
const standardInstances = [];
|
|
48
|
+
// Identify the triggers.
|
|
49
|
+
const selectors = ['a', 'button', 'li:not([role=menuitem])', '[onmouseenter]', '[onmouseover]'];
|
|
50
|
+
const selectorString = selectors.map(selector => `body ${selector}:visible`).join(', ');
|
|
51
|
+
const locAll = page.locator(selectorString);
|
|
52
|
+
const locsAll = await locAll.all();
|
|
53
|
+
// Get the population-to-sample ratio.
|
|
54
|
+
const psRatio = Math.max(1, locsAll.length / sampleSize);
|
|
55
|
+
// Get a sample of the triggers.
|
|
56
|
+
const sampleIndexes = getSample(locsAll, sampleSize);
|
|
57
|
+
const sample = locsAll.filter((loc, index) => sampleIndexes.includes(index));
|
|
58
|
+
// For each trigger in the sample:
|
|
59
|
+
for (const loc of sample) {
|
|
60
|
+
// Hover over it and get the fractional pixel change.
|
|
61
|
+
const hoverData = await visChange(page, {
|
|
62
|
+
delayBefore: 0,
|
|
63
|
+
delayBetween: 500,
|
|
64
|
+
exclusion: loc
|
|
46
65
|
});
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const remainerIndexes = await page.evaluate(args => {
|
|
72
|
-
const preDescendants = args[0];
|
|
73
|
-
const postDescendants = args[1];
|
|
74
|
-
const remainerIndexes = preDescendants
|
|
75
|
-
.map((element, index) => postDescendants.includes(element) ? index : -1)
|
|
76
|
-
.filter(index => index > -1);
|
|
77
|
-
return remainerIndexes;
|
|
78
|
-
}, [preDescendants, postDescendants]);
|
|
79
|
-
// Get the impacts of the hover event.
|
|
80
|
-
const additions = postDescendants.length - remainerIndexes.length;
|
|
81
|
-
const removals = preDescendants.length - remainerIndexes.length;
|
|
82
|
-
const remainers = [];
|
|
83
|
-
for (const index of remainerIndexes) {
|
|
84
|
-
remainers.push({
|
|
85
|
-
element: preDescendants[index],
|
|
86
|
-
preOpacity: preOpacities[index],
|
|
87
|
-
postOpacity: await page.evaluate(
|
|
88
|
-
element => window.getComputedStyle(element).opacity, preDescendants[index]
|
|
89
|
-
)
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
const opacityChangers = remainers
|
|
93
|
-
.filter(remainer => remainer.postOpacity !== remainer.preOpacity);
|
|
94
|
-
const opacityImpact = opacityChangers
|
|
95
|
-
? await page.evaluate(changers => changers.reduce(
|
|
96
|
-
(total, current) => total + current.element.querySelectorAll('*').length, 0
|
|
97
|
-
), opacityChangers)
|
|
98
|
-
: 0;
|
|
99
|
-
// If there are any impacts:
|
|
100
|
-
if (additions || removals || opacityChangers.length) {
|
|
101
|
-
// Return them.
|
|
102
|
-
return {
|
|
103
|
-
additions,
|
|
104
|
-
removals,
|
|
105
|
-
opacityChanges: opacityChangers.length,
|
|
106
|
-
opacityImpact
|
|
107
|
-
};
|
|
66
|
+
// If the hovering and measurement succeeded:
|
|
67
|
+
if (hoverData.success) {
|
|
68
|
+
// If any pixels changed:
|
|
69
|
+
if (hoverData.changePercent) {
|
|
70
|
+
// Get the ordinal severity from the fractional pixel change.
|
|
71
|
+
const ordinalSeverity = Math.floor(Math.min(3, 0.4 * Math.sqrt(hoverData.changePercent)));
|
|
72
|
+
// Add to the totals.
|
|
73
|
+
totals[ordinalSeverity] += psRatio;
|
|
74
|
+
// If itemization is required:
|
|
75
|
+
if (withItems) {
|
|
76
|
+
// Get data on the trigger.
|
|
77
|
+
const elData = await getLocatorData(loc);
|
|
78
|
+
// Add an instance to the result.
|
|
79
|
+
standardInstances.push({
|
|
80
|
+
ruleID: 'hover',
|
|
81
|
+
what: 'Hovering over the element changes the page',
|
|
82
|
+
ordinalSeverity,
|
|
83
|
+
tagName: elData.tagName,
|
|
84
|
+
id: elData.id,
|
|
85
|
+
location: elData.location,
|
|
86
|
+
excerpt: elData.excerpt
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
108
90
|
}
|
|
109
|
-
// Otherwise, i.e. if
|
|
91
|
+
// Otherwise, i.e. if hovering and measurement failed:
|
|
110
92
|
else {
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
93
|
+
// Add to the totals.
|
|
94
|
+
totals[3] += psRatio;
|
|
95
|
+
// If itemization is required:
|
|
96
|
+
if (withItems) {
|
|
97
|
+
// Get data on the trigger.
|
|
98
|
+
const elData = await getLocatorData(loc);
|
|
99
|
+
// Add an instance to the result.
|
|
100
|
+
standardInstances.push({
|
|
101
|
+
ruleID: 'hover',
|
|
102
|
+
what: 'Element is not hoverable',
|
|
103
|
+
ordinalSeverity: 3,
|
|
104
|
+
tagName: elData.tagName,
|
|
105
|
+
id: elData.id,
|
|
106
|
+
location: elData.location,
|
|
107
|
+
excerpt: elData.excerpt
|
|
108
|
+
});
|
|
109
|
+
}
|
|
117
110
|
}
|
|
118
|
-
|
|
119
|
-
// Otherwise, i.e. if the allowed trial count has been exhausted:
|
|
120
|
-
else {
|
|
121
|
-
// Report non-impact.
|
|
122
|
-
return null;
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
// Returns the hover-related style properties of a trigger.
|
|
126
|
-
const getHoverStyles = async (page, element) => await page.evaluate(
|
|
127
|
-
element => {
|
|
128
|
-
const {cursor, outline, color, backgroundColor} = window.getComputedStyle(element);
|
|
129
|
-
return {
|
|
130
|
-
cursor: cursor.replace(/^.+, */, ''),
|
|
131
|
-
outline,
|
|
132
|
-
color,
|
|
133
|
-
backgroundColor
|
|
134
|
-
};
|
|
135
|
-
}, element
|
|
136
|
-
);
|
|
137
|
-
// Recursively adds estimated and itemized impacts of hovering over triggers to data.
|
|
138
|
-
const find = async (data, withItems, page, sample) => {
|
|
139
|
-
// If any triggers remain and the test has not timed out:
|
|
140
|
-
if (sample.length && ! hasTimedOut) {
|
|
141
|
-
// Get and report the impacts until and unless the test times out.
|
|
111
|
+
// Reload the page to preserve locator integrity.
|
|
142
112
|
try {
|
|
143
|
-
|
|
144
|
-
const firstTrigger = sample[0];
|
|
145
|
-
const tagNameJSHandle = await firstTrigger.getProperty('tagName')
|
|
146
|
-
.catch(() => '');
|
|
147
|
-
if (tagNameJSHandle) {
|
|
148
|
-
const tagName = await tagNameJSHandle.jsonValue();
|
|
149
|
-
// Identify the root of a subtree likely to contain impacted elements.
|
|
150
|
-
let root = firstTrigger;
|
|
151
|
-
if (['A', 'BUTTON', 'LI'].includes(tagName)) {
|
|
152
|
-
const rootJSHandle = await page.evaluateHandle(
|
|
153
|
-
trigger => {
|
|
154
|
-
const parent = trigger.parentElement || trigger;
|
|
155
|
-
const grandparent = parent.parentElement || parent;
|
|
156
|
-
const greatGrandparent = grandparent.parentElement || parent;
|
|
157
|
-
return trigger.tagName === 'LI' ? grandparent : greatGrandparent;
|
|
158
|
-
},
|
|
159
|
-
firstTrigger
|
|
160
|
-
);
|
|
161
|
-
root = rootJSHandle.asElement();
|
|
162
|
-
}
|
|
163
|
-
// Identify all the visible descendants of the root.
|
|
164
|
-
const preDescendants = await root.$$(':visible');
|
|
165
|
-
// Identify their opacities.
|
|
166
|
-
const preOpacities = await page.evaluate(elements => elements.map(
|
|
167
|
-
element => window.getComputedStyle(element).opacity
|
|
168
|
-
), preDescendants);
|
|
169
|
-
// Get the style properties of the trigger.
|
|
170
|
-
const triggerPreStyles = await getHoverStyles(page, firstTrigger);
|
|
171
|
-
const multiplier = data.sampling.triggers / data.sampling.triggerSample;
|
|
172
|
-
const itemData = {
|
|
173
|
-
tagName,
|
|
174
|
-
id: (await firstTrigger.getAttribute('id')) || '',
|
|
175
|
-
text: await textOf(firstTrigger, 100)
|
|
176
|
-
};
|
|
177
|
-
try {
|
|
178
|
-
// Hover over the trigger.
|
|
179
|
-
await firstTrigger.hover({
|
|
180
|
-
timeout: 500,
|
|
181
|
-
noWaitAfter: true
|
|
182
|
-
});
|
|
183
|
-
// Repeatedly seek impacts of the hover at intervals.
|
|
184
|
-
const impacts = await getImpacts(
|
|
185
|
-
300, 4, root, page, preDescendants, preOpacities, firstTrigger
|
|
186
|
-
);
|
|
187
|
-
// Get the style properties of the trigger.
|
|
188
|
-
const triggerPostStyles = await getHoverStyles(page, firstTrigger);
|
|
189
|
-
// Add cursor and other style defects to the data.
|
|
190
|
-
const cursor = triggerPreStyles.cursor;
|
|
191
|
-
// If the trigger has no cursor:
|
|
192
|
-
if (cursor === 'none') {
|
|
193
|
-
// Add this fact to the data.
|
|
194
|
-
data.totals.noCursors += multiplier;
|
|
195
|
-
if (withItems) {
|
|
196
|
-
data.items.noCursors.push(itemData);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
// If the trigger has an improper cursor:
|
|
200
|
-
if (
|
|
201
|
-
tagName === 'A' && cursor !== 'pointer'
|
|
202
|
-
|| tagName === 'BUTTON' && cursor !== 'default'
|
|
203
|
-
){
|
|
204
|
-
// Add this fact to the data.
|
|
205
|
-
data.totals.badCursors += multiplier;
|
|
206
|
-
if (withItems) {
|
|
207
|
-
data.items.badCursors.push(itemData);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
// If hover indication is illicit but is present:
|
|
211
|
-
if (
|
|
212
|
-
tagName === 'LI'
|
|
213
|
-
&& JSON.stringify(triggerPostStyles) !== JSON.stringify(triggerPreStyles)
|
|
214
|
-
) {
|
|
215
|
-
// Add this fact to the data.
|
|
216
|
-
data.totals.badIndicators += multiplier;
|
|
217
|
-
if (withItems) {
|
|
218
|
-
data.items.badIndicators.push(itemData);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
// If there were any impacts:
|
|
222
|
-
if (impacts) {
|
|
223
|
-
// Hover over the upper-left corner of the page, to undo any impacts.
|
|
224
|
-
await page.hover('body', {
|
|
225
|
-
position: {
|
|
226
|
-
x: 0,
|
|
227
|
-
y: 0
|
|
228
|
-
},
|
|
229
|
-
timeout: 500,
|
|
230
|
-
force: true,
|
|
231
|
-
noWaitAfter: true
|
|
232
|
-
});
|
|
233
|
-
// Wait for any delayed and/or slowed reaction.
|
|
234
|
-
await page.waitForTimeout(200);
|
|
235
|
-
await root.waitForElementState('stable');
|
|
236
|
-
// Increment the estimated counts of triggers and impacts.
|
|
237
|
-
const {additions, removals, opacityChanges, opacityImpact} = impacts;
|
|
238
|
-
if (hasTimedOut) {
|
|
239
|
-
return Promise.resolve('');
|
|
240
|
-
}
|
|
241
|
-
else {
|
|
242
|
-
data.totals.impactTriggers += multiplier;
|
|
243
|
-
data.totals.additions += additions * multiplier;
|
|
244
|
-
data.totals.removals += removals * multiplier;
|
|
245
|
-
data.totals.opacityChanges += opacityChanges * multiplier;
|
|
246
|
-
data.totals.opacityImpact += opacityImpact * multiplier;
|
|
247
|
-
// If details are to be reported:
|
|
248
|
-
if (withItems) {
|
|
249
|
-
// Add them to the data.
|
|
250
|
-
data.items.impactTriggers.push({
|
|
251
|
-
tagName,
|
|
252
|
-
id: itemData.id,
|
|
253
|
-
text: await textOf(firstTrigger, 100),
|
|
254
|
-
additions,
|
|
255
|
-
removals,
|
|
256
|
-
opacityChanges,
|
|
257
|
-
opacityImpact
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
catch (error) {
|
|
264
|
-
console.log(`ERROR hovering (${error.message.replace(/\n.+/s, '')})`);
|
|
265
|
-
if (hasTimedOut) {
|
|
266
|
-
return Promise.resolve('');
|
|
267
|
-
}
|
|
268
|
-
else {
|
|
269
|
-
data.totals.unhoverables += multiplier;
|
|
270
|
-
if (withItems) {
|
|
271
|
-
try {
|
|
272
|
-
data.items.unhoverables.push(itemData);
|
|
273
|
-
}
|
|
274
|
-
catch(error) {
|
|
275
|
-
console.log('ERROR itemizing unhoverable element');
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
// Process the remaining potential triggers.
|
|
282
|
-
await find(data, withItems, page, sample.slice(1));
|
|
113
|
+
await page.reload({timeout: 5000});
|
|
283
114
|
}
|
|
284
115
|
catch(error) {
|
|
285
|
-
console.log(
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
else {
|
|
289
|
-
return Promise.resolve('');
|
|
290
|
-
}
|
|
291
|
-
};
|
|
292
|
-
// Performs the hover test and reports results.
|
|
293
|
-
exports.reporter = async (page, withItems, sampleSize = 20) => {
|
|
294
|
-
// Initialize the result.
|
|
295
|
-
let data = {
|
|
296
|
-
sampling: {
|
|
297
|
-
triggers: 0,
|
|
298
|
-
triggerSample: 0,
|
|
299
|
-
},
|
|
300
|
-
totals: {
|
|
301
|
-
impactTriggers: 0,
|
|
302
|
-
additions: 0,
|
|
303
|
-
removals: 0,
|
|
304
|
-
opacityChanges: 0,
|
|
305
|
-
opacityImpact: 0,
|
|
306
|
-
unhoverables: 0,
|
|
307
|
-
noCursors: 0,
|
|
308
|
-
badCursors: 0,
|
|
309
|
-
badIndicators: 0
|
|
116
|
+
console.log('ERROR: page reload timed out');
|
|
310
117
|
}
|
|
311
|
-
};
|
|
312
|
-
// If details are to be reported:
|
|
313
|
-
if (withItems) {
|
|
314
|
-
// Add properties for details to the initialized result.
|
|
315
|
-
data.items = {
|
|
316
|
-
impactTriggers: [],
|
|
317
|
-
unhoverables: [],
|
|
318
|
-
noCursors: [],
|
|
319
|
-
badCursors: [],
|
|
320
|
-
badIndicators: []
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
// Identify the triggers.
|
|
324
|
-
const selectors = ['a', 'button', 'li:not([role=menuitem])', '[onmouseenter]', '[onmouseover]'];
|
|
325
|
-
const triggers = await page.$$(selectors.map(selector => `body ${selector}:visible`).join(', '))
|
|
326
|
-
.catch(error => {
|
|
327
|
-
console.log(`ERROR getting hover triggers (${error.message})`);
|
|
328
|
-
data.prevented = true;
|
|
329
|
-
return [];
|
|
330
|
-
});
|
|
331
|
-
data.sampling.triggers = triggers.length;
|
|
332
|
-
// Get the sample.
|
|
333
|
-
const sample = getSample(triggers, sampleSize);
|
|
334
|
-
data.sampling.triggerSample = sample.length;
|
|
335
|
-
// Set a time limit to cover possible 2 seconds per trigger.
|
|
336
|
-
const timeLimit = Math.round(2.8 * sample.length + 2);
|
|
337
|
-
const timeout = setTimeout(async () => {
|
|
338
|
-
await page.close();
|
|
339
|
-
console.log(
|
|
340
|
-
`ERROR: hover test on sample of ${sample.length} triggers timed out at ${timeLimit} seconds; page closed`
|
|
341
|
-
);
|
|
342
|
-
hasTimedOut = true;
|
|
343
|
-
data = {
|
|
344
|
-
prevented: true,
|
|
345
|
-
error: 'ERROR: hover test timed out'
|
|
346
|
-
};
|
|
347
|
-
clearTimeout(timeout);
|
|
348
|
-
}, 1000 * timeLimit);
|
|
349
|
-
// Find and document the style defects and impacts of the sampled triggers.
|
|
350
|
-
if (sample.length && ! hasTimedOut) {
|
|
351
|
-
await find(data, withItems, page, sample);
|
|
352
|
-
}
|
|
353
|
-
clearTimeout(timeout);
|
|
354
|
-
// Round the reported totals.
|
|
355
|
-
if (! hasTimedOut) {
|
|
356
|
-
Object.keys(data.totals).forEach(key => {
|
|
357
|
-
data.totals[key] = Math.round(data.totals[key]);
|
|
358
|
-
});
|
|
359
118
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
noCursors: 3,
|
|
368
|
-
badCursors: 2,
|
|
369
|
-
badIndicators: 2
|
|
370
|
-
};
|
|
371
|
-
const what = {
|
|
372
|
-
impactTriggers: 'Hovering over the element has unexpected effects',
|
|
373
|
-
unhoverables: 'Operable element cannot be hovered over',
|
|
374
|
-
noCursors: 'Hoverable element hides the mouse cursor',
|
|
375
|
-
badCursors: 'Link or button makes the hovering mouse cursor nonstandard',
|
|
376
|
-
badIndicators: 'List item changes when hovered over'
|
|
377
|
-
};
|
|
378
|
-
const totals = [0, 0, 0, 0];
|
|
379
|
-
Object.keys(data.totals).forEach(category => {
|
|
380
|
-
totals[severity[category]] += data.totals[category];
|
|
381
|
-
});
|
|
382
|
-
const standardInstances = [];
|
|
383
|
-
if (data.items) {
|
|
384
|
-
Object.keys(data.items).forEach(category => {
|
|
385
|
-
data.items[category].forEach(item => {
|
|
119
|
+
// If itemization is not required:
|
|
120
|
+
if (! withItems) {
|
|
121
|
+
// For each ordinal severity:
|
|
122
|
+
for (const index in totals) {
|
|
123
|
+
// If there were any instances with it:
|
|
124
|
+
if (totals[index]) {
|
|
125
|
+
// Add a summary instance to the result.
|
|
386
126
|
standardInstances.push({
|
|
387
127
|
ruleID: 'hover',
|
|
388
|
-
what:
|
|
389
|
-
ordinalSeverity:
|
|
390
|
-
|
|
391
|
-
|
|
128
|
+
what: 'Hovering over elements changes the page or fails',
|
|
129
|
+
ordinalSeverity: index,
|
|
130
|
+
count: Math.round(totals[index]),
|
|
131
|
+
tagName: '',
|
|
132
|
+
id: '',
|
|
392
133
|
location: {
|
|
393
134
|
doc: '',
|
|
394
135
|
type: '',
|
|
395
136
|
spec: ''
|
|
396
137
|
},
|
|
397
|
-
excerpt:
|
|
138
|
+
excerpt: ''
|
|
398
139
|
});
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
else if (totals.some(total => total)) {
|
|
403
|
-
standardInstances.push({
|
|
404
|
-
ruleID: 'hover',
|
|
405
|
-
what: 'Hovering behaves unexpectedly',
|
|
406
|
-
ordinalSeverity: totals.reduce((max, current, index) => current ? index : max, 0),
|
|
407
|
-
count: Object.values(data.totals).reduce((total, current) => total + current),
|
|
408
|
-
tagName: '',
|
|
409
|
-
id: '',
|
|
410
|
-
location: {
|
|
411
|
-
doc: '',
|
|
412
|
-
type: '',
|
|
413
|
-
spec: ''
|
|
414
|
-
},
|
|
415
|
-
excerpt: ''
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
// Reload the page.
|
|
419
|
-
try {
|
|
420
|
-
await page.reload({timeout: 15000});
|
|
421
|
-
}
|
|
422
|
-
catch(error) {
|
|
423
|
-
console.log('ERROR: page reload timed out');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
424
142
|
}
|
|
143
|
+
// Round the totals.
|
|
144
|
+
totals.forEach((total, index) => {
|
|
145
|
+
totals[index] = Math.round(totals[index]);
|
|
146
|
+
});
|
|
425
147
|
// Return the result.
|
|
426
148
|
return {
|
|
427
149
|
data,
|
package/testaro/motion.js
CHANGED
|
@@ -5,16 +5,16 @@
|
|
|
5
5
|
by the time a user manages to stop motion, the motion may have caused annoyance or harm. For
|
|
6
6
|
superior accessibility, a page contains no motion until and unless the user authorizes it. The
|
|
7
7
|
test compares two screen shots of the viewport 2 seconds and 6 seconds after page load. It
|
|
8
|
-
reports a rule violation if
|
|
9
|
-
|
|
8
|
+
reports a rule violation if any pixels change. The larger the change fraction, the greater the
|
|
9
|
+
ordinal severity.
|
|
10
10
|
|
|
11
|
-
WARNING: This test uses the Playwright page.screenshot method, which
|
|
12
|
-
|
|
13
|
-
browser type usable with this test is webkit.
|
|
11
|
+
WARNING: This test uses the Playwright page.screenshot method, which is not implemented for the
|
|
12
|
+
firefox browser type.
|
|
14
13
|
*/
|
|
15
14
|
|
|
16
15
|
// IMPORTS
|
|
17
16
|
|
|
17
|
+
// Module to get pixel changes between two times.
|
|
18
18
|
const {visChange} = require('../procs/visChange');
|
|
19
19
|
|
|
20
20
|
// FUNCTIONS
|
|
@@ -31,10 +31,10 @@ exports.reporter = async page => {
|
|
|
31
31
|
});
|
|
32
32
|
// If the screenshots succeeded:
|
|
33
33
|
if (data.success) {
|
|
34
|
-
// Get the ordinal severity from the fractional pixel change.
|
|
35
|
-
const ordinalSeverity = Math.floor(Math.min(3, 0.4 * Math.sqrt(data.changePercent)));
|
|
36
34
|
// If any pixels were changed:
|
|
37
35
|
if (data.pixelChanges) {
|
|
36
|
+
// Get the ordinal severity from the fractional pixel change.
|
|
37
|
+
const ordinalSeverity = Math.floor(Math.min(3, 0.4 * Math.sqrt(data.changePercent)));
|
|
38
38
|
// Add to the totals.
|
|
39
39
|
totals[ordinalSeverity] = 1;
|
|
40
40
|
// Get a summary standard instance.
|
package/tests/testaro.js
CHANGED
|
@@ -22,6 +22,7 @@ const evalRules = {
|
|
|
22
22
|
focOp: 'discrepancies between focusability and operability',
|
|
23
23
|
focVis: 'links that are invisible when focused',
|
|
24
24
|
hover: 'hover-caused content changes',
|
|
25
|
+
hovInd: 'hover indication nonstandard',
|
|
25
26
|
labClash: 'labeling inconsistencies',
|
|
26
27
|
lineHeight: 'text with a line height less than 1.5 times its font size',
|
|
27
28
|
linkExt: 'links that automatically open new windows',
|