testaro 18.2.0 → 18.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.
Files changed (72) hide show
  1. package/aslint/utils/aria.test.ts +12 -0
  2. package/aslint/utils/aria.ts +120 -0
  3. package/aslint/utils/async.test.ts +25 -0
  4. package/aslint/utils/async.ts +22 -0
  5. package/aslint/utils/common.test.ts +239 -0
  6. package/aslint/utils/common.ts +168 -0
  7. package/aslint/utils/console.test.ts +85 -0
  8. package/aslint/utils/console.ts +89 -0
  9. package/aslint/utils/css.test.ts +153 -0
  10. package/aslint/utils/css.ts +191 -0
  11. package/aslint/utils/dom.test.ts +627 -0
  12. package/aslint/utils/dom.ts +1051 -0
  13. package/aslint/utils/env.test.ts +14 -0
  14. package/aslint/utils/env.ts +8 -0
  15. package/aslint/utils/func.test.ts +160 -0
  16. package/aslint/utils/func.ts +70 -0
  17. package/aslint/utils/global.test.ts +12 -0
  18. package/aslint/utils/global.ts +25 -0
  19. package/aslint/utils/object.test.ts +524 -0
  20. package/aslint/utils/object.ts +278 -0
  21. package/aslint/utils/report.test.ts +56 -0
  22. package/aslint/utils/report.ts +36 -0
  23. package/aslint/utils/text.test.ts +270 -0
  24. package/aslint/utils/text.ts +165 -0
  25. package/aslint/utils/time.test.ts +43 -0
  26. package/aslint/utils/time.ts +33 -0
  27. package/package.json +1 -1
  28. package/procs/getSource.js +35 -0
  29. package/testaro/dupAtt.js +86 -108
  30. package/testaro/headEl.js +83 -0
  31. package/testaro/hrRisk.js +67 -0
  32. package/tests/testaro.js +2 -0
  33. package/validation/tests/jobs/headEl.json +67 -0
  34. package/validation/tests/jobs/hrRisk.json +134 -0
  35. package/validation/tests/targets/headEl/index.html +16 -0
  36. package/validation/tests/targets/hrRisk/index.html +18 -0
  37. /package/aslint/app/rules/aslint/{aria-role-dialog → redundant/aria-role-dialog}/aria-role-dialog.documentation.md +0 -0
  38. /package/aslint/app/rules/aslint/{aria-role-dialog → redundant/aria-role-dialog}/aria-role-dialog.test.ts +0 -0
  39. /package/aslint/app/rules/aslint/{aria-role-dialog → redundant/aria-role-dialog}/aria-role-dialog.ts +0 -0
  40. /package/aslint/app/rules/aslint/{capital-letters-words → redundant/capital-letters-words}/capital-letters-words.documentation.md +0 -0
  41. /package/aslint/app/rules/aslint/{capital-letters-words → redundant/capital-letters-words}/capital-letters-words.test.ts +0 -0
  42. /package/aslint/app/rules/aslint/{capital-letters-words → redundant/capital-letters-words}/capital-letters-words.ts +0 -0
  43. /package/aslint/app/rules/aslint/{contentinfo-landmark-only-one → redundant/contentinfo-landmark-only-one}/contentinfo-landmark-only-one.documentation.md +0 -0
  44. /package/aslint/app/rules/aslint/{contentinfo-landmark-only-one → redundant/contentinfo-landmark-only-one}/contentinfo-landmark-only-one.test.ts +0 -0
  45. /package/aslint/app/rules/aslint/{contentinfo-landmark-only-one → redundant/contentinfo-landmark-only-one}/contentinfo-landmark-only-one.ts +0 -0
  46. /package/aslint/app/rules/aslint/{empty-title-attribute → redundant/empty-title-attribute}/empty-title-attribute.documentation.md +0 -0
  47. /package/aslint/app/rules/aslint/{empty-title-attribute → redundant/empty-title-attribute}/empty-title-attribute.test.ts +0 -0
  48. /package/aslint/app/rules/aslint/{empty-title-attribute → redundant/empty-title-attribute}/empty-title-attribute.ts +0 -0
  49. /package/aslint/app/rules/aslint/{flash-content → redundant/flash-content}/flash-content.documentation.md +0 -0
  50. /package/aslint/app/rules/aslint/{flash-content → redundant/flash-content}/flash-content.test.ts +0 -0
  51. /package/aslint/app/rules/aslint/{flash-content → redundant/flash-content}/flash-content.ts +0 -0
  52. /package/aslint/app/rules/aslint/{font-style-italic → redundant/font-style-italic}/font-style-italic.documentation.md +0 -0
  53. /package/aslint/app/rules/aslint/{font-style-italic → redundant/font-style-italic}/font-style-italic.test.ts +0 -0
  54. /package/aslint/app/rules/aslint/{font-style-italic → redundant/font-style-italic}/font-style-italic.ts +0 -0
  55. /package/aslint/app/rules/aslint/{h1-must-be → redundant/h1-must-be}/h1-must-be.documentation.md +0 -0
  56. /package/aslint/app/rules/aslint/{h1-must-be → redundant/h1-must-be}/h1-must-be.test.ts +0 -0
  57. /package/aslint/app/rules/aslint/{h1-must-be → redundant/h1-must-be}/h1-must-be.ts +0 -0
  58. /package/aslint/app/rules/aslint/{aria-hidden-false → unimportant/aria-hidden-false}/aria-hidden-false.test.ts +0 -0
  59. /package/aslint/app/rules/aslint/{aria-hidden-false → unimportant/aria-hidden-false}/aria-hidden-false.ts +0 -0
  60. /package/aslint/app/rules/aslint/{aria-hidden-false → unimportant/aria-hidden-false}/aria-hidden.documentation.md +0 -0
  61. /package/aslint/app/rules/aslint/{content-editable-missing-attributes → unimportant/content-editable-missing-attributes}/content-editable-missing-attributes.docmentation.md +0 -0
  62. /package/aslint/app/rules/aslint/{content-editable-missing-attributes → unimportant/content-editable-missing-attributes}/content-editable-missing-attributes.test.ts +0 -0
  63. /package/aslint/app/rules/aslint/{content-editable-missing-attributes → unimportant/content-editable-missing-attributes}/content-editable-missing-attributes.ts +0 -0
  64. /package/aslint/app/rules/aslint/{elements-not-allowed-in-head → used/elements-not-allowed-in-head}/elements-not-allowed-in-head.documentation.md +0 -0
  65. /package/aslint/app/rules/aslint/{elements-not-allowed-in-head → used/elements-not-allowed-in-head}/elements-not-allowed-in-head.test.ts +0 -0
  66. /package/aslint/app/rules/aslint/{elements-not-allowed-in-head → used/elements-not-allowed-in-head}/elements-not-allowed-in-head.ts +0 -0
  67. /package/aslint/app/rules/aslint/{headings-sibling-unique → used/headings-sibling-unique}/headings-sibling-unique.documentation.md +0 -0
  68. /package/aslint/app/rules/aslint/{headings-sibling-unique → used/headings-sibling-unique}/headings-sibling-unique.test.ts +0 -0
  69. /package/aslint/app/rules/aslint/{headings-sibling-unique → used/headings-sibling-unique}/headings-sibling-unique.ts +0 -0
  70. /package/aslint/app/rules/aslint/{horizontal-rule → used/horizontal-rule}/horizontal-rule.documentation.md +0 -0
  71. /package/aslint/app/rules/aslint/{horizontal-rule → used/horizontal-rule}/horizontal-rule.test.ts +0 -0
  72. /package/aslint/app/rules/aslint/{horizontal-rule → used/horizontal-rule}/horizontal-rule.ts +0 -0
@@ -0,0 +1,165 @@
1
+ import { ObjectUtility } from './object';
2
+
3
+ // Note: safeTrim related constants
4
+
5
+ const SP: string = ' ';
6
+ const TAB: string = '\t';
7
+ const CR: string = '\r';
8
+ const LF: string = '\n';
9
+ const CR_LF = '\r\n';
10
+ const FF: string = '\f';
11
+ const ZERO_WIDTH_SPACE: string = '\v' + // \x0B VT 垂直制表符
12
+ '\f' + // \x0C FF 换页符
13
+ '\u200B\u200C\u200D\u200E\u200F\u000b\u2028\u2029\uFEFF\u202D';
14
+ const OTHER_SPACE: string = '\xA0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000';
15
+
16
+ const ALL_SPACES: string = SP + TAB + CR + LF + CR_LF + ZERO_WIDTH_SPACE + OTHER_SPACE;
17
+
18
+ const leftReg: RegExp = new RegExp(`^[${ALL_SPACES}]+`);
19
+ const rightReg: RegExp = new RegExp(`[${ALL_SPACES}]+$`);
20
+ const zeroReg: RegExp = new RegExp(`[${ZERO_WIDTH_SPACE}]+`, 'g');
21
+ const otherReg: RegExp = new RegExp(`[${OTHER_SPACE}]+`, 'g');
22
+ const space: RegExp = new RegExp(`^[${SP+TAB+LF+FF+CR}]+$`);
23
+ const allWhiteSpaces: RegExp = new RegExp(`[${TAB + CR + LF + CR_LF + ZERO_WIDTH_SPACE + OTHER_SPACE}]`, 'g');
24
+
25
+ export class TextUtility {
26
+ private static isNativeTrimAvailable: boolean = String.prototype.trim && ObjectUtility.isNativeMethod(String.prototype.trim);
27
+ private static escapeEntityMap: { [key: string]: string } = {
28
+ '"': '"',
29
+ '&': '&',
30
+ '\'': ''',
31
+ '/': '/',
32
+ '<': '&lt;',
33
+ '=': '&#x3D;',
34
+ '>': '&gt;',
35
+ '`': '&#x60;'
36
+ };
37
+
38
+ private static unescapeEntityMap: { [key: string]: string } = {
39
+ '&#39;': '\'',
40
+ '&#x2F;': '/',
41
+ '&#x3D;': '=',
42
+ '&#x60;': '`',
43
+ '&amp;': '&',
44
+ '&gt;': '>',
45
+ '&lt;': '<',
46
+ '&quot;': '"'
47
+ };
48
+
49
+ public static trim(text: string): string {
50
+ if (TextUtility.isNativeTrimAvailable) {
51
+ return text.trim();
52
+ }
53
+
54
+ return text.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
55
+ }
56
+
57
+ public static truncateWords(str: string, numberOfChars?: number): string {
58
+ const amount: number = numberOfChars ? numberOfChars : 50;
59
+ const cut: number = str.indexOf(' ', amount);
60
+
61
+ if (cut === -1) {
62
+ return str;
63
+ }
64
+
65
+ return `${str.substring(0, cut)} [...]`;
66
+ }
67
+
68
+ public static truncate(str: string, numberOfChars?: number): string {
69
+ const amount: number = numberOfChars ? numberOfChars : 50;
70
+
71
+ return `${str.substring(0, amount)} [...]`;
72
+ }
73
+
74
+ public static truncateInTheMiddle(text: string, startChars?: number, endChars?: number, maxLength?: number): string {
75
+ let start: any;
76
+ let end: any;
77
+ const charsAtTheBeginning: number = startChars || 70;
78
+ const charsAtTheEnd: number = endChars || 70;
79
+ const maximumLength: number = maxLength || 120;
80
+ const content: string = text.trim();
81
+
82
+ if (content.length > maximumLength) {
83
+ start = content.substring(0, charsAtTheBeginning);
84
+ end = content.substring(content.length - charsAtTheEnd, content.length);
85
+
86
+ return `${start} [...] ${end}`;
87
+ }
88
+
89
+ return text;
90
+ }
91
+
92
+ public static isUpperCase(str: string): boolean {
93
+ if ((/[^\w]/).test(str)) {
94
+ return false;
95
+ }
96
+
97
+ if (isNaN(Number(str)) && str.length > 1) {
98
+ return str === str.toUpperCase();
99
+ }
100
+
101
+ return false;
102
+ }
103
+
104
+ public static containsOnlyWhiteSpaces(str: string): boolean {
105
+ return (/^\s*$/).test(str);
106
+ }
107
+
108
+ // Note: following by https://www.w3.org/TR/2010/WD-html-markup-20100624/terminology.html#space
109
+ public static containsSpaceCharacter(str: string): boolean {
110
+ return space.test(str);
111
+ }
112
+
113
+ public static safeTrim(string: string): string {
114
+ return string
115
+ .replace(leftReg, '')
116
+ .replace(rightReg, '')
117
+ .replace(new RegExp(TAB, 'g'), '')
118
+ .replace(new RegExp(CR_LF, 'g'), LF)
119
+ .replace(new RegExp(CR, 'g'), LF)
120
+ .replace(zeroReg, '')
121
+ .replace(otherReg, '')
122
+ .trim();
123
+ }
124
+
125
+ public static normalizeWhitespaces(string: string): string {
126
+ return string
127
+ .replace(allWhiteSpaces, ' ').replace(/\s+/g, ' ');
128
+ }
129
+
130
+ public static escape(str: string): string {
131
+ const fromEntityMap: any = (s: any): string => {
132
+ return this.escapeEntityMap[s];
133
+ };
134
+
135
+ return String(str).replace(/[&<>"'`=/]/g, fromEntityMap);
136
+ }
137
+
138
+ public static unescape(str: string): string {
139
+ const fromEntityMap: any = (s: any): string => {
140
+ return this.unescapeEntityMap[s];
141
+ };
142
+
143
+ return String(str).replace(/&([^;]+);/g, fromEntityMap);
144
+ }
145
+
146
+ public static convertUnderscoresToDashes(text: string): string {
147
+ return text.replace(/_/g, '-');
148
+ }
149
+
150
+ public static convertDashesToUnderscores(text: string): string {
151
+ return text.replace(/-/g, '_');
152
+ }
153
+
154
+ public static replacePlaceholder(text: string, words: string[], placeholder: string = '%'): string {
155
+ const replaceValue = (result: string, value: string, i: number): string => {
156
+ return result.replace(`${placeholder}${i}`, String(value));
157
+ };
158
+
159
+ return words.reduce(replaceValue, text);
160
+ }
161
+
162
+ public static areStringsTheSame(str1: string, str2: string): boolean {
163
+ return str1.toLocaleUpperCase() === str2.toLocaleUpperCase();
164
+ }
165
+ }
@@ -0,0 +1,43 @@
1
+ import { Time } from './time';
2
+
3
+ describe('Utils', () => {
4
+
5
+ describe('Time', () => {
6
+
7
+ describe('#format', () => {
8
+
9
+ it('should return "< 1 ms" when value is 0.1 ms', () => {
10
+ expect(Time.format(0.1)).toBe('< 1 ms');
11
+ });
12
+
13
+ it('should return seconds and miliseconds formatted when value is 1.0499999999992724 ms', () => {
14
+ expect(Time.format(1.0499999999992724)).toBe('1ms');
15
+ });
16
+
17
+ it('should return seconds and miliseconds formatted when value is 1300 ms', () => {
18
+ expect(Time.format(1300)).toBe('1s 300ms');
19
+ });
20
+
21
+ it('should return minutes, seconds and miliseconds formatted when value is 61003 ms', () => {
22
+ expect(Time.format(61003)).toBe('1m 1s 3ms');
23
+ });
24
+
25
+ it('should return minutes and miliseconds formatted when value is 60300 ms', () => {
26
+ expect(Time.format(60300)).toBe('1m 300ms');
27
+ });
28
+
29
+ it('should return minutes, seconds and miliseconds formatted when value is 62300 ms', () => {
30
+ expect(Time.format(62300)).toBe('1m 2s 300ms');
31
+ });
32
+
33
+ it('should return miliseconds formatted when value is 50 ms', () => {
34
+ expect(Time.format(50)).toBe('50ms');
35
+ });
36
+
37
+ it('should return formatted and rounded milliseconds long number', () => {
38
+ expect(Time.format(21.1234500000013)).toBe('21ms');
39
+ });
40
+ });
41
+
42
+ });
43
+ });
@@ -0,0 +1,33 @@
1
+ export class Time {
2
+
3
+ public static format(duration: any): any {
4
+ const milliseconds: any = Math.floor(duration % 1000);
5
+ const seconds: any = Math.floor(duration / 1000 % 60);
6
+ const minutes: any = Math.floor(duration / (60 * 1000) % 60);
7
+ const hours: any = Math.floor(duration / (60 * 60 * 1000) % 60);
8
+ let time: any = '';
9
+
10
+ if (duration < 1) {
11
+ return '< 1 ms';
12
+ }
13
+
14
+ if (hours > 0) {
15
+ time += `${hours}h `;
16
+ }
17
+
18
+ if (minutes > 0) {
19
+ time += `${minutes}m `;
20
+ }
21
+
22
+ if (seconds > 0) {
23
+ time += `${seconds}s `;
24
+ }
25
+
26
+ if (milliseconds > 0) {
27
+ time += `${milliseconds}ms`;
28
+ }
29
+
30
+ return time;
31
+ }
32
+
33
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testaro",
3
- "version": "18.2.0",
3
+ "version": "18.4.0",
4
4
  "description": "Run 920 web accessibility tests from 9 tools and get a standardized report",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -0,0 +1,35 @@
1
+ // ########## IMPORTS
2
+
3
+ // Module to make HTTP requests.
4
+ const fetch = require('node-fetch');
5
+ // Module to process files.
6
+ const fs = require('fs/promises');
7
+
8
+ // ########## FUNCTIONS
9
+
10
+ // Gets and returns the source of a document.
11
+ exports.getSource = async page => {
12
+ const url = page.url();
13
+ const scheme = url.replace(/:.+/, '');
14
+ const data = {
15
+ prevented: false,
16
+ source: ''
17
+ };
18
+ if (scheme === 'file') {
19
+ const filePath = url.slice(7);
20
+ const rawPage = await fs.readFile(filePath, 'utf8');
21
+ data.source = rawPage;
22
+ }
23
+ else {
24
+ try {
25
+ const rawPageResponse = await fetch(url);
26
+ const rawPage = await rawPageResponse.text();
27
+ data.source = rawPage;
28
+ }
29
+ catch(error) {
30
+ console.log(`ERROR getting source of page (${error.message})`);
31
+ data.prevented = true;
32
+ }
33
+ }
34
+ return data;
35
+ };
package/testaro/dupAtt.js CHANGED
@@ -5,139 +5,117 @@
5
5
 
6
6
  // ########## IMPORTS
7
7
 
8
- // Module to make HTTP requests.
9
- const fetch = require('node-fetch');
10
- // Module to process files.
11
- const fs = require('fs/promises');
8
+ // Module to get the document source.
9
+ const {getSource} = require('../procs/getSource');
12
10
 
13
11
  // ########## FUNCTIONS
14
12
 
15
13
  // Reports failures.
16
14
  exports.reporter = async (page, withItems) => {
17
- // Initialize the data.
15
+ // Initialize the data and standard result.
18
16
  const data = {total: 0};
19
17
  if (withItems) {
20
18
  data.items = [];
21
19
  }
22
- // Get the page.
23
- const url = page.url();
24
- const scheme = url.replace(/:.+/, '');
25
- let rawPage;
26
- if (scheme === 'file') {
27
- const filePath = url.slice(7);
28
- rawPage = await fs.readFile(filePath, 'utf8');
20
+ let totals = [];
21
+ const standardInstances = [];
22
+ // Get the source.
23
+ const sourceData = await getSource(page);
24
+ // If it was not obtained:
25
+ if (sourceData.prevented) {
26
+ // Report this.
27
+ data.prevented = true;
29
28
  }
29
+ // Otherwise, i.e. if it was obtained:
30
30
  else {
31
- try {
32
- const rawPageResponse = await fetch(url);
33
- rawPage = await rawPageResponse.text();
34
- }
35
- catch(error) {
36
- console.log(`ERROR getting page for dupAtt test (${error.message})`);
37
- data.prevented = true;
38
- return {
39
- data,
40
- totals: [0, 0, 0, 10],
41
- standardInstances: [
42
- {
43
- ruleID: 'dupAtt',
44
- what: 'Page prevented this test (treated as 10 instances)',
45
- ordinalSeverity: 3,
46
- count: 10,
47
- tagName: '',
48
- id: '',
49
- location: {
50
- doc: '',
51
- type: '',
52
- spec: ''
53
- },
54
- excerpt: ''
31
+ let rawPage = sourceData.source;
32
+ // Change any spacing character sequences in it to single spaces.
33
+ rawPage = rawPage.replace(/\s+/g, ' ');
34
+ // Delete any spaces adjacent to equal symbols in it.
35
+ rawPage = rawPage.replace(/ = | =|= /g, '=');
36
+ // Remove any escaped quotation marks from it.
37
+ rawPage = rawPage.replace(/\\"|\\'/g, '');
38
+ // Remove any script code from it.
39
+ rawPage = rawPage.replace(/<(script [^<>]+)>.*?<\/script>/g, '$1');
40
+ // Remove any comments from it.
41
+ rawPage = rawPage.replace(/<!--.*?-->/g, '');
42
+ // Extract the opening tags of its elements.
43
+ let elements = rawPage.match(/<[a-zA-Z][^<>]+>/g);
44
+ // Delete their enclosing angle brackets.
45
+ elements = elements.map(element => element.replace(/< ?| ?>/g, ''));
46
+ // Delete the values of any attributes in them.
47
+ const nvElements = elements.map(element => element.replace(/="[^"]*"/g, ''));
48
+ // For each element:
49
+ nvElements.forEach((element, index) => {
50
+ // Identify its tag name and attributes.
51
+ const terms = element.split(' ');
52
+ // If it has 2 or more attributes:
53
+ if (terms.length > 2) {
54
+ // If any is duplicated:
55
+ const tagName = terms[0].toUpperCase();
56
+ const attributes = terms.slice(1);
57
+ attributes.sort();
58
+ const duplicatedAttribute = attributes.find(
59
+ (current, attIndex) => attributes[attIndex + 1] === current
60
+ );
61
+ if (duplicatedAttribute) {
62
+ // Add data on the element to the data.
63
+ const id = terms.includes('id')
64
+ ? elements[index].replace(/^.+id="/, '').replace(/".+/, '')
65
+ : '';
66
+ data.total++;
67
+ if (withItems) {
68
+ data.items.push({
69
+ tagName,
70
+ id,
71
+ duplicatedAttribute
72
+ });
55
73
  }
56
- ]
57
- };
58
- }
59
- }
60
- // Change any spacing character sequences in it to single spaces.
61
- rawPage = rawPage.replace(/\s+/g, ' ');
62
- // Delete any spaces adjacent to equal symbols in it.
63
- rawPage = rawPage.replace(/ = | =|= /g, '=');
64
- // Remove any escaped quotation marks from it.
65
- rawPage = rawPage.replace(/\\"|\\'/g, '');
66
- // Remove any script code from it.
67
- rawPage = rawPage.replace(/<(script [^<>]+)>.*?<\/script>/g, '$1');
68
- // Remove any comments from it.
69
- rawPage = rawPage.replace(/<!--.*?-->/g, '');
70
- // Extract the opening tags of its elements.
71
- let elements = rawPage.match(/<[a-zA-Z][^<>]+>/g);
72
- // Delete their enclosing angle brackets.
73
- elements = elements.map(element => element.replace(/< ?| ?>/g, ''));
74
- // Delete the values of any attributes in them.
75
- const nvElements = elements.map(element => element.replace(/="[^"]*"/g, ''));
76
- // For each element:
77
- nvElements.forEach((element, index) => {
78
- // Identify its tag name and attributes.
79
- const terms = element.split(' ');
80
- // If it has 2 or more attributes:
81
- if (terms.length > 2) {
82
- // If any is duplicated:
83
- const tagName = terms[0].toUpperCase();
84
- const attributes = terms.slice(1);
85
- attributes.sort();
86
- const duplicatedAttribute = attributes.find(
87
- (current, attIndex) => attributes[attIndex + 1] === current
88
- );
89
- if (duplicatedAttribute) {
90
- // Add data on the element to the data.
91
- const id = terms.includes('id')
92
- ? elements[index].replace(/^.+id="/, '').replace(/".+/, '')
93
- : '';
94
- data.total++;
95
- if (withItems) {
96
- data.items.push({
97
- tagName,
98
- id,
99
- duplicatedAttribute
100
- });
101
74
  }
102
75
  }
76
+ });
77
+ // If itemization is required and there are any instances:
78
+ if (data.items) {
79
+ // For each instance:
80
+ data.items.forEach(item => {
81
+ // Add it.
82
+ standardInstances.push({
83
+ ruleID: 'dupAtt',
84
+ what: 'Element has 2 attributes with the same name',
85
+ ordinalSeverity: 2,
86
+ tagName: item.tagName,
87
+ id: item.id,
88
+ location: {
89
+ doc: item.id ? 'source' : '',
90
+ type: item.id ? 'selector' : '',
91
+ spec: `#${item.id}`
92
+ },
93
+ excerpt: item.duplicatedAttribute
94
+ });
95
+ });
103
96
  }
104
- });
105
- const standardInstances = [];
106
- if (data.items) {
107
- data.items.forEach(item => {
97
+ // Otherwise, if there are any instances:
98
+ else if (data.total) {
99
+ // Add a summary instance.
108
100
  standardInstances.push({
109
101
  ruleID: 'dupAtt',
110
- what: 'Element has 2 attributes with the same name',
102
+ what: 'In some elements 2 attributes have the same name',
111
103
  ordinalSeverity: 2,
112
- tagName: item.tagName,
113
- id: item.id,
104
+ count: data.total,
114
105
  location: {
115
- doc: item.id ? 'source' : '',
116
- type: item.id ? 'selector' : '',
117
- spec: `#${item.id}`
106
+ doc: '',
107
+ type: '',
108
+ spec: ''
118
109
  },
119
- excerpt: item.duplicatedAttribute
110
+ excerpt: ''
120
111
  });
121
- });
122
- }
123
- else if (data.total) {
124
- standardInstances.push({
125
- ruleID: 'dupAtt',
126
- what: 'In some elements 2 attributes have the same name',
127
- ordinalSeverity: 2,
128
- count: data.total,
129
- location: {
130
- doc: '',
131
- type: '',
132
- spec: ''
133
- },
134
- excerpt: ''
135
- });
112
+ }
113
+ totals = [0, 0, data.total, 0];
136
114
  }
137
115
  // Return the data.
138
116
  return {
139
117
  data,
140
- totals: [0, 0, data.total, 0],
118
+ totals,
141
119
  standardInstances
142
120
  };
143
121
  };
@@ -0,0 +1,83 @@
1
+ /*
2
+ headEl
3
+ Related to ASLint rule elements-not-allowed-in-head.
4
+ This test reports invalid descendants of the head element in the source of the document.
5
+ */
6
+
7
+ // ########## IMPORTS
8
+
9
+ // Module to get the document source.
10
+ const {getSource} = require('../procs/getSource');
11
+
12
+ // ########## FUNCTIONS
13
+
14
+ // Performs the test.
15
+ exports.reporter = async page => {
16
+ // Initialize the data and standard result.
17
+ const data = {
18
+ total: 0,
19
+ badTagNames: []
20
+ };
21
+ let totals = [];
22
+ const standardInstances = [];
23
+ // Get the source.
24
+ const sourceData = await getSource(page);
25
+ // If it was not obtained:
26
+ if (sourceData.prevented) {
27
+ // Report this.
28
+ data.prevented = true;
29
+ }
30
+ // Otherwise, i.e. if it was obtained:
31
+ else {
32
+ let rawPage = sourceData.source;
33
+ // Change any spacing character sequences in it to single spaces.
34
+ rawPage = rawPage.replace(/\s+/g, ' ');
35
+ // Delete everything in it except the head content.
36
+ rawPage = rawPage.replace(/^.*<head[^>]*>|<\/head>.*$/g, '');
37
+ // Get the tag names of the remaining elements.
38
+ const tags = rawPage.match(/<([a-z]+)/g);
39
+ const ucTagNames = tags.map(tag => tag.toUpperCase().slice(1));
40
+ const validTagNames = [
41
+ 'BASE',
42
+ 'LINK',
43
+ 'META',
44
+ 'SCRIPT',
45
+ 'STYLE',
46
+ 'TITLE',
47
+ 'NOSCRIPT',
48
+ 'TEMPLATE'
49
+ ];
50
+ // For each tag name:
51
+ ucTagNames.forEach(tagName => {
52
+ // If it is invalid:
53
+ if (! validTagNames.includes(tagName)) {
54
+ // Add this to the result.
55
+ data.total++;
56
+ data.badTagNames.push(tagName);
57
+ }
58
+ });
59
+ // If there are any instances:
60
+ if (data.total) {
61
+ // Add a summary instance.
62
+ standardInstances.push({
63
+ ruleID: 'headEl',
64
+ what: `Invalid elements within the head: ${data.badTagNames.join(', ')}`,
65
+ ordinalSeverity: 2,
66
+ count: data.total,
67
+ location: {
68
+ doc: '',
69
+ type: '',
70
+ spec: ''
71
+ },
72
+ excerpt: ''
73
+ });
74
+ }
75
+ totals = [0, 0, data.total, 0];
76
+ }
77
+ // Return the data.
78
+ return {
79
+ data,
80
+ totals,
81
+ standardInstances
82
+ };
83
+ };
@@ -0,0 +1,67 @@
1
+ /*
2
+ hrRisk
3
+ Related to ASLint test horizontal-rule.
4
+ This test reports the existence of hr elements. Their semantics are inconsistently defined and
5
+ interpreted and impair accessibility compared with stylistic segmentation.
6
+ */
7
+
8
+ // ########## IMPORTS
9
+
10
+ // Module to get locator data.
11
+ const {getLocatorData} = require('../procs/getLocatorData');
12
+
13
+ // ########## FUNCTIONS
14
+
15
+ // Performs the test.
16
+ exports.reporter = async (page, withItems) => {
17
+ // Initialize the standard result.
18
+ const data = {};
19
+ const totals = [0, 0, 0, 0];
20
+ const standardInstances = [];
21
+ // Get locators for all applicable elements.
22
+ const locAll = page.locator('body hr');
23
+ const locsAll = await locAll.all();
24
+ // For each of them:
25
+ for (const loc of locsAll) {
26
+ // Add to the totals.
27
+ totals[0]++;
28
+ // If itemization is required:
29
+ if (withItems) {
30
+ // Get data on the element.
31
+ const elData = await getLocatorData(loc);
32
+ // Add a standard instance.
33
+ standardInstances.push({
34
+ ruleID: 'hrRisk',
35
+ what: 'Element instead of styling is used for vertical segmentation',
36
+ ordinalSeverity: 0,
37
+ tagName: 'HR',
38
+ id: elData.id,
39
+ location: elData.location,
40
+ excerpt: elData.excerpt
41
+ });
42
+ }
43
+ }
44
+ // If itemization is not required and there are instances:
45
+ if (! withItems && totals[0]) {
46
+ // Add a summary instance.
47
+ standardInstances.push({
48
+ ruleID: 'hrRisk',
49
+ what: 'Elements instead of styling are used for vertical segmentation',
50
+ ordinalSeverity: 0,
51
+ count: totals[0],
52
+ tagName: 'HR',
53
+ id: '',
54
+ location: {
55
+ doc: '',
56
+ type: '',
57
+ spec: ''
58
+ },
59
+ excerpt: ''
60
+ });
61
+ }
62
+ return {
63
+ data,
64
+ totals,
65
+ standardInstances
66
+ };
67
+ };
package/tests/testaro.js CHANGED
@@ -21,9 +21,11 @@ const evalRules = {
21
21
  focInd: 'missing and nonstandard focus indicators',
22
22
  focOp: 'discrepancies between focusability and operability',
23
23
  focVis: 'links that are invisible when focused',
24
+ headEl: 'invalid elements within the head',
24
25
  headingAmb: 'same-level sibling headings with identical texts',
25
26
  hover: 'hover-caused content changes',
26
27
  hovInd: 'hover indication nonstandard',
28
+ hrRisk: 'hr element instead of styles used for vertical segmentation',
27
29
  labClash: 'labeling inconsistencies',
28
30
  lineHeight: 'text with a line height less than 1.5 times its font size',
29
31
  linkExt: 'links that automatically open new windows',