testaro 56.0.0 → 57.1.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": "56.0.0",
3
+ "version": "57.1.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": {
@@ -25,8 +25,7 @@
25
25
  /*
26
26
  isInlineLink
27
27
  Returns whether the link of a locator is inline.
28
- A link is classified as inline unless it is in an ordered or unordered list of at least 2 links
29
- with no other nonspacing text content.
28
+ A link is classified as inline unless its declared or effective display is block.
30
29
  */
31
30
 
32
31
  exports.isInlineLink = async loc => await loc.evaluate(element => {
package/run.js CHANGED
@@ -686,50 +686,53 @@ const doActs = async (report) => {
686
686
  const {toolTimes} = report.jobData;
687
687
  toolTimes[act.which] ??= 0;
688
688
  toolTimes[act.which] += time;
689
- // If the act was not prevented and standardization is required:
690
- if (['also', 'only'].includes(standard) && act.data && ! act.data.prevented) {
691
- // Initialize the standard result.
692
- act.standardResult = {
693
- totals: [0, 0, 0, 0],
694
- instances: []
695
- };
696
- // Populate it.
697
- standardize(act);
698
- // Launch a browser and navigate to the page.
699
- await launch(
700
- report,
701
- debug,
702
- waits,
703
- act.browserID || report.browserID || '',
704
- act.target && act.target.url || report.target && report.target.url || ''
705
- );
706
- // If this failed:
707
- if (page.prevented) {
708
- // Add this to the act.
709
- act.prevented = true;
710
- act.error = page.error || '';
711
- }
712
- // Otherwise, i.e. if it succeeded:
713
- else {
714
- // Add a box ID and a path ID to each of its standard instances if missing.
715
- for (const instance of act.standardResult.instances) {
716
- const elementID = await identify(instance, page);
717
- if (! instance.boxID) {
718
- instance.boxID = elementID ? elementID.boxID : '';
719
- }
720
- if (! instance.pathID) {
721
- instance.pathID = elementID ? elementID.pathID : '';
722
- }
689
+ // If the act was not prevented:
690
+ if (act.data && ! act.data.prevented) {
691
+ // If standardization is required:
692
+ if (['also', 'only'].includes(standard)) {
693
+ // Initialize the standard result.
694
+ act.standardResult = {
695
+ totals: [0, 0, 0, 0],
696
+ instances: []
723
697
  };
698
+ // Populate it.
699
+ standardize(act);
700
+ // Launch a browser and navigate to the page.
701
+ await launch(
702
+ report,
703
+ debug,
704
+ waits,
705
+ act.browserID || report.browserID || '',
706
+ act.target && act.target.url || report.target && report.target.url || ''
707
+ );
708
+ // If this failed:
709
+ if (page.prevented) {
710
+ // Add this to the act.
711
+ act.prevented = true;
712
+ act.error = page.error || '';
713
+ }
714
+ // Otherwise, i.e. if it succeeded:
715
+ else {
716
+ // Add a box ID and a path ID to each of its standard instances if missing.
717
+ for (const instance of act.standardResult.instances) {
718
+ const elementID = await identify(instance, page);
719
+ if (! instance.boxID) {
720
+ instance.boxID = elementID ? elementID.boxID : '';
721
+ }
722
+ if (! instance.pathID) {
723
+ instance.pathID = elementID ? elementID.pathID : '';
724
+ }
725
+ };
726
+ }
727
+ // If the original-format result is not to be included in the report:
728
+ if (standard === 'only') {
729
+ // Remove it.
730
+ delete act.result;
731
+ }
724
732
  }
725
- // If the original-format result is not to be included in the report:
726
- if (standard === 'only') {
727
- // Remove it.
728
- delete act.result;
729
- }
733
+ // If the act has expectations:
730
734
  const expectations = act.expect;
731
- // If the act was not prevented and has expectations:
732
- if (expectations && act.data && ! act.data.prevented) {
735
+ if (expectations) {
733
736
  // Initialize whether they were fulfilled.
734
737
  act.expectations = [];
735
738
  let failureCount = 0;
package/testaro/hovInd.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- © 2023 CVS Health and/or one of its affiliates. All rights reserved.
2
+ © 2023–2025 CVS Health and/or one of its affiliates. All rights reserved.
3
3
 
4
4
  MIT License
5
5
 
@@ -64,6 +64,7 @@ const getHoverStyles = async loc => await loc.evaluate(element => {
64
64
  outlineStyle,
65
65
  outlineWidth,
66
66
  outlineOffset,
67
+ color,
67
68
  backgroundColor
68
69
  } = window.getComputedStyle(element);
69
70
  return {
@@ -72,6 +73,7 @@ const getHoverStyles = async loc => await loc.evaluate(element => {
72
73
  cursor: cursor.replace(/^.+, */, ''),
73
74
  border: `${borderColor} ${borderStyle} ${borderWidth}`,
74
75
  outline: `${outlineColor} ${outlineStyle} ${outlineWidth} ${outlineOffset}`,
76
+ color,
75
77
  backgroundColor
76
78
  };
77
79
  });
@@ -109,7 +111,7 @@ const getCursorData = hovStyles => {
109
111
  // Returns whether two hover styles are effectively identical.
110
112
  const areAlike = (styles0, styles1) => {
111
113
  // Return whether they are effectively identical.
112
- const areAlike = ['outline', 'border', 'backgroundColor']
114
+ const areAlike = ['backgroundColor', 'border', 'color', 'outline']
113
115
  .every(style => styles1[style] === styles0[style]);
114
116
  return areAlike;
115
117
  };
@@ -144,18 +146,16 @@ exports.reporter = async (page, withItems, sampleSize = 20) => {
144
146
  await loc.focus({timeout: 500});
145
147
  // If focusing succeeds, get its style properties.
146
148
  const focStyles = await getHoverStyles(loc);
147
- // Try to hover over it.
149
+ // Try to blur it.
148
150
  try {
149
- await loc.hover({timeout: 500});
150
- // If hovering succeeds, get its style properties.
151
- const fhStyles = await getHoverStyles(loc);
152
- // Try to blur it.
151
+ await loc.blur({timeout: 500});
152
+ // If blurring succeeds, try to hover over it.
153
153
  try {
154
- await loc.blur({timeout: 500});
155
- // If blurring succeeds, get its style properties.
154
+ await loc.hover({timeout: 500});
155
+ // If hovering succeeds, get its style properties.
156
156
  const hovStyles = await getHoverStyles(loc);
157
- // If all 4 style declarations belong to the same element:
158
- if ([focStyles, fhStyles, hovStyles].every(style => style.code === preStyles.code)) {
157
+ // If all 3 style declarations belong to the same element:
158
+ if ([focStyles, hovStyles].every(style => style.code === preStyles.code)) {
159
159
  // Get data on the element if itemization is required.
160
160
  const elData = withItems ? await getLocatorData(loc) : null;
161
161
  // If the hover cursor is nonstandard:
@@ -188,7 +188,7 @@ exports.reporter = async (page, withItems, sampleSize = 20) => {
188
188
  // Add an instance to the result.
189
189
  standardInstances.push({
190
190
  ruleID: 'hovInd',
191
- what: 'Element border, outline, and background color do not change when hovered over',
191
+ what: 'Element border, outline, color, and background color do not change when hovered over',
192
192
  ordinalSeverity: 1,
193
193
  tagName: elData.tagName,
194
194
  id: elData.id,
@@ -197,7 +197,7 @@ exports.reporter = async (page, withItems, sampleSize = 20) => {
197
197
  });
198
198
  }
199
199
  }
200
- // If the hover and focus-hover states are indistinct but differ from the default state:
200
+ // If the hover and focus states are indistinct but differ from the default state:
201
201
  if (areAlike(hovStyles, focStyles) && ! areAlike(hovStyles, preStyles)) {
202
202
  // Add to the totals.
203
203
  totals[1] += psRatio;
@@ -207,7 +207,7 @@ exports.reporter = async (page, withItems, sampleSize = 20) => {
207
207
  // Add an instance to the result.
208
208
  standardInstances.push({
209
209
  ruleID: 'hovInd',
210
- what: 'Element border, outline, and background color are alike on hover and focus',
210
+ what: 'Element border, outline, color, and background color are alike on hover and focus',
211
211
  ordinalSeverity: 1,
212
212
  tagName: elData.tagName,
213
213
  id: elData.id,
@@ -225,19 +225,19 @@ exports.reporter = async (page, withItems, sampleSize = 20) => {
225
225
  break;
226
226
  }
227
227
  }
228
- // If blurring fails:
228
+ // If hovering fails:
229
229
  catch(error) {
230
230
  // Report this.
231
231
  data.prevented = true;
232
- data.error = 'ERROR: Page changes on focus or hover prevent test';
232
+ data.error = 'ERROR: Hovering failed';
233
233
  break;
234
234
  }
235
235
  }
236
- // If hovering fails:
236
+ // If blurring fails:
237
237
  catch(error) {
238
238
  // Report this.
239
239
  data.prevented = true;
240
- data.error = 'ERROR: Page changes on focus or hover prevent test';
240
+ data.error = 'ERROR: Blurring failed';
241
241
  break;
242
242
  }
243
243
  }
@@ -245,7 +245,7 @@ exports.reporter = async (page, withItems, sampleSize = 20) => {
245
245
  catch(error) {
246
246
  // Report this.
247
247
  data.prevented = true;
248
- data.error = 'ERROR: Page changes on focus or hover prevent test';
248
+ data.error = 'ERROR: Focusing failed';
249
249
  break;
250
250
  }
251
251
  }
@@ -294,14 +294,14 @@ exports.reporter = async (page, withItems, sampleSize = 20) => {
294
294
  excerpt: ''
295
295
  });
296
296
  }
297
- // If any triggers have focus-hover styles not distinct from their focus styles:
298
- if (data.typeTotals.fhLikeFocus) {
297
+ // If any triggers have hover styles not distinct from their focus styles:
298
+ if (data.typeTotals.hoverLikeFocus) {
299
299
  // Add a summary instance to the result.
300
300
  standardInstances.push({
301
301
  ruleID: 'hovInd',
302
- what: 'Element borders, outlines, and background colors on focus do not change when also hovered over',
302
+ what: 'Element borders, outlines, and background colors on focus and on hover do not differ',
303
303
  ordinalSeverity: 1,
304
- count: data.typeTotals.fhLikeFocus,
304
+ count: data.typeTotals.hoverLikeFocus,
305
305
  tagName: '',
306
306
  id: '',
307
307
  location: {
package/testaro/role.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- © 2021–2024 CVS Health and/or one of its affiliates. All rights reserved.
2
+ © 2021–2025 CVS Health and/or one of its affiliates. All rights reserved.
3
3
 
4
4
  MIT License
5
5
 
@@ -47,8 +47,17 @@ const {init, report} = require('../procs/testaro');
47
47
  dt: 'term',
48
48
  fieldset: 'group',
49
49
  figure: 'figure',
50
+ h1: 'heading',
51
+ h2: 'heading',
52
+ h3: 'heading',
53
+ h4: 'heading',
54
+ h5: 'heading',
55
+ h6: 'heading',
50
56
  hr: 'separator',
51
57
  html: 'document',
58
+ 'input[type=number]': 'spinbutton',
59
+ 'input[type=text]': 'textbox',
60
+ 'input[type=text, list]': 'combobox',
52
61
  li: 'listitem',
53
62
  main: 'main',
54
63
  math: 'math',
@@ -79,7 +88,7 @@ exports.reporter = async (page, withItems) => {
79
88
  for (const loc of all.allLocs) {
80
89
  // Get the explicit role of the element.
81
90
  const role = await loc.getAttribute('role');
82
- // If it is implicit:
91
+ // If it is also implicit:
83
92
  if (implicitRoles.has(role)) {
84
93
  // Add the locator to the array of violators.
85
94
  all.locs.push([loc, role]);
@@ -87,7 +96,7 @@ exports.reporter = async (page, withItems) => {
87
96
  }
88
97
  // Populate and return the result.
89
98
  const whats = [
90
- 'Element has an explicit __param__ role, but it is also an implicit HTML element role',
99
+ 'Element has an explicit __param__ role, which is also an implicit HTML element role',
91
100
  'Elements have roles assigned that are also implicit roles of HTML elements'
92
101
  ];
93
102
  return await report(withItems, all, 'role', whats, 0);
@@ -25,7 +25,8 @@
25
25
  /*
26
26
  targetSmall
27
27
  Related to Tenon rule 152, but stricter.
28
- This test reports buttons, inputs, and non-inline links with widths or heights smaller than 44 pixels.
28
+ This test reports visible buttons, inputs, and non-inline links with widths or heights smaller
29
+ than 44 pixels.
29
30
  */
30
31
 
31
32
  // ########## IMPORTS
@@ -40,7 +41,7 @@ const {isTooSmall} = require('../procs/target');
40
41
  // Runs the test and returns the result.
41
42
  exports.reporter = async (page, withItems) => {
42
43
  // Initialize the locators and result.
43
- const all = await init(100, page, 'a, button, input');
44
+ const all = await init(100, page, 'a:visible, button:visible, input:visible');
44
45
  // For each locator:
45
46
  for (const loc of all.allLocs) {
46
47
  // Get data on it if illicitly small.
@@ -56,5 +57,5 @@ exports.reporter = async (page, withItems) => {
56
57
  'Interactive element pixel size (__param__) is less than 44 by 44',
57
58
  'Interactive elements are smaller than 44 pixels wide and high'
58
59
  ];
59
- return await report(withItems, all, 'targetSmall', whats, 1);
60
+ return await report(withItems, all, 'targetSmall', whats, 0);
60
61
  };
@@ -25,7 +25,8 @@
25
25
  /*
26
26
  targetTiny
27
27
  Related to Tenon rule 152.
28
- This test reports buttons, inputs, and non-inline links with widths or heights smaller than 24 pixels.
28
+ This test reports visible buttons, inputs, and non-inline links with widths or heights smaller
29
+ than 24 pixels.
29
30
  */
30
31
 
31
32
  // ########## IMPORTS
@@ -40,7 +41,7 @@ const {isTooSmall} = require('../procs/target');
40
41
  // Runs the test and returns the result.
41
42
  exports.reporter = async (page, withItems) => {
42
43
  // Initialize the locators and result.
43
- const all = await init(100, page, 'a, button, input');
44
+ const all = await init(100, page, 'a:visible, button:visible, input:visible');
44
45
  // For each locator:
45
46
  for (const loc of all.allLocs) {
46
47
  // Get data on it if illicitly small.
@@ -51,7 +51,7 @@
51
51
  [
52
52
  "standardResult.totals.1",
53
53
  "=",
54
- 8
54
+ 7
55
55
  ],
56
56
  [
57
57
  "standardResult.totals.3",
@@ -66,7 +66,7 @@
66
66
  [
67
67
  "standardResult.instances.0.tagName",
68
68
  "=",
69
- "A"
69
+ "INPUT"
70
70
  ],
71
71
  [
72
72
  "standardResult.instances.0.location.doc",
@@ -76,40 +76,40 @@
76
76
  [
77
77
  "standardResult.instances.0.location.type",
78
78
  "=",
79
- "box"
79
+ "selector"
80
80
  ],
81
81
  [
82
- "standardResult.instances.0.location.spec.y",
83
- ">",
84
- 0
82
+ "standardResult.instances.0.location.spec",
83
+ "=",
84
+ "#textInput"
85
85
  ],
86
86
  [
87
87
  "standardResult.instances.0.excerpt",
88
88
  "i",
89
- "English"
89
+ "narrative"
90
90
  ],
91
91
  [
92
- "standardResult.instances.3.ruleID",
92
+ "standardResult.instances.2.ruleID",
93
93
  "=",
94
94
  "focInd"
95
95
  ],
96
96
  [
97
- "standardResult.instances.3.ordinalSeverity",
97
+ "standardResult.instances.2.ordinalSeverity",
98
98
  "=",
99
99
  1
100
100
  ],
101
101
  [
102
- "standardResult.instances.3.tagName",
102
+ "standardResult.instances.2.tagName",
103
103
  "=",
104
104
  "INPUT"
105
105
  ],
106
106
  [
107
- "standardResult.instances.3.id",
107
+ "standardResult.instances.2.id",
108
108
  "=",
109
109
  "thickOutlineInput"
110
110
  ],
111
111
  [
112
- "standardResult.instances.3.location.spec",
112
+ "standardResult.instances.2.location.spec",
113
113
  "=",
114
114
  "#thickOutlineInput"
115
115
  ]
@@ -128,7 +128,7 @@
128
128
  [
129
129
  "standardResult.totals.1",
130
130
  "=",
131
- 8
131
+ 7
132
132
  ],
133
133
  [
134
134
  "standardResult.totals.0",
@@ -148,7 +148,7 @@
148
148
  [
149
149
  "standardResult.instances.0.count",
150
150
  "=",
151
- 8
151
+ 7
152
152
  ],
153
153
  [
154
154
  "standardResult.instances.0.what",
@@ -103,7 +103,7 @@
103
103
  [
104
104
  "standardResult.instances.3.what",
105
105
  "i",
106
- "are alike"
106
+ "are alike on hover and focus"
107
107
  ],
108
108
  [
109
109
  "standardResult.instances.3.ordinalSeverity",
@@ -47,14 +47,14 @@
47
47
  "stopOnFail": true,
48
48
  "expect": [
49
49
  [
50
- "standardResult.totals.1",
50
+ "standardResult.totals.0",
51
51
  "=",
52
- 3
52
+ 8
53
53
  ],
54
54
  [
55
55
  "standardResult.totals.3",
56
56
  "=",
57
- 6
57
+ 0
58
58
  ],
59
59
  [
60
60
  "standardResult.instances.1.ruleID",
@@ -64,22 +64,17 @@
64
64
  [
65
65
  "standardResult.instances.1.what",
66
66
  "i",
67
- "invalid or native-replaceable explicit role section"
67
+ "which is also an implicit"
68
68
  ],
69
69
  [
70
70
  "standardResult.instances.1.ordinalSeverity",
71
71
  "=",
72
- 3
73
- ],
74
- [
75
- "standardResult.instances.1.count",
76
- "=",
77
- 1
72
+ 0
78
73
  ],
79
74
  [
80
75
  "standardResult.instances.1.tagName",
81
76
  "=",
82
- "SECTION"
77
+ "H2"
83
78
  ],
84
79
  [
85
80
  "standardResult.instances.2.ruleID",
@@ -89,37 +84,27 @@
89
84
  [
90
85
  "standardResult.instances.2.what",
91
86
  "i",
92
- "have redundant explicit role heading"
87
+ "which is also an implicit"
93
88
  ],
94
89
  [
95
90
  "standardResult.instances.2.ordinalSeverity",
96
91
  "=",
97
- 1
98
- ],
99
- [
100
- "standardResult.instances.2.count",
101
- "=",
102
- 1
92
+ 0
103
93
  ],
104
94
  [
105
95
  "standardResult.instances.2.tagName",
106
96
  "=",
107
- "H2"
97
+ "H3"
108
98
  ],
109
99
  [
110
100
  "standardResult.instances.4.what",
111
101
  "i",
112
- "invalid or native-replaceable explicit role heading"
102
+ "which is also an implicit"
113
103
  ],
114
104
  [
115
105
  "standardResult.instances.4.ordinalSeverity",
116
106
  "=",
117
- 3
118
- ],
119
- [
120
- "standardResult.instances.4.count",
121
- "=",
122
- 2
107
+ 0
123
108
  ],
124
109
  [
125
110
  "standardResult.instances.4.tagName",
@@ -127,14 +112,14 @@
127
112
  "H3"
128
113
  ],
129
114
  [
130
- "standardResult.instances.7.what",
115
+ "standardResult.instances.7.tagName",
131
116
  "i",
132
- "combobox"
117
+ "INPUT"
133
118
  ],
134
119
  [
135
120
  "standardResult.instances.4.ordinalSeverity",
136
121
  "=",
137
- 3
122
+ 0
138
123
  ]
139
124
  ],
140
125
  "withItems": true,
@@ -1,5 +1,5 @@
1
1
  {
2
- "rule": "targetSize",
2
+ "rule": "targetSmall",
3
3
  "timeLimit": 20,
4
4
  "acts": [
5
5
  {
@@ -18,27 +18,27 @@
18
18
  [
19
19
  "standardResult.totals.0",
20
20
  "=",
21
- 0
21
+ 6
22
22
  ],
23
23
  [
24
24
  "standardResult.totals.1",
25
25
  "=",
26
- 4
26
+ 0
27
27
  ],
28
28
  [
29
29
  "standardResult.instances.0.ruleID",
30
30
  "=",
31
- "targetSize"
31
+ "targetSmall"
32
32
  ],
33
33
  [
34
34
  "standardResult.instances.0.what",
35
35
  "i",
36
- "is less than"
36
+ "is less than 44"
37
37
  ],
38
38
  [
39
39
  "standardResult.instances.0.ordinalSeverity",
40
40
  "=",
41
- 1
41
+ 0
42
42
  ],
43
43
  [
44
44
  "standardResult.instances.0.tagName",
@@ -68,7 +68,7 @@
68
68
  [
69
69
  "standardResult.instances.0.excerpt",
70
70
  "i",
71
- "(too small)"
71
+ "Default (small)"
72
72
  ],
73
73
  [
74
74
  "standardResult.instances.2.what",
@@ -83,7 +83,7 @@
83
83
  [
84
84
  "standardResult.instances.2.excerpt",
85
85
  "i",
86
- "small box"
86
+ "Small box:"
87
87
  ],
88
88
  [
89
89
  "standardResult.instances.2.location.spec.y",
@@ -91,19 +91,19 @@
91
91
  0
92
92
  ],
93
93
  [
94
- "standardResult.instances.3.tagName",
94
+ "standardResult.instances.4.tagName",
95
95
  "=",
96
96
  "A"
97
97
  ],
98
98
  [
99
- "standardResult.instances.3.excerpt",
99
+ "standardResult.instances.4.excerpt",
100
100
  "=",
101
- "Wikimedia Foundation"
101
+ "Wikimedia Foundation (small)"
102
102
  ]
103
103
  ],
104
104
  "rules": [
105
105
  "y",
106
- "targetSize"
106
+ "targetSmall"
107
107
  ]
108
108
  },
109
109
  {
@@ -113,9 +113,9 @@
113
113
  "stopOnFail": true,
114
114
  "expect": [
115
115
  [
116
- "standardResult.totals.1",
116
+ "standardResult.totals.0",
117
117
  "=",
118
- 4
118
+ 6
119
119
  ],
120
120
  [
121
121
  "standardResult.totals.3",
@@ -125,7 +125,7 @@
125
125
  [
126
126
  "standardResult.instances.0.ruleID",
127
127
  "=",
128
- "targetSize"
128
+ "targetSmall"
129
129
  ],
130
130
  [
131
131
  "standardResult.instances.0.what",
@@ -135,17 +135,17 @@
135
135
  [
136
136
  "standardResult.instances.0.ordinalSeverity",
137
137
  "=",
138
- 1
138
+ 0
139
139
  ],
140
140
  [
141
141
  "standardResult.instances.0.count",
142
142
  "=",
143
- 4
143
+ 6
144
144
  ]
145
145
  ],
146
146
  "rules": [
147
147
  "y",
148
- "targetSize"
148
+ "targetSmall"
149
149
  ]
150
150
  }
151
151
  ]
@@ -0,0 +1,142 @@
1
+ {
2
+ "rule": "targetTiny",
3
+ "timeLimit": 20,
4
+ "acts": [
5
+ {
6
+ "type": "launch",
7
+ "target": {
8
+ "url": "file://validation/tests/targets/targetSize/index.html",
9
+ "what": "page with variously sized interaction targets"
10
+ }
11
+ },
12
+ {
13
+ "type": "test",
14
+ "which": "testaro",
15
+ "withItems": true,
16
+ "stopOnFail": true,
17
+ "expect": [
18
+ [
19
+ "standardResult.totals.0",
20
+ "=",
21
+ 0
22
+ ],
23
+ [
24
+ "standardResult.totals.1",
25
+ "=",
26
+ 3
27
+ ],
28
+ [
29
+ "standardResult.instances.0.ruleID",
30
+ "=",
31
+ "targetTiny"
32
+ ],
33
+ [
34
+ "standardResult.instances.0.what",
35
+ "i",
36
+ "is less than 24"
37
+ ],
38
+ [
39
+ "standardResult.instances.0.ordinalSeverity",
40
+ "=",
41
+ 1
42
+ ],
43
+ [
44
+ "standardResult.instances.0.tagName",
45
+ "=",
46
+ "BUTTON"
47
+ ],
48
+ [
49
+ "standardResult.instances.0.id",
50
+ "=",
51
+ ""
52
+ ],
53
+ [
54
+ "standardResult.instances.0.location.doc",
55
+ "=",
56
+ "dom"
57
+ ],
58
+ [
59
+ "standardResult.instances.0.location.type",
60
+ "=",
61
+ "box"
62
+ ],
63
+ [
64
+ "standardResult.instances.0.location.spec.y",
65
+ ">",
66
+ 0
67
+ ],
68
+ [
69
+ "standardResult.instances.0.excerpt",
70
+ "=",
71
+ "Tiny"
72
+ ],
73
+ [
74
+ "standardResult.instances.2.what",
75
+ "i",
76
+ "is less than 24"
77
+ ],
78
+ [
79
+ "standardResult.instances.2.tagName",
80
+ "=",
81
+ "A"
82
+ ],
83
+ [
84
+ "standardResult.instances.2.excerpt",
85
+ "=",
86
+ "Wikimedia Foundation (tiny)"
87
+ ],
88
+ [
89
+ "standardResult.instances.2.location.spec.x",
90
+ ">",
91
+ 0
92
+ ]
93
+ ],
94
+ "rules": [
95
+ "y",
96
+ "targetTiny"
97
+ ]
98
+ },
99
+ {
100
+ "type": "test",
101
+ "which": "testaro",
102
+ "withItems": false,
103
+ "stopOnFail": true,
104
+ "expect": [
105
+ [
106
+ "standardResult.totals.1",
107
+ "=",
108
+ 3
109
+ ],
110
+ [
111
+ "standardResult.totals.3",
112
+ "=",
113
+ 0
114
+ ],
115
+ [
116
+ "standardResult.instances.0.ruleID",
117
+ "=",
118
+ "targetTiny"
119
+ ],
120
+ [
121
+ "standardResult.instances.0.what",
122
+ "i",
123
+ "are smaller"
124
+ ],
125
+ [
126
+ "standardResult.instances.0.ordinalSeverity",
127
+ "=",
128
+ 1
129
+ ],
130
+ [
131
+ "standardResult.instances.0.count",
132
+ "=",
133
+ 3
134
+ ]
135
+ ],
136
+ "rules": [
137
+ "y",
138
+ "targetTiny"
139
+ ]
140
+ }
141
+ ]
142
+ }
@@ -1,6 +1,6 @@
1
1
  <!DOCTYPE html>
2
2
  <!--
3
- © 2022–2023 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2022–2025 CVS Health and/or one of its affiliates. All rights reserved.
4
4
 
5
5
  MIT License
6
6
 
@@ -75,15 +75,15 @@
75
75
  <body>
76
76
  <main>
77
77
  <h1>Page with mixed focus indication</h1>
78
- <p>Defect 1: This paragraph contains a link to <a href="https://en.wikipedia.org">English information</a>, which exhibits a default focus indicator (outline).</p>
78
+ <p>Nondefect 1: This paragraph contains a link to <a href="https://en.wikipedia.org">English information</a>, which exhibits a default focus indicator (outline), which may be hard to perceive but is generally classified as a non-defect.</p>
79
79
  <p>This paragraph contains a <button id="button" type="button">button</button> with a custom outline as its focus indicator (outline).</p>
80
- <p>Defect 2: This paragraph contains a <label>text input <input id="textInput" type="text"></label> with the focus indicator suppressed (none).</p>
81
- <p>Defect 3: This paragraph contains a <label>text input <input id="outlinedInput" type="text"></label> with a custom outline, which changes only color when the input is focused (other).</p>
82
- <p>Defect 4: This paragraph contains a <label>text input <input id="thickOutlineInput" type="text"></label> with a custom outline, which changes only thickness when the input is focused (other).</p>
83
- <p>Defect 5: This paragraph contains a checkbox with a custom focus-indication outline that finishes appearing 75 ms after the checkbox becomes focused (outline). <label><input id="checkbox" type="checkbox"> I am a bad page.</label></p>
84
- <p>Defect 6: This paragraph contains a link to <a id="slowLink" href="https://fr.wikipedia.org">French information</a> with a custom border as its only focus indicator. The border appears 125 ms after the link becomes focused (other).</p>
85
- <p>Defect 7: This paragraph contains a link to <a id="sluggishLink" href="https://de.wikipedia.org">German information</a> with a custom border as its only focus indicator. The border appears 525 ms after the link becomes focused (none).</p>
86
- <p>Defect 8: This paragraph contains a button with a focus indicator consisting solely of an underline and an overline (other). <button id="special" type="button">special button</button></p>
80
+ <p>Defect 1: This paragraph contains a <label>narrative input <input id="textInput" type="text"></label> with the focus indicator suppressed (none).</p>
81
+ <p>Defect 2: This paragraph contains a <label>text input <input id="outlinedInput" type="text"></label> with a custom outline, which changes only color when the input is focused (other).</p>
82
+ <p>Defect 3: This paragraph contains a <label>text input <input id="thickOutlineInput" type="text"></label> with a custom outline, which changes only thickness when the input is focused (other).</p>
83
+ <p>Defect 4: This paragraph contains a checkbox with a custom focus-indication outline that finishes appearing 75 ms after the checkbox becomes focused (outline). <label><input id="checkbox" type="checkbox"> I am a bad page.</label></p>
84
+ <p>Defect 5: This paragraph contains a link to <a id="slowLink" href="https://fr.wikipedia.org">French information</a> with a custom border as its only focus indicator. The border appears 125 ms after the link becomes focused (other).</p>
85
+ <p>Defect 6: This paragraph contains a link to <a id="sluggishLink" href="https://de.wikipedia.org">German information</a> with a custom border as its only focus indicator. The border appears 525 ms after the link becomes focused (none).</p>
86
+ <p>Defect 7: This paragraph contains a button with a focus indicator consisting solely of an underline and an overline (other). <button id="special" type="button">special button</button></p>
87
87
  </main>
88
88
  </body>
89
89
  </html>
@@ -1,6 +1,6 @@
1
1
  <!DOCTYPE html>
2
2
  <!--
3
- © 2023 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2023–2025 CVS Health and/or one of its affiliates. All rights reserved.
4
4
 
5
5
  MIT License
6
6
 
@@ -32,7 +32,9 @@
32
32
  .ambig:hover, .ambig:focus {
33
33
  color: blue;
34
34
  background-color: yellow;
35
- outline: 2px solid yellow
35
+ border: 1px solid black;
36
+ outline: 2px solid yellow;
37
+ outline-offset: 2px;
36
38
  }
37
39
  .cursorless {
38
40
  cursor: none;
@@ -52,15 +54,15 @@
52
54
  <body>
53
55
  <main>
54
56
  <h1>Page with various hover indicators</h1>
55
- <p>This <a href="https://www.w3.org/">Trigger 0</a> is plain.</p>
56
- <p>Here is a link named <a id="trigger1" class="qCursor" href="https://en.wikipedia.org">Trigger 1</a>. When hovered over, it wrongly changes the cursor to a question mark.</p>
57
- <p>This is a button named <button class="cursorless">Trigger 2</button>. It makes the cursor invisible when being hovered over. It also makes no change to its other styles on hover. The browser may slightly darken the background color on hover, but keeps the computed background-color style unchanged. Since the change by the browser is almost imperceptible, this test treats it as no change.</p>
58
- <p>This is an input with a default type. <label>Trigger 3: Enter something <input></label></p>
59
- <p>This is an email input that fails to distinguish hover from focus states. <label>Trigger 4: Enter an email address <input class="ambig" type="email"></label></p>
60
- <p>This is a button named <button class="inert">Trigger 5</button>. It does not change when hovered over.</p>
61
- <p>This is a button named <button class="hoverBC">Trigger 6</button>. Its background color changes when hovered over.</p>
62
- <p>Trigger 1 has a bad hover cursor. Trigger 2 has no hover cursor. Trigger 4 has the same focus and hover indicator. Trigger 5 has no hover indicator.</p>
63
- <p>Impact severities: bad cursor 2 (2 instances), ambiguous indicator 1 (1 instance), missing hover indicator 1 (2 instances).
57
+ <p>This <a href="https://www.w3.org/">Trigger 0</a> is plain. No violation.</p>
58
+ <p>Here is a link named <a id="trigger1" class="qCursor" href="https://en.wikipedia.org">Trigger 1</a>. When hovered over, it wrongly changes the cursor to a question mark. This is violation 0.</p>
59
+ <p>This is a button named <button class="cursorless">Trigger 2</button>. It makes the cursor invisible when being hovered over (violation 1). It also makes no change to its other styles on hover (violation 2). The browser may slightly darken the background color on hover, but keeps the computed background-color style unchanged. Since the change by the browser is almost imperceptible, this test treats it as no change.</p>
60
+ <p>This is an input with a default type; no violation. <label>Trigger 3: Enter something <input></label></p>
61
+ <p>This is an email input that fails to distinguish hover from focus states (violation 3). <label>Trigger 4: Enter an email address <input class="ambig" type="email"></label></p>
62
+ <p>This is a button named <button class="inert">Trigger 5</button>. It does not change when hovered over. This is violation 4.</p>
63
+ <p>This is a button named <button class="hoverBC">Trigger 6</button>. Its background color changes when hovered over. No violation.</p>
64
+ <p>Trigger 1 has a bad hover cursor (violation 0). Trigger 2 has no hover cursor (violation 1). Trigger 2 has missing hover indicator (violation 2). Trigger 4 has the same focus and hover indicator (violation 3). Trigger 5 has no hover indicator (violation 4).</p>
65
+ <p>Impact severities: bad hover cursor 2 (2 instances, 0 and 1), ambiguous indicators 1 (1 instance, 3), missing hover indicator 1 (2 instances, 2 and 4).
64
66
  </main>
65
67
  </body>
66
68
  </html>
@@ -1,6 +1,6 @@
1
1
  <!DOCTYPE html>
2
2
  <!--
3
- © 2021–2023 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2021–2025 CVS Health and/or one of its affiliates. All rights reserved.
4
4
 
5
5
  MIT License
6
6
 
@@ -25,52 +25,48 @@
25
25
  <html lang="en-US">
26
26
  <head>
27
27
  <meta charset="utf-8">
28
- <title>Page with deviant role elements</title>
28
+ <title>Page with elements having explicit native-replacing roles</title>
29
29
  <meta name="description" content="tester">
30
30
  <meta name="viewport" content="width=device-width, initial-scale=1">
31
31
  </head>
32
32
  <body>
33
33
  <section role="main">
34
- <h1>Page with deviant role elements</h1>
34
+ <h1>Page with elements having explicit native-replacing roles</h1>
35
+ <section>
36
+ <h2>Conflict</h2>
37
+ <p>The parent section of these sections has an unnecessary explicit role <code>main</code>, instead of a <code>main</code> element with the same implicit role. Violation 0.</p>
38
+ </section>
35
39
  <section role="section">
36
40
  <h2>Abstraction</h2>
37
- <p>This section has an abstract role, so it is bad.</p>
41
+ <p>This section has an abstract role, which is invalid, but it does not violate this rule.</p>
38
42
  </section>
39
43
  <section>
40
44
  <h2 role="heading">Redundancy</h2>
41
- <p>The heading of this section has a redundant role, with an inferred level.</p>
45
+ <p>The heading of this section has a redundant role, with an inferred level. Violation 1.</p>
42
46
  <section>
43
47
  <h3 role="heading" aria-level="3">Redundancy with explicit level</h3>
44
- <p>The heading of this section has a redundant role because the stated level is 3 and the implicit level is 3.</p>
48
+ <p>The heading of this section has a redundant role because the stated level is 3 and the implicit level is 3. Violation 2.</p>
45
49
  </section>
46
50
  <section>
47
51
  <h3 role="heading">Failed redundancy for missing level</h3>
48
- <p>The heading of this section has a heading that fails redundancy because the inferred level is 2 but the explicit level is 3. So the element role is bad.</p>
52
+ <p>The heading of this section has a heading that fails redundancy because the inferred level is 2 but the explicit level is 3. It violates this rule regardless of the missing level. Violation 3.</p>
49
53
  </section>
50
54
  <section>
51
55
  <h3 role="heading" aria-level="4">Failed redundancy for wrong level</h3>
52
- <p>The heading of this section has a heading that fails redundancy because the attributional level is 4 but the explicit level is 3. So the element role is bad.</p>
56
+ <p>The heading of this section has a heading that fails redundancy because the attributional level is 4 but the explicit level is 3. So the element role is bad. It violates the rule regardless of the level error. Violation 4.</p>
53
57
  </section>
54
58
  </section>
55
- <section>
56
- <h2>Conflict</h2>
57
- <p>The parent section of these sections has an unnecessary explicit role <code>main</code>, instead of a <code>main</code> element with the same implicit role.</p>
58
- </section>
59
59
  <section>
60
60
  <h2>Attributes</h2>
61
61
  <h3>Valid redundancy</h3>
62
- <p>This paragraph contains an input for a number. It has an implicit <code>spinbutton</code> role and the same redundant explicit role. <input type="number" role="spinbutton"></p>
62
+ <p>This paragraph contains an input for a number. It has an implicit <code>spinbutton</code> role and the same redundant explicit role. Violation 5. <input type="number" role="spinbutton"></p>
63
63
  <h3>Failed redundancy</h3>
64
- <p>This paragraph contains an input for a number. It has an implicit <code>spinbutton</code> role but an explicit <code>textbox</code> role, so its role is bad. <input type="number" role="textbox"></p>
64
+ <p>This paragraph contains an input for a number. It has an implicit <code>spinbutton</code> role but an explicit <code>textbox</code> role, so its role is bad. Violation 6. <input type="number" role="textbox"></p>
65
65
  <h3>Attribute existence</h3>
66
66
  <datalist id="options"><option value="a"></option><option value="b"></option></datalist>
67
- <p>This input is identical, except that it omits the <code>list</code> attribute. That makes its implicit role <code>textbox</code>, so the explicit role of <code>combobox</code> is bad. <input role="combobox"></p>
67
+ <p>This input is identical, except that it omits the <code>list</code> attribute. That makes its implicit role <code>textbox</code>, so the explicit role of <code>combobox</code> is bad. Violation 7. <input role="combobox"></p>
68
68
  </section>
69
- <p>Redundant: h2/heading, h3/heading, section/main, input/spinbutton</p>
70
- <p>Bad: section/section, h3/heading(2), input/textbox, input/combobox</p>
71
- <p>Redundant order: section/main, h2/heading, h3/heading, input/spinbutton</p>
72
- <p>Bad order: section/section, h3/heading, input/textbox, input/combobox</p>
73
- <p>Instance order: section(main, section), h2(heading), h3(heading[3]), input(spinbutton, textbox, combobox)</p>
69
+ <p>8 rule violations, ordinal severity 0</p>
74
70
  </section>
75
71
  </body>
76
72
  </html>
@@ -1,6 +1,6 @@
1
1
  <!DOCTYPE html>
2
2
  <!--
3
- © 2023 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2023–2025 CVS Health and/or one of its affiliates. All rights reserved.
4
4
 
5
5
  MIT License
6
6
 
@@ -38,16 +38,24 @@
38
38
  padding-top: 1.5rem;
39
39
  padding-bottom: 1.5rem;
40
40
  }
41
+ a.normal {
42
+ display: inline-block;
43
+ padding-top: 0.5rem;
44
+ padding-bottom: 0.5rem;
45
+ }
46
+ a.shallow {
47
+ display: inline-block;
48
+ padding-top: 0;
49
+ padding-bottom: 0;
50
+ }
51
+ #defaultbutton {
52
+ min-height: 25px;
53
+ }
41
54
  li {
42
55
  text-align: left;
43
56
  background-color: #ccc;
44
57
  margin: 0.5rem 0;
45
58
  }
46
- ul {
47
- display: flex;
48
- flex-direction: column;
49
- align-items: flex-start;
50
- }
51
59
  </style>
52
60
  </head>
53
61
  <body>
@@ -56,25 +64,28 @@
56
64
  <h2>Buttons</h2>
57
65
  <p>Here are three buttons:</p>
58
66
  <p><button style="min-width: 3rem; min-height: 3rem" type="button">Big enough</button></p>
59
- <p><button id="defaultbutton" type="button">Default (too small)</button></p>
60
- <p><button style="height: 1.5rem" type="button">Too small</button></p>
67
+ <p><button id="defaultbutton" type="button">Default (small)</button></p>
68
+ <p><button style="height: 1.25rem" type="button">Tiny</button></p>
61
69
  <h2>Inputs</h2>
62
- <p>Here are two inputs:</p>
63
- <p><label>Enter a word in this big box: <input style="min-width: 3rem; min-height: 3rem" size="20" maxlength="30"></label></p>
64
- <p><label>Enter a word in this small box: <input size="20" maxlength="30"></label></p>
70
+ <p>Here are three inputs:</p>
71
+ <p><label>Big box: <input style="min-width: 3rem; min-height: 3rem" size="20" maxlength="30"></label></p>
72
+ <p><label>Small box: <input style="min-height: 1.5rem" size="20" maxlength="30"></label></p>
73
+ <p><label>Tiny box: <input style="max-height: 1rem" size="20" maxlength="30"></label></p>
65
74
  <h2>Links</h2>
75
+ <p>Here are three links:</p>
66
76
  <ul>
67
- <li style="min-height: 3rem"><a class="deep" href="https://w3c.org">World Wide Web Consortium</a></li>
68
- <li><a href="https://wikimedia.org">Wikimedia Foundation</a></li>
77
+ <li><a class="deep" href="https://w3c.org">World Wide Web Consortium (big)</a></li>
78
+ <li><a class="normal" href="https://wikimedia.org">Wikimedia Foundation (small)</a></li>
79
+ <li><a class="shallow" href="https://wikimedia.org">Wikimedia Foundation (tiny)</a></li>
69
80
  </ul>
70
81
  <ul>
71
- <li style="min-height: 3rem">W3C: <a class="deep" href="https://w3c.org">World Wide Web Consortium</a></li>
72
- <li>WF: <a href="https://wikimedia.org">Wikimedia Foundation</a></li>
82
+ <li style="min-height: 3rem">W3C: <a class="deep" href="https://w3c.org">World Wide Web Consortium (big)</a></li>
83
+ <li>WF: <a href="https://wikimedia.org">Wikimedia Foundation (small but inline)</a></li>
84
+ <li>WF: <a href="https://wikimedia.org">Wikimedia Foundation (tiny but inline)</a></li>
85
+ <h2>Violations</h2>
86
+ <p>Small: 2 buttons, 2 inputs, 2 links</p>
87
+ <p>Tiny: 1 button, 1 input, 1 link</p>
73
88
  </ul>
74
- <h2>Conclusion</h2>
75
- <p>2 buttons are too small.</p>
76
- <p>1 input is too small.</p>
77
- <p>1 link is too small.</p>
78
89
  </main>
79
90
  </body>
80
91
  </html>
@@ -35,7 +35,6 @@ const fs = require('fs').promises;
35
35
  const {doJob} = require('../run');
36
36
 
37
37
  // CONSTANTS
38
-
39
38
  const job = {
40
39
  id: '250101T0000-aaa-00',
41
40
  what: '',
@@ -73,6 +72,9 @@ exports.validateTest = async testID => {
73
72
  // Get data for a job for the test.
74
73
  const jobProperties = require(`./tests/jobProperties/${testID}.json`);
75
74
  // Use the data to complete the job.
75
+ if (jobProperties.standard) {
76
+ job.standard = jobProperties.standard;
77
+ }
76
78
  job.what = `validate Testaro test ${jobProperties.rule}`;
77
79
  job.timeLimit = jobProperties.timeLimit;
78
80
  job.acts = jobProperties.acts;