testaro 5.17.2 → 5.18.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/tests/hover.js +179 -103
- package/tests/titledEl.js +3 -1
- package/validation/tests/scripts/hover.json +29 -7
- package/validation/tests/targets/hover/bad.html +6 -0
- package/validation/tests/targets/hover/good.html +5 -0
- package/validation/tests/targets/hover/styleBad.html +35 -0
package/package.json
CHANGED
package/tests/hover.js
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
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 suppression of 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
|
+
When such an element is hovered over, the test examines the impacts on descendants of the great
|
|
10
|
+
grandparents of the elements with tag names 'A' and 'BUTTON', grandparents of elements with tag
|
|
11
|
+
name 'LI', and otherwise the descendants of the elements 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
|
|
12
14
|
changes. The test checks up to 4 times for hovering impacts at intervals of 0.3 second.
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
The test also examines how the hover event is indicated to the user with the mouse cursor and with
|
|
17
|
+
style changes.
|
|
18
|
+
|
|
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
21
|
execution to another. Because hover impacts typically occur near the beginning of a page, the
|
|
17
22
|
probability of the inclusion of a trigger in a sample decreases with the index of the trigger.
|
|
@@ -30,7 +35,10 @@ let hasTimedOut = false;
|
|
|
30
35
|
|
|
31
36
|
// FUNCTIONS
|
|
32
37
|
|
|
33
|
-
//
|
|
38
|
+
// Gets the probability of a trigger being sampled.
|
|
39
|
+
const samProb = (index, popSize, sampleRatio) =>
|
|
40
|
+
sampleRatio * (1 + Math.sin(Math.PI * index / popSize + Math.PI / 2));
|
|
41
|
+
// Samples the trigger population and returns the sample and each member’s sampling probability.
|
|
34
42
|
const getSample = (population, sampleSize) => {
|
|
35
43
|
const popSize = population.length;
|
|
36
44
|
// If the sample is at least as large as the population:
|
|
@@ -43,13 +51,9 @@ const getSample = (population, sampleSize) => {
|
|
|
43
51
|
// Force the sample size to be an integer and at least 1.
|
|
44
52
|
sampleSize = Math.floor(Math.max(1, sampleSize));
|
|
45
53
|
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
54
|
// Get the sample.
|
|
51
55
|
const sample = population.map((trigger, index) => {
|
|
52
|
-
const itemProb = samProb(index);
|
|
56
|
+
const itemProb = samProb(index, popSize, sampleRatio);
|
|
53
57
|
return [trigger, itemProb];
|
|
54
58
|
}).filter(pair => pair[1] > Math.random());
|
|
55
59
|
return sample;
|
|
@@ -59,9 +63,84 @@ const getSample = (population, sampleSize) => {
|
|
|
59
63
|
const textOf = async (element, limit) => {
|
|
60
64
|
let text = await element.textContent();
|
|
61
65
|
text = text.trim() || await element.innerHTML();
|
|
62
|
-
return text.trim().replace(/\s
|
|
66
|
+
return text.trim().replace(/\s+/sg, ' ').slice(0, limit);
|
|
67
|
+
};
|
|
68
|
+
// Returns the impacts of hovering over a sampled trigger.
|
|
69
|
+
const getImpacts = async (
|
|
70
|
+
interval, triesLeft, root, page, preDescendants, preOpacities
|
|
71
|
+
) => {
|
|
72
|
+
// If the allowed trial count has not yet been exhausted:
|
|
73
|
+
if (triesLeft-- && ! hasTimedOut) {
|
|
74
|
+
// Get the collection of descendants of the root.
|
|
75
|
+
const postDescendants = await root.$$(':visible');
|
|
76
|
+
// Identify the prior descendants of the root still in existence.
|
|
77
|
+
const remainerIndexes = await page.evaluate(args => {
|
|
78
|
+
const preDescendants = args[0];
|
|
79
|
+
const postDescendants = args[1];
|
|
80
|
+
const remainerIndexes = preDescendants
|
|
81
|
+
.map((element, index) => postDescendants.includes(element) ? index : -1)
|
|
82
|
+
.filter(index => index > -1);
|
|
83
|
+
return remainerIndexes;
|
|
84
|
+
}, [preDescendants, postDescendants]);
|
|
85
|
+
// Get the impacts of the hover event.
|
|
86
|
+
const additions = postDescendants.length - remainerIndexes.length;
|
|
87
|
+
const removals = preDescendants.length - remainerIndexes.length;
|
|
88
|
+
const remainers = [];
|
|
89
|
+
for (const index of remainerIndexes) {
|
|
90
|
+
remainers.push({
|
|
91
|
+
element: preDescendants[index],
|
|
92
|
+
preOpacity: preOpacities[index],
|
|
93
|
+
postOpacity: await page.evaluate(
|
|
94
|
+
element => window.getComputedStyle(element).opacity, preDescendants[index]
|
|
95
|
+
)
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
const opacityChangers = remainers
|
|
99
|
+
.filter(remainer => remainer.postOpacity !== remainer.preOpacity);
|
|
100
|
+
const opacityImpact = opacityChangers
|
|
101
|
+
? await page.evaluate(changers => changers.reduce(
|
|
102
|
+
(total, current) => total + current.element.querySelectorAll('*').length, 0
|
|
103
|
+
), opacityChangers)
|
|
104
|
+
: 0;
|
|
105
|
+
// If there are any impacts:
|
|
106
|
+
if (additions || removals || opacityChangers.length) {
|
|
107
|
+
// Return them.
|
|
108
|
+
return {
|
|
109
|
+
additions,
|
|
110
|
+
removals,
|
|
111
|
+
opacityChanges: opacityChangers.length,
|
|
112
|
+
opacityImpact
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
// Otherwise, i.e. if there are no impacts:
|
|
116
|
+
else {
|
|
117
|
+
// Try again.
|
|
118
|
+
return await new Promise(resolve => {
|
|
119
|
+
setTimeout(() => {
|
|
120
|
+
resolve(getImpacts(interval, triesLeft, root, page, preDescendants, preOpacities));
|
|
121
|
+
}, interval);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Otherwise, i.e. if the allowed trial count has been exhausted:
|
|
126
|
+
else {
|
|
127
|
+
// Report non-impact.
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
63
130
|
};
|
|
64
|
-
//
|
|
131
|
+
// Returns the hover-related style properties of a trigger.
|
|
132
|
+
const getHoverStyles = async (page, element) => await page.evaluate(
|
|
133
|
+
element => {
|
|
134
|
+
const {cursor, outline, color, backgroundColor} = window.getComputedStyle(element);
|
|
135
|
+
return {
|
|
136
|
+
cursor: cursor.replace(/^.+, */, ''),
|
|
137
|
+
outline,
|
|
138
|
+
color,
|
|
139
|
+
backgroundColor
|
|
140
|
+
};
|
|
141
|
+
}, element
|
|
142
|
+
);
|
|
143
|
+
// Recursively adds estimated and itemized impacts of hovering over triggers to data.
|
|
65
144
|
const find = async (data, withItems, page, sample) => {
|
|
66
145
|
// If any triggers remain and the test has not timed out:
|
|
67
146
|
if (sample.length && ! hasTimedOut) {
|
|
@@ -69,6 +148,8 @@ const find = async (data, withItems, page, sample) => {
|
|
|
69
148
|
try {
|
|
70
149
|
// Identify the first trigger and its sampling probability.
|
|
71
150
|
const firstTrigger = sample[0];
|
|
151
|
+
const onmouseenter = await firstTrigger[0].getAttribute('onmouseenter');
|
|
152
|
+
const onmouseover = await firstTrigger[0].getAttribute('onmouseover');
|
|
72
153
|
const tagNameJSHandle = await firstTrigger[0].getProperty('tagName')
|
|
73
154
|
.catch(() => '');
|
|
74
155
|
if (tagNameJSHandle) {
|
|
@@ -93,78 +174,70 @@ const find = async (data, withItems, page, sample) => {
|
|
|
93
174
|
const preOpacities = await page.evaluate(elements => elements.map(
|
|
94
175
|
element => window.getComputedStyle(element).opacity
|
|
95
176
|
), preDescendants);
|
|
177
|
+
// Get the style properties of the trigger.
|
|
178
|
+
const triggerPreStyles = await getHoverStyles(page, firstTrigger[0]);
|
|
179
|
+
const totalEstimate = 1 / firstTrigger[1];
|
|
180
|
+
const itemData = {
|
|
181
|
+
tagName,
|
|
182
|
+
id: (await firstTrigger[0].getAttribute('id')) || '',
|
|
183
|
+
text: await textOf(firstTrigger[0], 50)
|
|
184
|
+
};
|
|
96
185
|
try {
|
|
97
186
|
// Hover over the trigger.
|
|
98
187
|
await firstTrigger[0].hover({
|
|
99
188
|
timeout: 500,
|
|
100
189
|
noWaitAfter: true
|
|
101
190
|
});
|
|
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
|
-
}
|
|
191
|
+
// Repeatedly seek impacts of the hover at intervals.
|
|
192
|
+
const impacts = await getImpacts(
|
|
193
|
+
300, 4, root, page, preDescendants, preOpacities, firstTrigger
|
|
194
|
+
);
|
|
195
|
+
// Get the style properties of the trigger.
|
|
196
|
+
const triggerPostStyles = await getHoverStyles(page, firstTrigger[0]);
|
|
197
|
+
// Add cursor and other style defects to the data.
|
|
198
|
+
const cursor = triggerPreStyles.cursor;
|
|
199
|
+
// If the trigger has no cursor:
|
|
200
|
+
if (cursor === 'none') {
|
|
201
|
+
// Add this fact to the data.
|
|
202
|
+
data.totals.noCursors += totalEstimate;
|
|
203
|
+
if (withItems) {
|
|
204
|
+
data.items.noCursors.push(itemData);
|
|
157
205
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
206
|
+
}
|
|
207
|
+
// If the trigger has an improper cursor:
|
|
208
|
+
if (
|
|
209
|
+
tagName === 'A' && cursor !== 'pointer'
|
|
210
|
+
|| tagName === 'BUTTON' && cursor !== 'default'
|
|
211
|
+
){
|
|
212
|
+
// Add this fact to the data.
|
|
213
|
+
data.totals.badCursors += totalEstimate;
|
|
214
|
+
if (withItems) {
|
|
215
|
+
data.items.badCursors.push(itemData);
|
|
162
216
|
}
|
|
163
|
-
}
|
|
164
|
-
//
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
217
|
+
}
|
|
218
|
+
// If hover indication is required but is absent:
|
|
219
|
+
if (
|
|
220
|
+
(tagName === 'BUTTON' || onmouseenter || onmouseover)
|
|
221
|
+
&& JSON.stringify(triggerPostStyles) === JSON.stringify(triggerPreStyles)
|
|
222
|
+
) {
|
|
223
|
+
// Add this fact to the data.
|
|
224
|
+
data.totals.noIndicators += totalEstimate;
|
|
225
|
+
if (withItems) {
|
|
226
|
+
data.items.noIndicators.push(itemData);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// If hover indication is illicit but is present:
|
|
230
|
+
if (
|
|
231
|
+
tagName === 'LI'
|
|
232
|
+
&& JSON.stringify(triggerPostStyles) !== JSON.stringify(triggerPreStyles)
|
|
233
|
+
) {
|
|
234
|
+
// Add this fact to the data.
|
|
235
|
+
data.totals.badIndicators += totalEstimate;
|
|
236
|
+
if (withItems) {
|
|
237
|
+
data.items.badIndicators.push(itemData);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// If there were any impacts:
|
|
168
241
|
if (impacts) {
|
|
169
242
|
// Hover over the upper-left corner of the page, to undo any impacts.
|
|
170
243
|
await page.hover('body', {
|
|
@@ -176,30 +249,30 @@ const find = async (data, withItems, page, sample) => {
|
|
|
176
249
|
force: true,
|
|
177
250
|
noWaitAfter: true
|
|
178
251
|
});
|
|
179
|
-
// Wait for any delayed and/or slowed
|
|
252
|
+
// Wait for any delayed and/or slowed reaction.
|
|
180
253
|
await page.waitForTimeout(200);
|
|
181
254
|
await root.waitForElementState('stable');
|
|
182
255
|
// Increment the estimated counts of triggers and impacts.
|
|
183
|
-
const {
|
|
256
|
+
const {additions, removals, opacityChanges, opacityImpact} = impacts;
|
|
184
257
|
if (hasTimedOut) {
|
|
185
258
|
return Promise.resolve('');
|
|
186
259
|
}
|
|
187
260
|
else {
|
|
188
|
-
data.totals.impactTriggers +=
|
|
189
|
-
data.totals.additions +=
|
|
190
|
-
data.totals.removals +=
|
|
191
|
-
data.totals.opacityChanges += opacityChanges;
|
|
192
|
-
data.totals.opacityImpact += opacityImpact;
|
|
261
|
+
data.totals.impactTriggers += totalEstimate;
|
|
262
|
+
data.totals.additions += additions / firstTrigger[1];
|
|
263
|
+
data.totals.removals += removals / firstTrigger[1];
|
|
264
|
+
data.totals.opacityChanges += opacityChanges / firstTrigger[1];
|
|
265
|
+
data.totals.opacityImpact += opacityImpact / firstTrigger[1];
|
|
193
266
|
// If details are to be reported:
|
|
194
267
|
if (withItems) {
|
|
195
|
-
//
|
|
268
|
+
// Add them to the data.
|
|
196
269
|
data.items.impactTriggers.push({
|
|
197
270
|
tagName,
|
|
198
271
|
text: await textOf(firstTrigger[0], 50),
|
|
199
|
-
additions
|
|
200
|
-
removals
|
|
201
|
-
opacityChanges
|
|
202
|
-
opacityImpact
|
|
272
|
+
additions,
|
|
273
|
+
removals,
|
|
274
|
+
opacityChanges,
|
|
275
|
+
opacityImpact
|
|
203
276
|
});
|
|
204
277
|
}
|
|
205
278
|
}
|
|
@@ -211,15 +284,10 @@ const find = async (data, withItems, page, sample) => {
|
|
|
211
284
|
return Promise.resolve('');
|
|
212
285
|
}
|
|
213
286
|
else {
|
|
214
|
-
data.totals.unhoverables +=
|
|
287
|
+
data.totals.unhoverables += totalEstimate;
|
|
215
288
|
if (withItems) {
|
|
216
289
|
try {
|
|
217
|
-
|
|
218
|
-
data.items.unhoverables.push({
|
|
219
|
-
tagName,
|
|
220
|
-
id: id || '',
|
|
221
|
-
text: await textOf(firstTrigger[0], 50)
|
|
222
|
-
});
|
|
290
|
+
data.items.unhoverables.push(itemData);
|
|
223
291
|
}
|
|
224
292
|
catch(error) {
|
|
225
293
|
console.log('ERROR itemizing unhoverable element');
|
|
@@ -251,7 +319,11 @@ exports.reporter = async (page, sampleSize = -1, withItems) => {
|
|
|
251
319
|
removals: 0,
|
|
252
320
|
opacityChanges: 0,
|
|
253
321
|
opacityImpact: 0,
|
|
254
|
-
unhoverables: 0
|
|
322
|
+
unhoverables: 0,
|
|
323
|
+
noCursors: 0,
|
|
324
|
+
badCursors: 0,
|
|
325
|
+
noIndicators: 0,
|
|
326
|
+
badIndicators: 0
|
|
255
327
|
}
|
|
256
328
|
};
|
|
257
329
|
// If details are to be reported:
|
|
@@ -259,7 +331,11 @@ exports.reporter = async (page, sampleSize = -1, withItems) => {
|
|
|
259
331
|
// Add properties for details to the initialized result.
|
|
260
332
|
data.items = {
|
|
261
333
|
impactTriggers: [],
|
|
262
|
-
unhoverables: []
|
|
334
|
+
unhoverables: [],
|
|
335
|
+
noCursors: [],
|
|
336
|
+
badCursors: [],
|
|
337
|
+
noIndicators: [],
|
|
338
|
+
badIndicators: []
|
|
263
339
|
};
|
|
264
340
|
}
|
|
265
341
|
// Identify the triggers.
|
|
@@ -288,7 +364,7 @@ exports.reporter = async (page, sampleSize = -1, withItems) => {
|
|
|
288
364
|
};
|
|
289
365
|
clearTimeout(timeout);
|
|
290
366
|
}, 1000 * timeLimit);
|
|
291
|
-
// Find and document the impacts.
|
|
367
|
+
// Find and document the style defects and impacts of the sampled triggers.
|
|
292
368
|
if (sample.length && ! hasTimedOut) {
|
|
293
369
|
await find(data, withItems, page, sample);
|
|
294
370
|
}
|
package/tests/titledEl.js
CHANGED
|
@@ -10,7 +10,9 @@ exports.reporter = async (page, withItems) => {
|
|
|
10
10
|
badTitleElements => {
|
|
11
11
|
// FUNCTION DEFINITION START
|
|
12
12
|
// Returns a space-minimized copy of a string.
|
|
13
|
-
const compact = string => string
|
|
13
|
+
const compact = string => string
|
|
14
|
+
? string.replace(/[\t\n]/g, '').replace(/\s{2,}/g, ' ').trim()
|
|
15
|
+
: '';
|
|
14
16
|
// FUNCTION DEFINITION END
|
|
15
17
|
return badTitleElements.map(element => ({
|
|
16
18
|
tagName: element.tagName,
|
|
@@ -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>
|