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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testaro",
3
- "version": "69.1.0",
3
+ "version": "69.3.0",
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,78 +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
- );
72
- const innerText = element.closest('head') ? '' : element.innerText;
73
- const segments = innerText?.trim().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
- const text = addToCatalog(index, cat, 'text', neededSegments.join('\n'));
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
- const boxID = addToCatalog(
80
- index,
81
- cat,
82
- 'boxID',
83
- domRect
84
- ? ['x', 'y', 'width', 'height'].map(key => Math.round(domRect[key])).join(':')
85
- : ''
86
- );
87
- const pathID = addToCatalog(index, cat, 'pathID', window.getXPath(element));
88
- // Add an entry for it to the element data in the catalog.
89
- 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] = {
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(cat.text).forEach(text => {
101
- const textElementIndexes = cat.text[text];
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.element[elementIndex].pathID)
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.element[index].pathID.includes(excluder))
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.element[index].textLinkable = true;
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 an elements-only catalog.
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
- const prunedCatalog = {};
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 at least 1 instance:
164
- if (citedElementIndexes.has(elementIndex)) {
165
- // Add it to the pruned catalog.
166
- 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];
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]?.[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.