testaro 4.9.0 → 4.10.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
CHANGED
package/tests/styleDiff.js
CHANGED
|
@@ -6,145 +6,148 @@
|
|
|
6
6
|
particular style properties, listed in the 'mainStyles' and 'headingStyles' arrays.
|
|
7
7
|
*/
|
|
8
8
|
exports.reporter = async (page, withItems) => {
|
|
9
|
-
// Get an object with arrays of list and adjacent links as properties.
|
|
9
|
+
// Get an object with arrays of list links and adjacent links as properties.
|
|
10
10
|
const linkTypes = await require('../procs/linksByType').linksByType(page);
|
|
11
11
|
return await page.$eval('body', (body, args) => {
|
|
12
|
-
const
|
|
13
|
-
const
|
|
12
|
+
const linkTypes = args[0];
|
|
13
|
+
const withItems = args[1];
|
|
14
14
|
// Identify the settable style properties to be compared for all tag names.
|
|
15
15
|
const mainStyles = [
|
|
16
|
-
'borderStyle',
|
|
17
|
-
'borderWidth',
|
|
18
16
|
'fontStyle',
|
|
19
17
|
'fontWeight',
|
|
18
|
+
'opacity',
|
|
19
|
+
'textDecorationLine',
|
|
20
|
+
'textDecorationStyle',
|
|
21
|
+
'textDecorationThickness'
|
|
22
|
+
];
|
|
23
|
+
// Identify those only for buttons.
|
|
24
|
+
const buttonStyles = [
|
|
25
|
+
'borderStyle',
|
|
26
|
+
'borderWidth',
|
|
27
|
+
'height',
|
|
20
28
|
'lineHeight',
|
|
21
29
|
'maxHeight',
|
|
22
30
|
'maxWidth',
|
|
23
31
|
'minHeight',
|
|
24
32
|
'minWidth',
|
|
25
|
-
'opacity',
|
|
26
33
|
'outlineOffset',
|
|
27
34
|
'outlineStyle',
|
|
28
|
-
'outlineWidth'
|
|
29
|
-
'textDecorationLine',
|
|
30
|
-
'textDecorationStyle',
|
|
31
|
-
'textDecorationThickness'
|
|
35
|
+
'outlineWidth'
|
|
32
36
|
];
|
|
33
37
|
// Identify those for headings.
|
|
34
38
|
const headingStyles = [
|
|
39
|
+
'color',
|
|
35
40
|
'fontSize'
|
|
36
41
|
];
|
|
42
|
+
// Identify those for list links.
|
|
43
|
+
const listLinkStyles = [
|
|
44
|
+
'color',
|
|
45
|
+
'fontSize',
|
|
46
|
+
'lineHeight'
|
|
47
|
+
];
|
|
37
48
|
// Initialize the data to be returned.
|
|
38
49
|
const data = {
|
|
39
50
|
mainStyles,
|
|
51
|
+
buttonStyles,
|
|
40
52
|
headingStyles,
|
|
53
|
+
listLinkStyles,
|
|
41
54
|
totals: {}
|
|
42
55
|
};
|
|
43
56
|
if (withItems) {
|
|
44
57
|
data.items = {};
|
|
45
58
|
}
|
|
46
|
-
//
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
// Initialize an object of elements to be analyzed, including links.
|
|
60
|
+
const elements = {
|
|
61
|
+
buttons: [],
|
|
62
|
+
headings: {
|
|
63
|
+
h1: [],
|
|
64
|
+
h2: [],
|
|
65
|
+
h3: [],
|
|
66
|
+
h4: [],
|
|
67
|
+
h5: [],
|
|
68
|
+
h6: []
|
|
69
|
+
},
|
|
70
|
+
links: {
|
|
71
|
+
adjacent: linkTypes.adjacent,
|
|
72
|
+
list: linkTypes.list
|
|
56
73
|
}
|
|
57
74
|
};
|
|
75
|
+
// Identify the heading tag names to be analyzed.
|
|
76
|
+
const headingNames = Object.keys(elements.headings);
|
|
77
|
+
// Add the buttons to the object.
|
|
78
|
+
elements.buttons = Array.from(body.querySelectorAll('button, input[type=button]'));
|
|
58
79
|
// For each heading tag name:
|
|
59
80
|
headingNames.forEach(tagName => {
|
|
60
81
|
// Add its elements to the object.
|
|
61
|
-
|
|
62
|
-
});
|
|
63
|
-
// For each other tag name:
|
|
64
|
-
otherNames.forEach(tagName => {
|
|
65
|
-
// Add its elements to the object.
|
|
66
|
-
elementClasses.other[tagName] = Array.from(body.getElementsByTagName(tagName));
|
|
82
|
+
elements.headings[tagName] = Array.from(body.getElementsByTagName(tagName));
|
|
67
83
|
});
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
84
|
+
// Tabulates the distribution of style properties for elements of a type.
|
|
85
|
+
const tallyStyles = (typeName, elements, typeStyles, withItems) => {
|
|
86
|
+
// If there are any elements:
|
|
87
|
+
const elementCount = elements.length;
|
|
88
|
+
if (elementCount) {
|
|
89
|
+
const styleProps = {};
|
|
90
|
+
const styleTexts = {};
|
|
91
|
+
// For each element:
|
|
92
|
+
elements.forEach(element => {
|
|
93
|
+
// Get its values on the style properties to be compared.
|
|
94
|
+
const styleDec = window.getComputedStyle(element);
|
|
95
|
+
const style = {};
|
|
96
|
+
const styles = mainStyles.concat(typeStyles);
|
|
97
|
+
styles.forEach(styleName => {
|
|
98
|
+
style[styleName] = styleDec[styleName];
|
|
99
|
+
});
|
|
100
|
+
// Get a text representation of the style, limited to those properties.
|
|
101
|
+
const styleText = JSON.stringify(style);
|
|
102
|
+
// Increment the total of elements with that style.
|
|
103
|
+
styleTexts[styleText] = ++styleTexts[styleText] || 1;
|
|
104
|
+
// If details are to be reported:
|
|
78
105
|
if (withItems) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
// For each of them:
|
|
84
|
-
elements.forEach(element => {
|
|
85
|
-
// Get its values on the style properties to be compared.
|
|
86
|
-
const styleDec = window.getComputedStyle(element);
|
|
87
|
-
const style = {};
|
|
88
|
-
// Identify the styles to be compared.
|
|
89
|
-
const styles = mainStyles;
|
|
90
|
-
if (superClass === 'headings') {
|
|
91
|
-
styles.push(...headingStyles);
|
|
106
|
+
// Add the element’s values and text to the style details.
|
|
107
|
+
if (! styleProps[typeName]) {
|
|
108
|
+
styleProps[typeName] = {};
|
|
92
109
|
}
|
|
110
|
+
const elementText = element.textContent.trim().replace(/\s/g, ' ');
|
|
111
|
+
// For each style property being compared:
|
|
93
112
|
styles.forEach(styleName => {
|
|
94
|
-
|
|
113
|
+
if (! styleProps[typeName][styleName]) {
|
|
114
|
+
styleProps[typeName][styleName] = {};
|
|
115
|
+
}
|
|
116
|
+
if (! styleProps[typeName][styleName][style[styleName]]) {
|
|
117
|
+
styleProps[typeName][styleName][style[styleName]] = [];
|
|
118
|
+
}
|
|
119
|
+
styleProps[typeName][styleName][style[styleName]].push(elementText);
|
|
95
120
|
});
|
|
96
|
-
// Get a text representation of the style.
|
|
97
|
-
const styleText = JSON.stringify(style);
|
|
98
|
-
// Increment the total of elements with that style declaration.
|
|
99
|
-
styleTexts[styleText] = ++styleTexts[styleText] || 1;
|
|
100
|
-
// If details are required:
|
|
101
|
-
if (withItems) {
|
|
102
|
-
// For each style property:
|
|
103
|
-
styles.forEach(styleName => {
|
|
104
|
-
const styleValue = style[styleName];
|
|
105
|
-
// Increment the total of elements with the same value on it as the element.
|
|
106
|
-
if (styleProps[styleName]) {
|
|
107
|
-
styleProps[styleName][styleValue] = ++styleProps[styleName][styleValue] || 1;
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
styleProps[styleName] = {[styleValue]: 1};
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
// Add the total to the result.
|
|
116
|
-
data.totals[tagName] = {total: elementCount};
|
|
117
|
-
const styleCounts = Object.values(styleTexts);
|
|
118
|
-
// If the elements in the element class differ in style:
|
|
119
|
-
if (styleCounts.length > 1) {
|
|
120
|
-
// Add the distribution of its style counts to the result.
|
|
121
|
-
data.totals[tagName].subtotals = styleCounts.sort((a, b) => b - a);
|
|
122
121
|
}
|
|
123
|
-
|
|
122
|
+
});
|
|
123
|
+
// Add the total to the result.
|
|
124
|
+
data.totals[typeName] = {total: elementCount};
|
|
125
|
+
const styleCounts = Object.values(styleTexts);
|
|
126
|
+
// If the elements of the type differ in style:
|
|
127
|
+
if (styleCounts.length > 1) {
|
|
128
|
+
// Add the distribution of its style counts to the result.
|
|
129
|
+
data.totals[typeName].subtotals = styleCounts.sort((a, b) => b - a);
|
|
130
|
+
// If details are to be reported:
|
|
124
131
|
if (withItems) {
|
|
125
|
-
//
|
|
126
|
-
Object.keys(styleProps).forEach(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
delete styleProps[styleProp];
|
|
130
|
-
}
|
|
131
|
-
// Otherwise:
|
|
132
|
-
else {
|
|
133
|
-
if (! data.items[tagName][styleProp]) {
|
|
134
|
-
data.items[tagName][styleProp] = {};
|
|
135
|
-
}
|
|
136
|
-
// Sort the values in order of decreasing count.
|
|
137
|
-
const sortedEntries = Object.entries(styleProps[styleProp]).sort((a, b) => b[1] - a[1]);
|
|
138
|
-
sortedEntries.forEach(entry => {
|
|
139
|
-
const propData = data.items[tagName][styleProp];
|
|
140
|
-
propData[entry[0]] = (propData[entry[0]] || 0) + entry[1];
|
|
141
|
-
});
|
|
132
|
+
// Delete the data on uniform style properties.
|
|
133
|
+
Object.keys(styleProps[typeName]).forEach(styleName => {
|
|
134
|
+
if (Object.keys(styleProps[typeName][styleName]).length === 1) {
|
|
135
|
+
delete styleProps[typeName][styleName];
|
|
142
136
|
}
|
|
143
137
|
});
|
|
138
|
+
// Add the element values and texts to the result.
|
|
139
|
+
data.items[typeName] = styleProps[typeName];
|
|
144
140
|
}
|
|
145
141
|
}
|
|
146
|
-
}
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
// Report the style-property distributions for the element types.
|
|
145
|
+
tallyStyles('button', elements.buttons, buttonStyles, withItems);
|
|
146
|
+
tallyStyles('adjacentLink', elements.links.adjacent, [], withItems);
|
|
147
|
+
tallyStyles('listLink', elements.links.list, listLinkStyles, withItems);
|
|
148
|
+
headingNames.forEach(headingName => {
|
|
149
|
+
tallyStyles(headingName, elements.headings[headingName], headingStyles, withItems);
|
|
147
150
|
});
|
|
148
151
|
return {result: data};
|
|
149
|
-
}, [
|
|
152
|
+
}, [linkTypes, withItems]);
|
|
150
153
|
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// app.js
|
|
2
|
+
// Validator for Testaro tests.
|
|
3
|
+
|
|
4
|
+
const fs = require('fs').promises;
|
|
5
|
+
const {handleRequest} = require(`${__dirname}/../../run`);
|
|
6
|
+
const validateTests = async () => {
|
|
7
|
+
const totals = {
|
|
8
|
+
attempts: 0,
|
|
9
|
+
successes: 0
|
|
10
|
+
};
|
|
11
|
+
const scriptFileNames = await fs.readdir(`${__dirname}/../tests/scripts`);
|
|
12
|
+
for (const scriptFileName of scriptFileNames.filter(fileName => fileName === 'styleDiff.json')) {
|
|
13
|
+
const rawScriptJSON = await fs
|
|
14
|
+
.readFile(`${__dirname}/../tests/scripts/${scriptFileName}`, 'utf8');
|
|
15
|
+
const scriptJSON = rawScriptJSON
|
|
16
|
+
.replace(/__targets__/g, `file://${__dirname}/../tests/targets`);
|
|
17
|
+
const script = JSON.parse(scriptJSON);
|
|
18
|
+
const report = {script};
|
|
19
|
+
report.log = [];
|
|
20
|
+
report.acts = [];
|
|
21
|
+
await handleRequest(report);
|
|
22
|
+
const {log, acts} = report;
|
|
23
|
+
if (log.length === 2 && log[1].event === 'endTime' && /^\d{4}-.+$/.test(log[1].value)) {
|
|
24
|
+
console.log('Success: Log has been correctly populated');
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
console.log('Failure: Log empty or invalid');
|
|
28
|
+
console.log(JSON.stringify(log, null, 2));
|
|
29
|
+
}
|
|
30
|
+
if (
|
|
31
|
+
acts.length === script.commands.length
|
|
32
|
+
&& acts.every(
|
|
33
|
+
act => act.type && act.type === 'test'
|
|
34
|
+
? act.result && act.result.failureCount !== undefined
|
|
35
|
+
: true
|
|
36
|
+
)
|
|
37
|
+
) {
|
|
38
|
+
totals.attempts++;
|
|
39
|
+
totals.successes++;
|
|
40
|
+
console.log('Success: Reports have been correctly populated');
|
|
41
|
+
if (acts.every(
|
|
42
|
+
act => act.type === 'test' ? act.result.failureCount === 0 : true
|
|
43
|
+
)) {
|
|
44
|
+
totals.attempts++;
|
|
45
|
+
totals.successes++;
|
|
46
|
+
console.log('Success: No failures');
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
totals.attempts++;
|
|
50
|
+
console.log('Failure: At least one test has at least one failure');
|
|
51
|
+
console.log(JSON.stringify(acts, null, 2));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
totals.attempts++;
|
|
56
|
+
console.log('Failure: Reports empty or invalid');
|
|
57
|
+
console.log(JSON.stringify(acts, null, 2));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
console.log(`Grand totals: attempts ${totals.attempts}, successes ${totals.successes}`);
|
|
61
|
+
};
|
|
62
|
+
validateTests();
|
|
@@ -15,16 +15,16 @@
|
|
|
15
15
|
{
|
|
16
16
|
"type": "test",
|
|
17
17
|
"which": "styleDiff",
|
|
18
|
+
"withItems": true,
|
|
18
19
|
"what": "styleDiff",
|
|
19
|
-
"withItems": false,
|
|
20
20
|
"expect": [
|
|
21
|
-
["totals.
|
|
22
|
-
["totals.
|
|
21
|
+
["totals.adjacentLink.total", "=", 2],
|
|
22
|
+
["totals.listLink.total", "=", 2],
|
|
23
23
|
["totals.button.total", "=", 2],
|
|
24
24
|
["totals.h1.total", "=", 1],
|
|
25
25
|
["totals.h2.total", "=", 4],
|
|
26
|
-
["totals.
|
|
27
|
-
["totals.
|
|
26
|
+
["totals.adjacentLink.subtotals"],
|
|
27
|
+
["totals.listLink.subtotals"],
|
|
28
28
|
["totals.button.subtotals"],
|
|
29
29
|
["totals.h1.subtotals"],
|
|
30
30
|
["totals.h2.subtotals"]
|
|
@@ -38,23 +38,24 @@
|
|
|
38
38
|
{
|
|
39
39
|
"type": "test",
|
|
40
40
|
"which": "styleDiff",
|
|
41
|
+
"withItems": true,
|
|
41
42
|
"what": "styleDiff",
|
|
42
|
-
"withItems": false,
|
|
43
43
|
"expect": [
|
|
44
|
-
["totals.
|
|
45
|
-
["totals.
|
|
44
|
+
["totals.adjacentLink.total", "=", 2],
|
|
45
|
+
["totals.listLink.total", "=", 2],
|
|
46
46
|
["totals.button.total", "=", 2],
|
|
47
47
|
["totals.h1.total", "=", 1],
|
|
48
48
|
["totals.h2.total", "=", 4],
|
|
49
|
-
["totals.
|
|
50
|
-
["totals.
|
|
51
|
-
["totals.
|
|
52
|
-
["totals.
|
|
49
|
+
["totals.adjacentLink.subtotals.0", "=", 1],
|
|
50
|
+
["totals.adjacentLink.subtotals.1", "=", 1],
|
|
51
|
+
["totals.listLink.subtotals.0", "=", 1],
|
|
52
|
+
["totals.listLink.subtotals.1", "=", 1],
|
|
53
53
|
["totals.button.subtotals.0", "=", 1],
|
|
54
54
|
["totals.button.subtotals.1", "=", 1],
|
|
55
55
|
["totals.h1.subtotals"],
|
|
56
56
|
["totals.h2.subtotals.0", "=", 3],
|
|
57
|
-
["totals.h2.subtotals.1", "=", 1]
|
|
57
|
+
["totals.h2.subtotals.1", "=", 1],
|
|
58
|
+
["items.adjacentLink.textDecorationStyle.double.0", "=", "French information"]
|
|
58
59
|
]
|
|
59
60
|
}
|
|
60
61
|
]
|
|
@@ -19,17 +19,17 @@
|
|
|
19
19
|
<main>
|
|
20
20
|
<h1>Page with inconsistent styles</h1>
|
|
21
21
|
<h2>Inline links</h2>
|
|
22
|
-
<p>This paragraph contains
|
|
22
|
+
<p>This paragraph contains adjacent links to <a href="https://en.wikipedia.org">English information</a> and <a style="text-decoration-style: double" href="https://fr.wikipedia.org">French information</a>. They have different styles.</p>
|
|
23
23
|
<h2>Block links</h2>
|
|
24
|
-
<p>The following links are
|
|
24
|
+
<p>The following links are list links. They have different styles.</p>
|
|
25
25
|
<ul>
|
|
26
26
|
<li><a style="font-weight: 700" href="https://sp.wikipedia.org">Spanish information</a></li>
|
|
27
27
|
<li><a href="https://eo.wikipedia.org">Esperanto information</a></li>
|
|
28
28
|
</ul>
|
|
29
29
|
<h2>Buttons</h2>
|
|
30
|
-
<p>This paragraph contains two buttons with
|
|
30
|
+
<p>This paragraph contains two buttons with different custom styles. <button type="button">Wikipedia</button> <button type="button" style="border-width: 3px">Wiktionary</button></p>
|
|
31
31
|
<h2 class="italic">Headings</h2>
|
|
32
|
-
<p>This page contains 4 <code>h2</code> headings. This one has a deviant style.</p>
|
|
32
|
+
<p>This page contains 4 <code>h2</code> headings. This last one has a deviant style.</p>
|
|
33
33
|
</main>
|
|
34
34
|
</body>
|
|
35
35
|
</html>
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
<main>
|
|
21
21
|
<h1>Page with consistent styles</h1>
|
|
22
22
|
<h2>Inline links</h2>
|
|
23
|
-
<p>This paragraph contains
|
|
23
|
+
<p>This paragraph contains adjacent links to <a href="https://en.wikipedia.org">English information</a> and <a href="https://fr.wikipedia.org">French information</a>. They both have a default style.</p>
|
|
24
24
|
<h2>Block links</h2>
|
|
25
|
-
<p>The following links are
|
|
25
|
+
<p>The following links are list links. They have a uniform custom underline.</p>
|
|
26
26
|
<ul class="dotted">
|
|
27
27
|
<li><a href="https://sp.wikipedia.org">Spanish information</a></li>
|
|
28
28
|
<li><a href="https://eo.wikipedia.org">Esperanto information</a></li>
|