testaro 5.17.3 → 5.18.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/package.json
CHANGED
package/tests/hover.js
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
/*
|
|
2
2
|
hover
|
|
3
3
|
This test reports unexpected impacts of hovering. The effects include additions and removals
|
|
4
|
-
of visible elements, opacity changes,
|
|
5
|
-
subjected to hovering (called “triggers”) are the Playwright-visible
|
|
6
|
-
'BUTTON', or 'LI' tag names or have 'onmouseenter' or 'onmouseover'
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
4
|
+
of visible elements, opacity changes, unhoverable elements, and nonstandard hover indication.
|
|
5
|
+
The elements that are subjected to hovering (called “triggers”) are the Playwright-visible
|
|
6
|
+
elements that have 'A', 'BUTTON', or 'LI' tag names or have 'onmouseenter' or 'onmouseover'
|
|
7
|
+
attributes.
|
|
8
|
+
|
|
9
|
+
The test examines how the hover event is indicated to the user with the mouse cursor and with
|
|
10
|
+
changes of the styles of the trigger.
|
|
11
|
+
|
|
12
|
+
When a trigger is hovered over, the test also examines the impacts on descendants of the great
|
|
13
|
+
grandparents of triggers with tag names 'A' and 'BUTTON', grandparents of triggers with tag
|
|
14
|
+
name 'LI', and otherwise the descendants of the triggers themselves. Four impacts are counted:
|
|
15
|
+
(1) an element is added or becomes visible, (2) an element is removed or becomes invisible, (3)
|
|
16
|
+
the opacity of an element changes, and (4) the element is a descendant of an element whose opacity
|
|
12
17
|
changes. The test checks up to 4 times for hovering impacts at intervals of 0.3 second.
|
|
13
18
|
|
|
14
|
-
Despite
|
|
19
|
+
Despite the delay, the test can make the execution time practical by randomly sampling triggers
|
|
15
20
|
instead of hovering over all of them. When sampling is performed, the results may vary from one
|
|
16
|
-
execution to another. Because hover impacts typically occur near the beginning of a page
|
|
17
|
-
probability of the inclusion of a trigger in a sample decreases with the
|
|
21
|
+
execution to another. Because hover impacts typically occur near the beginning of a page with
|
|
22
|
+
navigation menus, the probability of the inclusion of a trigger in a sample decreases with the
|
|
23
|
+
index of the trigger.
|
|
18
24
|
|
|
19
25
|
An element is reported as unhoverable when it fails the Playwright actionability checks for
|
|
20
26
|
hovering, i.e. fails to be attached to the DOM, visible, stable (not or no longer animating), and
|
|
@@ -30,7 +36,10 @@ let hasTimedOut = false;
|
|
|
30
36
|
|
|
31
37
|
// FUNCTIONS
|
|
32
38
|
|
|
33
|
-
//
|
|
39
|
+
// Gets the probability of a trigger being sampled.
|
|
40
|
+
const samProb = (index, popSize, sampleRatio) =>
|
|
41
|
+
sampleRatio * (1 + Math.sin(Math.PI * index / popSize + Math.PI / 2));
|
|
42
|
+
// Samples the trigger population and returns the sample and each member’s sampling probability.
|
|
34
43
|
const getSample = (population, sampleSize) => {
|
|
35
44
|
const popSize = population.length;
|
|
36
45
|
// If the sample is at least as large as the population:
|
|
@@ -43,13 +52,9 @@ const getSample = (population, sampleSize) => {
|
|
|
43
52
|
// Force the sample size to be an integer and at least 1.
|
|
44
53
|
sampleSize = Math.floor(Math.max(1, sampleSize));
|
|
45
54
|
const sampleRatio = sampleSize / popSize;
|
|
46
|
-
// FUNCTION DEFINITION START
|
|
47
|
-
// Gets the probability of a trigger being sampled.
|
|
48
|
-
const samProb = index => (1 + Math.sin(Math.PI * index / popSize + Math.PI / 2)) * sampleRatio;
|
|
49
|
-
// FUNCTION DEFINITION END
|
|
50
55
|
// Get the sample.
|
|
51
56
|
const sample = population.map((trigger, index) => {
|
|
52
|
-
const itemProb = samProb(index);
|
|
57
|
+
const itemProb = samProb(index, popSize, sampleRatio);
|
|
53
58
|
return [trigger, itemProb];
|
|
54
59
|
}).filter(pair => pair[1] > Math.random());
|
|
55
60
|
return sample;
|
|
@@ -59,9 +64,84 @@ const getSample = (population, sampleSize) => {
|
|
|
59
64
|
const textOf = async (element, limit) => {
|
|
60
65
|
let text = await element.textContent();
|
|
61
66
|
text = text.trim() || await element.innerHTML();
|
|
62
|
-
return text.trim().replace(/\s
|
|
67
|
+
return text.trim().replace(/\s+/sg, ' ').slice(0, limit);
|
|
68
|
+
};
|
|
69
|
+
// Returns the impacts of hovering over a sampled trigger.
|
|
70
|
+
const getImpacts = async (
|
|
71
|
+
interval, triesLeft, root, page, preDescendants, preOpacities
|
|
72
|
+
) => {
|
|
73
|
+
// If the allowed trial count has not yet been exhausted:
|
|
74
|
+
if (triesLeft-- && ! hasTimedOut) {
|
|
75
|
+
// Get the collection of descendants of the root.
|
|
76
|
+
const postDescendants = await root.$$(':visible');
|
|
77
|
+
// Identify the prior descendants of the root still in existence.
|
|
78
|
+
const remainerIndexes = await page.evaluate(args => {
|
|
79
|
+
const preDescendants = args[0];
|
|
80
|
+
const postDescendants = args[1];
|
|
81
|
+
const remainerIndexes = preDescendants
|
|
82
|
+
.map((element, index) => postDescendants.includes(element) ? index : -1)
|
|
83
|
+
.filter(index => index > -1);
|
|
84
|
+
return remainerIndexes;
|
|
85
|
+
}, [preDescendants, postDescendants]);
|
|
86
|
+
// Get the impacts of the hover event.
|
|
87
|
+
const additions = postDescendants.length - remainerIndexes.length;
|
|
88
|
+
const removals = preDescendants.length - remainerIndexes.length;
|
|
89
|
+
const remainers = [];
|
|
90
|
+
for (const index of remainerIndexes) {
|
|
91
|
+
remainers.push({
|
|
92
|
+
element: preDescendants[index],
|
|
93
|
+
preOpacity: preOpacities[index],
|
|
94
|
+
postOpacity: await page.evaluate(
|
|
95
|
+
element => window.getComputedStyle(element).opacity, preDescendants[index]
|
|
96
|
+
)
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
const opacityChangers = remainers
|
|
100
|
+
.filter(remainer => remainer.postOpacity !== remainer.preOpacity);
|
|
101
|
+
const opacityImpact = opacityChangers
|
|
102
|
+
? await page.evaluate(changers => changers.reduce(
|
|
103
|
+
(total, current) => total + current.element.querySelectorAll('*').length, 0
|
|
104
|
+
), opacityChangers)
|
|
105
|
+
: 0;
|
|
106
|
+
// If there are any impacts:
|
|
107
|
+
if (additions || removals || opacityChangers.length) {
|
|
108
|
+
// Return them.
|
|
109
|
+
return {
|
|
110
|
+
additions,
|
|
111
|
+
removals,
|
|
112
|
+
opacityChanges: opacityChangers.length,
|
|
113
|
+
opacityImpact
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
// Otherwise, i.e. if there are no impacts:
|
|
117
|
+
else {
|
|
118
|
+
// Try again.
|
|
119
|
+
return await new Promise(resolve => {
|
|
120
|
+
setTimeout(() => {
|
|
121
|
+
resolve(getImpacts(interval, triesLeft, root, page, preDescendants, preOpacities));
|
|
122
|
+
}, interval);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Otherwise, i.e. if the allowed trial count has been exhausted:
|
|
127
|
+
else {
|
|
128
|
+
// Report non-impact.
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
63
131
|
};
|
|
64
|
-
//
|
|
132
|
+
// Returns the hover-related style properties of a trigger.
|
|
133
|
+
const getHoverStyles = async (page, element) => await page.evaluate(
|
|
134
|
+
element => {
|
|
135
|
+
const {cursor, outline, color, backgroundColor} = window.getComputedStyle(element);
|
|
136
|
+
return {
|
|
137
|
+
cursor: cursor.replace(/^.+, */, ''),
|
|
138
|
+
outline,
|
|
139
|
+
color,
|
|
140
|
+
backgroundColor
|
|
141
|
+
};
|
|
142
|
+
}, element
|
|
143
|
+
);
|
|
144
|
+
// Recursively adds estimated and itemized impacts of hovering over triggers to data.
|
|
65
145
|
const find = async (data, withItems, page, sample) => {
|
|
66
146
|
// If any triggers remain and the test has not timed out:
|
|
67
147
|
if (sample.length && ! hasTimedOut) {
|
|
@@ -69,6 +149,8 @@ const find = async (data, withItems, page, sample) => {
|
|
|
69
149
|
try {
|
|
70
150
|
// Identify the first trigger and its sampling probability.
|
|
71
151
|
const firstTrigger = sample[0];
|
|
152
|
+
const onmouseenter = await firstTrigger[0].getAttribute('onmouseenter');
|
|
153
|
+
const onmouseover = await firstTrigger[0].getAttribute('onmouseover');
|
|
72
154
|
const tagNameJSHandle = await firstTrigger[0].getProperty('tagName')
|
|
73
155
|
.catch(() => '');
|
|
74
156
|
if (tagNameJSHandle) {
|
|
@@ -93,78 +175,70 @@ const find = async (data, withItems, page, sample) => {
|
|
|
93
175
|
const preOpacities = await page.evaluate(elements => elements.map(
|
|
94
176
|
element => window.getComputedStyle(element).opacity
|
|
95
177
|
), preDescendants);
|
|
178
|
+
// Get the style properties of the trigger.
|
|
179
|
+
const triggerPreStyles = await getHoverStyles(page, firstTrigger[0]);
|
|
180
|
+
const totalEstimate = 1 / firstTrigger[1];
|
|
181
|
+
const itemData = {
|
|
182
|
+
tagName,
|
|
183
|
+
id: (await firstTrigger[0].getAttribute('id')) || '',
|
|
184
|
+
text: await textOf(firstTrigger[0], 50)
|
|
185
|
+
};
|
|
96
186
|
try {
|
|
97
187
|
// Hover over the trigger.
|
|
98
188
|
await firstTrigger[0].hover({
|
|
99
189
|
timeout: 500,
|
|
100
190
|
noWaitAfter: true
|
|
101
191
|
});
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return remainerIndexes;
|
|
117
|
-
}, [preDescendants, postDescendants]);
|
|
118
|
-
// Get the impacts of the hover event.
|
|
119
|
-
const additionCount = postDescendants.length - remainerIndexes.length;
|
|
120
|
-
const removalCount = preDescendants.length - remainerIndexes.length;
|
|
121
|
-
const remainers = [];
|
|
122
|
-
for (const index of remainerIndexes) {
|
|
123
|
-
remainers.push({
|
|
124
|
-
element: preDescendants[index],
|
|
125
|
-
preOpacity: preOpacities[index],
|
|
126
|
-
postOpacity: await page.evaluate(
|
|
127
|
-
element => window.getComputedStyle(element).opacity, preDescendants[index]
|
|
128
|
-
)
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
const opacityChangers = remainers
|
|
132
|
-
.filter(remainer => remainer.postOpacity !== remainer.preOpacity);
|
|
133
|
-
const opacityImpact = opacityChangers
|
|
134
|
-
? await page.evaluate(changers => changers.reduce(
|
|
135
|
-
(total, current) => total + current.element.querySelectorAll('*').length, 0
|
|
136
|
-
), opacityChangers)
|
|
137
|
-
: 0;
|
|
138
|
-
// If there are any impacts:
|
|
139
|
-
if (additionCount || removalCount || opacityChangers.length) {
|
|
140
|
-
// Return them as estimated population impacts.
|
|
141
|
-
return {
|
|
142
|
-
additionCount: additionCount / firstTrigger[1],
|
|
143
|
-
removalCount: removalCount / firstTrigger[1],
|
|
144
|
-
opacityChanges: opacityChangers.length / firstTrigger[1],
|
|
145
|
-
opacityImpact: opacityImpact / firstTrigger[1]
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
// Otherwise, i.e. if there are no impacts:
|
|
149
|
-
else {
|
|
150
|
-
// Try again.
|
|
151
|
-
return await new Promise(resolve => {
|
|
152
|
-
setTimeout(() => {
|
|
153
|
-
resolve(getImpacts(interval, triesLeft));
|
|
154
|
-
}, interval);
|
|
155
|
-
});
|
|
156
|
-
}
|
|
192
|
+
// Repeatedly seek impacts of the hover at intervals.
|
|
193
|
+
const impacts = await getImpacts(
|
|
194
|
+
300, 4, root, page, preDescendants, preOpacities, firstTrigger
|
|
195
|
+
);
|
|
196
|
+
// Get the style properties of the trigger.
|
|
197
|
+
const triggerPostStyles = await getHoverStyles(page, firstTrigger[0]);
|
|
198
|
+
// Add cursor and other style defects to the data.
|
|
199
|
+
const cursor = triggerPreStyles.cursor;
|
|
200
|
+
// If the trigger has no cursor:
|
|
201
|
+
if (cursor === 'none') {
|
|
202
|
+
// Add this fact to the data.
|
|
203
|
+
data.totals.noCursors += totalEstimate;
|
|
204
|
+
if (withItems) {
|
|
205
|
+
data.items.noCursors.push(itemData);
|
|
157
206
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
207
|
+
}
|
|
208
|
+
// If the trigger has an improper cursor:
|
|
209
|
+
if (
|
|
210
|
+
tagName === 'A' && cursor !== 'pointer'
|
|
211
|
+
|| tagName === 'BUTTON' && cursor !== 'default'
|
|
212
|
+
){
|
|
213
|
+
// Add this fact to the data.
|
|
214
|
+
data.totals.badCursors += totalEstimate;
|
|
215
|
+
if (withItems) {
|
|
216
|
+
data.items.badCursors.push(itemData);
|
|
162
217
|
}
|
|
163
|
-
}
|
|
164
|
-
//
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
218
|
+
}
|
|
219
|
+
// If hover indication is required but is absent:
|
|
220
|
+
if (
|
|
221
|
+
(tagName === 'BUTTON' || onmouseenter || onmouseover)
|
|
222
|
+
&& JSON.stringify(triggerPostStyles) === JSON.stringify(triggerPreStyles)
|
|
223
|
+
) {
|
|
224
|
+
// Add this fact to the data.
|
|
225
|
+
data.totals.noIndicators += totalEstimate;
|
|
226
|
+
if (withItems) {
|
|
227
|
+
data.items.noIndicators.push(itemData);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// If hover indication is illicit but is present:
|
|
231
|
+
if (
|
|
232
|
+
tagName === 'LI'
|
|
233
|
+
&& JSON.stringify(triggerPostStyles) !== JSON.stringify(triggerPreStyles)
|
|
234
|
+
) {
|
|
235
|
+
// Add this fact to the data.
|
|
236
|
+
data.totals.badIndicators += totalEstimate;
|
|
237
|
+
if (withItems) {
|
|
238
|
+
data.items.badIndicators.push(itemData);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
// If there were any impacts:
|
|
168
242
|
if (impacts) {
|
|
169
243
|
// Hover over the upper-left corner of the page, to undo any impacts.
|
|
170
244
|
await page.hover('body', {
|
|
@@ -176,30 +250,30 @@ const find = async (data, withItems, page, sample) => {
|
|
|
176
250
|
force: true,
|
|
177
251
|
noWaitAfter: true
|
|
178
252
|
});
|
|
179
|
-
// Wait for any delayed and/or slowed
|
|
253
|
+
// Wait for any delayed and/or slowed reaction.
|
|
180
254
|
await page.waitForTimeout(200);
|
|
181
255
|
await root.waitForElementState('stable');
|
|
182
256
|
// Increment the estimated counts of triggers and impacts.
|
|
183
|
-
const {
|
|
257
|
+
const {additions, removals, opacityChanges, opacityImpact} = impacts;
|
|
184
258
|
if (hasTimedOut) {
|
|
185
259
|
return Promise.resolve('');
|
|
186
260
|
}
|
|
187
261
|
else {
|
|
188
|
-
data.totals.impactTriggers +=
|
|
189
|
-
data.totals.additions +=
|
|
190
|
-
data.totals.removals +=
|
|
191
|
-
data.totals.opacityChanges += opacityChanges;
|
|
192
|
-
data.totals.opacityImpact += opacityImpact;
|
|
262
|
+
data.totals.impactTriggers += totalEstimate;
|
|
263
|
+
data.totals.additions += additions / firstTrigger[1];
|
|
264
|
+
data.totals.removals += removals / firstTrigger[1];
|
|
265
|
+
data.totals.opacityChanges += opacityChanges / firstTrigger[1];
|
|
266
|
+
data.totals.opacityImpact += opacityImpact / firstTrigger[1];
|
|
193
267
|
// If details are to be reported:
|
|
194
268
|
if (withItems) {
|
|
195
|
-
//
|
|
269
|
+
// Add them to the data.
|
|
196
270
|
data.items.impactTriggers.push({
|
|
197
271
|
tagName,
|
|
198
272
|
text: await textOf(firstTrigger[0], 50),
|
|
199
|
-
additions
|
|
200
|
-
removals
|
|
201
|
-
opacityChanges
|
|
202
|
-
opacityImpact
|
|
273
|
+
additions,
|
|
274
|
+
removals,
|
|
275
|
+
opacityChanges,
|
|
276
|
+
opacityImpact
|
|
203
277
|
});
|
|
204
278
|
}
|
|
205
279
|
}
|
|
@@ -211,15 +285,10 @@ const find = async (data, withItems, page, sample) => {
|
|
|
211
285
|
return Promise.resolve('');
|
|
212
286
|
}
|
|
213
287
|
else {
|
|
214
|
-
data.totals.unhoverables +=
|
|
288
|
+
data.totals.unhoverables += totalEstimate;
|
|
215
289
|
if (withItems) {
|
|
216
290
|
try {
|
|
217
|
-
|
|
218
|
-
data.items.unhoverables.push({
|
|
219
|
-
tagName,
|
|
220
|
-
id: id || '',
|
|
221
|
-
text: await textOf(firstTrigger[0], 50)
|
|
222
|
-
});
|
|
291
|
+
data.items.unhoverables.push(itemData);
|
|
223
292
|
}
|
|
224
293
|
catch(error) {
|
|
225
294
|
console.log('ERROR itemizing unhoverable element');
|
|
@@ -251,7 +320,11 @@ exports.reporter = async (page, sampleSize = -1, withItems) => {
|
|
|
251
320
|
removals: 0,
|
|
252
321
|
opacityChanges: 0,
|
|
253
322
|
opacityImpact: 0,
|
|
254
|
-
unhoverables: 0
|
|
323
|
+
unhoverables: 0,
|
|
324
|
+
noCursors: 0,
|
|
325
|
+
badCursors: 0,
|
|
326
|
+
noIndicators: 0,
|
|
327
|
+
badIndicators: 0
|
|
255
328
|
}
|
|
256
329
|
};
|
|
257
330
|
// If details are to be reported:
|
|
@@ -259,7 +332,11 @@ exports.reporter = async (page, sampleSize = -1, withItems) => {
|
|
|
259
332
|
// Add properties for details to the initialized result.
|
|
260
333
|
data.items = {
|
|
261
334
|
impactTriggers: [],
|
|
262
|
-
unhoverables: []
|
|
335
|
+
unhoverables: [],
|
|
336
|
+
noCursors: [],
|
|
337
|
+
badCursors: [],
|
|
338
|
+
noIndicators: [],
|
|
339
|
+
badIndicators: []
|
|
263
340
|
};
|
|
264
341
|
}
|
|
265
342
|
// Identify the triggers.
|
|
@@ -288,7 +365,7 @@ exports.reporter = async (page, sampleSize = -1, withItems) => {
|
|
|
288
365
|
};
|
|
289
366
|
clearTimeout(timeout);
|
|
290
367
|
}, 1000 * timeLimit);
|
|
291
|
-
// Find and document the impacts.
|
|
368
|
+
// Find and document the style defects and impacts of the sampled triggers.
|
|
292
369
|
if (sample.length && ! hasTimedOut) {
|
|
293
370
|
await find(data, withItems, page, sample);
|
|
294
371
|
}
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
"type": "test",
|
|
17
17
|
"which": "hover",
|
|
18
18
|
"what": "hover",
|
|
19
|
-
"sampleSize":
|
|
19
|
+
"sampleSize": 5,
|
|
20
20
|
"withItems": true,
|
|
21
21
|
"expect": [
|
|
22
|
-
["totals.triggers", "=",
|
|
22
|
+
["totals.triggers", "=", 4],
|
|
23
23
|
["totals.impactTriggers", "=", 0],
|
|
24
24
|
["totals.additions", "=", 0],
|
|
25
25
|
["totals.removals", "=", 0],
|
|
@@ -38,21 +38,23 @@
|
|
|
38
38
|
"type": "test",
|
|
39
39
|
"which": "hover",
|
|
40
40
|
"what": "hover",
|
|
41
|
-
"sampleSize":
|
|
41
|
+
"sampleSize": 8,
|
|
42
42
|
"withItems": true,
|
|
43
43
|
"expect": [
|
|
44
|
-
["totals.triggers", "=",
|
|
45
|
-
["totals.impactTriggers", "=",
|
|
44
|
+
["totals.triggers", "=", 6],
|
|
45
|
+
["totals.impactTriggers", "=", 3],
|
|
46
46
|
["totals.additions", "=", 3],
|
|
47
|
-
["totals.removals", "=",
|
|
47
|
+
["totals.removals", "=", 1],
|
|
48
48
|
["totals.opacityChanges", "=", 1],
|
|
49
49
|
["totals.opacityImpact", "=", 1],
|
|
50
50
|
["totals.unhoverables", "=", 1],
|
|
51
51
|
["items.impactTriggers.0.tagName", "=", "A"],
|
|
52
52
|
["items.impactTriggers.1.tagName", "=", "BUTTON"],
|
|
53
|
+
["items.impactTriggers.2.tagName", "=", "LI"],
|
|
53
54
|
["items.impactTriggers.0.text", "=", "information"],
|
|
54
55
|
["items.impactTriggers.0.additions", "=", 0],
|
|
55
56
|
["items.impactTriggers.1.additions", "=", 3],
|
|
57
|
+
["items.impactTriggers.2.removals", "=", 1],
|
|
56
58
|
["items.impactTriggers.0.opacityChanges", "=", 1],
|
|
57
59
|
["items.impactTriggers.1.opacityImpact", "=", 0],
|
|
58
60
|
["items.unhoverables.0.tagName", "=", "BUTTON"],
|
|
@@ -70,7 +72,7 @@
|
|
|
70
72
|
"which": "hover",
|
|
71
73
|
"what": "hover",
|
|
72
74
|
"sampleSize": 10,
|
|
73
|
-
"withItems":
|
|
75
|
+
"withItems": false,
|
|
74
76
|
"expect": [
|
|
75
77
|
["totals.triggers", "=", 20],
|
|
76
78
|
["totals.impactTriggers", ">", -1],
|
|
@@ -82,6 +84,26 @@
|
|
|
82
84
|
["totals.opacityImpact", "=", 0],
|
|
83
85
|
["totals.unhoverables", "=", 0]
|
|
84
86
|
]
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"type": "url",
|
|
90
|
+
"which": "__targets__/hover/styleBad.html",
|
|
91
|
+
"what": "page with deviant trigger styles"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"type": "test",
|
|
95
|
+
"which": "hover",
|
|
96
|
+
"what": "hover",
|
|
97
|
+
"sampleSize": 5,
|
|
98
|
+
"withItems": true,
|
|
99
|
+
"expect": [
|
|
100
|
+
["totals.noIndicators", "=", 1],
|
|
101
|
+
["totals.noCursors", "=", 1],
|
|
102
|
+
["totals.badIndicators", "=", 1],
|
|
103
|
+
["items.noIndicators.0.text", "=", "button"],
|
|
104
|
+
["items.noCursors.0.tagName", "=", "LI"],
|
|
105
|
+
["items.badIndicators.0.id", "=", "li2"]
|
|
106
|
+
]
|
|
85
107
|
}
|
|
86
108
|
]
|
|
87
109
|
}
|
|
@@ -14,6 +14,12 @@
|
|
|
14
14
|
<p id="translucent" style="opacity: 0.4">The first link, when hovered over, changes the opacity of this paragraph from 0.4 to 1. That indirectly changes the opacity of this <span>word</span>, too.</p>
|
|
15
15
|
<p>The small button is mostly covered by a large one here, preventing the small button from receiving a hover event.</p>
|
|
16
16
|
<p style="position: relative"><button id="smallButton" style="position: absolute; left: 10rem">button</button><button style="position: absolute; left: 11rem; top: -0.5rem; font-size: x-large">bigger button</button></p>
|
|
17
|
+
<p>Hovering over the first of the following list items changes content.</p>
|
|
18
|
+
<ul>
|
|
19
|
+
<li onmouseover="document.getElementById('listDependent').setAttribute('hidden', true)">This is a list item.</li>
|
|
20
|
+
<li>This is another list item.</li>
|
|
21
|
+
</ul>
|
|
22
|
+
<p id="listDependent">This paragraph becomes invisible when you hover over the first list item.</p>
|
|
17
23
|
</main>
|
|
18
24
|
</body>
|
|
19
25
|
</html>
|
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
<main>
|
|
11
11
|
<h1>Page with standard hover behavior</h1>
|
|
12
12
|
<p>This paragraph contains a link to <a href="https://en.wikipedia.org">information</a> and a <button type="button">button</button>. Both of them can be hovered over, and hovering over either of them does not trigger any change in content.</p>
|
|
13
|
+
<p>Hovering over either of the following list items changes no content.</p>
|
|
14
|
+
<ul>
|
|
15
|
+
<li>This is a list item.</li>
|
|
16
|
+
<li>This is another list item.</li>
|
|
17
|
+
</ul>
|
|
13
18
|
</main>
|
|
14
19
|
</body>
|
|
15
20
|
</html>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en-US">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>Page with deviant hover indicators</title>
|
|
6
|
+
<meta name="description" content="tester">
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
8
|
+
<style>
|
|
9
|
+
button, button:hover {
|
|
10
|
+
color: red;
|
|
11
|
+
background-color: white;
|
|
12
|
+
border: 2px solid black;
|
|
13
|
+
}
|
|
14
|
+
li.cursorless {
|
|
15
|
+
cursor: none;
|
|
16
|
+
}
|
|
17
|
+
li.hoverChanger:hover {
|
|
18
|
+
color: blue;
|
|
19
|
+
background-color: yellow;
|
|
20
|
+
}
|
|
21
|
+
</style>
|
|
22
|
+
</head>
|
|
23
|
+
<body>
|
|
24
|
+
<main>
|
|
25
|
+
<h1>Page with deviant hover indicators</h1>
|
|
26
|
+
<p>This page contains a link to <a href="https://en.wikipedia.org">information</a> and a <button type="button">button</button>. The default style change of the button is prevented.</p>
|
|
27
|
+
<p>This is a list.</p>
|
|
28
|
+
<ul>
|
|
29
|
+
<li>This is a normal list item.</li>
|
|
30
|
+
<li class="cursorless">This is a list item that loses its cursor when hovered over.</li>
|
|
31
|
+
<li id="li2" class="hoverChanger">This is a list item that changes when hovered over although it should not.</li>
|
|
32
|
+
</ul>
|
|
33
|
+
</main>
|
|
34
|
+
</body>
|
|
35
|
+
</html>
|