testaro 4.7.2 → 4.10.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": "4.7.2",
3
+ "version": "4.10.0",
4
4
  "description": "Automation of accessibility testing",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/tests/labClash.js CHANGED
@@ -71,33 +71,32 @@ exports.reporter = async (page, withItems) => {
71
71
  }
72
72
  }
73
73
  }
74
- // Content label if details required.
75
- if (withItems) {
76
- // Of button.
77
- if (labelee.tagName === 'BUTTON') {
78
- const content = debloat(labelee.textContent);
79
- if (content) {
80
- texts.content = content;
81
- }
74
+ // Content label.
75
+ // Of button.
76
+ if (labelee.tagName === 'BUTTON') {
77
+ const content = debloat(labelee.textContent);
78
+ if (content) {
79
+ texts.content = content;
82
80
  }
83
- // Of submit input.
84
- else if (labelee.tagName === 'INPUT' && labelee.type === 'submit' && labelee.value) {
85
- const content = debloat(labelee.value);
86
- if (content) {
87
- texts.content = content;
88
- }
81
+ }
82
+ // Of submit input.
83
+ else if (labelee.tagName === 'INPUT' && labelee.type === 'submit' && labelee.value) {
84
+ const content = debloat(labelee.value);
85
+ if (content) {
86
+ texts.content = content;
87
+ }
88
+ }
89
+ // Of image input.
90
+ else if (labelee.tagName === 'INPUT' && labelee.type === 'image' && labelee.alt) {
91
+ const content = debloat(labelee.alt);
92
+ if (content) {
93
+ texts.content = content;
89
94
  }
90
95
  }
91
96
  const {totals} = data;
92
97
  const labelTypeCount = labelTypes.length;
93
98
  // If it is well labeled:
94
- if (
95
- labelTypeCount === 1
96
- || ! labelTypeCount && (
97
- labelee.tagName === 'BUTTON' && debloat(labelee.textContent).length
98
- || labelee.tagName === 'INPUT' && labelee.type === 'submit' && labelee.value
99
- )
100
- ) {
99
+ if (labelTypeCount === 1 || (texts.content && ! labelTypeCount)) {
101
100
  // Increment the count of well-labeled items in the report.
102
101
  totals.wellLabeled++;
103
102
  // Add data on the item to the report, if required.
package/tests/role.js CHANGED
@@ -22,8 +22,6 @@ exports.reporter = async page => await page.$eval('body', body => {
22
22
  'complementary',
23
23
  'contentinfo',
24
24
  'definition',
25
- 'dialog',
26
- 'document',
27
25
  'figure',
28
26
  'graphics-document',
29
27
  'gridcell',
@@ -38,7 +36,6 @@ exports.reporter = async page => await page.$eval('body', body => {
38
36
  'option',
39
37
  'progressbar',
40
38
  'radio',
41
- 'region',
42
39
  'row',
43
40
  'rowgroup',
44
41
  'rowheader',
@@ -6,145 +6,148 @@
6
6
  particular style properties, listed in the 'mainStyles' and 'headingStyles' arrays.
7
7
  */
8
8
  exports.reporter = async (page, withItems) => {
9
- // Get an object with arrays of list and adjacent links as properties.
9
+ // Get an object with arrays of list links and adjacent links as properties.
10
10
  const linkTypes = await require('../procs/linksByType').linksByType(page);
11
11
  return await page.$eval('body', (body, args) => {
12
- const withItems = args[0];
13
- const linkTypes = args[1];
12
+ const linkTypes = args[0];
13
+ const withItems = args[1];
14
14
  // Identify the settable style properties to be compared for all tag names.
15
15
  const mainStyles = [
16
- 'borderStyle',
17
- 'borderWidth',
18
16
  'fontStyle',
19
17
  'fontWeight',
18
+ 'opacity',
19
+ 'textDecorationLine',
20
+ 'textDecorationStyle',
21
+ 'textDecorationThickness'
22
+ ];
23
+ // Identify those only for buttons.
24
+ const buttonStyles = [
25
+ 'borderStyle',
26
+ 'borderWidth',
27
+ 'height',
20
28
  'lineHeight',
21
29
  'maxHeight',
22
30
  'maxWidth',
23
31
  'minHeight',
24
32
  'minWidth',
25
- 'opacity',
26
33
  'outlineOffset',
27
34
  'outlineStyle',
28
- 'outlineWidth',
29
- 'textDecorationLine',
30
- 'textDecorationStyle',
31
- 'textDecorationThickness'
35
+ 'outlineWidth'
32
36
  ];
33
37
  // Identify those for headings.
34
38
  const headingStyles = [
39
+ 'color',
35
40
  'fontSize'
36
41
  ];
42
+ // Identify those for list links.
43
+ const listLinkStyles = [
44
+ 'color',
45
+ 'fontSize',
46
+ 'lineHeight'
47
+ ];
37
48
  // Initialize the data to be returned.
38
49
  const data = {
39
50
  mainStyles,
51
+ buttonStyles,
40
52
  headingStyles,
53
+ listLinkStyles,
41
54
  totals: {}
42
55
  };
43
56
  if (withItems) {
44
57
  data.items = {};
45
58
  }
46
- // Identify the heading tag names to be analyzed.
47
- const headingNames = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
48
- // Identify the other nonlink tag names to be analyzed.
49
- const otherNames = ['button'];
50
- // Initialize an object of elements to be analyzed.
51
- const elementClasses = {
52
- headings: {},
53
- other: {
54
- aAdjacent: linkTypes.adjacent,
55
- aList: linkTypes.list
59
+ // Initialize an object of elements to be analyzed, including links.
60
+ const elements = {
61
+ buttons: [],
62
+ headings: {
63
+ h1: [],
64
+ h2: [],
65
+ h3: [],
66
+ h4: [],
67
+ h5: [],
68
+ h6: []
69
+ },
70
+ links: {
71
+ adjacent: linkTypes.adjacent,
72
+ list: linkTypes.list
56
73
  }
57
74
  };
75
+ // Identify the heading tag names to be analyzed.
76
+ const headingNames = Object.keys(elements.headings);
77
+ // Add the buttons to the object.
78
+ elements.buttons = Array.from(body.querySelectorAll('button, input[type=button]'));
58
79
  // For each heading tag name:
59
80
  headingNames.forEach(tagName => {
60
81
  // Add its elements to the object.
61
- elementClasses.headings[tagName] = Array.from(body.getElementsByTagName(tagName));
62
- });
63
- // For each other tag name:
64
- otherNames.forEach(tagName => {
65
- // Add its elements to the object.
66
- elementClasses.other[tagName] = Array.from(body.getElementsByTagName(tagName));
82
+ elements.headings[tagName] = Array.from(body.getElementsByTagName(tagName));
67
83
  });
68
- // For each element superclass:
69
- ['headings', 'other'].forEach(superClass => {
70
- // For each class in the superclass:
71
- Object.keys(elementClasses[superClass]).forEach(tagName => {
72
- const elements = elementClasses[superClass][tagName];
73
- const elementCount = elements.length;
74
- // If there are any:
75
- if (elementCount) {
76
- const styleProps = {};
77
- const styleTexts = {};
84
+ // Tabulates the distribution of style properties for elements of a type.
85
+ const tallyStyles = (typeName, elements, typeStyles, withItems) => {
86
+ // If there are any elements:
87
+ const elementCount = elements.length;
88
+ if (elementCount) {
89
+ const styleProps = {};
90
+ const styleTexts = {};
91
+ // For each element:
92
+ elements.forEach(element => {
93
+ // Get its values on the style properties to be compared.
94
+ const styleDec = window.getComputedStyle(element);
95
+ const style = {};
96
+ const styles = mainStyles.concat(typeStyles);
97
+ styles.forEach(styleName => {
98
+ style[styleName] = styleDec[styleName];
99
+ });
100
+ // Get a text representation of the style, limited to those properties.
101
+ const styleText = JSON.stringify(style);
102
+ // Increment the total of elements with that style.
103
+ styleTexts[styleText] = ++styleTexts[styleText] || 1;
104
+ // If details are to be reported:
78
105
  if (withItems) {
79
- if (! data.items[tagName]) {
80
- data.items[tagName] = {};
81
- }
82
- }
83
- // For each of them:
84
- elements.forEach(element => {
85
- // Get its values on the style properties to be compared.
86
- const styleDec = window.getComputedStyle(element);
87
- const style = {};
88
- // Identify the styles to be compared.
89
- const styles = mainStyles;
90
- if (superClass === 'headings') {
91
- styles.push(...headingStyles);
106
+ // Add the element’s values and text to the style details.
107
+ if (! styleProps[typeName]) {
108
+ styleProps[typeName] = {};
92
109
  }
110
+ const elementText = element.textContent.trim().replace(/\s/g, ' ');
111
+ // For each style property being compared:
93
112
  styles.forEach(styleName => {
94
- style[styleName] = styleDec[styleName];
113
+ if (! styleProps[typeName][styleName]) {
114
+ styleProps[typeName][styleName] = {};
115
+ }
116
+ if (! styleProps[typeName][styleName][style[styleName]]) {
117
+ styleProps[typeName][styleName][style[styleName]] = [];
118
+ }
119
+ styleProps[typeName][styleName][style[styleName]].push(elementText);
95
120
  });
96
- // Get a text representation of the style.
97
- const styleText = JSON.stringify(style);
98
- // Increment the total of elements with that style declaration.
99
- styleTexts[styleText] = ++styleTexts[styleText] || 1;
100
- // If details are required:
101
- if (withItems) {
102
- // For each style property:
103
- styles.forEach(styleName => {
104
- const styleValue = style[styleName];
105
- // Increment the total of elements with the same value on it as the element.
106
- if (styleProps[styleName]) {
107
- styleProps[styleName][styleValue] = ++styleProps[styleName][styleValue] || 1;
108
- }
109
- else {
110
- styleProps[styleName] = {[styleValue]: 1};
111
- }
112
- });
113
- }
114
- });
115
- // Add the total to the result.
116
- data.totals[tagName] = {total: elementCount};
117
- const styleCounts = Object.values(styleTexts);
118
- // If the elements in the element class differ in style:
119
- if (styleCounts.length > 1) {
120
- // Add the distribution of its style counts to the result.
121
- data.totals[tagName].subtotals = styleCounts.sort((a, b) => b - a);
122
121
  }
123
- // If details are required:
122
+ });
123
+ // Add the total to the result.
124
+ data.totals[typeName] = {total: elementCount};
125
+ const styleCounts = Object.values(styleTexts);
126
+ // If the elements of the type differ in style:
127
+ if (styleCounts.length > 1) {
128
+ // Add the distribution of its style counts to the result.
129
+ data.totals[typeName].subtotals = styleCounts.sort((a, b) => b - a);
130
+ // If details are to be reported:
124
131
  if (withItems) {
125
- // For each style property:
126
- Object.keys(styleProps).forEach(styleProp => {
127
- // Ignore it if all the elements have the same value.
128
- if (Object.keys(styleProps[styleProp]).length === 1) {
129
- delete styleProps[styleProp];
130
- }
131
- // Otherwise:
132
- else {
133
- if (! data.items[tagName][styleProp]) {
134
- data.items[tagName][styleProp] = {};
135
- }
136
- // Sort the values in order of decreasing count.
137
- const sortedEntries = Object.entries(styleProps[styleProp]).sort((a, b) => b[1] - a[1]);
138
- sortedEntries.forEach(entry => {
139
- const propData = data.items[tagName][styleProp];
140
- propData[entry[0]] = (propData[entry[0]] || 0) + entry[1];
141
- });
132
+ // Delete the data on uniform style properties.
133
+ Object.keys(styleProps[typeName]).forEach(styleName => {
134
+ if (Object.keys(styleProps[typeName][styleName]).length === 1) {
135
+ delete styleProps[typeName][styleName];
142
136
  }
143
137
  });
138
+ // Add the element values and texts to the result.
139
+ data.items[typeName] = styleProps[typeName];
144
140
  }
145
141
  }
146
- });
142
+ }
143
+ };
144
+ // Report the style-property distributions for the element types.
145
+ tallyStyles('button', elements.buttons, buttonStyles, withItems);
146
+ tallyStyles('adjacentLink', elements.links.adjacent, [], withItems);
147
+ tallyStyles('listLink', elements.links.list, listLinkStyles, withItems);
148
+ headingNames.forEach(headingName => {
149
+ tallyStyles(headingName, elements.headings[headingName], headingStyles, withItems);
147
150
  });
148
151
  return {result: data};
149
- }, [withItems, linkTypes]);
152
+ }, [linkTypes, withItems]);
150
153
  };
@@ -0,0 +1,62 @@
1
+ // app.js
2
+ // Validator for Testaro tests.
3
+
4
+ const fs = require('fs').promises;
5
+ const {handleRequest} = require(`${__dirname}/../../run`);
6
+ const validateTests = async () => {
7
+ const totals = {
8
+ attempts: 0,
9
+ successes: 0
10
+ };
11
+ const scriptFileNames = await fs.readdir(`${__dirname}/../tests/scripts`);
12
+ for (const scriptFileName of scriptFileNames.filter(fileName => fileName === 'styleDiff.json')) {
13
+ const rawScriptJSON = await fs
14
+ .readFile(`${__dirname}/../tests/scripts/${scriptFileName}`, 'utf8');
15
+ const scriptJSON = rawScriptJSON
16
+ .replace(/__targets__/g, `file://${__dirname}/../tests/targets`);
17
+ const script = JSON.parse(scriptJSON);
18
+ const report = {script};
19
+ report.log = [];
20
+ report.acts = [];
21
+ await handleRequest(report);
22
+ const {log, acts} = report;
23
+ if (log.length === 2 && log[1].event === 'endTime' && /^\d{4}-.+$/.test(log[1].value)) {
24
+ console.log('Success: Log has been correctly populated');
25
+ }
26
+ else {
27
+ console.log('Failure: Log empty or invalid');
28
+ console.log(JSON.stringify(log, null, 2));
29
+ }
30
+ if (
31
+ acts.length === script.commands.length
32
+ && acts.every(
33
+ act => act.type && act.type === 'test'
34
+ ? act.result && act.result.failureCount !== undefined
35
+ : true
36
+ )
37
+ ) {
38
+ totals.attempts++;
39
+ totals.successes++;
40
+ console.log('Success: Reports have been correctly populated');
41
+ if (acts.every(
42
+ act => act.type === 'test' ? act.result.failureCount === 0 : true
43
+ )) {
44
+ totals.attempts++;
45
+ totals.successes++;
46
+ console.log('Success: No failures');
47
+ }
48
+ else {
49
+ totals.attempts++;
50
+ console.log('Failure: At least one test has at least one failure');
51
+ console.log(JSON.stringify(acts, null, 2));
52
+ }
53
+ }
54
+ else {
55
+ totals.attempts++;
56
+ console.log('Failure: Reports empty or invalid');
57
+ console.log(JSON.stringify(acts, null, 2));
58
+ }
59
+ }
60
+ console.log(`Grand totals: attempts ${totals.attempts}, successes ${totals.successes}`);
61
+ };
62
+ validateTests();
@@ -20,7 +20,7 @@
20
20
  "expect": [
21
21
  ["totals.mislabeled", "=", 0],
22
22
  ["totals.unlabeled", "=", 0],
23
- ["totals.wellLabeled", "=", 6]
23
+ ["totals.wellLabeled", "=", 7]
24
24
  ]
25
25
  },
26
26
  {
@@ -35,7 +35,7 @@
35
35
  "withItems": false,
36
36
  "expect": [
37
37
  ["totals.mislabeled", "=", 3],
38
- ["totals.unlabeled", "=", 2],
38
+ ["totals.unlabeled", "=", 3],
39
39
  ["totals.wellLabeled", "=", 0]
40
40
  ]
41
41
  }
@@ -15,16 +15,16 @@
15
15
  {
16
16
  "type": "test",
17
17
  "which": "styleDiff",
18
+ "withItems": true,
18
19
  "what": "styleDiff",
19
- "withItems": false,
20
20
  "expect": [
21
- ["totals.aAdjacent.total", "=", 2],
22
- ["totals.aList.total", "=", 2],
21
+ ["totals.adjacentLink.total", "=", 2],
22
+ ["totals.listLink.total", "=", 2],
23
23
  ["totals.button.total", "=", 2],
24
24
  ["totals.h1.total", "=", 1],
25
25
  ["totals.h2.total", "=", 4],
26
- ["totals.aAdjacent.subtotals"],
27
- ["totals.aList.subtotals"],
26
+ ["totals.adjacentLink.subtotals"],
27
+ ["totals.listLink.subtotals"],
28
28
  ["totals.button.subtotals"],
29
29
  ["totals.h1.subtotals"],
30
30
  ["totals.h2.subtotals"]
@@ -38,23 +38,24 @@
38
38
  {
39
39
  "type": "test",
40
40
  "which": "styleDiff",
41
+ "withItems": true,
41
42
  "what": "styleDiff",
42
- "withItems": false,
43
43
  "expect": [
44
- ["totals.aAdjacent.total", "=", 2],
45
- ["totals.aList.total", "=", 2],
44
+ ["totals.adjacentLink.total", "=", 2],
45
+ ["totals.listLink.total", "=", 2],
46
46
  ["totals.button.total", "=", 2],
47
47
  ["totals.h1.total", "=", 1],
48
48
  ["totals.h2.total", "=", 4],
49
- ["totals.aAdjacent.subtotals.0", "=", 1],
50
- ["totals.aAdjacent.subtotals.1", "=", 1],
51
- ["totals.aList.subtotals.0", "=", 1],
52
- ["totals.aList.subtotals.1", "=", 1],
49
+ ["totals.adjacentLink.subtotals.0", "=", 1],
50
+ ["totals.adjacentLink.subtotals.1", "=", 1],
51
+ ["totals.listLink.subtotals.0", "=", 1],
52
+ ["totals.listLink.subtotals.1", "=", 1],
53
53
  ["totals.button.subtotals.0", "=", 1],
54
54
  ["totals.button.subtotals.1", "=", 1],
55
55
  ["totals.h1.subtotals"],
56
56
  ["totals.h2.subtotals.0", "=", 3],
57
- ["totals.h2.subtotals.1", "=", 1]
57
+ ["totals.h2.subtotals.1", "=", 1],
58
+ ["items.adjacentLink.textDecorationStyle.double.0", "=", "French information"]
58
59
  ]
59
60
  }
60
61
  ]
@@ -12,6 +12,10 @@
12
12
  <p>This paragraph contains an empty <button type="button" aria-label></button> button, with no accessible name.</p>
13
13
  <p>This is another <button id="labButton" type="button" aria-labelledby="buttonLabel">button</button> with its text content overridden by both an explicit label and a reference label as a <label id="buttonLabel" for="labButton">special type of button</label>.</p>
14
14
  <p>This is an implicitly and attribute-labeled <label>checkbox input <input id="cbox" type="checkbox" aria-label="a checkbox-type input"></label>.</p>
15
+ <p style="display: flex; align-items: center;">
16
+ <span>Here is an image input without a text alternative:</span>
17
+ <input type="image" src="delete.png" width="30rem">
18
+ </p>
15
19
  <p><span id="textInputLabel">Who is your favorite actor?</span> <input type="text" aria-labelledby="textInputLabel" aria-label="your favorite actor">.</p>
16
20
  <p>What do you think? Enter your thoughts into this text area, which is unlabeled.</p>
17
21
  <p><textarea aria-label></textarea></p>
@@ -10,6 +10,10 @@
10
10
  <main>
11
11
  <h1>Page with standard labeling</h1>
12
12
  <p>This paragraph contains a <button type="button">button</button> with its text content as accessible name, another <button id="labButton" type="button">button</button> with its text content overridden by an explicit label as a <label for="labButton">special type of button</label>, an <label>implicitly labeled text input <input type="text"></label>, and an explicitly labeled <label for="cbox">checkbox input</label>: <input id="cbox" type="checkbox">.</p>
13
+ <p style="display: flex; align-items: center;">
14
+ <span>Here is an image input with a text alternative:</span>
15
+ <input type="image" src="delete.png" alt="delete" width="30rem">
16
+ </p>
13
17
  <p>This <label>implicitly labeled text input <input id="textInput" type="text"></label> also has an explicit label, which validly provides <label for="textInput">additional information</label>.</p>
14
18
  <p><span id="what">What do you think?</span> Enter your thoughts into this text area, which is labeled by reference.</p>
15
19
  <p><textarea aria-labelledby="what"></textarea></p>
@@ -19,7 +19,7 @@
19
19
  </section>
20
20
  <section>
21
21
  <h2>Conflict</h2>
22
- <p>The parent section of these sections has an unnecessary explicit role.</p>
22
+ <p>The parent section of these sections has an unnecessary explicit role, instead of an element with the same implicit role.</p>
23
23
  </section>
24
24
  </section>
25
25
  </body>
@@ -19,17 +19,17 @@
19
19
  <main>
20
20
  <h1>Page with inconsistent styles</h1>
21
21
  <h2>Inline links</h2>
22
- <p>This paragraph contains inline links to <a href="https://en.wikipedia.org">English information</a> and <a style="text-decoration: underline; text-decoration-style: double" href="https://fr.wikipedia.org">French information</a>. They have different styles.</p>
22
+ <p>This paragraph contains adjacent links to <a href="https://en.wikipedia.org">English information</a> and <a style="text-decoration-style: double" href="https://fr.wikipedia.org">French information</a>. They have different styles.</p>
23
23
  <h2>Block links</h2>
24
- <p>The following links are not inline. They have a different styles.</p>
24
+ <p>The following links are list links. They have different styles.</p>
25
25
  <ul>
26
26
  <li><a style="font-weight: 700" href="https://sp.wikipedia.org">Spanish information</a></li>
27
27
  <li><a href="https://eo.wikipedia.org">Esperanto information</a></li>
28
28
  </ul>
29
29
  <h2>Buttons</h2>
30
- <p>This paragraph contains two buttons with a different custom styles. <button type="button">Wikipedia</button> <button type="button" style="border-width: 3px">Wiktionary</button></p>
30
+ <p>This paragraph contains two buttons with different custom styles. <button type="button">Wikipedia</button> <button type="button" style="border-width: 3px">Wiktionary</button></p>
31
31
  <h2 class="italic">Headings</h2>
32
- <p>This page contains 4 <code>h2</code> headings. This one has a deviant style.</p>
32
+ <p>This page contains 4 <code>h2</code> headings. This last one has a deviant style.</p>
33
33
  </main>
34
34
  </body>
35
35
  </html>
@@ -20,9 +20,9 @@
20
20
  <main>
21
21
  <h1>Page with consistent styles</h1>
22
22
  <h2>Inline links</h2>
23
- <p>This paragraph contains inline links to <a href="https://en.wikipedia.org">English information</a> and <a href="https://fr.wikipedia.org">French information</a>. They both have a default style.</p>
23
+ <p>This paragraph contains adjacent links to <a href="https://en.wikipedia.org">English information</a> and <a href="https://fr.wikipedia.org">French information</a>. They both have a default style.</p>
24
24
  <h2>Block links</h2>
25
- <p>The following links are not inline. They have a uniform custom underline.</p>
25
+ <p>The following links are list links. They have a uniform custom underline.</p>
26
26
  <ul class="dotted">
27
27
  <li><a href="https://sp.wikipedia.org">Spanish information</a></li>
28
28
  <li><a href="https://eo.wikipedia.org">Esperanto information</a></li>