testaro 67.0.0 → 68.0.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 (95) hide show
  1. package/LICENSE +4 -16
  2. package/README.md +10 -2
  3. package/UPGRADES.md +1 -1
  4. package/dirWatch.js +2 -3
  5. package/ed11y/editoria11y.min.js +109 -690
  6. package/ed11y/editoria11y210.min.js +747 -0
  7. package/netWatch.js +6 -6
  8. package/package.json +1 -1
  9. package/procs/aslint.js +2 -2
  10. package/procs/catalog.js +190 -0
  11. package/procs/{dateOf.js → dateTime.js} +6 -4
  12. package/procs/doActs.js +1227 -0
  13. package/procs/doTestAct.js +63 -29
  14. package/procs/error.js +53 -0
  15. package/procs/job.js +64 -38
  16. package/procs/launch.js +596 -0
  17. package/procs/nu.js +3 -18
  18. package/procs/shoot.js +18 -2
  19. package/procs/testaro.js +102 -125
  20. package/procs/xPath.js +62 -0
  21. package/run.js +42 -1938
  22. package/scratch/README.md +9 -0
  23. package/testaro/adbID.js +3 -3
  24. package/testaro/allCaps.js +4 -5
  25. package/testaro/allHidden.js +19 -18
  26. package/testaro/allSlanted.js +4 -5
  27. package/testaro/altScheme.js +3 -3
  28. package/testaro/attVal.js +19 -35
  29. package/testaro/autocomplete.js +65 -62
  30. package/testaro/bulk.js +21 -20
  31. package/testaro/buttonMenu.js +112 -33
  32. package/testaro/captionLoc.js +3 -3
  33. package/testaro/datalistRef.js +4 -5
  34. package/testaro/distortion.js +3 -3
  35. package/testaro/docType.js +6 -9
  36. package/testaro/dupAtt.js +12 -25
  37. package/testaro/elements.js +4 -3
  38. package/testaro/embAc.js +4 -2
  39. package/testaro/focAll.js +6 -13
  40. package/testaro/focAndOp.js +3 -3
  41. package/testaro/focInd.js +3 -3
  42. package/testaro/focVis.js +4 -3
  43. package/testaro/headEl.js +5 -12
  44. package/testaro/headingAmb.js +45 -88
  45. package/testaro/hovInd.js +5 -5
  46. package/testaro/hover.js +44 -8
  47. package/testaro/hr.js +4 -4
  48. package/testaro/imageLink.js +3 -3
  49. package/testaro/labClash.js +3 -3
  50. package/testaro/legendLoc.js +3 -3
  51. package/testaro/lineHeight.js +3 -3
  52. package/testaro/linkAmb.js +25 -17
  53. package/testaro/linkExt.js +5 -5
  54. package/testaro/linkOldAtt.js +4 -3
  55. package/testaro/linkTo.js +4 -3
  56. package/testaro/linkUl.js +4 -5
  57. package/testaro/miniText.js +4 -3
  58. package/testaro/motion.js +3 -22
  59. package/testaro/nonTable.js +4 -5
  60. package/testaro/optRoleSel.js +3 -3
  61. package/testaro/phOnly.js +3 -3
  62. package/testaro/pseudoP.js +5 -5
  63. package/testaro/radioSet.js +4 -5
  64. package/testaro/role.js +4 -5
  65. package/testaro/secHeading.js +4 -5
  66. package/testaro/shoot0.js +3 -2
  67. package/testaro/shoot1.js +3 -2
  68. package/testaro/styleDiff.js +5 -12
  69. package/testaro/tabNav.js +30 -118
  70. package/testaro/targetSmall.js +30 -15
  71. package/testaro/textNodes.js +3 -1
  72. package/testaro/textSem.js +4 -5
  73. package/testaro/title.js +4 -2
  74. package/testaro/titledEl.js +3 -3
  75. package/testaro/zIndex.js +3 -3
  76. package/tests/alfa.js +28 -54
  77. package/tests/aslint.js +20 -53
  78. package/tests/axe.js +76 -13
  79. package/tests/ed11y.js +69 -141
  80. package/tests/htmlcs.js +69 -38
  81. package/tests/ibm.js +54 -9
  82. package/tests/nuVal.js +65 -12
  83. package/tests/nuVnu.js +76 -26
  84. package/tests/qualWeb.js +89 -44
  85. package/tests/testaro.js +288 -273
  86. package/tests/wave.js +142 -117
  87. package/tests/wax.js +61 -42
  88. package/procs/getLocatorData.js +0 -192
  89. package/procs/identify.js +0 -250
  90. package/procs/isInlineLink.js +0 -42
  91. package/procs/screenShot.js +0 -32
  92. package/procs/standardize.js +0 -524
  93. package/procs/target.js +0 -90
  94. package/procs/tellServer.js +0 -43
  95. package/scripts/dumpAlts.js +0 -28
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  © 2025 CVS Health and/or one of its affiliates. All rights reserved.
3
3
  © 2025 Juan S. Casado.
4
- © 2025 Jonathan Robert Pool.
4
+ © 2025–2026 Jonathan Robert Pool.
5
5
 
6
6
  Licensed under the MIT License. See LICENSE file at the project root or
7
7
  https://opensource.org/license/mit/ for details.
@@ -20,7 +20,7 @@ const {doTest} = require('../procs/testaro');
20
20
 
21
21
  // FUNCTIONS
22
22
 
23
- exports.reporter = async (page, withItems) => {
23
+ exports.reporter = async (page, catalog, withItems) => {
24
24
  const getBadWhat = element => {
25
25
  const parent = element.parentElement;
26
26
  // If the element is not the first child of a table element:
@@ -31,6 +31,6 @@ exports.reporter = async (page, withItems) => {
31
31
  };
32
32
  const whats = 'caption elements are not the first children of table elements';
33
33
  return await doTest(
34
- page, withItems, 'captionLoc', 'caption', whats, 3, 'CAPTION', getBadWhat.toString()
34
+ page, catalog, withItems, 'captionLoc', 'caption', whats, 3, getBadWhat.toString()
35
35
  );
36
36
  };
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  © 2025 CVS Health and/or one of its affiliates. All rights reserved.
3
3
  © 2025 Juan S. Casado.
4
- © 2025 Jonathan Robert Pool.
4
+ © 2025–2026 Jonathan Robert Pool.
5
5
 
6
- Licensed under the MIT License. See LICENSE file at the project root or
7
- https://opensource.org/license/mit/ for details.
6
+ Licensed under the MIT License. See LICENSE file at the project root or https://opensource.org/license/mit/ for details.
8
7
 
9
8
  SPDX-License-Identifier: MIT
10
9
  */
@@ -20,7 +19,7 @@ const {doTest} = require('../procs/testaro');
20
19
 
21
20
  // FUNCTIONS
22
21
 
23
- exports.reporter = async (page, withItems) => {
22
+ exports.reporter = async (page, catalog, withItems) => {
24
23
  const getBadWhat = element => {
25
24
  // Get the ID of the datalist element referenced by the list attribute of the element.
26
25
  const listID = element.getAttribute('list');
@@ -47,6 +46,6 @@ exports.reporter = async (page, withItems) => {
47
46
  };
48
47
  const whats = 'list attributes of input elements are empty or IDs of no or non-datalist elements';
49
48
  return await doTest(
50
- page, withItems, 'datalistRef', 'input[list]', whats, 3, 'INPUT', getBadWhat.toString()
49
+ page, catalog, withItems, 'datalistRef', 'input[list]', whats, 3, getBadWhat.toString()
51
50
  );
52
51
  };
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  © 2023 CVS Health and/or one of its affiliates. All rights reserved.
3
- © 2025 Jonathan Robert Pool.
3
+ © 2025–2026 Jonathan Robert Pool.
4
4
 
5
5
  Licensed under the MIT License. See LICENSE file at the project root or
6
6
  https://opensource.org/license/mit/ for details.
@@ -21,7 +21,7 @@ const {doTest} = require('../procs/testaro');
21
21
  // FUNCTIONS
22
22
 
23
23
  // Runs the test and returns the result.
24
- exports.reporter = async (page, withItems) => {
24
+ exports.reporter = async (page, catalog, withItems) => {
25
25
  const getBadWhat = element => {
26
26
  const styleDec = window.getComputedStyle(element);
27
27
  const {transform} = styleDec;
@@ -38,6 +38,6 @@ exports.reporter = async (page, withItems) => {
38
38
  };
39
39
  const whats = 'Elements distort their texts';
40
40
  return await doTest(
41
- page, withItems, 'distortion', 'body *', whats, 0, null, getBadWhat.toString()
41
+ page, catalog, withItems, 'distortion', 'body *', whats, 0, getBadWhat.toString()
42
42
  );
43
43
  };
@@ -1,5 +1,6 @@
1
1
  /*
2
2
  © 2022–2023 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2026 Jonathan Robert Pool.
3
4
 
4
5
  Licensed under the MIT License. See LICENSE file at the project root or
5
6
  https://opensource.org/license/mit/ for details.
@@ -12,13 +13,16 @@
12
13
  Derived from the bbc-a11y allDocumentsMustHaveAW3cRecommendedDoctype test.
13
14
  This test reports a failure to equip the page document with a W3C-recommended HTML doctype.
14
15
  */
16
+
17
+ // Runs the test and returns the result.
15
18
  exports.reporter = async page => {
16
- // Identify the visible links without href attributes.
19
+ // Returns whether the page declares a document type.
17
20
  const docHasType = await page.evaluate(() => {
18
21
  const docType = document.doctype;
19
22
  const docHasType = !! docType && docType.name && docType.name.toLowerCase() === 'html';
20
23
  return docHasType;
21
24
  });
25
+ // Return data, totals, and, if no document type is declared, a standard instance.
22
26
  return {
23
27
  data: {docHasType},
24
28
  totals: [0, 0, 0, docHasType ? 0 : 1],
@@ -26,14 +30,7 @@ exports.reporter = async page => {
26
30
  ruleID: 'docType',
27
31
  what: 'Document has no standard HTML doctype preamble',
28
32
  ordinalSeverity: 3,
29
- tagName: 'HTML',
30
- id: '',
31
- location: {
32
- doc: 'dom',
33
- type: 'selector',
34
- spec: 'html'
35
- },
36
- excerpt: ''
33
+ count: 1
37
34
  }]
38
35
  };
39
36
  };
package/testaro/dupAtt.js CHANGED
@@ -1,15 +1,15 @@
1
1
  /*
2
2
  © 2023–2024 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2026 Jonathan Robert Pool.
3
4
 
4
- Licensed under the MIT License. See LICENSE file at the project root or
5
- https://opensource.org/license/mit/ for details.
5
+ Licensed under the MIT License. See LICENSE file at the project root or https://opensource.org/license/mit/ for details.
6
6
 
7
7
  SPDX-License-Identifier: MIT
8
8
  */
9
9
 
10
10
  /*
11
11
  dupAtt.js
12
- This test reports duplicate attributes in the source of a document.
12
+ This test reports duplicate attributes. The test is performed on the source of the document, because browsers delete additional same-name attributes in the DOM.
13
13
  */
14
14
 
15
15
  // ########## IMPORTS
@@ -19,8 +19,8 @@ const {getSource} = require('../procs/getSource');
19
19
 
20
20
  // ########## FUNCTIONS
21
21
 
22
- // Reports failures.
23
- exports.reporter = async (page, withItems) => {
22
+ // Runs the test and returns the result.
23
+ exports.reporter = async (page, _, withItems) => {
24
24
  // Initialize the data and standard result.
25
25
  const data = {total: 0};
26
26
  if (withItems) {
@@ -88,38 +88,25 @@ exports.reporter = async (page, withItems) => {
88
88
  });
89
89
  // If itemization is required and there are any instances:
90
90
  if (data.items) {
91
- // For each instance:
91
+ // For each violator:
92
92
  data.items.forEach(item => {
93
- // Add it.
93
+ // Add an instance to the standard instances.
94
94
  standardInstances.push({
95
95
  ruleID: 'dupAtt',
96
- what: 'Element has 2 attributes with the same name',
96
+ what: `${item.tagName} element has 2 attributes named ${item.duplicatedAttribute}`,
97
97
  ordinalSeverity: 2,
98
- tagName: item.tagName,
99
- id: item.id,
100
- location: {
101
- doc: item.id ? 'source' : '',
102
- type: item.id ? 'selector' : '',
103
- spec: item.id ? `#${item.id}` : ''
104
- },
105
- excerpt: item.duplicatedAttribute
98
+ count: 1
106
99
  });
107
100
  });
108
101
  }
109
- // Otherwise, if there are any instances:
102
+ // Otherwise, if itemization is not required and there are any violations:
110
103
  else if (data.total) {
111
- // Add a summary instance.
104
+ // Add a summary instance to the standard instances.
112
105
  standardInstances.push({
113
106
  ruleID: 'dupAtt',
114
107
  what: 'In some elements 2 attributes have the same name',
115
108
  ordinalSeverity: 2,
116
- count: data.total,
117
- location: {
118
- doc: '',
119
- type: '',
120
- spec: ''
121
- },
122
- excerpt: ''
109
+ count: data.total
123
110
  });
124
111
  }
125
112
  totals = [0, 0, data.total, 0];
@@ -1,8 +1,8 @@
1
1
  /*
2
2
  © 2023 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2026 Jonathan Robert Pool.
3
4
 
4
- Licensed under the MIT License. See LICENSE file at the project root or
5
- https://opensource.org/license/mit/ for details.
5
+ Licensed under the MIT License. See LICENSE file at the project root or https://opensource.org/license/mit/ for details.
6
6
 
7
7
  SPDX-License-Identifier: MIT
8
8
  */
@@ -15,8 +15,9 @@
15
15
  2. Data on each specified element also include the text content of the parent element.
16
16
  3. Data on each specified element also include data on its sibling nodes.
17
17
  */
18
+
18
19
  exports.reporter = async (
19
- page, withItems, detailLevel = 0, tagName = null, onlyVisible = false, attribute
20
+ page, _, _, detailLevel = 0, tagName = null, onlyVisible = false, attribute
20
21
  ) => {
21
22
  // Determine a selector of the specified elements, including any descendants of open shadow roots.
22
23
  let selector = `body ${tagName ? tagName.toLowerCase() : '*'}`;
package/testaro/embAc.js CHANGED
@@ -1,5 +1,6 @@
1
1
  /*
2
2
  © 2021–2023 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2026 Jonathan Robert Pool.
3
4
 
4
5
  Licensed under the MIT License. See LICENSE file at the project root or
5
6
  https://opensource.org/license/mit/ for details.
@@ -18,7 +19,8 @@ const {doTest} = require('../procs/testaro');
18
19
 
19
20
  // FUNCTIONS
20
21
 
21
- exports.reporter = async (page, withItems) => {
22
+ // Runs the test and returns the result.
23
+ exports.reporter = async (page, catalog, withItems) => {
22
24
  const getBadWhat = element => {
23
25
  // Get whether the embedding element is a link or a button.
24
26
  const embedder = element.parentElement.closest('a, button');
@@ -30,5 +32,5 @@ exports.reporter = async (page, withItems) => {
30
32
  .map(tag => `a ${tag}, button ${tag}`)
31
33
  .join(', ');
32
34
  const whats = 'interactive elements are embedded in links or buttons';
33
- return await doTest(page, withItems, 'embAc', selector, whats, 2, null, getBadWhat.toString());
35
+ return await doTest(page, catalog, withItems, 'embAc', selector, whats, 2, getBadWhat.toString());
34
36
  };
package/testaro/focAll.js CHANGED
@@ -1,9 +1,8 @@
1
1
  /*
2
2
  © 2021–2023 CVS Health and/or one of its affiliates. All rights reserved.
3
- © 2025 Jonathan Robert Pool.
3
+ © 2025–2026 Jonathan Robert Pool.
4
4
 
5
- Licensed under the MIT License. See LICENSE file at the project root or
6
- https://opensource.org/license/mit/ for details.
5
+ Licensed under the MIT License. See LICENSE file at the project root or https://opensource.org/license/mit/ for details.
7
6
 
8
7
  SPDX-License-Identifier: MIT
9
8
  */
@@ -12,6 +11,8 @@
12
11
  focAll
13
12
  This test reports discrepancies between focusable and Tab-focused element counts. The test first counts all the visible focusable (i.e. with tabIndex 0) elements (except counting each group of radio buttons as only one focusable element). Then it repeatedly presses the Tab (or Option-Tab in webkit) key until it has reached all the elements it can and counts those elements. If the two counts differ, navigation can be made more difficult. The cause may be surprising changes in content during navigation with the Tab key, or inability to reach every focusable element (or widget, such as one radio button or tab in each group) merely by pressing the Tab key.
14
13
  */
14
+
15
+ // Runs the test and returns the result.
15
16
  exports.reporter = async page => {
16
17
  // Get locators of visible elements.
17
18
  const locAll = await page.locator('body *:visible');
@@ -32,7 +33,7 @@ exports.reporter = async page => {
32
33
  */
33
34
  let tabFocused = 0;
34
35
  let refocused = 0;
35
- const keyName = page.browserID === 'webkit' ? 'Alt+Tab' : 'Tab';
36
+ const keyName = page.context().browser()?.browserType().name() === 'webkit' ? 'Alt+Tab' : 'Tab';
36
37
  while (refocused < 100 && tabFocused < 2000) {
37
38
  await page.keyboard.press(keyName);
38
39
  const isNewFocus = await page.evaluate(() => {
@@ -65,16 +66,8 @@ exports.reporter = async page => {
65
66
  standardInstances: count ? [{
66
67
  ruleID: 'focAll',
67
68
  what: 'Some focusable elements are not Tab-focusable or vice versa',
68
- count,
69
69
  ordinalSeverity: 2,
70
- tagName: '',
71
- id: '',
72
- location: {
73
- doc: '',
74
- type: '',
75
- spec: ''
76
- },
77
- excerpt: ''
70
+ count
78
71
  }] : []
79
72
  };
80
73
  };
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  © 2021–2024 CVS Health and/or one of its affiliates. All rights reserved.
3
- © 2025 Jonathan Robert Pool.
3
+ © 2025–2026 Jonathan Robert Pool.
4
4
 
5
5
  Licensed under the MIT License. See LICENSE file at the project root or
6
6
  https://opensource.org/license/mit/ for details.
@@ -22,7 +22,7 @@ const {doTest} = require('../procs/testaro');
22
22
  // FUNCTIONS
23
23
 
24
24
  // Runs the test and returns the result.
25
- exports.reporter = async (page, withItems) => {
25
+ exports.reporter = async (page, catalog, withItems) => {
26
26
  const getBadWhat = element => {
27
27
  // Get whether the element is visible.
28
28
  const isVisible = element.checkVisibility({
@@ -116,6 +116,6 @@ exports.reporter = async (page, withItems) => {
116
116
  };
117
117
  const whats = 'Elements are Tab-focusable but not operable or vice versa';
118
118
  return await doTest(
119
- page, withItems, 'focAndOp', 'body *', whats, 2, null, getBadWhat.toString()
119
+ page, catalog, withItems, 'focAndOp', 'body *', whats, 2, getBadWhat.toString()
120
120
  );
121
121
  };
package/testaro/focInd.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  © 2021–2023 CVS Health and/or one of its affiliates. All rights reserved.
3
- © 2025 Jonathan Robert Pool.
3
+ © 2025–2026 Jonathan Robert Pool.
4
4
 
5
5
  Licensed under the MIT License. See LICENSE file at the project root or
6
6
  https://opensource.org/license/mit/ for details.
@@ -27,7 +27,7 @@ const {doTest} = require('../procs/testaro');
27
27
  // FUNCTIONS
28
28
 
29
29
  // Runs the test and returns the result.
30
- exports.reporter = async (page, withItems) => {
30
+ exports.reporter = async (page, catalog, withItems) => {
31
31
  const getBadWhat = element => {
32
32
  // Get whether the element is visible.
33
33
  const isVisible = element.checkVisibility({
@@ -86,6 +86,6 @@ exports.reporter = async (page, withItems) => {
86
86
  };
87
87
  const whats = 'Elements fail to have standard focus indicators';
88
88
  return await doTest(
89
- page, withItems, 'focInd', 'body *', whats, 1, null, getBadWhat.toString()
89
+ page, catalog, withItems, 'focInd', 'body *', whats, 1, getBadWhat.toString()
90
90
  );
91
91
  };
package/testaro/focVis.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  © 2022–2025 CVS Health and/or one of its affiliates. All rights reserved.
3
- © 2025 Jonathan Robert Pool.
3
+ © 2025–2026 Jonathan Robert Pool.
4
4
 
5
5
  Licensed under the MIT License. See LICENSE file at the project root or
6
6
  https://opensource.org/license/mit/ for details.
@@ -20,7 +20,8 @@ const {doTest} = require('../procs/testaro');
20
20
 
21
21
  // FUNCTIONS
22
22
 
23
- exports.reporter = async (page, withItems) => {
23
+ // Runs the test and returns the result.
24
+ exports.reporter = async (page, catalog, withItems) => {
24
25
  const getBadWhat = element => {
25
26
  const isVisible = element.checkVisibility({
26
27
  contentVisibilityAuto: true,
@@ -41,6 +42,6 @@ exports.reporter = async (page, withItems) => {
41
42
  };
42
43
  const whats = 'Visible links are above or to the left of the display';
43
44
  return await doTest(
44
- page, withItems, 'focVis', 'a', whats, 2, 'A', getBadWhat.toString()
45
+ page, catalog, withItems, 'focVis', 'a', whats, 2, getBadWhat.toString()
45
46
  );
46
47
  };
package/testaro/headEl.js CHANGED
@@ -1,9 +1,8 @@
1
1
  /*
2
2
  © 2023–2024 CVS Health and/or one of its affiliates. All rights reserved.
3
- © 2025 Jonathan Robert Pool.
3
+ © 2025–2026 Jonathan Robert Pool.
4
4
 
5
- Licensed under the MIT License. See LICENSE file at the project root or
6
- https://opensource.org/license/mit/ for details.
5
+ Licensed under the MIT License. See LICENSE file at the project root or https://opensource.org/license/mit/ for details.
7
6
 
8
7
  SPDX-License-Identifier: MIT
9
8
  */
@@ -16,7 +15,7 @@
16
15
 
17
16
  // ########## FUNCTIONS
18
17
 
19
- // Performs the test.
18
+ // Runs the test and returns the result.
20
19
  exports.reporter = async page => {
21
20
  // Initialize the data and standard result.
22
21
  const data = {
@@ -56,18 +55,12 @@ exports.reporter = async page => {
56
55
  });
57
56
  // If there are any instances:
58
57
  if (data.total) {
59
- // Add a summary instance.
58
+ // Add a summary instance to the standard instances.
60
59
  standardInstances.push({
61
60
  ruleID: 'headEl',
62
61
  what: `Invalid elements within the head: ${data.badTagNames.join(', ')}`,
63
62
  ordinalSeverity: 2,
64
- count: data.total,
65
- location: {
66
- doc: '',
67
- type: '',
68
- spec: ''
69
- },
70
- excerpt: ''
63
+ count: data.total
71
64
  });
72
65
  }
73
66
  totals = [0, 0, data.total, 0];
@@ -1,8 +1,8 @@
1
1
  /*
2
2
  © 2023–2024 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2026 Jonathan Robert Pool.
3
4
 
4
- Licensed under the MIT License. See LICENSE file at the project root or
5
- https://opensource.org/license/mit/ for details.
5
+ Licensed under the MIT License. See LICENSE file at the project root or https://opensource.org/license/mit/ for details.
6
6
 
7
7
  SPDX-License-Identifier: MIT
8
8
  */
@@ -13,101 +13,58 @@
13
13
  This test reports adjacent headings with the same levels and text contents.
14
14
  */
15
15
 
16
- // ########## IMPORTS
16
+ // IMPORTS
17
17
 
18
- // Module to get locator data.
19
- const {getLocatorData} = require('../procs/getLocatorData');
18
+ const {doTest} = require('../procs/testaro');
20
19
 
21
20
  // ########## FUNCTIONS
22
21
 
23
- // Performs the test.
24
- exports.reporter = async (page, withItems) => {
25
- // Initialize the standard result.
26
- const data = {};
27
- const totals = [0, 0, 0, 0];
28
- const standardInstances = [];
29
- // Get locators for all the headings.
30
- const headingLevels = [1, 2, 3, 4, 5, 6];
31
- const locAll = page.locator(headingLevels.map(level => `body h${level}`).join(', '));
32
- const locsAll = await locAll.all();
33
- const ambIndexes = await locAll.evaluateAll(headings => {
34
- // Initialize the array of indexes of violating headings.
35
- const badIndexes = [];
36
- // For each heading:
37
- headings.forEach((heading, index) => {
38
- // Get its level.
39
- const level = heading.tagName[1];
40
- // Get the prior headings.
41
- const priorHeadings = headings.slice(0, index);
42
- // Get the non-inferior ones among them.
43
- const nonInferiors = priorHeadings.filter(priorHeading => priorHeading.tagName[1] <= level);
44
- // If there are any:
45
- const nonInferiorCount = nonInferiors.length;
46
- if (nonInferiorCount) {
47
- // Get the last of them.
48
- const prior = nonInferiors[nonInferiorCount - 1];
49
- // If they have the same level and text:
50
- if (['tagName', 'textContent'].every(property => prior[property] === heading[property])) {
51
- // Add the index of the later heading to the index of violating headings.
52
- badIndexes.push(index);
22
+ // Runs the test and returns the result.
23
+ exports.reporter = async (page, catalog, withItems) => {
24
+ const getBadWhat = element => {
25
+ const {tagName} = element;
26
+ const level = tagName[1];
27
+ const textContent = element.textContent.trim().replace(/\s+/g, ' ');
28
+ const headingTagNames = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6'];
29
+ // Initialize the inspected element as the previous element sibling of the element.
30
+ let inspectedElement = element.previousElementSibling;
31
+ // As long as the inspected element exists:
32
+ while (inspectedElement) {
33
+ const inspectedTagName = inspectedElement.tagName;
34
+ // If it is a heading:
35
+ if (headingTagNames.includes(inspectedTagName)) {
36
+ // If it is inferior to the element:
37
+ if (inspectedTagName[1] > level) {
38
+ // Stop inspecting it and start inspecting its previous sibling.
39
+ inspectedElement = inspectedElement.previousElementSibling;
40
+ continue;
53
41
  }
54
- }
55
- });
56
- return badIndexes;
57
- });
58
- // If there were any instances:
59
- if (ambIndexes.length) {
60
- // Add to the totals.
61
- totals[1] = ambIndexes.length;
62
- // If itemization is required:
63
- if (withItems) {
64
- // For each instance:
65
- for (const index of ambIndexes) {
66
- // If it exists:
67
- const loc = locsAll[index];
68
- if (loc) {
69
- // Get data on the element.
70
- const elData = await getLocatorData(loc);
71
- // Add a standard instance.
72
- standardInstances.push({
73
- ruleID: 'headingAmb',
74
- what: 'Heading has the same text as the prior same-level sibling heading',
75
- ordinalSeverity: 1,
76
- tagName: elData.tagName,
77
- id: elData.id,
78
- location: elData.location,
79
- excerpt: elData.excerpt
80
- });
42
+ // Otherwise, if its level is the same as that of the element:
43
+ else if (inspectedTagName === tagName) {
44
+ const inspectedTextContent = inspectedElement.textContent.trim().replace(/\s+/g, ' ');
45
+ // If they have identical text contents:
46
+ if (inspectedTextContent === textContent) {
47
+ // Return a violation description.
48
+ return 'Heading has the same text as the prior same-level sibling heading';
49
+ }
81
50
  }
82
- // Otherwise, i.e. if it does not exist:
51
+ // Otherwise, i.e. if it is superior to the element:
83
52
  else {
84
- // Report this.
85
- console.log('ERROR: Reportedly same-text adjacent sibling heading not found');
53
+ // Stop inspecting.
54
+ break;
86
55
  }
87
56
  }
57
+ // Otherwise, i.e. if it is not a heading:
58
+ else {
59
+ // Inspect its previous sibling.
60
+ inspectedElement = inspectedElement.previousElementSibling;
61
+ }
88
62
  }
89
- // Otherwise, i.e. if itemization is not required:
90
- else {
91
- // Add a summary instance.
92
- standardInstances.push({
93
- ruleID: 'headingAmb',
94
- what: 'Adjacent sibling same-level headings have the same text',
95
- ordinalSeverity: 1,
96
- count: totals[1],
97
- tagName: '',
98
- id: '',
99
- location: {
100
- doc: '',
101
- type: '',
102
- spec: ''
103
- },
104
- excerpt: ''
105
- });
106
- }
107
- }
108
- return {
109
- data,
110
- totals,
111
- standardInstances
112
63
  };
64
+ const headingLevels = [1, 2, 3, 4, 5, 6];
65
+ const selector = headingLevels.map(level => `body h${level}`).join(', ');
66
+ const whats = 'Adjacent sibling same-level headings have the same text';
67
+ return await doTest(
68
+ page, catalog, withItems, 'headingAmb', selector, whats, 1, getBadWhat.toString()
69
+ );
113
70
  };
package/testaro/hovInd.js CHANGED
@@ -1,9 +1,8 @@
1
1
  /*
2
2
  © 2023–2025 CVS Health and/or one of its affiliates. All rights reserved.
3
- © 2025 Jonathan Robert Pool.
3
+ © 2025–2026 Jonathan Robert Pool.
4
4
 
5
- Licensed under the MIT License. See LICENSE file at the project root or
6
- https://opensource.org/license/mit/ for details.
5
+ Licensed under the MIT License. See LICENSE file at the project root or https://opensource.org/license/mit/ for details.
7
6
 
8
7
  SPDX-License-Identifier: MIT
9
8
  */
@@ -19,7 +18,8 @@ const {doTest} = require('../procs/testaro');
19
18
 
20
19
  // FUNCTIONS
21
20
 
22
- exports.reporter = async (page, withItems) => {
21
+ // Runs the test and returns the result.
22
+ exports.reporter = async (page, _, withItems) => {
23
23
  const getBadWhat = element => {
24
24
  const violationTypes = [];
25
25
  const isVisible = element.checkVisibility({
@@ -140,5 +140,5 @@ exports.reporter = async (page, withItems) => {
140
140
  };
141
141
  const selector = 'a, button, input, [onmouseenter], [onmouseover]';
142
142
  const whats = 'elements have confusing hover indicators';
143
- return await doTest(page, withItems, 'hovInd', selector, whats, 1, null, getBadWhat.toString());
143
+ return await doTest(page, withItems, 'hovInd', selector, whats, 1, getBadWhat.toString());
144
144
  };