testaro 69.1.0 → 69.3.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/procs/catalog.js +92 -66
- package/procs/doActs.js +0 -2
- package/procs/xPath.js +1 -1
- package/tests/axe.js +2 -0
package/package.json
CHANGED
package/procs/catalog.js
CHANGED
|
@@ -9,6 +9,16 @@
|
|
|
9
9
|
/*
|
|
10
10
|
catalog
|
|
11
11
|
Creates and returns a catalog of a target.
|
|
12
|
+
|
|
13
|
+
A catalog is an object with one property per element in the target. Each property has the element index as its key and has an object as its value, with 7 properties:
|
|
14
|
+
- tagName
|
|
15
|
+
- id
|
|
16
|
+
- startTag
|
|
17
|
+
- text
|
|
18
|
+
- textLinkable
|
|
19
|
+
- boxID
|
|
20
|
+
- pathID
|
|
21
|
+
- headingIndex
|
|
12
22
|
*/
|
|
13
23
|
|
|
14
24
|
// IMPORTS
|
|
@@ -34,78 +44,90 @@ exports.getCatalog = async report => {
|
|
|
34
44
|
});
|
|
35
45
|
// If the launch and navigation succeeded:
|
|
36
46
|
if (page) {
|
|
37
|
-
//
|
|
47
|
+
// Get a catalog of the elements in the page.
|
|
38
48
|
const catalog = await page.evaluate(() => {
|
|
39
|
-
// Adds an element property to a catalog and returns its value.
|
|
40
|
-
const addToCatalog = (elementIndex, catalog, propertyName, value) => {
|
|
41
|
-
if (value) {
|
|
42
|
-
catalog[propertyName] ??= {};
|
|
43
|
-
catalog[propertyName][value] ??= [];
|
|
44
|
-
catalog[propertyName][value].push(elementIndex);
|
|
45
|
-
return value;
|
|
46
|
-
}
|
|
47
|
-
return '';
|
|
48
|
-
};
|
|
49
49
|
const elements = Array.from(document.querySelectorAll('*'));
|
|
50
50
|
// Initialize a catalog.
|
|
51
|
-
const cat = {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
text: {},
|
|
57
|
-
boxID: {},
|
|
58
|
-
pathID: {}
|
|
59
|
-
};
|
|
51
|
+
const cat = {};
|
|
52
|
+
// Initialize a directory of text fragments.
|
|
53
|
+
const texts = {};
|
|
54
|
+
// Initialize the index of the current heading.
|
|
55
|
+
let headingIndex = '';
|
|
60
56
|
// For each element in the page:
|
|
61
57
|
for (const index in elements) {
|
|
62
58
|
const element = elements[index];
|
|
63
|
-
//
|
|
64
|
-
const tagName =
|
|
65
|
-
|
|
66
|
-
const startTag =
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
59
|
+
// Get its ID and tag name.
|
|
60
|
+
const {id, tagName} = element;
|
|
61
|
+
// Get its start tag.
|
|
62
|
+
const startTag = element.outerHTML?.replace(/^.*?</s, '<').replace(/>.*$/s, '>') ?? '';
|
|
63
|
+
// Get whether it is eligible for text-fragment acquisition.
|
|
64
|
+
const isTextable = element.closest('body')
|
|
65
|
+
&& ! element.closest('svg')
|
|
66
|
+
&& ! ['SCRIPT', 'STYLE', 'svg'].includes(element.tagName);
|
|
67
|
+
const innerText = isTextable
|
|
68
|
+
? element.innerText.trim() || (element.parentElement?.innerText?.trim() ?? '')
|
|
69
|
+
: '';
|
|
70
|
+
let text = '';
|
|
71
|
+
// If it is eligible and has an inner text:
|
|
72
|
+
if (innerText) {
|
|
73
|
+
const segments = innerText?.split('\n') ?? [];
|
|
74
|
+
const tidySegments = segments.map(segment => segment.trim().replace(/\s+/g, ' '));
|
|
75
|
+
const neededSegments = tidySegments.filter(segment => segment.length);
|
|
76
|
+
neededSegments.splice(1, neededSegments.length - 2);
|
|
77
|
+
// Get its text fragments.
|
|
78
|
+
text = neededSegments.join('\n');
|
|
79
|
+
// Add its index to the directory of text fragments.
|
|
80
|
+
texts[text] ??= [];
|
|
81
|
+
texts[text].push(index);
|
|
82
|
+
}
|
|
78
83
|
const domRect = element.getBoundingClientRect();
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
84
|
+
// Get its box ID.
|
|
85
|
+
const boxID = domRect
|
|
86
|
+
? ['x', 'y', 'width', 'height'].map(key => Math.round(domRect[key])).join(':')
|
|
87
|
+
: '';
|
|
88
|
+
// Get its path ID.
|
|
89
|
+
const pathID = window.getXPath(element);
|
|
90
|
+
// If it is a heading that nullifies an existing current heading index:
|
|
91
|
+
if (
|
|
92
|
+
headingIndex
|
|
93
|
+
&& ['H1', 'H2', 'H3', 'H4', 'H5', 'H6'].includes(tagName)
|
|
94
|
+
&& cat[headingIndex].tagName >= tagName
|
|
95
|
+
) {
|
|
96
|
+
// Nullify the current heading index.
|
|
97
|
+
headingIndex = '';
|
|
98
|
+
}
|
|
99
|
+
// Add an entry for it to the catalog.
|
|
100
|
+
cat[index] = {
|
|
90
101
|
tagName,
|
|
91
|
-
id,
|
|
102
|
+
id: id || '',
|
|
92
103
|
startTag,
|
|
93
104
|
text,
|
|
94
105
|
textLinkable: false,
|
|
95
106
|
boxID,
|
|
96
|
-
pathID
|
|
107
|
+
pathID,
|
|
108
|
+
headingIndex
|
|
97
109
|
};
|
|
110
|
+
// If the element is a heading:
|
|
111
|
+
if (['H1', 'H2', 'H3', 'H4', 'H5', 'H6'].includes(tagName)) {
|
|
112
|
+
// Assign its index to the current heading index.
|
|
113
|
+
headingIndex = index;
|
|
114
|
+
}
|
|
115
|
+
// If the element has a path ID:
|
|
116
|
+
if (pathID) {
|
|
117
|
+
// Add it to the temporary path ID directory in the catalog.
|
|
118
|
+
cat.pathID ??= {};
|
|
119
|
+
cat.pathID[pathID] = index;
|
|
120
|
+
}
|
|
98
121
|
}
|
|
99
122
|
// For each text in the catalog:
|
|
100
|
-
Object.keys(
|
|
101
|
-
const textElementIndexes =
|
|
102
|
-
// If every element that has it is in the same subtree, so is page-unique:
|
|
123
|
+
Object.keys(texts).forEach(text => {
|
|
124
|
+
const textElementIndexes = texts[text].sort((a, b) => Number(a) - Number(b));
|
|
125
|
+
// If every element that has it is in the same subtree, so the text is page-unique:
|
|
103
126
|
if (
|
|
104
127
|
textElementIndexes.slice(0, -1).every(
|
|
105
|
-
(elementIndex, index) => cat
|
|
106
|
-
.element[textElementIndexes[index + 1]]
|
|
128
|
+
(elementIndex, index) => cat[textElementIndexes[index + 1]]
|
|
107
129
|
.pathID
|
|
108
|
-
.startsWith(cat
|
|
130
|
+
.startsWith(cat[elementIndex].pathID)
|
|
109
131
|
)
|
|
110
132
|
) {
|
|
111
133
|
// For each element that has it:
|
|
@@ -113,10 +135,10 @@ exports.getCatalog = async report => {
|
|
|
113
135
|
// If it is not in the head, a script, a style, or a noscript element:
|
|
114
136
|
if (
|
|
115
137
|
! ['/head[1]', '/script[', '/style[', '/noscript[']
|
|
116
|
-
.some(excluder => cat
|
|
138
|
+
.some(excluder => cat[index].pathID.includes(excluder))
|
|
117
139
|
) {
|
|
118
140
|
// Mark it as linkable in the element data in the catalog.
|
|
119
|
-
cat
|
|
141
|
+
cat[index].textLinkable = true;
|
|
120
142
|
}
|
|
121
143
|
});
|
|
122
144
|
}
|
|
@@ -136,9 +158,9 @@ exports.getCatalog = async report => {
|
|
|
136
158
|
console.log('ERROR: Job omits browser ID or target URL, preventing catalog creation');
|
|
137
159
|
return {};
|
|
138
160
|
};
|
|
139
|
-
// Prunes
|
|
161
|
+
// Prunes a catalog.
|
|
140
162
|
exports.pruneCatalog = report => {
|
|
141
|
-
const {acts} = report;
|
|
163
|
+
const {acts, catalog} = report;
|
|
142
164
|
const citedElementIndexes = new Set();
|
|
143
165
|
// For each act in the report:
|
|
144
166
|
acts.forEach(act => {
|
|
@@ -152,22 +174,26 @@ exports.pruneCatalog = report => {
|
|
|
152
174
|
if (catalogIndex) {
|
|
153
175
|
// Ensure the index is classified as cited.
|
|
154
176
|
citedElementIndexes.add(catalogIndex);
|
|
177
|
+
const {headingIndex} = catalog[catalogIndex];
|
|
178
|
+
// If the catalog item has a heading index:
|
|
179
|
+
if (headingIndex) {
|
|
180
|
+
// Ensure it, too, is classified as cited.
|
|
181
|
+
citedElementIndexes.add(headingIndex);
|
|
182
|
+
}
|
|
155
183
|
}
|
|
156
184
|
});
|
|
157
185
|
}
|
|
158
186
|
});
|
|
159
|
-
|
|
187
|
+
// Delete the temporary path ID directory.
|
|
188
|
+
delete catalog.pathID;
|
|
160
189
|
// For each element in the catalog:
|
|
161
|
-
const {catalog} = report;
|
|
162
190
|
Object.keys(catalog).forEach(elementIndex => {
|
|
163
|
-
// If it is cited by
|
|
164
|
-
if (citedElementIndexes.has(elementIndex)) {
|
|
165
|
-
//
|
|
166
|
-
|
|
191
|
+
// If it is not cited by any instance or by any cited element:
|
|
192
|
+
if (! citedElementIndexes.has(elementIndex)) {
|
|
193
|
+
// Delete it in the catalog.
|
|
194
|
+
delete catalog[elementIndex];
|
|
167
195
|
}
|
|
168
196
|
});
|
|
169
|
-
// Replace the catalog with the pruned catalog.
|
|
170
|
-
report.catalog = prunedCatalog;
|
|
171
197
|
};
|
|
172
198
|
// Adds a catalog index or, if necessary, an XPath to a proto-instance.
|
|
173
199
|
exports.addCatalogIndex = async (protoInstance, locator, catalog) => {
|
package/procs/doActs.js
CHANGED
|
@@ -1150,8 +1150,6 @@ exports.doActs = async (report, opts = {}) => {
|
|
|
1150
1150
|
console.log('Acts completed');
|
|
1151
1151
|
// If the results were standardized:
|
|
1152
1152
|
if (['also', 'only'].includes(standard)) {
|
|
1153
|
-
// Reassign the element property of the catalog to the catalog, deleting the rest.
|
|
1154
|
-
localReport.catalog = localReport.catalog.element;
|
|
1155
1153
|
// If the native results are not to be included in the report:
|
|
1156
1154
|
if (standard === 'only') {
|
|
1157
1155
|
// Remove them.
|
package/procs/xPath.js
CHANGED
|
@@ -57,6 +57,6 @@ exports.getAttributeXPath = html => {
|
|
|
57
57
|
};
|
|
58
58
|
// Gets a catalog index as a string from an XPath.
|
|
59
59
|
exports.getXPathCatalogIndex = (catalog, xPath) => {
|
|
60
|
-
const index = catalog.pathID[xPath]
|
|
60
|
+
const index = catalog.pathID[xPath] ?? '';
|
|
61
61
|
return index;
|
|
62
62
|
};
|
package/tests/axe.js
CHANGED
|
@@ -151,6 +151,8 @@ exports.reporter = async (page, report, actIndex) => {
|
|
|
151
151
|
// Get the ordinal severity of the suspicion.
|
|
152
152
|
const ordinalSeverity = severityWeights[node.impact]
|
|
153
153
|
+ (certainty === 'violations' ? 2 : 0);
|
|
154
|
+
// Increment the standard total.
|
|
155
|
+
standardResult.totals[ordinalSeverity]++;
|
|
154
156
|
// Get the XPath of the suspected element from its data-xpath attribute.
|
|
155
157
|
const xPath = getAttributeXPath(node.html);
|
|
156
158
|
// Get the catalog index of the suspected element from its XPath.
|