testaro 60.16.2 → 60.18.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/testaro/linkUl.js CHANGED
@@ -10,43 +10,37 @@
10
10
 
11
11
  /*
12
12
  linkUl
13
- This test reports failures to underline inline links. Underlining and color are the traditional style properties that identify links. Lists of links containing only links can be recognized without underlines, but other links are difficult or impossible to distinguish visually from surrounding text if not underlined. Underlining adjacent links only on hover provides an indicator valuable only to mouse users, and even they must traverse the text with a mouse merely to discover which passages are links.
13
+ This test reports failures to underline inline links. Underlining and color are the traditional style properties that identify links. Lists of links containing only links may be recognizable without underlines, but other links are difficult or impossible to distinguish visually from surrounding text if not underlined. Underlining adjacent links only on hover provides an indicator valuable only to mouse users, and even they must traverse the text with a mouse merely to discover which passages are links.
14
14
  */
15
15
 
16
- // ########## IMPORTS
16
+ // IMPORTS
17
17
 
18
- // Module to perform common operations.
19
- const {simplify} = require('../procs/testaro');
20
- // Module to classify links.
21
- const {isInlineLink} = require('../procs/isInlineLink');
18
+ const {doTest} = require('../procs/testaro');
22
19
 
23
- // ########## FUNCTIONS
20
+ // FUNCTIONS
24
21
 
25
22
  // Runs the test and returns the result.
26
23
  exports.reporter = async (page, withItems) => {
27
- // Specify the rule.
28
- const ruleData = {
29
- ruleID: 'linkUl',
30
- selector: 'a',
31
- pruner: async loc => {
32
- // Get whether each link is underlined.
33
- const isUnderlined = await loc.evaluate(el => {
34
- const styleDec = window.getComputedStyle(el);
35
- return styleDec.textDecorationLine === 'underline';
36
- });
37
- // If it is not:
38
- if (! isUnderlined) {
39
- // Return whether it is a violator.
40
- return await isInlineLink(loc);
24
+ const getBadWhat = element => {
25
+ const liAncestor = element.closest('li');
26
+ // If the element is not the only link inside a list item:
27
+ if (! (liAncestor && liAncestor.getElementsByTagName('a').length === 1)) {
28
+ const styleDec = window.getComputedStyle(element);
29
+ const {textDecoration} = styleDec;
30
+ // If the element text is not underlined:
31
+ if (! textDecoration.includes('underline')) {
32
+ const styleDec = window.getComputedStyle(element);
33
+ const {display} = styleDec;
34
+ // If the element has does not have a block display style:
35
+ if (display !== 'block') {
36
+ // Return a violation description.
37
+ return 'Element is not a list item but is not underlined';
38
+ }
41
39
  }
42
- },
43
- complaints: {
44
- instance: 'Link is inline but has no underline',
45
- summary: 'Inline links are missing underlines'
46
- },
47
- ordinalSeverity: 1,
48
- summaryTagName: 'A'
40
+ }
49
41
  };
50
- // Run the test and return the result.
51
- return await simplify(page, withItems, ruleData);
42
+ const whats = 'Links that are not list items are not underlined';
43
+ return await doTest(
44
+ page, withItems, 'linkUl', 'a', whats, 1, 'A', getBadWhat.toString()
45
+ );
52
46
  };
@@ -14,65 +14,49 @@
14
14
  This test reports tables used for layout.
15
15
  */
16
16
 
17
- // ########## IMPORTS
17
+ // IMPORTS
18
18
 
19
- // Module to perform common operations.
20
- const {simplify} = require('../procs/testaro');
19
+ const {doTest} = require('../procs/testaro');
21
20
 
22
- // ########## FUNCTIONS
21
+ // FUNCTIONS
23
22
 
24
23
  // Runs the test and returns the result.
25
24
  exports.reporter = async (page, withItems) => {
26
- // Specify the rule.
27
- const ruleData = {
28
- ruleID: 'nonTable',
29
- selector: 'table',
30
- pruner: async loc => await loc.evaluate(el => {
31
- const role = el.getAttribute('role');
32
- // If it contains another table:
33
- if (el.querySelector('table')) {
34
- // Return misuse.
35
- return true;
36
- }
37
- // Otherwise, if it has only 1 column or 1 row:
38
- else if (
39
- el.querySelectorAll('tr').length === 1
40
- || Math.max(
41
- ... Array
42
- .from(el.querySelectorAll('tr'))
43
- .map(row => Array.from(row.querySelectorAll('th, td')).length)
44
- ) === 1
45
- ) {
46
- // Return misuse.
47
- return true;
48
- }
49
- // Otherwise, if it contains an object or player:
50
- else if (el.querySelector('object, embed, applet, audio, video')) {
51
- // Return misuse.
52
- return true;
53
- }
54
- // Otherwise, if it contains a table-compatible element:
55
- else if (
56
- el.caption
57
- || ['grid', 'treegrid'].includes(role)
58
- || el.querySelector('col, colgroup, tfoot, thead, th')
59
- ) {
60
- // Return validity.
61
- return false;
62
- }
63
- // Otherwise:
64
- else {
65
- // Return misuse.
66
- return true;
67
- }
68
- }),
69
- complaints: {
70
- instance: 'Table is misused to arrange content',
71
- summary: 'Tables are misused to arrange content'
72
- },
73
- ordinalSeverity: 2,
74
- summaryTagName: 'TABLE'
25
+ const getBadWhat = element => {
26
+ // If the element contains another table:
27
+ if (element.querySelector('table')) {
28
+ // Return a violation description.
29
+ return 'Element contains another table';
30
+ }
31
+ const rowCount = element.querySelectorAll('tr').length;
32
+ const columnCount = Math.max(
33
+ ... Array
34
+ .from(element.querySelectorAll('tr'))
35
+ .map(row => Array.from(row.querySelectorAll('th, td')).length)
36
+ );
37
+ // Otherwise, if it has only 1 column or 1 row:
38
+ if (rowCount === 1 || columnCount === 1) {
39
+ // Return a violation description.
40
+ return 'Element has only one row or one column';
41
+ }
42
+ // Otherwise, if it contains an object or player:
43
+ if (element.querySelector('object, embed, applet, audio, video')) {
44
+ // Return a violation description.
45
+ return 'Element contains an object or player';
46
+ }
47
+ const role = element.getAttribute('role');
48
+ // Otherwise, if it has no table-compatible explicit role or descendant element:
49
+ if (! (
50
+ ['grid', 'treegrid'].includes(role)
51
+ || element.caption
52
+ || element.querySelector('col, colgroup, tfoot, th, thead')
53
+ )) {
54
+ // Return a violation description.
55
+ return 'Element has no table-compatible explicit role or descendant element';
56
+ }
75
57
  };
76
- // Run the test and return the result.
77
- return await simplify(page, withItems, ruleData);
58
+ const whats = 'table elements are misused for non-table content';
59
+ return await doTest(
60
+ page, withItems, 'nonTable', 'table', whats, 2, 'TABLE', getBadWhat.toString()
61
+ );
78
62
  };
@@ -1,6 +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
5
 
5
6
  Licensed under the MIT License. See LICENSE file at the project root or
6
7
  https://opensource.org/license/mit/ for details.
@@ -11,24 +12,26 @@
11
12
  /*
12
13
  optRoleSel
13
14
  Clean-room rule.
14
- This test reports elements with role="option" that are missing aria-selected attributes.
15
+ This test reports elements with role=option that are missing aria-selected attributes.
15
16
  */
16
17
 
17
- const {simplify} = require('../procs/testaro');
18
+ // IMPORTS
18
19
 
20
+ const {doTest} = require('../procs/testaro');
21
+
22
+ // FUNCTIONS
23
+
24
+ // Runs the test and returns the result.
19
25
  exports.reporter = async (page, withItems) => {
20
- const ruleData = {
21
- ruleID: 'optRoleSel',
22
- selector: '[role="option"]',
23
- pruner: async (loc) => await loc.evaluate(el => {
24
- return ! el.hasAttribute('aria-selected');
25
- }),
26
- complaints: {
27
- instance: 'Element has an explicit option role but no aria-selected attribute',
28
- summary: 'Elements with explicit option roles have no aria-selected attributes'
29
- },
30
- ordinalSeverity: 1,
31
- summaryTagName: ''
26
+ const getBadWhat = element => {
27
+ // If the element has no aria-selected attribute:
28
+ if (! element.hasAttribute('aria-selected')) {
29
+ // Return a violation description.
30
+ return 'Element has role=option but no aria-selected attribute';
31
+ }
32
32
  };
33
- return await simplify(page, withItems, ruleData);
33
+ const whats = 'Elements with role=option have no aria-selected attributes';
34
+ return await doTest(
35
+ page, withItems, 'optRoleSel', '[role="option"]', whats, 1, null, getBadWhat.toString()
36
+ );
34
37
  };
@@ -0,0 +1,54 @@
1
+ /*
2
+ © 2023 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2025 Jonathan Robert Pool.
4
+
5
+ Licensed under the MIT License. See LICENSE file at the project root or
6
+ https://opensource.org/license/mit/ for details.
7
+
8
+ SPDX-License-Identifier: MIT
9
+ */
10
+ /*
11
+ pseudoP
12
+ This test reports 2 or more sequential br elements without intervening text content. They may be inferior substitutes for p elements.
13
+ */
14
+
15
+ // IMPORTS
16
+
17
+ const {doTest} = require('../procs/testaro');
18
+
19
+ // FUNCTIONS
20
+
21
+ exports.reporter = async (page, withItems) => {
22
+ const getBadWhat = element => {
23
+ // Get the node before the element node.
24
+ const previousNode = element.previousSibling;
25
+ let isBad = false;
26
+ // If it is a br element:
27
+ if (previousNode && previousNode.nodeType === Node.ELEMENT_NODE && previousNode.tagName === 'BR') {
28
+ // Classify the element as a violator.
29
+ isBad = true;
30
+ }
31
+ // Otherwise, if it is a text node:
32
+ else if (previousNode && previousNode.nodeType === Node.TEXT_NODE) {
33
+ // If the text node contains only whitespace:
34
+ if (previousNode.textContent.trim() === '') {
35
+ // Get the node before the text node.
36
+ const beforeText = previousNode.previousSibling;
37
+ // If that node is a br element:
38
+ if (beforeText && beforeText.nodeType === Node.ELEMENT_NODE && beforeText.tagName === 'BR') {
39
+ // Classify the element as a violator.
40
+ isBad = true;
41
+ }
42
+ }
43
+ }
44
+ // If the element is a violator:
45
+ if (isBad) {
46
+ // Return a violation description.
47
+ return `Element follows another br element, possibly constituting a pseudo-paragraph`;
48
+ }
49
+ };
50
+ const whats = 'br elements follow other br elements, possibly constituting pseudo-paragraphs';
51
+ return await doTest(
52
+ page, withItems, 'pseudoP', 'body br', whats, 0, 'BR', getBadWhat.toString()
53
+ );
54
+ };
package/testaro/role.js CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  /*
12
12
  role
13
- This test reports elements with native-replacing explicit role attributes.
13
+ This test reports elements with native-replacing explicit role attributes. This test uses the getBasicResult function in order to have access to the aria-query dependency.
14
14
  */
15
15
 
16
16
  // IMPORTS
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  © 2025 CVS Health and/or one of its affiliates. All rights reserved.
3
- © 2025 Juan S. Casado.
3
+ © 2025 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.
@@ -0,0 +1,34 @@
1
+ /*
2
+ © 2023–2025 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2025 Jonathan Robert Pool.
4
+
5
+ Licensed under the MIT License. See LICENSE file at the project root or
6
+ https://opensource.org/license/mit/ for details.
7
+
8
+ SPDX-License-Identifier: MIT
9
+ */
10
+
11
+ /*
12
+ titledEl
13
+ This test reports suspicious use of title attributes.
14
+ */
15
+
16
+ // IMPORTS
17
+
18
+ const {doTest} = require('../procs/testaro');
19
+
20
+ // FUNCTIONS
21
+
22
+ // Runs the test and returns the result.
23
+ exports.reporter = async (page, withItems) => {
24
+ const getBadWhat = element => {
25
+ const elementType = element.tagName.toLowerCase();
26
+ // Return a violation description.
27
+ return `Likely ineffective title attribute is used on the ${elementType} element`;
28
+ }
29
+ const selector = '[title]:not(iframe, link, style)';
30
+ const whats = 'title attributes are used on elements they are likely ineffective on';
31
+ return await doTest(
32
+ page, withItems, 'titledEl', selector, whats, 0, null, getBadWhat.toString()
33
+ );
34
+ };