testaro 14.3.0 → 14.4.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/README.md CHANGED
@@ -204,11 +204,13 @@ If the `STANDARD` environment variable has the value `also` (which it has by def
204
204
 
205
205
  The standard format of each tool report has two properties:
206
206
  - `totals`: an array of 4 integers, representing the counts of issue instances classified by the tool into 4 ordinal degrees of severity. For example, `[2, 13, 0, 5]` would mean that the tool reported 2 instances at the lowest severity, 13 at the next-lowest, none at the third-lowest, and 5 at the highest.
207
- - `instances`: an array of objects describing facts about issue instances reported by the tool. Insofar as each tool permits, this object has these properties:
207
+ - `instances`: an array of objects describing facts about issue instances reported by the tool. This object has these properties, some of which have empty strings as values when the tool does not provide values:
208
208
  - `issueID`: a code identifying the issue
209
209
  - `what`: a description of the issue
210
210
  - `count` (optional): the count of instances if this instance represents multiple instances
211
211
  - `ordinalSeverity`: how the tool ranks the severity of the instance, on a 4-point ordinal scale
212
+ - `tagName`: upper-case tagName of the affected element
213
+ - `id`: value of the `id` property of that element
212
214
  - `location`: an object with three properties:
213
215
  - `doc`: whether the source (`source`) or the browser rendition (`dom`) was tested
214
216
  - `type`: the type of location information provided by the tool (`line`, `selector`, or `xpath`)
@@ -225,6 +227,8 @@ standardResult: {
225
227
  issueID: 'rule01',
226
228
  what: 'Button type invalid',
227
229
  ordinalSeverity: 0,
230
+ tagName: 'BUTTON'
231
+ id: '',
228
232
  location: {
229
233
  doc: 'dom',
230
234
  type: 'line',
@@ -236,6 +240,8 @@ standardResult: {
236
240
  issueID: 'rule01',
237
241
  what: 'Button type invalid',
238
242
  ordinalSeverity: 1,
243
+ tagName: 'BUTTON',
244
+ id: 'submitbutton',
239
245
  location: {
240
246
  doc: 'dom',
241
247
  type: 'line',
@@ -248,6 +254,8 @@ standardResult: {
248
254
  what: 'Links have empty href attributes',
249
255
  count: 17,
250
256
  ordinalSeverity: 3,
257
+ tagName: 'A',
258
+ id: '',
251
259
  location: {
252
260
  doc: '',
253
261
  type: '',
package/actSpecs.js CHANGED
@@ -200,7 +200,7 @@ exports.actSpecs = {
200
200
  false,
201
201
  'array',
202
202
  'areStrings',
203
- 'message specifications (e.g., error:Bad value), if not all'
203
+ 'message specifications (type and start of message, e.g., error:Bad value), if not all'
204
204
  ]
205
205
  }
206
206
  ],
package/call.js CHANGED
@@ -71,6 +71,7 @@ if (fn === 'run' && fnArgs.length === 1) {
71
71
  callRun(fnArgs)
72
72
  .then(() => {
73
73
  console.log('Execution completed');
74
+ process.exit(0);
74
75
  });
75
76
  }
76
77
  else if (fn === 'watch' && fnArgs.length === 3) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testaro",
3
- "version": "14.3.0",
3
+ "version": "14.4.0",
4
4
  "description": "Automation of accessibility testing",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/run.js CHANGED
@@ -994,31 +994,43 @@ const doActs = async (report, actIndex, page) => {
994
994
  }
995
995
  // Conduct, report, and time the test.
996
996
  const startTime = Date.now();
997
- const testReport = await require(`./tests/${act.which}`).reporter(...args);
998
- const expectations = act.expect;
999
- // If the test has expectations:
1000
- if (expectations) {
1001
- // Initialize whether they were fulfilled.
1002
- testReport.result.expectations = [];
1003
- let failureCount = 0;
1004
- // For each expectation:
1005
- expectations.forEach(spec => {
1006
- const truth = isTrue(testReport, spec);
1007
- testReport.result.expectations.push({
1008
- property: spec[0],
1009
- relation: spec[1],
1010
- criterion: spec[2],
1011
- actual: truth[0],
1012
- passed: truth[1]
997
+ let testReport = {
998
+ result: {
999
+ success: false
1000
+ }
1001
+ };
1002
+ try {
1003
+ testReport = await require(`./tests/${act.which}`).reporter(...args);
1004
+ const expectations = act.expect;
1005
+ // If the test has expectations:
1006
+ if (expectations) {
1007
+ // Initialize whether they were fulfilled.
1008
+ testReport.result.expectations = [];
1009
+ let failureCount = 0;
1010
+ // For each expectation:
1011
+ expectations.forEach(spec => {
1012
+ const truth = isTrue(testReport, spec);
1013
+ testReport.result.expectations.push({
1014
+ property: spec[0],
1015
+ relation: spec[1],
1016
+ criterion: spec[2],
1017
+ actual: truth[0],
1018
+ passed: truth[1]
1019
+ });
1020
+ if (! truth[1]) {
1021
+ failureCount++;
1022
+ }
1013
1023
  });
1014
- if (! truth[1]) {
1015
- failureCount++;
1016
- }
1017
- });
1018
- testReport.result.failureCount = failureCount;
1024
+ testReport.result.failureCount = failureCount;
1025
+ }
1026
+ testReport.result.success = true;
1019
1027
  }
1020
- testReport.result.success = true;
1021
- report.jobData.testTimes.push([act.which, Math.round((Date.now() - startTime) / 1000)]);
1028
+ catch(error) {
1029
+ console.log(`ERROR: Test act ${act.which} failed (${error.message.slice(0, 400)})`);
1030
+ }
1031
+ report.jobData.testTimes.push(
1032
+ [act.which, Math.round((Date.now() - startTime) / 1000)]
1033
+ );
1022
1034
  report.jobData.testTimes.sort((a, b) => b[1] - a[1]);
1023
1035
  // Add the result object (possibly an array) to the act.
1024
1036
  const resultCount = Object.keys(testReport.result).length;
package/standardize.js CHANGED
@@ -35,10 +35,23 @@ const doAxe = (result, standardResult, certainty) => {
35
35
  critical: 1
36
36
  };
37
37
  const ordinalSeverity = severityWeights[node.impact] + (certainty === 'violations' ? 2 : 0);
38
+ const tagName = node.html && node.html.replace(/^<|[ >].*$/sg, '').toUpperCase();
39
+ let id = '';
40
+ if (node.target && node.target.length && node.target[0].startsWith('#')) {
41
+ id = node.target[0].slice(1);
42
+ }
43
+ else if (node.html) {
44
+ const idArray = node.html.match(/\sid="([^"]+)"/);
45
+ if (idArray && idArray.length === 2) {
46
+ id = idArray[1];
47
+ }
48
+ }
38
49
  const instance = {
39
50
  issueID: rule.id,
40
51
  what: Array.from(whatSet.values()).join('; '),
41
52
  ordinalSeverity,
53
+ tagName,
54
+ id,
42
55
  location: {
43
56
  doc: 'dom',
44
57
  type: 'selector',
@@ -58,17 +71,19 @@ const doHTMLCS = (result, standardResult, severity) => {
58
71
  const ruleData = result[severity][ruleID];
59
72
  Object.keys(ruleData).forEach(what => {
60
73
  ruleData[what].forEach(item => {
61
- const {tagName, code} = item;
74
+ const {tagName, id, code} = item;
62
75
  const instance = {
63
76
  issueID: ruleID,
64
77
  what,
65
78
  ordinalSeverity: ['Warning', '', '', 'Error'].indexOf(severity),
79
+ tagName: tagName.toUpperCase(),
80
+ id: id.slice(1),
66
81
  location: {
67
82
  doc: 'dom',
68
83
  type: '',
69
84
  spec: ''
70
85
  },
71
- excerpt: cap(`${tagName ? tagName + ': ' : ''}${code || ''}`)
86
+ excerpt: cap(code)
72
87
  };
73
88
  standardResult.instances.push(instance);
74
89
  });
@@ -81,10 +96,27 @@ const doNuVal = (result, standardResult, docType) => {
81
96
  const items = result[docType] && result[docType].messages;
82
97
  if (items && items.length) {
83
98
  items.forEach(item => {
99
+ let tagName = '';
100
+ let id = '';
101
+ if (item.extract) {
102
+ const tagNameLCArray = item.extract.match(
103
+ /^Element ([^ ]+)|^An (img) element| (meta|script) element| element (script)| tag (script)/
104
+ );
105
+ if (tagNameLCArray && tagNameLCArray.length > 1) {
106
+ tagName = tagNameLCArray[1].toUpperCase();
107
+ }
108
+ const idArray = item.extract.match(/^.+\sid="([^"]+)"/);
109
+ if (idArray && idArray.length === 2) {
110
+ id = idArray[1];
111
+ }
112
+ }
113
+ // Include the message twice, because in scoring it is likely to be replaced by a pattern.
84
114
  const instance = {
85
115
  issueID: item.message,
86
116
  what: item.message,
87
117
  ordinalSeverity: -1,
118
+ tagName,
119
+ id,
88
120
  location: {
89
121
  doc: docType === 'pageContent' ? 'dom' : 'source',
90
122
  type: 'line',
@@ -125,16 +157,31 @@ const doQualWeb = (result, standardResult, ruleClassName) => {
125
157
  const ruleResult = ruleClass.assertions[rule];
126
158
  ruleResult.results.forEach(item => {
127
159
  item.elements.forEach(element => {
160
+ const {htmlCode} = element;
161
+ let tagName = '';
162
+ let id = '';
163
+ if (htmlCode) {
164
+ const tagNameArray = htmlCode.match(/^<([^ >]+)/);
165
+ if (tagNameArray && tagNameArray.length === 2) {
166
+ tagName = tagNameArray[1].toUpperCase();
167
+ }
168
+ const idArray = htmlCode.match(/\sid="([^"]+)"/);
169
+ if (idArray && idArray.length === 2) {
170
+ id = idArray[1];
171
+ }
172
+ }
128
173
  const instance = {
129
174
  issueID: rule,
130
175
  what: ruleResult.description,
131
176
  ordinalSeverity: severities[ruleClassName][item.verdict],
177
+ tagName,
178
+ id,
132
179
  location: {
133
180
  doc: 'dom',
134
181
  type: 'selector',
135
182
  spec: element.pointer
136
183
  },
137
- excerpt: cap(element.htmlCode)
184
+ excerpt: cap(htmlCode)
138
185
  };
139
186
  standardResult.instances.push(instance);
140
187
  standardResult.totals[instance.ordinalSeverity]++;
@@ -150,10 +197,25 @@ const doWAVE = (result, standardResult, categoryName) => {
150
197
  const ordinalSeverity = categoryName === 'alert' ? 0 : 3;
151
198
  Object.keys(category.items).forEach(rule => {
152
199
  category.items[rule].selectors.forEach(selector => {
200
+ let tagName = '';
201
+ let id = '';
202
+ if (typeof selector === 'string') {
203
+ const finalTerm = selector.replace(/^.+\s/, '');
204
+ if (finalTerm.includes('#')) {
205
+ const finalArray = finalTerm.split('#');
206
+ tagName = finalArray[0].replace(/:.*/, '');
207
+ id = finalArray[1];
208
+ }
209
+ else {
210
+ tagName = finalTerm.replace(/:.*/, '');
211
+ }
212
+ }
153
213
  const instance = {
154
214
  issueID: rule,
155
215
  what: category.items[rule].description,
156
216
  ordinalSeverity,
217
+ tagName,
218
+ id,
157
219
  location: {
158
220
  doc: 'dom',
159
221
  type: 'selector',
@@ -167,28 +229,39 @@ const doWAVE = (result, standardResult, categoryName) => {
167
229
  }
168
230
  };
169
231
  // Converts a result.
170
- const convert = (testName, result, standardResult) => {
232
+ const convert = (toolName, result, standardResult) => {
171
233
  // alfa
172
- if (testName === 'alfa' && result.totals) {
234
+ if (toolName === 'alfa' && result.totals) {
173
235
  standardResult.totals = [result.totals.warnings, 0, 0, result.totals.failures];
174
236
  result.items.forEach(item => {
237
+ const {codeLines} = item.target;
238
+ let id = '';
239
+ if (codeLines && codeLines.length) {
240
+ const code = codeLines[0];
241
+ const idMatchArray = code.match(/\sid="([^"]+)"/);
242
+ if (idMatchArray && idMatchArray.length === 2) {
243
+ id = idMatchArray[1];
244
+ }
245
+ }
175
246
  const instance = {
176
247
  issueID: item.rule.ruleID,
177
248
  what: item.rule.ruleSummary,
178
249
  ordinalSeverity: ['cantTell', '', '', 'failed'].indexOf(item.verdict),
250
+ tagName: item.target.tagName.toUpperCase(),
251
+ id,
179
252
  location: {
180
253
  doc: 'dom',
181
254
  type: 'xpath',
182
255
  spec: item.target.path
183
256
  },
184
- excerpt: cap(item.target.codeLines.join(' '))
257
+ excerpt: Array.isArray(codeLines) ? cap(codeLines.join(' ')) : ''
185
258
  };
186
259
  standardResult.instances.push(instance);
187
260
  });
188
261
  }
189
262
  // axe
190
263
  else if (
191
- testName === 'axe'
264
+ toolName === 'axe'
192
265
  && result.totals
193
266
  && (result.totals.rulesWarned || result.totals.rulesViolated)
194
267
  ) {
@@ -203,13 +276,25 @@ const convert = (testName, result, standardResult) => {
203
276
  doAxe(result, standardResult, 'violations');
204
277
  }
205
278
  // continuum
206
- else if (testName === 'continuum' && Array.isArray(result) && result.length) {
279
+ else if (toolName === 'continuum' && Array.isArray(result) && result.length) {
207
280
  standardResult.totals = [0, 0, 0, result.length];
208
281
  result.forEach(item => {
282
+ let tagName = '';
283
+ let id = '';
284
+ if (item.fingerprint && item.fingerprint.css) {
285
+ const {css} = item.fingerprint;
286
+ tagName = css.replace(/[^a-z].*$/, '').toUpperCase();
287
+ const idArray = css.match(/\[id="([^"]+)"\]/);
288
+ if (idArray && idArray.length) {
289
+ id = idArray[1];
290
+ }
291
+ }
209
292
  const instance = {
210
293
  issueID: item.engineTestId.toString(),
211
294
  what: item.attributeDetail,
212
- ordinalSeverity: 0,
295
+ ordinalSeverity: 3,
296
+ tagName,
297
+ id,
213
298
  location: {
214
299
  doc: 'dom',
215
300
  type: 'selector',
@@ -221,7 +306,7 @@ const convert = (testName, result, standardResult) => {
221
306
  });
222
307
  }
223
308
  // htmlcs
224
- else if (testName === 'htmlcs' && result) {
309
+ else if (toolName === 'htmlcs' && result) {
225
310
  doHTMLCS(result, standardResult, 'Warning');
226
311
  doHTMLCS(result, standardResult, 'Error');
227
312
  const {instances} = standardResult;
@@ -233,13 +318,29 @@ const convert = (testName, result, standardResult) => {
233
318
  ];
234
319
  }
235
320
  // ibm
236
- else if (testName === 'ibm' && result.totals) {
321
+ else if (toolName === 'ibm' && result.totals) {
237
322
  standardResult.totals = [0, result.totals.recommendation, 0, result.totals.violation];
238
323
  result.items.forEach(item => {
324
+ let tagName = '';
325
+ let id = '';
326
+ if (item.path && item.path.dom) {
327
+ const tagNameArray = item.path.dom.match(/^.+\/([^/[]+)/s);
328
+ if (tagNameArray && tagNameArray.length === 2) {
329
+ tagName = tagNameArray[1];
330
+ }
331
+ if (item.snippet) {
332
+ const idArray = item.snippet.match(/^.+\sid="([^"]+)"/s);
333
+ if (idArray && idArray.length === 2) {
334
+ id = idArray[1];
335
+ }
336
+ }
337
+ }
239
338
  const instance = {
240
339
  issueID: item.ruleId,
241
340
  what: item.message,
242
341
  ordinalSeverity: ['', 'recommendation', '', 'violation'].indexOf(item.level),
342
+ tagName,
343
+ id,
243
344
  location: {
244
345
  doc: 'dom',
245
346
  type: 'xpath',
@@ -251,7 +352,7 @@ const convert = (testName, result, standardResult) => {
251
352
  });
252
353
  }
253
354
  // nuVal
254
- else if (testName === 'nuVal' && (result.pageContent || result.rawPage)) {
355
+ else if (toolName === 'nuVal' && (result.pageContent || result.rawPage)) {
255
356
  if (result.pageContent) {
256
357
  doNuVal(result, standardResult, 'pageContent');
257
358
  }
@@ -268,7 +369,7 @@ const convert = (testName, result, standardResult) => {
268
369
  }
269
370
  // qualWeb
270
371
  else if (
271
- testName === 'qualWeb'
372
+ toolName === 'qualWeb'
272
373
  && result.modules
273
374
  && (
274
375
  result.modules['act-rules']
@@ -288,14 +389,30 @@ const convert = (testName, result, standardResult) => {
288
389
  }
289
390
  }
290
391
  // tenon
291
- else if (testName === 'tenon' && result.data && result.data.resultSet) {
392
+ else if (toolName === 'tenon' && result.data && result.data.resultSet) {
292
393
  result.data.resultSet.forEach(item => {
394
+ let tagName = '';
395
+ if (item.xpath) {
396
+ const tagNameArray = item.xpath.match(/^.+\/([^/[]+)/);
397
+ if (tagNameArray && tagNameArray.length === 2) {
398
+ tagName = tagNameArray[1].toUpperCase();
399
+ }
400
+ }
401
+ let id = '';
402
+ if (item.errorSnippet) {
403
+ const idArray = item.errorSnippet.match(/^.+\sid="([^"]+)"/);
404
+ if (idArray && idArray.length === 2) {
405
+ id = idArray[1];
406
+ }
407
+ }
293
408
  const instance = {
294
409
  issueID: item.tID ? item.tID.toString() : '',
295
410
  what: item.errorTitle || '',
296
411
  ordinalSeverity: Math.min(
297
412
  3, Math.max(0, Math.round((item.certainty || 0) * (item.priority || 0) / 3333))
298
413
  ),
414
+ tagName,
415
+ id,
299
416
  location: {
300
417
  doc: 'dom',
301
418
  type: 'xpath',
@@ -311,7 +428,7 @@ const convert = (testName, result, standardResult) => {
311
428
  });
312
429
  }
313
430
  // testaro
314
- else if (testName === 'testaro') {
431
+ else if (toolName === 'testaro') {
315
432
  const rules = result.rules ? Object.keys(result.rules) : [];
316
433
  standardResult.totals = [0, 0, 0, 0];
317
434
  rules.forEach(rule => {
@@ -332,7 +449,7 @@ const convert = (testName, result, standardResult) => {
332
449
  }
333
450
  // wave
334
451
  else if (
335
- testName === 'wave'
452
+ toolName === 'wave'
336
453
  && result.categories
337
454
  && (
338
455
  result.categories.error
@@ -39,6 +39,16 @@ exports.reporter = async page => {
39
39
  document: document.documentElement.ariaHidden === 'true',
40
40
  body: body ? body.ariaHidden === 'true' : false,
41
41
  main: main ? main.ariaHidden === 'true' : false
42
+ },
43
+ tagName: {
44
+ document: 'HTML',
45
+ body: 'BODY',
46
+ main: main ? main.tagName : ''
47
+ },
48
+ id: {
49
+ document: document.id,
50
+ body: body.id,
51
+ main: main ? main.id : ''
42
52
  }
43
53
  };
44
54
  // Identify whether each region is really hidden.
@@ -69,6 +79,8 @@ exports.reporter = async page => {
69
79
  issueID: `allHidden-${hider}-${region}`,
70
80
  what: `${reportables[region][1]} ${reportables[hider][1]}`,
71
81
  ordinalSeverity: reportables[region][0] + reportables[hider][0] || 0,
82
+ tagName: region.tagName,
83
+ id: region.id,
72
84
  location: {
73
85
  doc: 'dom',
74
86
  type: 'selector',
package/testaro/attVal.js CHANGED
@@ -32,6 +32,7 @@ exports.reporter = async (page, withItems, attributeName, areLicit, values) => {
32
32
  });
33
33
  const report = badElements.map(el => ({
34
34
  tagName: el.tagName,
35
+ id: el.id,
35
36
  textStart: textOf(el, 70),
36
37
  attributeValue: el.getAttribute(attributeName)
37
38
  }));
@@ -51,6 +52,8 @@ exports.reporter = async (page, withItems, attributeName, areLicit, values) => {
51
52
  what:
52
53
  `${item.tagName} element has attribute ${attributeName} with illicit value ${item.attributeValue}`,
53
54
  ordinalSeverity: 2,
55
+ tagName: item.tagName,
56
+ id: item.id,
54
57
  location: {
55
58
  doc: '',
56
59
  type: '',
@@ -64,7 +67,10 @@ exports.reporter = async (page, withItems, attributeName, areLicit, values) => {
64
67
  standardInstances.push({
65
68
  issueID: 'attVal',
66
69
  what: 'Elements have attributes with illicit values',
70
+ count: data.total,
67
71
  ordinalSeverity: 2,
72
+ tagName: '',
73
+ id: '',
68
74
  location: {
69
75
  doc: '',
70
76
  type: '',
@@ -20,7 +20,7 @@ const addFailure = async (withItems, input, inputText, autocomplete, data) => {
20
20
  // If itemization is required:
21
21
  if (withItems) {
22
22
  // Add the item to the data.
23
- data.items.push([autocomplete, inputText.slice(0, 100)]);
23
+ data.items.push([autocomplete, input.id || '', inputText.slice(0, 100)]);
24
24
  }
25
25
  }
26
26
  };
@@ -77,12 +77,14 @@ exports.reporter = async (page, withItems) => {
77
77
  issueID: `autocomplete-${item[0]}`,
78
78
  what: `Input is missing the required autocomplete attribute with value ${item[0]}`,
79
79
  ordinalSeverity: 2,
80
+ tagName: 'INPUT',
81
+ id: item[1] || '',
80
82
  location: {
81
83
  doc: '',
82
84
  type: '',
83
85
  spec: ''
84
86
  },
85
- excerpt: item[1]
87
+ excerpt: item[2]
86
88
  });
87
89
  });
88
90
  }
@@ -91,6 +93,8 @@ exports.reporter = async (page, withItems) => {
91
93
  issueID: 'autocomplete',
92
94
  what: 'Inputs are missing required autocomplete attributes',
93
95
  ordinalSeverity: 2,
96
+ tagName: '',
97
+ id: '',
94
98
  location: {
95
99
  doc: '',
96
100
  type: '',
package/testaro/bulk.js CHANGED
@@ -27,6 +27,8 @@ exports.reporter = async page => {
27
27
  what: 'Page contains a large number of visible elements',
28
28
  count,
29
29
  ordinalSeverity: 0,
30
+ tagName: 'HTML',
31
+ id: '',
30
32
  location: {
31
33
  doc: '',
32
34
  type: '',
@@ -17,6 +17,8 @@ exports.reporter = async page => {
17
17
  issueID: 'docType',
18
18
  what: 'Document has no standard HTML doctype preamble',
19
19
  ordinalSeverity: 3,
20
+ tagName: 'HTML',
21
+ id: '',
20
22
  location: {
21
23
  doc: '',
22
24
  type: '',
package/testaro/dupAtt.js CHANGED
@@ -53,11 +53,12 @@ exports.reporter = async (page, withItems) => {
53
53
  // Extract the opening tags of its elements.
54
54
  let elements = rawPage.match(/<[a-zA-Z][^<>]+>/g);
55
55
  // Delete their enclosing angle brackets and the values of any attributes in them.
56
- elements = elements.map(el => el.replace(/^< *|=[^ ]*| *>$/g, ''));
56
+ const elementsWithVals = elements.map(el => el.replace(/^< *| *>$/g, ''));
57
+ const elementsWithoutVals = elementsWithVals.map(el => el.replace(/^ *|=[^ ]*$/g, ''));
57
58
  // For each element:
58
- elements.forEach(element => {
59
+ elementsWithoutVals.forEach((elementWithoutVals, index) => {
59
60
  // Identify its tag name and attributes.
60
- const terms = element.split(' ');
61
+ const terms = elementWithoutVals.split(' ');
61
62
  // If it has 2 or more attributes:
62
63
  if (terms.length > 2) {
63
64
  // If any is duplicated:
@@ -65,11 +66,14 @@ exports.reporter = async (page, withItems) => {
65
66
  const attributes = terms.slice(1);
66
67
  const attSet = new Set(attributes);
67
68
  if (attSet.size < attributes.length) {
68
- // Add this to the data.
69
+ // Add it to the data.
69
70
  data.total++;
70
71
  if (withItems) {
71
72
  data.items.push({
72
73
  tagName,
74
+ id: terms.includes('id')
75
+ ? elementsWithVals[index].replace(/^.+id=/, '').replace(/ .*$/, '')
76
+ : '',
73
77
  attributes
74
78
  });
75
79
  }
@@ -83,6 +87,8 @@ exports.reporter = async (page, withItems) => {
83
87
  issueID: 'dupAtt',
84
88
  what: `Element ${item.tagName} has 2 attributes with the same name`,
85
89
  ordinalSeverity: 2,
90
+ tagName: item.tagName,
91
+ id: item.id,
86
92
  location: {
87
93
  doc: '',
88
94
  type: '',
package/testaro/embAc.js CHANGED
@@ -32,6 +32,7 @@ exports.reporter = async (page, withItems) => await page.$$eval(
32
32
  }
33
33
  items.push({
34
34
  embeddedElement: bad.tagName,
35
+ embeddedID: bad.id,
35
36
  excerpt: compact(container.outerHTML)
36
37
  });
37
38
  }
@@ -45,8 +46,10 @@ exports.reporter = async (page, withItems) => await page.$$eval(
45
46
  items.forEach(item => {
46
47
  standardInstances.push({
47
48
  issueID: `embAc-${item.embeddedElement}`,
48
- what: `Element ${item.embeddedElement} is embedded in a link or button`,
49
+ what: `${item.embeddedElement} element is embedded in a link or button`,
49
50
  ordinalSeverity: 2,
51
+ tagName: item.embeddedElement,
52
+ id: item.id,
50
53
  location: {
51
54
  doc: '',
52
55
  type: '',
@@ -62,6 +65,8 @@ exports.reporter = async (page, withItems) => await page.$$eval(
62
65
  what: 'Interactive elements are contained by links or buttons',
63
66
  count: total,
64
67
  ordinalSeverity: 2,
68
+ tagName: '',
69
+ id: '',
65
70
  location: {
66
71
  doc: '',
67
72
  type: '',
package/testaro/filter.js CHANGED
@@ -40,6 +40,7 @@ exports.reporter = async (page, withItems) => {
40
40
  filterData.forEach(filterDatum => {
41
41
  data.items.push({
42
42
  tagName: filterDatum.element.tagName,
43
+ id: filterDatum.element.id,
43
44
  text: compact(filterDatum.element.textContent) || compact(filterDatum.element.outerHTML),
44
45
  impact: filterDatum.impact
45
46
  });
@@ -53,8 +54,10 @@ exports.reporter = async (page, withItems) => {
53
54
  data.items.forEach(item => {
54
55
  standardInstances.push({
55
56
  issueID: 'filterStyle',
56
- what: `Element ${item.tagName} has a filter style that impacts ${item.impact} elements`,
57
+ what: `${item.tagName} element has a filter style that impacts ${item.impact} elements`,
57
58
  ordinalSeverity: 2,
59
+ tagName: item.toUpperCase(),
60
+ id: item.id,
58
61
  location: {
59
62
  doc: '',
60
63
  type: '',
@@ -69,6 +72,8 @@ exports.reporter = async (page, withItems) => {
69
72
  issueID: 'filterStyle',
70
73
  what: 'Elements have filter styles impacting other elements',
71
74
  ordinalSeverity: 2,
75
+ tagName: '',
76
+ id: '',
72
77
  location: {
73
78
  doc: '',
74
79
  type: '',
package/testaro/focAll.js CHANGED
@@ -69,6 +69,8 @@ exports.reporter = async page => {
69
69
  what: 'Some focusable elements are not Tab-focusable or vice versa',
70
70
  count: Math.abs(data.discrepancy),
71
71
  ordinalSeverity: 2,
72
+ tagName: '',
73
+ id: '',
72
74
  location: {
73
75
  doc: '',
74
76
  type: '',