testaro 58.3.5 → 59.0.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/CONTRIBUTING.md +0 -2
- package/package.json +1 -1
- package/procs/testaro.js +2 -11
- package/run.js +0 -1
- package/testaro/allCaps.js +1 -1
- package/testaro/allSlanted.js +1 -1
- package/testaro/buttonMenu.js +1 -7
- package/testaro/distortion.js +1 -1
- package/testaro/focAll.js +1 -7
- package/testaro/focOp.js +1 -7
- package/testaro/hover.js +1 -7
- package/testaro/imageLink.js +0 -1
- package/testaro/legendLoc.js +0 -1
- package/testaro/linkTitle.js +1 -1
- package/testaro/linkUl.js +1 -1
- package/testaro/nonTable.js +1 -1
- package/testaro/opFoc.js +1 -7
- package/testaro/optRoleSel.js +0 -1
- package/testaro/tabNav.js +1 -7
- package/tests/testaro.js +87 -22
package/CONTRIBUTING.md
CHANGED
|
@@ -114,8 +114,6 @@ More complex Testaro rules are implemented in JavaScript. Some rules are _simpli
|
|
|
114
114
|
|
|
115
115
|
The `selector` value is a CSS selector that identifies candidate elements for violation reporting. What makes this rule simplifiable, instead of simple, is that these elements may or may not be determined to violate the rule. Each of the elements identified by the selector must be further analyzed by the pruner. The pruner takes a Playwright locator as its argument and returns `true` if it finds that the element located by the locator violates the rule, or `false` if not.
|
|
116
116
|
|
|
117
|
-
The `isDestructive` property should be set to `true` if your pruner modifies the page. Any pruner that calls the `isOperable()` function from the `operable` module does so.
|
|
118
|
-
|
|
119
117
|
### Complex rules
|
|
120
118
|
|
|
121
119
|
Even more complex Testaro rules require analysis that cannot fit into the simple or simplifiable category. You can begin with existing JavaScript rules, or the `data/template.js` file, as an example.
|
package/package.json
CHANGED
package/procs/testaro.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2023–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2025 Jonathan Robert Pool. All rights reserved.
|
|
3
4
|
|
|
4
5
|
MIT License
|
|
5
6
|
|
|
@@ -129,7 +130,7 @@ const report = exports.report = async (withItems, all, ruleID, whats, ordinalSev
|
|
|
129
130
|
// Performs a simplifiable test.
|
|
130
131
|
exports.simplify = async (page, withItems, ruleData) => {
|
|
131
132
|
const {
|
|
132
|
-
ruleID, selector, pruner,
|
|
133
|
+
ruleID, selector, pruner, complaints, ordinalSeverity, summaryTagName
|
|
133
134
|
} = ruleData;
|
|
134
135
|
// Get an object with initialized violation locators and result as properties.
|
|
135
136
|
const all = await init(100, page, selector);
|
|
@@ -149,16 +150,6 @@ exports.simplify = async (page, withItems, ruleData) => {
|
|
|
149
150
|
complaints.summary
|
|
150
151
|
];
|
|
151
152
|
const result = await report(withItems, all, ruleID, whats, ordinalSeverity, summaryTagName);
|
|
152
|
-
// If the pruner modifies the page:
|
|
153
|
-
if (isDestructive) {
|
|
154
|
-
// Reload the page.
|
|
155
|
-
try {
|
|
156
|
-
await page.reload({timeout: 15000});
|
|
157
|
-
}
|
|
158
|
-
catch(error) {
|
|
159
|
-
console.log('ERROR: page reload timed out');
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
153
|
// Return the result.
|
|
163
154
|
return result;
|
|
164
155
|
};
|
package/run.js
CHANGED
|
@@ -1447,7 +1447,6 @@ exports.doJob = async (job, opts = {}) => {
|
|
|
1447
1447
|
}
|
|
1448
1448
|
});
|
|
1449
1449
|
// Perform the acts and get a report.
|
|
1450
|
-
console.log('Performing the job acts');
|
|
1451
1450
|
report = await doActs(report, opts);
|
|
1452
1451
|
// Add the end time and duration to the report.
|
|
1453
1452
|
const endTime = new Date();
|
package/testaro/allCaps.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2023 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2025 Jonathan Robert Pool. All rights reserved.
|
|
3
4
|
|
|
4
5
|
MIT License
|
|
5
6
|
|
|
@@ -64,7 +65,6 @@ exports.reporter = async (page, withItems) => {
|
|
|
64
65
|
return transformStyle === 'uppercase' && elText.length > 7;
|
|
65
66
|
}
|
|
66
67
|
}),
|
|
67
|
-
isDestructive: false,
|
|
68
68
|
complaints: {
|
|
69
69
|
instance: 'Element contains all-capital text',
|
|
70
70
|
summary: 'Elements contain all-capital text'
|
package/testaro/allSlanted.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2023 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2025 Jonathan Robert Pool. All rights reserved.
|
|
3
4
|
|
|
4
5
|
MIT License
|
|
5
6
|
|
|
@@ -47,7 +48,6 @@ exports.reporter = async (page, withItems) => {
|
|
|
47
48
|
const elText = el.textContent;
|
|
48
49
|
return ['italic', 'oblique'].includes(elStyleDec.fontStyle) && elText.length > 39;
|
|
49
50
|
}),
|
|
50
|
-
isDestructive: false,
|
|
51
51
|
complaints: {
|
|
52
52
|
instance: 'Element contains all-italic or all-oblique text',
|
|
53
53
|
summary: 'Elements contain all-italic or all-oblique text'
|
package/testaro/buttonMenu.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2023–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2025 Jonathan Robert Pool. All rights reserved.
|
|
3
4
|
|
|
4
5
|
MIT License
|
|
5
6
|
|
|
@@ -292,13 +293,6 @@ exports.reporter = async (page, withItems, trialKeySpecs = []) => {
|
|
|
292
293
|
excerpt: ''
|
|
293
294
|
});
|
|
294
295
|
}
|
|
295
|
-
// Reload the page, because attributes of elements were modified.
|
|
296
|
-
try {
|
|
297
|
-
await page.reload({timeout: 15000});
|
|
298
|
-
}
|
|
299
|
-
catch(error) {
|
|
300
|
-
console.log('ERROR: page reload timed out');
|
|
301
|
-
}
|
|
302
296
|
return {
|
|
303
297
|
data,
|
|
304
298
|
totals,
|
package/testaro/distortion.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2023 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2025 Jonathan Robert Pool. All rights reserved.
|
|
3
4
|
|
|
4
5
|
MIT License
|
|
5
6
|
|
|
@@ -48,7 +49,6 @@ exports.reporter = async (page, withItems) => {
|
|
|
48
49
|
return transform
|
|
49
50
|
&& ['matrix', 'perspective', 'rotate', 'scale', 'skew'].some(key => transform.includes(key));
|
|
50
51
|
}),
|
|
51
|
-
isDestructive: false,
|
|
52
52
|
complaints: {
|
|
53
53
|
instance: 'Element distorts its text',
|
|
54
54
|
summary: 'Elements distort their texts'
|
package/testaro/focAll.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2021–2023 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2025 Jonathan Robert Pool. All rights reserved.
|
|
3
4
|
|
|
4
5
|
MIT License
|
|
5
6
|
|
|
@@ -77,13 +78,6 @@ exports.reporter = async page => {
|
|
|
77
78
|
tabFocused,
|
|
78
79
|
discrepancy: tabFocused - focusableCount
|
|
79
80
|
};
|
|
80
|
-
// Reload the page, because properties were added to elements.
|
|
81
|
-
try {
|
|
82
|
-
await page.reload({timeout: 15000});
|
|
83
|
-
}
|
|
84
|
-
catch(error) {
|
|
85
|
-
console.log('ERROR: page reload timed out');
|
|
86
|
-
}
|
|
87
81
|
const count = Math.abs(data.discrepancy);
|
|
88
82
|
// Return the result.
|
|
89
83
|
return {
|
package/testaro/focOp.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2021–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2025 Jonathan Robert Pool. All rights reserved.
|
|
3
4
|
|
|
4
5
|
MIT License
|
|
5
6
|
|
|
@@ -70,12 +71,5 @@ exports.reporter = async (page, withItems) => {
|
|
|
70
71
|
'Element is Tab-focusable but not operable', 'Elements are Tab-focusable but not operable'
|
|
71
72
|
];
|
|
72
73
|
const result = await report(withItems, all, 'focOp', whats, 2);
|
|
73
|
-
// Reload the page, because isOperable() modified it.
|
|
74
|
-
try {
|
|
75
|
-
await page.reload({timeout: 15000});
|
|
76
|
-
}
|
|
77
|
-
catch(error) {
|
|
78
|
-
console.log('ERROR: page reload timed out');
|
|
79
|
-
}
|
|
80
74
|
return result;
|
|
81
75
|
};
|
package/testaro/hover.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2021–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2025 Jonathan Robert Pool. All rights reserved.
|
|
3
4
|
|
|
4
5
|
MIT License
|
|
5
6
|
|
|
@@ -104,12 +105,5 @@ exports.reporter = async (page, withItems) => {
|
|
|
104
105
|
'Hovering over the element __param__',
|
|
105
106
|
'Hovering over elements adds elements to or subtracts elements from the page'
|
|
106
107
|
];
|
|
107
|
-
// Reload the page, because hovering may have caused content changes.
|
|
108
|
-
try {
|
|
109
|
-
await page.reload({timeout: 15000});
|
|
110
|
-
}
|
|
111
|
-
catch(error) {
|
|
112
|
-
console.log('ERROR: page reload timed out');
|
|
113
|
-
}
|
|
114
108
|
return await report(withItems, all, 'hover', whats, 0);
|
|
115
109
|
};
|
package/testaro/imageLink.js
CHANGED
|
@@ -40,7 +40,6 @@ exports.reporter = async (page, withItems) => {
|
|
|
40
40
|
return /\.(?:png|jpe?g|gif|svg|webp|ico)(?:$|[?#])/i.test(href);
|
|
41
41
|
});
|
|
42
42
|
},
|
|
43
|
-
isDestructive: false,
|
|
44
43
|
complaints: {
|
|
45
44
|
instance: 'Link destination is an image file',
|
|
46
45
|
summary: 'Links have image files as their destinations'
|
package/testaro/legendLoc.js
CHANGED
|
@@ -48,7 +48,6 @@ exports.reporter = async (page, withItems) => {
|
|
|
48
48
|
return true;
|
|
49
49
|
});
|
|
50
50
|
},
|
|
51
|
-
isDestructive: false,
|
|
52
51
|
complaints: {
|
|
53
52
|
instance: 'Element is not the first child of a fieldset element',
|
|
54
53
|
summary: 'legend elements are not the first children of fieldset elements'
|
package/testaro/linkTitle.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2023 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2025 Jonathan Robert Pool. All rights reserved.
|
|
3
4
|
|
|
4
5
|
MIT License
|
|
5
6
|
|
|
@@ -48,7 +49,6 @@ exports.reporter = async (page, withItems) => {
|
|
|
48
49
|
const title = await loc.getAttribute('title');
|
|
49
50
|
return elData.excerpt.toLowerCase().includes(title.toLowerCase());
|
|
50
51
|
},
|
|
51
|
-
isDestructive: false,
|
|
52
52
|
complaints: {
|
|
53
53
|
instance: 'Link has a title attribute that repeats link text content',
|
|
54
54
|
summary: 'Links have title attributes that repeat link text contents'
|
package/testaro/linkUl.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2021–2023 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2025 Jonathan Robert Pool. All rights reserved.
|
|
3
4
|
|
|
4
5
|
MIT License
|
|
5
6
|
|
|
@@ -59,7 +60,6 @@ exports.reporter = async (page, withItems) => {
|
|
|
59
60
|
return await isInlineLink(loc);
|
|
60
61
|
}
|
|
61
62
|
},
|
|
62
|
-
isDestructive: false,
|
|
63
63
|
complaints: {
|
|
64
64
|
instance: 'Link is inline but has no underline',
|
|
65
65
|
summary: 'Inline links are missing underlines'
|
package/testaro/nonTable.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2022–2023 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2025 Jonathan Robert Pool. All rights reserved.
|
|
3
4
|
|
|
4
5
|
MIT License
|
|
5
6
|
|
|
@@ -80,7 +81,6 @@ exports.reporter = async (page, withItems) => {
|
|
|
80
81
|
return true;
|
|
81
82
|
}
|
|
82
83
|
}),
|
|
83
|
-
isDestructive: false,
|
|
84
84
|
complaints: {
|
|
85
85
|
instance: 'Table is misused to arrange content',
|
|
86
86
|
summary: 'Tables are misused to arrange content'
|
package/testaro/opFoc.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2023–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2025 Jonathan Robert Pool. All rights reserved.
|
|
3
4
|
|
|
4
5
|
MIT License
|
|
5
6
|
|
|
@@ -71,12 +72,5 @@ exports.reporter = async (page, withItems) => {
|
|
|
71
72
|
'Elements are operable but not Tab-focusable'
|
|
72
73
|
];
|
|
73
74
|
const result = await report(withItems, all, 'opFoc', whats, 3);
|
|
74
|
-
// Reload the page, because isOperable() modified it.
|
|
75
|
-
try {
|
|
76
|
-
await page.reload({timeout: 15000});
|
|
77
|
-
}
|
|
78
|
-
catch(error) {
|
|
79
|
-
console.log('ERROR: page reload timed out');
|
|
80
|
-
}
|
|
81
75
|
return result;
|
|
82
76
|
};
|
package/testaro/optRoleSel.js
CHANGED
|
@@ -39,7 +39,6 @@ exports.reporter = async (page, withItems) => {
|
|
|
39
39
|
return ! el.hasAttribute('aria-selected');
|
|
40
40
|
});
|
|
41
41
|
},
|
|
42
|
-
isDestructive: false,
|
|
43
42
|
complaints: {
|
|
44
43
|
instance: 'Element has an explicit option role but no aria-selected attribute',
|
|
45
44
|
summary: 'Elements with explicit option roles have no aria-selected attributes'
|
package/testaro/tabNav.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2021–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2025 Jonathan Robert Pool. All rights reserved.
|
|
3
4
|
|
|
4
5
|
MIT License
|
|
5
6
|
|
|
@@ -454,13 +455,6 @@ exports.reporter = async (page, withItems) => {
|
|
|
454
455
|
const tabLists = await page.$$('[role=tablist]:visible');
|
|
455
456
|
if (tabLists.length) {
|
|
456
457
|
await testTabLists(tabLists, withItems, page);
|
|
457
|
-
// Reload the page, because keyboard navigation may have triggered content changes.
|
|
458
|
-
try {
|
|
459
|
-
await page.reload({timeout: 15000});
|
|
460
|
-
}
|
|
461
|
-
catch(error) {
|
|
462
|
-
console.log('ERROR: page reload timed out');
|
|
463
|
-
}
|
|
464
458
|
}
|
|
465
459
|
// Get the totals of navigation errors, bad tabs, and bad tab lists.
|
|
466
460
|
const totals = data.totals ? [
|
package/tests/testaro.js
CHANGED
|
@@ -32,6 +32,8 @@
|
|
|
32
32
|
|
|
33
33
|
// Module to perform common operations.
|
|
34
34
|
const {init, report} = require('../procs/testaro');
|
|
35
|
+
// Function to launch a browser.
|
|
36
|
+
const {launch} = require('../run');
|
|
35
37
|
// Module to handle files.
|
|
36
38
|
const fs = require('fs/promises');
|
|
37
39
|
|
|
@@ -59,23 +61,17 @@ const futureEvalRulesCleanRoom = {
|
|
|
59
61
|
// when preparing clean-room submissions.
|
|
60
62
|
const futureRules = new Set([]);
|
|
61
63
|
const evalRules = {
|
|
62
|
-
altScheme: 'img elements with alt attributes having URLs as their entire values',
|
|
63
|
-
captionLoc: 'caption elements that are not first children of table elements',
|
|
64
|
-
datalistRef: 'elements with ambiguous or missing referenced datalist elements',
|
|
65
|
-
secHeading: 'headings that violate the logical level order in their sectioning containers',
|
|
66
|
-
textSem: 'semantically vague elements i, b, and/or small',
|
|
67
64
|
adbID: 'elements with ambiguous or missing referenced descriptions',
|
|
68
|
-
imageLink: 'links with image files as their destinations',
|
|
69
|
-
legendLoc: 'legend elements that are not first children of fieldset elements',
|
|
70
|
-
optRoleSel: 'Non-option elements with option roles that have no aria-selected attributes',
|
|
71
|
-
phOnly: 'input elements with placeholders but no accessible names',
|
|
72
65
|
allCaps: 'leaf elements with entirely upper-case text longer than 7 characters',
|
|
73
66
|
allHidden: 'page that is entirely or mostly hidden',
|
|
74
67
|
allSlanted: 'leaf elements with entirely italic or oblique text longer than 39 characters',
|
|
68
|
+
altScheme: 'img elements with alt attributes having URLs as their entire values',
|
|
75
69
|
attVal: 'duplicate attribute values',
|
|
76
70
|
autocomplete: 'name and email inputs without autocomplete attributes',
|
|
77
71
|
bulk: 'large count of visible elements',
|
|
78
72
|
buttonMenu: 'nonstandard keyboard navigation between items of button-controlled menus',
|
|
73
|
+
captionLoc: 'caption elements that are not first children of table elements',
|
|
74
|
+
datalistRef: 'elements with ambiguous or missing referenced datalist elements',
|
|
79
75
|
distortion: 'distorted text',
|
|
80
76
|
docType: 'document without a doctype property',
|
|
81
77
|
dupAtt: 'elements with duplicate attributes',
|
|
@@ -89,7 +85,9 @@ const evalRules = {
|
|
|
89
85
|
hover: 'hover-caused content changes',
|
|
90
86
|
hovInd: 'hover indication nonstandard',
|
|
91
87
|
hr: 'hr element instead of styles used for vertical segmentation',
|
|
88
|
+
imageLink: 'links with image files as their destinations',
|
|
92
89
|
labClash: 'labeling inconsistencies',
|
|
90
|
+
legendLoc: 'legend elements that are not first children of fieldset elements',
|
|
93
91
|
lineHeight: 'text with a line height less than 1.5 times its font size',
|
|
94
92
|
linkAmb: 'links with identical texts but different destinations',
|
|
95
93
|
linkExt: 'links that automatically open new windows',
|
|
@@ -101,13 +99,17 @@ const evalRules = {
|
|
|
101
99
|
motion: 'motion without user request',
|
|
102
100
|
nonTable: 'table elements used for layout',
|
|
103
101
|
opFoc: 'operable elements that are not Tab-focusable',
|
|
102
|
+
optRoleSel: 'Non-option elements with option roles that have no aria-selected attributes',
|
|
103
|
+
phOnly: 'input elements with placeholders but no accessible names',
|
|
104
104
|
pseudoP: 'adjacent br elements suspected of nonsemantically simulating p elements',
|
|
105
105
|
radioSet: 'radio buttons not grouped into standard field sets',
|
|
106
106
|
role: 'native-replacing explicit roles',
|
|
107
|
+
secHeading: 'headings that violate the logical level order in their sectioning containers',
|
|
107
108
|
styleDiff: 'style inconsistencies',
|
|
108
109
|
tabNav: 'nonstandard keyboard navigation between elements with the tab role',
|
|
109
110
|
targetSmall: 'buttons, inputs, and non-inline links smaller than 44 pixels wide and high',
|
|
110
111
|
targetTiny: 'buttons, inputs, and non-inline links smaller than 24 pixels wide and high',
|
|
112
|
+
textSem: 'semantically vague elements i, b, and/or small',
|
|
111
113
|
titledEl: 'title attributes on inappropriate elements',
|
|
112
114
|
zIndex: 'non-default Z indexes'
|
|
113
115
|
};
|
|
@@ -117,6 +119,37 @@ const etcRules = {
|
|
|
117
119
|
textNodes: 'data on specified text nodes',
|
|
118
120
|
title: 'page title',
|
|
119
121
|
};
|
|
122
|
+
// Tests that modify the page.
|
|
123
|
+
const contaminators = [
|
|
124
|
+
'buttonMenu',
|
|
125
|
+
'elements',
|
|
126
|
+
'focAll',
|
|
127
|
+
'focOp',
|
|
128
|
+
'focInd',
|
|
129
|
+
'hover',
|
|
130
|
+
'hovInd',
|
|
131
|
+
'motion',
|
|
132
|
+
'opFoc',
|
|
133
|
+
'tabNav',
|
|
134
|
+
'textNodes'
|
|
135
|
+
];
|
|
136
|
+
// Extraordinary time limits on rules.
|
|
137
|
+
const slowTestLimits = {
|
|
138
|
+
allCaps: 10,
|
|
139
|
+
buttonMenu: 15,
|
|
140
|
+
distortion: 10,
|
|
141
|
+
docType: 10,
|
|
142
|
+
focAll: 10,
|
|
143
|
+
focVis: 10,
|
|
144
|
+
hover: 10,
|
|
145
|
+
hovInd: 10,
|
|
146
|
+
labClash: 10,
|
|
147
|
+
lineHeight: 10,
|
|
148
|
+
motion: 15,
|
|
149
|
+
opFoc: 10,
|
|
150
|
+
tabNav: 10,
|
|
151
|
+
textSem: 10
|
|
152
|
+
};
|
|
120
153
|
|
|
121
154
|
// ######## FUNCTIONS
|
|
122
155
|
|
|
@@ -148,6 +181,7 @@ const wait = ms => {
|
|
|
148
181
|
};
|
|
149
182
|
// Conducts and reports Testaro tests.
|
|
150
183
|
exports.reporter = async (page, report, actIndex) => {
|
|
184
|
+
const url = await page.url();
|
|
151
185
|
const act = report.acts[actIndex];
|
|
152
186
|
const {args, stopOnFail, withItems} = act;
|
|
153
187
|
const argRules = args ? Object.keys(args) : null;
|
|
@@ -178,18 +212,44 @@ exports.reporter = async (page, report, actIndex) => {
|
|
|
178
212
|
) {
|
|
179
213
|
// Wait 1 second to prevent out-of-order logging with granular reporting.
|
|
180
214
|
await wait(1000);
|
|
181
|
-
|
|
182
|
-
const calledRules = rules[0] === 'y'
|
|
215
|
+
let calledRules = rules[0] === 'y'
|
|
183
216
|
? rules.slice(1)
|
|
184
217
|
: Object.keys(evalRules).filter(ruleID => ! rules.slice(1).includes(ruleID));
|
|
218
|
+
const calledContaminators = calledRules.filter(rule => contaminators.includes(rule)).sort();
|
|
219
|
+
const calledBenignRules = calledRules.filter(rule => ! contaminators.includes(rule)).sort();
|
|
185
220
|
const testTimes = [];
|
|
186
|
-
|
|
221
|
+
let contaminatorsStarted = false;
|
|
222
|
+
// Starting with the noncontaminators, for each rule invoked:
|
|
223
|
+
for (const rule of calledBenignRules.concat(calledContaminators)) {
|
|
224
|
+
const pageClosed = page.isClosed();
|
|
225
|
+
const isContaminator = contaminators.includes(rule);
|
|
226
|
+
// If it is a contaminator other than the first one or the page has closed:
|
|
227
|
+
if (contaminatorsStarted || pageClosed) {
|
|
228
|
+
// If the page has closed:
|
|
229
|
+
if (pageClosed) {
|
|
230
|
+
// Report this.
|
|
231
|
+
console.log(`WARNING: Relaunching browser for test ${rule} after abnormal closure`);
|
|
232
|
+
}
|
|
233
|
+
// Replace the browser and the page and navigate to the target.
|
|
234
|
+
await launch(
|
|
235
|
+
report,
|
|
236
|
+
process.env.DEBUG === 'true',
|
|
237
|
+
Number.parseInt(process.env.WAITS) || 0,
|
|
238
|
+
report.browserID,
|
|
239
|
+
url
|
|
240
|
+
);
|
|
241
|
+
page = require('../run').page;
|
|
242
|
+
}
|
|
243
|
+
// If it is a contaminator, ensure that future tests use new browsers.
|
|
244
|
+
if (isContaminator) {
|
|
245
|
+
contaminatorsStarted = true;
|
|
246
|
+
}
|
|
187
247
|
// Initialize an argument array.
|
|
188
248
|
const ruleArgs = [page, withItems];
|
|
189
|
-
// If the rule is defined with JavaScript or JSON but not both:
|
|
190
249
|
const ruleFileNames = await fs.readdir(`${__dirname}/../testaro`);
|
|
191
250
|
const isJS = ruleFileNames.includes(`${rule}.js`);
|
|
192
251
|
const isJSON = ruleFileNames.includes(`${rule}.json`);
|
|
252
|
+
// If the rule is defined with JavaScript or JSON but not both:
|
|
193
253
|
if ((isJS || isJSON) && ! (isJS && isJSON)) {
|
|
194
254
|
// If with JavaScript and it has extra arguments:
|
|
195
255
|
if (isJS && argRules && argRules.includes(rule)) {
|
|
@@ -204,8 +264,10 @@ exports.reporter = async (page, report, actIndex) => {
|
|
|
204
264
|
result[rule].what = what;
|
|
205
265
|
const startTime = Date.now();
|
|
206
266
|
try {
|
|
207
|
-
// Apply a
|
|
267
|
+
// Apply a time limit to the test.
|
|
268
|
+
const timeLimit = 1000 * (slowTestLimits[rule] ?? 5);
|
|
208
269
|
let timeout;
|
|
270
|
+
// If the time limit expires during the test:
|
|
209
271
|
const timer = new Promise(resolve => {
|
|
210
272
|
timeout = setTimeout(() => {
|
|
211
273
|
// Add data about the test, including its prevention, to the result.
|
|
@@ -216,24 +278,26 @@ exports.reporter = async (page, report, actIndex) => {
|
|
|
216
278
|
result[rule].standardInstances = [];
|
|
217
279
|
console.log(`ERROR: Test of testaro rule ${rule} timed out`);
|
|
218
280
|
resolve({timedOut: true});
|
|
219
|
-
},
|
|
281
|
+
}, timeLimit);
|
|
220
282
|
});
|
|
283
|
+
// Perform the test, subject to the time limit.
|
|
221
284
|
const ruleReport = isJS
|
|
222
285
|
? require(`../testaro/${rule}`).reporter(... ruleArgs)
|
|
223
286
|
: jsonTest(rule, ruleArgs);
|
|
224
|
-
|
|
287
|
+
// Get the test result or a timeout result.
|
|
288
|
+
const ruleOrTimeoutReport = await Promise.race([timer, ruleReport]);
|
|
225
289
|
clearTimeout(timeout);
|
|
226
|
-
// If the test was completed
|
|
227
|
-
if (!
|
|
290
|
+
// If the test was completed:
|
|
291
|
+
if (! ruleOrTimeoutReport.timedOut) {
|
|
228
292
|
// Add data from the test to the result.
|
|
229
293
|
const endTime = Date.now();
|
|
230
294
|
testTimes.push([rule, Math.round((endTime - startTime) / 1000)]);
|
|
231
|
-
Object.keys(
|
|
232
|
-
result[rule][key] =
|
|
295
|
+
Object.keys(ruleOrTimeoutReport).forEach(key => {
|
|
296
|
+
result[rule][key] = ruleOrTimeoutReport[key];
|
|
233
297
|
});
|
|
234
298
|
result[rule].totals = result[rule].totals.map(total => Math.round(total));
|
|
235
299
|
// If testing is to stop after a failure and the page failed the test:
|
|
236
|
-
if (stopOnFail &&
|
|
300
|
+
if (stopOnFail && ruleOrTimeoutReport.totals.some(total => total)) {
|
|
237
301
|
// Stop testing.
|
|
238
302
|
break;
|
|
239
303
|
}
|
|
@@ -241,7 +305,7 @@ exports.reporter = async (page, report, actIndex) => {
|
|
|
241
305
|
}
|
|
242
306
|
// If an error is thrown by the test:
|
|
243
307
|
catch(error) {
|
|
244
|
-
// Report
|
|
308
|
+
// Report the error.
|
|
245
309
|
data.rulePreventions.push(rule);
|
|
246
310
|
data.rulePreventionMessages[rule] = error.message;
|
|
247
311
|
console.log(`ERROR: Test of testaro rule ${rule} prevented (${error.message})`);
|
|
@@ -254,6 +318,7 @@ exports.reporter = async (page, report, actIndex) => {
|
|
|
254
318
|
console.log(`ERROR: Rule ${rule} not validly defined`);
|
|
255
319
|
}
|
|
256
320
|
}
|
|
321
|
+
// Record the test times in descending order.
|
|
257
322
|
testTimes.sort((a, b) => b[1] - a[1]).forEach(pair => {
|
|
258
323
|
data.ruleTestTimes[pair[0]] = pair[1];
|
|
259
324
|
});
|