testaro 4.12.2 → 4.14.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 +1 -1
- package/run.js +13 -9
- package/tests/focInd.js +2 -1
- package/tests/hover.js +122 -126
- package/tests/ibm.js +1 -1
package/package.json
CHANGED
package/run.js
CHANGED
|
@@ -70,16 +70,17 @@ const tenonData = {
|
|
|
70
70
|
};
|
|
71
71
|
// Keywords in log messages indicating errors.
|
|
72
72
|
const errorWords = [
|
|
73
|
-
'
|
|
73
|
+
'content security policy',
|
|
74
|
+
'deprecated',
|
|
74
75
|
'error',
|
|
75
|
-
'
|
|
76
|
+
'failed',
|
|
77
|
+
'missing',
|
|
78
|
+
'but not used',
|
|
76
79
|
'refused',
|
|
77
|
-
'content security policy',
|
|
78
|
-
'unrecognized',
|
|
79
80
|
'requires',
|
|
80
|
-
'
|
|
81
|
-
'
|
|
82
|
-
'
|
|
81
|
+
'suspicious',
|
|
82
|
+
'unrecognized',
|
|
83
|
+
'warning'
|
|
83
84
|
];
|
|
84
85
|
|
|
85
86
|
// ########## VARIABLES
|
|
@@ -288,9 +289,12 @@ const launch = async typeName => {
|
|
|
288
289
|
browserContext = await browser.newContext(viewport);
|
|
289
290
|
// When a page is added to the browser context:
|
|
290
291
|
browserContext.on('page', page => {
|
|
291
|
-
// Make its console messages get reported
|
|
292
|
+
// Make abbreviations of its console messages get reported in the Playwright console.
|
|
292
293
|
page.on('console', msg => {
|
|
293
|
-
|
|
294
|
+
let msgText = msg.text();
|
|
295
|
+
if (msgText.length > 300) {
|
|
296
|
+
msgText = `${msgText.slice(0, 150)} ... ${msgText.slice(-150)}`;
|
|
297
|
+
}
|
|
294
298
|
console.log(`[${msgText}]`);
|
|
295
299
|
const msgTextLC = msgText.toLowerCase();
|
|
296
300
|
const msgLength = msgText.length;
|
package/tests/focInd.js
CHANGED
|
@@ -128,7 +128,7 @@ exports.reporter = async (page, revealAll, allowedDelay, withItems) => {
|
|
|
128
128
|
if (! hasOutline) {
|
|
129
129
|
// Returns whether a style property differs between focused and not focused.
|
|
130
130
|
const diff = prop => styleDec[prop] !== styleBlurred[prop];
|
|
131
|
-
// Determine whether the element has another
|
|
131
|
+
// Determine whether the element has another recognized focus indicator.
|
|
132
132
|
const hasDiffOutline = styleDec.outlineWidth !== '0px'
|
|
133
133
|
&& styleDec.outlineColor !== 'rgba(0, 0, 0, 0)'
|
|
134
134
|
&& (diff('outlineStyle') || diff('outlineWidth'));
|
|
@@ -138,6 +138,7 @@ exports.reporter = async (page, revealAll, allowedDelay, withItems) => {
|
|
|
138
138
|
const hasIndicator
|
|
139
139
|
= hasDiffOutline
|
|
140
140
|
|| hasDiffBorder
|
|
141
|
+
|| diff('box-shadow')
|
|
141
142
|
|| diff('fontSize')
|
|
142
143
|
|| diff('fontStyle')
|
|
143
144
|
|| diff('textDecorationLine')
|
package/tests/hover.js
CHANGED
|
@@ -1,47 +1,43 @@
|
|
|
1
1
|
/*
|
|
2
2
|
hover
|
|
3
|
-
This test reports unexpected
|
|
4
|
-
visible
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
3
|
+
This test reports unexpected impacts of hovering. The effects include additions and removals
|
|
4
|
+
of visible elements, opacity changes, and unhoverable elements. The elements that are
|
|
5
|
+
subjected to hovering (called “triggers”) are the Playwright-visible elements that have 'A',
|
|
6
|
+
'BUTTON', or 'LI' tag names or have 'onmouseenter' or 'onmouseover' attributes. When such an
|
|
7
|
+
element is hovered over, the test examines the impacts on descendants of the great grandparents
|
|
8
|
+
of the elements with tag names 'A' and 'BUTTON', grandparents of elements with tag name 'LI',
|
|
9
|
+
and otherwise the descendants of the elements themselves. Four impacts are counted: (1) an
|
|
10
|
+
element is added or becomes visible, (2) an element is removed or becomes invisible, (3) the
|
|
11
|
+
opacity of an element changes, and (4) the element is a descendant of an element whose opacity
|
|
12
|
+
changes. The test checks up to 4 times for hovering impacts at intervals of 0.3 second.
|
|
13
|
+
|
|
14
|
+
Despite this delay, the test can make the execution time practical by randomly sampling targets
|
|
15
|
+
instead of hovering over all of them. When sampling is performed, the results may vary from one
|
|
16
|
+
execution to another.
|
|
17
|
+
|
|
18
|
+
An element is reported as unhoverable when it fails the Playwright actionability checks for
|
|
19
|
+
hovering, i.e. fails to be attached to the DOM, visible, stable (not or no longer animating), and
|
|
20
|
+
able to receive events. All triggers satisfy the first two conditions, so only the last two might
|
|
21
|
+
fail. Playwright defines the ability to receive events as being the target of an action on the
|
|
22
|
+
location where the center of the element is, rather than some other element with a higher zIndex
|
|
23
|
+
value in the same location being the target.
|
|
21
24
|
*/
|
|
22
25
|
|
|
23
26
|
// CONSTANTS
|
|
24
27
|
|
|
25
|
-
// Selectors of active elements likely to be disclosed by a hover.
|
|
26
|
-
const targetSelectors = ['a', 'button', 'input', '[role=menuitem]', 'span']
|
|
27
|
-
.map(selector => `${selector}:visible`)
|
|
28
|
-
.join(', ');
|
|
29
28
|
// Initialize the result.
|
|
30
29
|
const data = {
|
|
30
|
+
populationSize: 0,
|
|
31
31
|
totals: {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
impactTriggers: 0,
|
|
33
|
+
additions: 0,
|
|
34
|
+
removals: 0,
|
|
35
|
+
opacityChanges: 0,
|
|
36
|
+
opacityEffects: 0,
|
|
36
37
|
unhoverables: 0
|
|
37
38
|
}
|
|
38
39
|
};
|
|
39
40
|
|
|
40
|
-
// VARIABLES
|
|
41
|
-
|
|
42
|
-
// Counter.
|
|
43
|
-
let elementsChecked = 0;
|
|
44
|
-
|
|
45
41
|
// FUNCTIONS
|
|
46
42
|
|
|
47
43
|
// Samples a population and returns the sample.
|
|
@@ -65,9 +61,9 @@ const textOf = async (element, limit) => {
|
|
|
65
61
|
text = text.trim() || await element.innerHTML();
|
|
66
62
|
return text.trim().replace(/\s*/sg, '').slice(0, limit);
|
|
67
63
|
};
|
|
68
|
-
// Recursively
|
|
64
|
+
// Recursively reports impacts of hovering over triggers.
|
|
69
65
|
const find = async (withItems, page, triggers) => {
|
|
70
|
-
// If any potential
|
|
66
|
+
// If any potential triggers remain:
|
|
71
67
|
if (triggers.length) {
|
|
72
68
|
// Identify the first of them.
|
|
73
69
|
const firstTrigger = triggers[0];
|
|
@@ -78,63 +74,86 @@ const find = async (withItems, page, triggers) => {
|
|
|
78
74
|
});
|
|
79
75
|
if (tagNameJSHandle) {
|
|
80
76
|
const tagName = await tagNameJSHandle.jsonValue();
|
|
81
|
-
// Identify the root of a subtree likely to contain
|
|
77
|
+
// Identify the root of a subtree likely to contain impacted elements.
|
|
82
78
|
let root = firstTrigger;
|
|
83
|
-
if (['A', 'BUTTON'].includes(tagName)) {
|
|
79
|
+
if (['A', 'BUTTON', 'LI'].includes(tagName)) {
|
|
84
80
|
const rootJSHandle = await page.evaluateHandle(
|
|
85
81
|
firstTrigger => {
|
|
86
|
-
const parent = firstTrigger.parentElement;
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
else {
|
|
91
|
-
return firstTrigger;
|
|
92
|
-
}
|
|
82
|
+
const parent = firstTrigger.parentElement || firstTrigger;
|
|
83
|
+
const grandparent = parent.parentElement || parent;
|
|
84
|
+
const greatGrandparent = grandparent.parentElement || parent;
|
|
85
|
+
return firstTrigger.tagName === 'LI' ? grandparent : greatGrandparent;
|
|
93
86
|
},
|
|
94
87
|
firstTrigger
|
|
95
88
|
);
|
|
96
89
|
root = rootJSHandle.asElement();
|
|
97
90
|
}
|
|
98
|
-
// Identify the visible active descendants of the root before the hover.
|
|
99
|
-
const preVisibles = await root.$$(targetSelectors);
|
|
100
91
|
// Identify all the descendants of the root.
|
|
101
|
-
const
|
|
102
|
-
// Identify their opacities
|
|
103
|
-
const preOpacities = await page.evaluate(
|
|
104
|
-
|
|
105
|
-
);
|
|
92
|
+
const preDescendants = await root.$$('*');
|
|
93
|
+
// Identify their opacities.
|
|
94
|
+
const preOpacities = await page.evaluate(elements => elements.map(
|
|
95
|
+
element => window.getComputedStyle(element).opacity
|
|
96
|
+
), preDescendants);
|
|
106
97
|
try {
|
|
107
|
-
// Hover over the
|
|
108
|
-
await firstTrigger.hover({
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
98
|
+
// Hover over the trigger.
|
|
99
|
+
await firstTrigger.hover({
|
|
100
|
+
timeout: 500,
|
|
101
|
+
noWaitAfter: true
|
|
102
|
+
});
|
|
103
|
+
// Repeatedly seeks impacts.
|
|
104
|
+
const getImpacts = async (interval, triesLeft) => {
|
|
105
|
+
if (triesLeft--) {
|
|
106
|
+
const postDescendants = await root.$$('*');
|
|
107
|
+
const remainerIndexes = await page.evaluate(args => {
|
|
108
|
+
const preDescendants = args[0];
|
|
109
|
+
const postDescendants = args[1];
|
|
110
|
+
const remainerIndexes = preDescendants
|
|
111
|
+
.map((element, index) => postDescendants.includes(element) ? index : -1)
|
|
112
|
+
.filter(index => index > -1);
|
|
113
|
+
return remainerIndexes;
|
|
114
|
+
}, [preDescendants, postDescendants]);
|
|
115
|
+
const additionCount = postDescendants.length - remainerIndexes.length;
|
|
116
|
+
const removalCount = preDescendants.length - remainerIndexes.length;
|
|
117
|
+
const remainers = [];
|
|
118
|
+
for (const index of remainerIndexes) {
|
|
119
|
+
remainers.push({
|
|
120
|
+
element: preDescendants[index],
|
|
121
|
+
preOpacity: preOpacities[index],
|
|
122
|
+
postOpacity: await page.evaluate(
|
|
123
|
+
element => window.getComputedStyle(element).opacity, preDescendants[index]
|
|
124
|
+
)
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
const opacityChangers = remainers
|
|
128
|
+
.filter(remainer => remainer.postOpacity !== remainer.preOpacity);
|
|
129
|
+
const opacityImpact = await page.evaluate(changers => changers.reduce(
|
|
130
|
+
(total, current) => total + current.element.querySelectorAll('*').length, 0
|
|
131
|
+
), opacityChangers);
|
|
132
|
+
if (additionCount || removalCount || opacityChangers.length) {
|
|
133
|
+
return {
|
|
134
|
+
additionCount,
|
|
135
|
+
removalCount,
|
|
136
|
+
opacityChangers,
|
|
137
|
+
opacityImpact
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
return await new Promise(resolve => {
|
|
142
|
+
setTimeout(() => {
|
|
143
|
+
resolve(getImpacts(interval, triesLeft));
|
|
144
|
+
}, interval);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
136
147
|
}
|
|
137
|
-
|
|
148
|
+
else {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
// Repeatedly seek impacts of the hover at intervals.
|
|
153
|
+
const impacts = await getImpacts(300, 4);
|
|
154
|
+
// If there were any:
|
|
155
|
+
if (impacts) {
|
|
156
|
+
// Hover over the upper-left corner of the page, to undo any impacts.
|
|
138
157
|
await page.hover('body', {
|
|
139
158
|
position: {
|
|
140
159
|
x: 0,
|
|
@@ -144,54 +163,35 @@ const find = async (withItems, page, triggers) => {
|
|
|
144
163
|
// Wait for any delayed and/or slowed hover reaction.
|
|
145
164
|
await page.waitForTimeout(200);
|
|
146
165
|
await root.waitForElementState('stable');
|
|
147
|
-
// Increment the counts of triggers and
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
data.totals.
|
|
151
|
-
data.totals.
|
|
152
|
-
data.totals.
|
|
166
|
+
// Increment the counts of triggers and impacts.
|
|
167
|
+
const {additionCount, removalCount, opacityChangers, opacityImpact} = impacts;
|
|
168
|
+
data.totals.impactTriggers++;
|
|
169
|
+
data.totals.additions += additionCount;
|
|
170
|
+
data.totals.removals += removalCount;
|
|
171
|
+
data.totals.opacityChanges += opacityChangers.length;
|
|
172
|
+
data.totals.opacityImpact += opacityImpact;
|
|
153
173
|
// If details are to be reported:
|
|
154
174
|
if (withItems) {
|
|
155
175
|
// Report them.
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const postVisibles = args[2];
|
|
165
|
-
const madeVisible = postVisibles
|
|
166
|
-
.filter(el => ! preVisibles.includes(el))
|
|
167
|
-
.map(el => ({
|
|
168
|
-
tagName: el.tagName,
|
|
169
|
-
text: textOf(el, 50)
|
|
170
|
-
}));
|
|
171
|
-
const opacityChanged = args[3].map(el => ({
|
|
172
|
-
tagName: el.tagName,
|
|
173
|
-
text: textOf(el, 50)
|
|
174
|
-
}));
|
|
175
|
-
return {
|
|
176
|
-
tagName: trigger.tagName,
|
|
177
|
-
id: trigger.id || '',
|
|
178
|
-
text: textOf(trigger, 50),
|
|
179
|
-
madeVisible,
|
|
180
|
-
opacityChanged
|
|
181
|
-
};
|
|
182
|
-
}, [firstTrigger, preVisibles, postVisibles, opacityTargets]);
|
|
183
|
-
const triggerData = await triggerDataJSHandle.jsonValue();
|
|
184
|
-
data.items.triggers.push(triggerData);
|
|
176
|
+
data.items.impactTriggers.push({
|
|
177
|
+
tagName,
|
|
178
|
+
text: await textOf(firstTrigger, 50),
|
|
179
|
+
additions: additionCount,
|
|
180
|
+
removals: removalCount,
|
|
181
|
+
opacityChanges: opacityChangers.length,
|
|
182
|
+
opacityImpact
|
|
183
|
+
});
|
|
185
184
|
}
|
|
186
185
|
}
|
|
187
186
|
}
|
|
188
187
|
catch (error) {
|
|
189
|
-
console.log(
|
|
188
|
+
console.log(`ERROR hovering (${error.message})`);
|
|
190
189
|
data.totals.unhoverables++;
|
|
191
190
|
if (withItems) {
|
|
191
|
+
const id = await firstTrigger.getAttribute('id');
|
|
192
192
|
data.items.unhoverables.push({
|
|
193
|
-
tagName
|
|
194
|
-
id:
|
|
193
|
+
tagName,
|
|
194
|
+
id: id || '',
|
|
195
195
|
text: await textOf(firstTrigger, 50)
|
|
196
196
|
});
|
|
197
197
|
}
|
|
@@ -207,18 +207,13 @@ exports.reporter = async (page, sampleSize = Infinity, withItems) => {
|
|
|
207
207
|
if (withItems) {
|
|
208
208
|
// Add properties for details to the initialized result.
|
|
209
209
|
data.items = {
|
|
210
|
-
|
|
210
|
+
impactTriggers: [],
|
|
211
211
|
unhoverables: []
|
|
212
212
|
};
|
|
213
213
|
}
|
|
214
214
|
// Identify the triggers.
|
|
215
|
-
const selectors = [
|
|
216
|
-
|
|
217
|
-
'body button:visible',
|
|
218
|
-
'body li:visible, body [onmouseenter]:visible',
|
|
219
|
-
'body [onmouseover]:visible'
|
|
220
|
-
];
|
|
221
|
-
const triggers = await page.$$(selectors.join(', '))
|
|
215
|
+
const selectors = ['a', 'button', 'li', '[onmouseenter]', '[onmouseover]'];
|
|
216
|
+
const triggers = await page.$$(selectors.map(selector => `body ${selector}:visible`).join(', '))
|
|
222
217
|
.catch(error => {
|
|
223
218
|
console.log(`ERROR getting hover triggers (${error.message})`);
|
|
224
219
|
data.prevented = true;
|
|
@@ -226,8 +221,9 @@ exports.reporter = async (page, sampleSize = Infinity, withItems) => {
|
|
|
226
221
|
});
|
|
227
222
|
// If they number more than the sample size limit, sample them.
|
|
228
223
|
const triggerCount = triggers.length;
|
|
224
|
+
data.populationSize = triggerCount;
|
|
229
225
|
const triggerSample = triggerCount > sampleSize ? getSample(triggers, sampleSize) : triggers;
|
|
230
|
-
// Find and document the hover-triggered
|
|
226
|
+
// Find and document the hover-triggered impacts.
|
|
231
227
|
await find(withItems, page, triggerSample);
|
|
232
228
|
// If the triggers were sampled:
|
|
233
229
|
if (triggerCount > sampleSize) {
|
package/tests/ibm.js
CHANGED
|
@@ -25,7 +25,6 @@ const run = async content => {
|
|
|
25
25
|
const nowLabel = (new Date()).toISOString().slice(0, 19);
|
|
26
26
|
// Return the result of a test.
|
|
27
27
|
const ibmReport = await getCompliance(content, nowLabel);
|
|
28
|
-
await close();
|
|
29
28
|
return ibmReport;
|
|
30
29
|
};
|
|
31
30
|
// Trims an IBM report.
|
|
@@ -118,5 +117,6 @@ exports.reporter = async (page, withItems, withNewContent) => {
|
|
|
118
117
|
console.log('ERROR: Getting ibm test report from URL took too long');
|
|
119
118
|
}
|
|
120
119
|
}
|
|
120
|
+
await close();
|
|
121
121
|
return {result};
|
|
122
122
|
};
|