testaro 69.2.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 +86 -65
- 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,83 +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
|
-
cat,
|
|
69
|
-
'startTag',
|
|
70
|
-
element.outerHTML?.replace(/^.*?</s, '<').replace(/>.*$/s, '>') ?? ''
|
|
71
|
-
);
|
|
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.
|
|
72
64
|
const isTextable = element.closest('body')
|
|
73
65
|
&& ! element.closest('svg')
|
|
74
66
|
&& ! ['SCRIPT', 'STYLE', 'svg'].includes(element.tagName);
|
|
75
67
|
const innerText = isTextable
|
|
76
68
|
? element.innerText.trim() || (element.parentElement?.innerText?.trim() ?? '')
|
|
77
69
|
: '';
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
+
}
|
|
83
83
|
const domRect = element.getBoundingClientRect();
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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] = {
|
|
95
101
|
tagName,
|
|
96
|
-
id,
|
|
102
|
+
id: id || '',
|
|
97
103
|
startTag,
|
|
98
104
|
text,
|
|
99
105
|
textLinkable: false,
|
|
100
106
|
boxID,
|
|
101
|
-
pathID
|
|
107
|
+
pathID,
|
|
108
|
+
headingIndex
|
|
102
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
|
+
}
|
|
103
121
|
}
|
|
104
122
|
// For each text in the catalog:
|
|
105
|
-
Object.keys(
|
|
106
|
-
const textElementIndexes =
|
|
107
|
-
// 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:
|
|
108
126
|
if (
|
|
109
127
|
textElementIndexes.slice(0, -1).every(
|
|
110
|
-
(elementIndex, index) => cat
|
|
111
|
-
.element[textElementIndexes[index + 1]]
|
|
128
|
+
(elementIndex, index) => cat[textElementIndexes[index + 1]]
|
|
112
129
|
.pathID
|
|
113
|
-
.startsWith(cat
|
|
130
|
+
.startsWith(cat[elementIndex].pathID)
|
|
114
131
|
)
|
|
115
132
|
) {
|
|
116
133
|
// For each element that has it:
|
|
@@ -118,10 +135,10 @@ exports.getCatalog = async report => {
|
|
|
118
135
|
// If it is not in the head, a script, a style, or a noscript element:
|
|
119
136
|
if (
|
|
120
137
|
! ['/head[1]', '/script[', '/style[', '/noscript[']
|
|
121
|
-
.some(excluder => cat
|
|
138
|
+
.some(excluder => cat[index].pathID.includes(excluder))
|
|
122
139
|
) {
|
|
123
140
|
// Mark it as linkable in the element data in the catalog.
|
|
124
|
-
cat
|
|
141
|
+
cat[index].textLinkable = true;
|
|
125
142
|
}
|
|
126
143
|
});
|
|
127
144
|
}
|
|
@@ -141,9 +158,9 @@ exports.getCatalog = async report => {
|
|
|
141
158
|
console.log('ERROR: Job omits browser ID or target URL, preventing catalog creation');
|
|
142
159
|
return {};
|
|
143
160
|
};
|
|
144
|
-
// Prunes
|
|
161
|
+
// Prunes a catalog.
|
|
145
162
|
exports.pruneCatalog = report => {
|
|
146
|
-
const {acts} = report;
|
|
163
|
+
const {acts, catalog} = report;
|
|
147
164
|
const citedElementIndexes = new Set();
|
|
148
165
|
// For each act in the report:
|
|
149
166
|
acts.forEach(act => {
|
|
@@ -157,22 +174,26 @@ exports.pruneCatalog = report => {
|
|
|
157
174
|
if (catalogIndex) {
|
|
158
175
|
// Ensure the index is classified as cited.
|
|
159
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
|
+
}
|
|
160
183
|
}
|
|
161
184
|
});
|
|
162
185
|
}
|
|
163
186
|
});
|
|
164
|
-
|
|
187
|
+
// Delete the temporary path ID directory.
|
|
188
|
+
delete catalog.pathID;
|
|
165
189
|
// For each element in the catalog:
|
|
166
|
-
const {catalog} = report;
|
|
167
190
|
Object.keys(catalog).forEach(elementIndex => {
|
|
168
|
-
// If it is cited by
|
|
169
|
-
if (citedElementIndexes.has(elementIndex)) {
|
|
170
|
-
//
|
|
171
|
-
|
|
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];
|
|
172
195
|
}
|
|
173
196
|
});
|
|
174
|
-
// Replace the catalog with the pruned catalog.
|
|
175
|
-
report.catalog = prunedCatalog;
|
|
176
197
|
};
|
|
177
198
|
// Adds a catalog index or, if necessary, an XPath to a proto-instance.
|
|
178
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.
|