testaro 69.2.0 → 69.3.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testaro",
3
- "version": "69.2.0",
3
+ "version": "69.3.1",
4
4
  "description": "Run 1000 web accessibility tests from 11 tools and get a standardized report",
5
5
  "main": "index.js",
6
6
  "scripts": {
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
- // Create a catalog of the elements in the page.
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
- element: {},
53
- tagName: {},
54
- id: {},
55
- startTag: {},
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
- // Index it by its properties in the catalog.
64
- const tagName = addToCatalog(index, cat, 'tagName', element.tagName || '');
65
- const id = addToCatalog(index, cat, 'id', element.id || '');
66
- const startTag = addToCatalog(
67
- index,
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
- const segments = innerText?.split('\n') ?? [];
79
- const tidySegments = segments.map(segment => segment.trim().replace(/\s+/g, ' '));
80
- const neededSegments = tidySegments.filter(segment => segment.length);
81
- neededSegments.splice(1, neededSegments.length - 2);
82
- const text = addToCatalog(index, cat, 'text', neededSegments.join('\n'));
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
- const boxID = addToCatalog(
85
- index,
86
- cat,
87
- 'boxID',
88
- domRect
89
- ? ['x', 'y', 'width', 'height'].map(key => Math.round(domRect[key])).join(':')
90
- : ''
91
- );
92
- const pathID = addToCatalog(index, cat, 'pathID', window.getXPath(element));
93
- // Add an entry for it to the element data in the catalog.
94
- cat.element[index] = {
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(cat.text).forEach(text => {
106
- const textElementIndexes = cat.text[text];
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.element[elementIndex].pathID)
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.element[index].pathID.includes(excluder))
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.element[index].textLinkable = true;
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 an elements-only catalog.
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
- const prunedCatalog = {};
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 at least 1 instance:
169
- if (citedElementIndexes.has(elementIndex)) {
170
- // Add it to the pruned catalog.
171
- prunedCatalog[elementIndex] = catalog[elementIndex];
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,13 +1150,13 @@ 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.
1158
1156
  localReport.acts.forEach(act => {
1159
- delete act.result.nativeResult;
1157
+ if (act.result?.nativeResult) {
1158
+ delete act.result.nativeResult;
1159
+ }
1160
1160
  });
1161
1161
  }
1162
1162
  // If a catalog was created:
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]?.[0] ?? '';
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.