testilo 3.8.3 → 3.8.6
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 +1 -1
- package/procs/compare/cp14a/index.html +47 -0
- package/procs/compare/cp14a/index.js +71 -0
- package/procs/digest/dp14a/index.html +79 -0
- package/procs/digest/dp14a/index.js +126 -0
- package/procs/score/sp14a.js +312 -36
package/package.json
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<!DOCTYPE HTML>
|
|
2
|
+
<html lang="en-US">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<meta name="author" content="Testilo">
|
|
7
|
+
<meta name="creator" content="Testilo">
|
|
8
|
+
<meta name="publisher" name="Testilo">
|
|
9
|
+
<meta name="description" content="comparison of accessibility scores">
|
|
10
|
+
<meta name="keywords" content="accessibility a11y web testing">
|
|
11
|
+
<title>Accessibility score comparison</title>
|
|
12
|
+
<link rel="icon" href="favicon.png">
|
|
13
|
+
<link rel="stylesheet" href="style.css">
|
|
14
|
+
</head>
|
|
15
|
+
<body>
|
|
16
|
+
<main>
|
|
17
|
+
<header>
|
|
18
|
+
<h1>Accessibility score comparison</h1>
|
|
19
|
+
</header>
|
|
20
|
+
<h2>Introduction</h2>
|
|
21
|
+
<p>The table below compares __pageCount__ web pages on <a href="https://www.w3.org/WAI/fundamentals/accessibility-intro/">accessibility</a>. The page names link to the pages on the web. The scores link to digests that explain in detail how the scores were computed.</p>
|
|
22
|
+
<p>The pages were:</p>
|
|
23
|
+
<ol id="summary">
|
|
24
|
+
<li>Tested by <a href="https://www.npmjs.com/package/testaro">Testaro</a> with procedure <code>tp14</code></li>
|
|
25
|
+
<li>Scored by <a href="https://www.npmjs.com/package/testilo">Testilo</a> with procedure <code>sp14a</code></li>
|
|
26
|
+
<li>Digested by Testilo with procedure <code>dp14a</code></li>
|
|
27
|
+
<li>Compared by Testilo with procedure <code>cp14a</code></li>
|
|
28
|
+
</ol>
|
|
29
|
+
<p>The Testaro procedure performs 1075 tests on each page. Of these, 16 tests are custom tests defined by Testaro, and the others belong to packages of tests created by others.</p>
|
|
30
|
+
<h2>Comparison</h2>
|
|
31
|
+
<table class="allBorder">
|
|
32
|
+
<caption>Accessibility scores of web pages</caption>
|
|
33
|
+
<thead>
|
|
34
|
+
<tr><th scope="col">Page</th><th scope="col" colspan="2">Score (lower is better)</tr>
|
|
35
|
+
</thead>
|
|
36
|
+
<tbody class="linkSmaller secondCellRight">
|
|
37
|
+
__tableBody__
|
|
38
|
+
</tbody>
|
|
39
|
+
</table>
|
|
40
|
+
<h2>Discussion</h2>
|
|
41
|
+
<p>Tests and scoring formulae are fallible and subjective. The reported faults merit investigation as potential opportunities for improved accessibility. But some may not actually harm accessibility, and some other accessibility faults are not detectable with these tests. Different reasonable procedures could yield different test results and different scores. Testaro and Testilo can be customized to fit different definitions and weightings of types of accessibility.</p>
|
|
42
|
+
<footer>
|
|
43
|
+
<p class="date">Produced <time itemprop="datePublished" datetime="__dateISO__">__dateSlash__</time></p>
|
|
44
|
+
</footer>
|
|
45
|
+
</main>
|
|
46
|
+
</body>
|
|
47
|
+
</html>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/*
|
|
2
|
+
cp12b.js
|
|
3
|
+
Returns a query for an HTML page including a bar-graph table.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ########## IMPORTS
|
|
7
|
+
|
|
8
|
+
// Module to keep secrets local.
|
|
9
|
+
require('dotenv').config();
|
|
10
|
+
// Module to access files.
|
|
11
|
+
const fs = require('fs/promises');
|
|
12
|
+
|
|
13
|
+
// ########## CONSTANTS
|
|
14
|
+
|
|
15
|
+
const reportDirScored = process.env.REPORTDIR_SCORED || 'reports/scored';
|
|
16
|
+
const query = {};
|
|
17
|
+
|
|
18
|
+
// ########## FUNCTIONS
|
|
19
|
+
|
|
20
|
+
// Returns data on the hosts in the report directory.
|
|
21
|
+
const getData = async () => {
|
|
22
|
+
const reportDirAbs = `${__dirname}/../../../${reportDirScored}`;
|
|
23
|
+
const reportFileNamesAll = await fs.readdir(reportDirAbs);
|
|
24
|
+
const reportFileNamesSource = reportFileNamesAll.filter(fileName => fileName.endsWith('.json'));
|
|
25
|
+
const pageCount = reportFileNamesSource.length;
|
|
26
|
+
const bodyData = [];
|
|
27
|
+
for (const fileName of reportFileNamesSource) {
|
|
28
|
+
const fileJSON = await fs.readFile(`${reportDirAbs}/${fileName}`, 'utf8');
|
|
29
|
+
const file = JSON.parse(fileJSON);
|
|
30
|
+
const {id, host, score} = file;
|
|
31
|
+
bodyData.push({
|
|
32
|
+
id,
|
|
33
|
+
org: host.what,
|
|
34
|
+
url: host.which,
|
|
35
|
+
score: score.summary.total
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
pageCount,
|
|
40
|
+
bodyData
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
// Returns the maximum score.
|
|
44
|
+
const getMaxScore = tableData => tableData.reduce((max, item) => Math.max(max, item.score), 0);
|
|
45
|
+
// Converts report data to a table body.
|
|
46
|
+
const getTableBody = async bodyData => {
|
|
47
|
+
const maxScore = getMaxScore(bodyData);
|
|
48
|
+
const rows = bodyData
|
|
49
|
+
.sort((a, b) => a.score - b.score)
|
|
50
|
+
.map(item => {
|
|
51
|
+
const {id, org, url, score} = item;
|
|
52
|
+
const pageCell = `<th scope="row"><a href="${url}">${org}</a></th>`;
|
|
53
|
+
const numCell = `<td><a href="digests/${id}.html">${score}</a></td>`;
|
|
54
|
+
const barWidth = 100 * score / maxScore;
|
|
55
|
+
const bar = `<rect height="100%" width="${barWidth}%" fill="red"></rect>`;
|
|
56
|
+
const barCell = `<td aria-hidden="true"><svg width="100%" height="0.7em">${bar}</svg></td>`;
|
|
57
|
+
const row = `<tr>${pageCell}${numCell}${barCell}</tr>`;
|
|
58
|
+
return row;
|
|
59
|
+
});
|
|
60
|
+
return rows.join('\n ');
|
|
61
|
+
};
|
|
62
|
+
// Returns a query for a comparative table.
|
|
63
|
+
exports.getQuery = async () => {
|
|
64
|
+
const data = await getData();
|
|
65
|
+
query.pageCount = data.pageCount;
|
|
66
|
+
query.tableBody = await getTableBody(data.bodyData);
|
|
67
|
+
const date = new Date();
|
|
68
|
+
query.dateISO = date.toISOString().slice(0, 10);
|
|
69
|
+
query.dateSlash = query.dateISO.replace(/-/g, '/');
|
|
70
|
+
return query;
|
|
71
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<!DOCTYPE HTML>
|
|
2
|
+
<html lang="en-US">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<meta name="author" content="Testilo">
|
|
7
|
+
<meta name="creator" content="Testilo">
|
|
8
|
+
<meta name="publisher" name="Testilo">
|
|
9
|
+
<meta name="description" content="report of accessibility testing of a web page">
|
|
10
|
+
<meta name="keywords" content="accessibility a11y web testing">
|
|
11
|
+
<title>Accessibility test digest</title>
|
|
12
|
+
<link rel="icon" href="favicon.png">
|
|
13
|
+
<link rel="stylesheet" href="style.css">
|
|
14
|
+
</head>
|
|
15
|
+
<body>
|
|
16
|
+
<main>
|
|
17
|
+
<header>
|
|
18
|
+
<h1>Accessibility test digest</h1>
|
|
19
|
+
<h2>Synopsis</h2>
|
|
20
|
+
<div id="synopsis">
|
|
21
|
+
<p><strong>Page</strong>: __org__</p>
|
|
22
|
+
<p><strong>URL</strong>: __url__</p>
|
|
23
|
+
<p><strong>Score</strong>: __totalScore__</p>
|
|
24
|
+
<p><strong>Tested by</strong>: Testaro, procedure <code>tp14</code></p>
|
|
25
|
+
<p><strong>Scored by</strong>: Testilo, procedure <code>sp14a</code></p>
|
|
26
|
+
<p><strong>Digested by</strong>: Testilo, procedure <code>dp14a</code></p>
|
|
27
|
+
</div>
|
|
28
|
+
</header>
|
|
29
|
+
<h2>Introduction</h2>
|
|
30
|
+
<p>The <a href="https://www.npmjs.com/package/testaro">Testaro</a> application used its <code>tp14</code> testing procedure to test the <a href="https://www.w3.org/WAI/fundamentals/accessibility-intro/"><dfn>accessibility</dfn></a> (barrier-free design and coding) of the __org__ web page at <a href="__url__">__url__</a> on __dateSlash__. The procedure performed 1075 tests. Of these, 16 are custom tests defined by Testaro, and the others belong to these seven other packages (programs that perform collections of tests):</p>
|
|
31
|
+
<ul>
|
|
32
|
+
<li><a href="https://github.com/Siteimprove/alfa">Alfa</a> by Siteimprove</li>
|
|
33
|
+
<li><a href="https://www.npmjs.com/package/axe-core">Axe-core</a> by Deque</li>
|
|
34
|
+
<li><a href="https://www.webaccessibility.com/tools/">Continuum</a> by Level Access</li>
|
|
35
|
+
<li>
|
|
36
|
+
<a href="https://www.npmjs.com/package/html_codesniffer">HTML CodeSniffer</a> by Squiz Labs
|
|
37
|
+
</li>
|
|
38
|
+
<li><a href="https://github.com/IBMa/equal-access">Equal Access</a> by IBM</li>
|
|
39
|
+
<li><a href="https://tenon.io/documentation/apiv2.php">Tenon</a> by Level Access</li>
|
|
40
|
+
<li><a href="https://wave.webaim.org/api/">WAVE</a> by WebAIM</li>
|
|
41
|
+
</ul>
|
|
42
|
+
<p>Testaro produced a report enumerating the test results.</p>
|
|
43
|
+
<p><a href="https://www.npmjs.com/package/testilo">Testilo</a> processed the report and used the <code>sp14a</code> scoring procedure to compute partial and total scores for the page. The total score is __totalScore__ (where 0 is the best possible score). The scored report is appended below.</p>
|
|
44
|
+
<p>Finally, Testilo used procedure <code>dp14a</code> to produce this digest, briefly explaining how <code>sp14a</code> computed the scores.</p>
|
|
45
|
+
<h2>Score summary</h2>
|
|
46
|
+
<table class="allBorder secondCellRight">
|
|
47
|
+
<caption>Score components</caption>
|
|
48
|
+
<tbody class="headersLeft">
|
|
49
|
+
__scoreRows__
|
|
50
|
+
</tbody>
|
|
51
|
+
</table>
|
|
52
|
+
<h2>Issue summary</h2>
|
|
53
|
+
<h3>Special issues</h3>
|
|
54
|
+
__specialSummary__
|
|
55
|
+
<h3>Classified issues</h3>
|
|
56
|
+
__groupSummary__
|
|
57
|
+
<h2>Discussion</h2>
|
|
58
|
+
<p>Although there are widely accepted <a href="https://www.w3.org/WAI/standards-guidelines/">accessibility standards</a>, there is no unanimity about how to define, test, and quantify accessibility. The failures reported in this digest merit investigation as potential opportunities for improved accessibility. Investigation may lead you to conclude that some of the reported failures do not actually harm accessibility. Conversely, some substantial accessibility faults can escape detection by any of these tests. You may question the attempt to assign an accessibility score to a web page, or you may prefer weightings and formulas different from those used by <code>sp14a</code>. You can modify and extend Testaro and Testilo to fit other theories and priorities.</p>
|
|
59
|
+
<p>Here, in brief, is how <code>sp14a</code> computes a score for a page.</p>
|
|
60
|
+
<ul>
|
|
61
|
+
<li>It finds all the defects and warnings (let’s call them <q>issues</q>) recorded in the report.</li>
|
|
62
|
+
<li>It classifies them according to type. For example, a link that looks like the text around it is one issue category, while a video that has no captions is another issue category.</li>
|
|
63
|
+
<li>It also classifies the issues according to severity. For example, an issue that prevents a transaction is more severe than an issue that only complicates the transaction, and a warning about a possible issue is less severe than a definite finding of an issue. (Some packages rate the severity of each issue; for the other packages, <code>sp12b</code> assigns a severity weight to the issue type and uses that weight.)</li>
|
|
64
|
+
<li>It assigns quality ratings to particular tests that are judged abnormally reliable or unreliable.</li>
|
|
65
|
+
<li>It assigns a score to each issue reported by each test of each package.</li>
|
|
66
|
+
<li>It aggregates the issue scores, weighting them by severity, test quality, and redundancy. Redundancy occurs, and causes downweighting, when two or more packages contain tests that are designed to discover the same or mostly the same issues. So the score for a category is not simply the sum of the scores of the tests in that category.</li>
|
|
67
|
+
<li>It assigns a score for issues in the page logged by the browser.</li>
|
|
68
|
+
<li>It assigns an estimated score each time the page prevents one of the packages or one of the Testaro tests from being run on the page.</li>
|
|
69
|
+
<li>It adds the scores together to obtain a total score.</li>
|
|
70
|
+
</ul>
|
|
71
|
+
<p>The precise rules of <code>sp14a</code> are found in the <a href="https://github.com/jrpool/testilo/blob/main/procs/score/sp12b.js">code itself</a>.</p>
|
|
72
|
+
<h2>Report</h2>
|
|
73
|
+
<pre>__report__</pre>
|
|
74
|
+
<footer>
|
|
75
|
+
<p class="date">Produced <time itemprop="datePublished" datetime="__dateISO__">__dateSlash__</time></p>
|
|
76
|
+
</footer>
|
|
77
|
+
</main>
|
|
78
|
+
</body>
|
|
79
|
+
</html>
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/*
|
|
2
|
+
index: digester for scoring procedure sp14a.
|
|
3
|
+
Creator of parameters for substitution into index.html.
|
|
4
|
+
Usage example for selected files in REPORTDIR_SCORED: node digest dp14a 35k1r
|
|
5
|
+
Usage example for all files in REPORTDIR_SCORED: node digest dp14a
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// CONSTANTS
|
|
9
|
+
|
|
10
|
+
// Newlines with indentations.
|
|
11
|
+
const joiner = '\n ';
|
|
12
|
+
const innerJoiner = '\n ';
|
|
13
|
+
const specialMessages = {
|
|
14
|
+
log: 'This is based on the amount of browser error logging and miscellaneous logging during the tests.',
|
|
15
|
+
preventions: 'This is based on tests that the page did not allow to be run. That impedes accessibility progress and risks interfering with tools that users with disabilities need.',
|
|
16
|
+
solos: 'This is based on issues reported by unclassified tests. Details are in the report.'
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// FUNCTIONS
|
|
20
|
+
|
|
21
|
+
// Makes strings HTML-safe.
|
|
22
|
+
const htmlEscape = textOrNumber => textOrNumber
|
|
23
|
+
.toString()
|
|
24
|
+
.replace(/&/g, '&')
|
|
25
|
+
.replace(/</g, '<');
|
|
26
|
+
// Gets a row of the score-summary table.
|
|
27
|
+
const getScoreRow = (component, score) => `<tr><th>${component}</th><td>${score}</td></tr>`;
|
|
28
|
+
// Gets the start of a paragraph about a special score.
|
|
29
|
+
const getSpecialPStart = (summary, scoreID) =>
|
|
30
|
+
`<p><span class="componentID">${scoreID}</span>: Score ${summary[scoreID]}.`;
|
|
31
|
+
// Adds parameters to a query for a digest.
|
|
32
|
+
exports.makeQuery = (report, query) => {
|
|
33
|
+
// Add an HTML-safe copy of the host report to the query to be appended to the digest.
|
|
34
|
+
const {script, host, score} = report;
|
|
35
|
+
const reportJSON = JSON.stringify(report, null, 2);
|
|
36
|
+
const reportJSONSafe = htmlEscape(reportJSON);
|
|
37
|
+
query.report = reportJSONSafe;
|
|
38
|
+
// Add the job data to the query.
|
|
39
|
+
query.dateISO = report.endTime.slice(0, 10);
|
|
40
|
+
query.dateSlash = query.dateISO.replace(/-/g, '/');
|
|
41
|
+
if (host && host.what && host.which) {
|
|
42
|
+
query.org = host.what;
|
|
43
|
+
query.url = host.which;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
const firstURLCommand = script.commands.find(command => command.type === 'url');
|
|
47
|
+
if (firstURLCommand && firstURLCommand.what && firstURLCommand.which) {
|
|
48
|
+
query.org = firstURLCommand.what;
|
|
49
|
+
query.url = firstURLCommand.which;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
console.log('ERROR: host missing or invalid');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const {groupDetails, summary} = score;
|
|
57
|
+
const {total, groups} = summary;
|
|
58
|
+
if (typeof total === 'number') {
|
|
59
|
+
query.totalScore = total;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.log('ERROR: missing or invalid total score');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// Add the total and any special rows of the score-summary table to the query.
|
|
66
|
+
const scoreRows = [];
|
|
67
|
+
const specialComponentIDs = ['log', 'preventions', 'solos'];
|
|
68
|
+
['total'].concat(specialComponentIDs).forEach(item => {
|
|
69
|
+
if (summary[item]) {
|
|
70
|
+
scoreRows.push(getScoreRow(item, summary[item]));
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
// Add the group rows of the score-summary table to the query.
|
|
74
|
+
groups.forEach(group => {
|
|
75
|
+
scoreRows.push(getScoreRow(`${group.groupName}`, group.score));
|
|
76
|
+
});
|
|
77
|
+
query.scoreRows = scoreRows.join(innerJoiner);
|
|
78
|
+
// If the score has any special components:
|
|
79
|
+
const scoredSpecialIDs = specialComponentIDs.filter(item => summary[item]);
|
|
80
|
+
if (scoredSpecialIDs.length) {
|
|
81
|
+
// Add paragraphs about them for the issue summary to the query.
|
|
82
|
+
const specialPs = [];
|
|
83
|
+
scoredSpecialIDs.forEach(id => {
|
|
84
|
+
specialPs.push(`${getSpecialPStart(summary, id)} ${specialMessages[id]}`);
|
|
85
|
+
});
|
|
86
|
+
query.specialSummary = specialPs.join(joiner);
|
|
87
|
+
}
|
|
88
|
+
// Otherwise, i.e. if the score has no special components:
|
|
89
|
+
else {
|
|
90
|
+
// Add a paragraph stating this for the issue summary to the query.
|
|
91
|
+
query.specialSummary = '<p>No special issues contributed to the score.</p>'
|
|
92
|
+
}
|
|
93
|
+
// If the score has any classified issues as components:
|
|
94
|
+
if (groups.length) {
|
|
95
|
+
// Add paragraphs about them for the special summary to the query.
|
|
96
|
+
const groupSummaryItems = [];
|
|
97
|
+
groups.forEach(group => {
|
|
98
|
+
const {groupName, score} = group;
|
|
99
|
+
const groupP = `<p><span class="componentID">${groupName}</span>: Score ${score}. Issues reported by tests in this category:</p>`;
|
|
100
|
+
const groupListItems = [];
|
|
101
|
+
const groupData = groupDetails.groups[groupName];
|
|
102
|
+
const packageIDs = Object.keys(groupData);
|
|
103
|
+
packageIDs.forEach(packageID => {
|
|
104
|
+
const testIDs = Object.keys(groupData[packageID]);
|
|
105
|
+
testIDs.forEach(testID => {
|
|
106
|
+
const testData = groupData[packageID][testID];
|
|
107
|
+
const {score, what} = testData;
|
|
108
|
+
const listItem = `<li>Package <code>${packageID}</code>, test <code>${testID}</code>, score ${score} (${what})</li>`;
|
|
109
|
+
groupListItems.push(listItem);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
const groupList = [
|
|
113
|
+
'<ul>',
|
|
114
|
+
groupListItems.join('\n '),
|
|
115
|
+
'</ul>'
|
|
116
|
+
].join(joiner);
|
|
117
|
+
groupSummaryItems.push(groupP, groupList);
|
|
118
|
+
});
|
|
119
|
+
query.groupSummary = groupSummaryItems.join(joiner);
|
|
120
|
+
}
|
|
121
|
+
// Otherwise, i.e. if the score has no classified issues as components:
|
|
122
|
+
else {
|
|
123
|
+
// Add a paragraph stating this for the group summary to the query.
|
|
124
|
+
query.groupSummary = '<p>No classified issues contributed to the score.</p>'
|
|
125
|
+
}
|
|
126
|
+
};
|
package/procs/score/sp14a.js
CHANGED
|
@@ -101,6 +101,17 @@ const groups = {
|
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
},
|
|
104
|
+
componentNoText: {
|
|
105
|
+
weight: 4,
|
|
106
|
+
packages: {
|
|
107
|
+
ibm: {
|
|
108
|
+
Rpt_Aria_WidgetLabels_Implicit: {
|
|
109
|
+
quality: 1,
|
|
110
|
+
what: 'Interactive component has no programmatically associated name'
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
},
|
|
104
115
|
regionNoText: {
|
|
105
116
|
weight: 4,
|
|
106
117
|
packages: {
|
|
@@ -115,6 +126,12 @@ const groups = {
|
|
|
115
126
|
quality: 1,
|
|
116
127
|
what: 'Element with a region role has no mechanism that allows an accessible name to be calculated'
|
|
117
128
|
}
|
|
129
|
+
},
|
|
130
|
+
ibm: {
|
|
131
|
+
Rpt_Aria_RegionLabel_Implicit: {
|
|
132
|
+
quality: 1,
|
|
133
|
+
what: 'Element with a region role has no label that describes its purpose'
|
|
134
|
+
}
|
|
118
135
|
}
|
|
119
136
|
}
|
|
120
137
|
},
|
|
@@ -150,6 +167,10 @@ const groups = {
|
|
|
150
167
|
509: {
|
|
151
168
|
quality: 1,
|
|
152
169
|
what: 'element with a textbox role has no mechanism that allows an accessible name to be calculated'
|
|
170
|
+
},
|
|
171
|
+
510: {
|
|
172
|
+
quality: 1,
|
|
173
|
+
what: 'element with a combobox role has no mechanism that allows an accessible name to be calculated'
|
|
153
174
|
}
|
|
154
175
|
},
|
|
155
176
|
htmlcs: {
|
|
@@ -180,6 +201,10 @@ const groups = {
|
|
|
180
201
|
'e:AA.4_1_2.H91.InputCheckbox.Name': {
|
|
181
202
|
quality: 1,
|
|
182
203
|
what: 'Checkbox input has no accessible name'
|
|
204
|
+
},
|
|
205
|
+
'e:AA.4_1_2.H91.InputRadio.Name': {
|
|
206
|
+
quality: 1,
|
|
207
|
+
what: 'Radio input has no accessible name'
|
|
183
208
|
}
|
|
184
209
|
}
|
|
185
210
|
}
|
|
@@ -217,7 +242,7 @@ const groups = {
|
|
|
217
242
|
}
|
|
218
243
|
},
|
|
219
244
|
ibm: {
|
|
220
|
-
|
|
245
|
+
WCAG20_Input_ExplicitLabelImage: {
|
|
221
246
|
quality: 1,
|
|
222
247
|
what: 'Input element of type image has no text alternative'
|
|
223
248
|
}
|
|
@@ -266,7 +291,7 @@ const groups = {
|
|
|
266
291
|
}
|
|
267
292
|
},
|
|
268
293
|
ibm: {
|
|
269
|
-
|
|
294
|
+
WCAG20_Img_HasAlt: {
|
|
270
295
|
quality: 1,
|
|
271
296
|
what: 'Image has no alt attribute conveying its meaning, or alt="" if decorative'
|
|
272
297
|
}
|
|
@@ -433,7 +458,7 @@ const groups = {
|
|
|
433
458
|
}
|
|
434
459
|
},
|
|
435
460
|
ibm: {
|
|
436
|
-
|
|
461
|
+
WCAG20_Elem_Lang_Valid: {
|
|
437
462
|
quality: 1,
|
|
438
463
|
what: 'lang attribute does not include a valid primary language'
|
|
439
464
|
}
|
|
@@ -519,7 +544,7 @@ const groups = {
|
|
|
519
544
|
}
|
|
520
545
|
},
|
|
521
546
|
ibm: {
|
|
522
|
-
|
|
547
|
+
WCAG20_Object_HasText: {
|
|
523
548
|
quality: 1,
|
|
524
549
|
what: 'Object element has no text alternative'
|
|
525
550
|
}
|
|
@@ -570,11 +595,11 @@ const groups = {
|
|
|
570
595
|
}
|
|
571
596
|
},
|
|
572
597
|
ibm: {
|
|
573
|
-
|
|
598
|
+
HAAC_Img_UsemapAlt: {
|
|
574
599
|
quality: 1,
|
|
575
600
|
what: 'Image map or child area has no text alternative'
|
|
576
601
|
},
|
|
577
|
-
'
|
|
602
|
+
'WCAG20_Area_HasAlt': {
|
|
578
603
|
quality: 1,
|
|
579
604
|
what: 'Area element in an image map has no text alternative'
|
|
580
605
|
}
|
|
@@ -678,6 +703,12 @@ const groups = {
|
|
|
678
703
|
quality: 1,
|
|
679
704
|
what: 'aria-activedescendant attribute is set to an invalid or duplicate id'
|
|
680
705
|
}
|
|
706
|
+
},
|
|
707
|
+
ibm: {
|
|
708
|
+
HAAC_ActiveDescendantCheck: {
|
|
709
|
+
quality: 1,
|
|
710
|
+
what: 'aria-activedescendant property does not reference the id of a non-empty, non-hidden active child element'
|
|
711
|
+
}
|
|
681
712
|
}
|
|
682
713
|
}
|
|
683
714
|
},
|
|
@@ -703,6 +734,17 @@ const groups = {
|
|
|
703
734
|
}
|
|
704
735
|
}
|
|
705
736
|
},
|
|
737
|
+
labelFollows: {
|
|
738
|
+
weight: 1,
|
|
739
|
+
packages: {
|
|
740
|
+
ibm: {
|
|
741
|
+
WCAG20_Input_LabelBefore: {
|
|
742
|
+
quality: 1,
|
|
743
|
+
what: 'Text input or select element label follows the input control'
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
},
|
|
706
748
|
labelBadID: {
|
|
707
749
|
weight: 4,
|
|
708
750
|
packages: {
|
|
@@ -726,6 +768,12 @@ const groups = {
|
|
|
726
768
|
what: 'aria-labelledby attribute references a nonexistent element'
|
|
727
769
|
}
|
|
728
770
|
},
|
|
771
|
+
ibm: {
|
|
772
|
+
WCAG20_Label_RefValid: {
|
|
773
|
+
quality: 1,
|
|
774
|
+
what: 'for attribute does not reference a non-empty, unique id attribute of an input element'
|
|
775
|
+
}
|
|
776
|
+
},
|
|
729
777
|
wave: {
|
|
730
778
|
'a:label_orphaned': {
|
|
731
779
|
quality: 1,
|
|
@@ -734,6 +782,17 @@ const groups = {
|
|
|
734
782
|
}
|
|
735
783
|
}
|
|
736
784
|
},
|
|
785
|
+
haspopupBad: {
|
|
786
|
+
weight: 4,
|
|
787
|
+
packages: {
|
|
788
|
+
ibm: {
|
|
789
|
+
combobox_haspopup: {
|
|
790
|
+
quality: 1,
|
|
791
|
+
what: 'aria-haspopup value is invalid for the role of the controlled or owned element'
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
},
|
|
737
796
|
ownerConflict: {
|
|
738
797
|
weight: 4,
|
|
739
798
|
packages: {
|
|
@@ -789,7 +848,7 @@ const groups = {
|
|
|
789
848
|
}
|
|
790
849
|
},
|
|
791
850
|
ibm: {
|
|
792
|
-
|
|
851
|
+
WCAG20_A_HasText: {
|
|
793
852
|
quality: 1,
|
|
794
853
|
what: 'Hyperlink has no text description'
|
|
795
854
|
}
|
|
@@ -1064,6 +1123,12 @@ const groups = {
|
|
|
1064
1123
|
quality: 1,
|
|
1065
1124
|
what: 'ARIA role is not contained by a required parent'
|
|
1066
1125
|
}
|
|
1126
|
+
},
|
|
1127
|
+
ibm: {
|
|
1128
|
+
Rpt_Aria_RequiredParent_Native_Host_Sematics: {
|
|
1129
|
+
quality: 1,
|
|
1130
|
+
what: 'Element is not contained within a role-valid element'
|
|
1131
|
+
}
|
|
1067
1132
|
}
|
|
1068
1133
|
}
|
|
1069
1134
|
},
|
|
@@ -1298,7 +1363,7 @@ const groups = {
|
|
|
1298
1363
|
}
|
|
1299
1364
|
},
|
|
1300
1365
|
ibm: {
|
|
1301
|
-
|
|
1366
|
+
WCAG20_Frame_HasTitle: {
|
|
1302
1367
|
quality: 1,
|
|
1303
1368
|
what: 'Inline frame has an empty or nonunique title attribute'
|
|
1304
1369
|
}
|
|
@@ -1351,9 +1416,33 @@ const groups = {
|
|
|
1351
1416
|
}
|
|
1352
1417
|
},
|
|
1353
1418
|
ibm: {
|
|
1354
|
-
|
|
1419
|
+
aria_role_redundant: {
|
|
1420
|
+
quality: 1,
|
|
1421
|
+
what: 'Explicitly assigned ARIA role is redundant with the implicit role of the element'
|
|
1422
|
+
},
|
|
1423
|
+
aria_semantics_role: {
|
|
1355
1424
|
quality: 1,
|
|
1356
1425
|
what: 'ARIA role is not valid for the element to which it is assigned'
|
|
1426
|
+
},
|
|
1427
|
+
element_tabbable_role_valid: {
|
|
1428
|
+
quality: 1,
|
|
1429
|
+
what: 'Tabbable element has a non-widget role'
|
|
1430
|
+
},
|
|
1431
|
+
Rpt_Aria_ContentinfoWithNoMain_Implicit: {
|
|
1432
|
+
quality: 1,
|
|
1433
|
+
what: 'Element has a contentinfo role when no element has a main role'
|
|
1434
|
+
},
|
|
1435
|
+
Rpt_Aria_ValidRole: {
|
|
1436
|
+
quality: 1,
|
|
1437
|
+
what: 'Element has an invalid role'
|
|
1438
|
+
},
|
|
1439
|
+
Rpt_Aria_EventHandlerMissingRole_Native_Host_Sematics: {
|
|
1440
|
+
quality: 1,
|
|
1441
|
+
what: 'Element has an event handler but no valid ARIA role'
|
|
1442
|
+
},
|
|
1443
|
+
table_aria_descendants: {
|
|
1444
|
+
quality: 1,
|
|
1445
|
+
what: 'Table structure element specifies an explicit role within the table container'
|
|
1357
1446
|
}
|
|
1358
1447
|
},
|
|
1359
1448
|
testaro: {
|
|
@@ -1374,7 +1463,7 @@ const groups = {
|
|
|
1374
1463
|
}
|
|
1375
1464
|
},
|
|
1376
1465
|
ibm: {
|
|
1377
|
-
|
|
1466
|
+
Rpt_Aria_RequiredProperties: {
|
|
1378
1467
|
quality: 1,
|
|
1379
1468
|
what: 'ARIA role on an element does not have a required attribute'
|
|
1380
1469
|
}
|
|
@@ -1398,6 +1487,10 @@ const groups = {
|
|
|
1398
1487
|
1042: {
|
|
1399
1488
|
quality: 1,
|
|
1400
1489
|
what: 'element with an option role has no aria-selected attribute'
|
|
1490
|
+
},
|
|
1491
|
+
1043: {
|
|
1492
|
+
quality: 1,
|
|
1493
|
+
what: 'element with a radio role has no aria-checked attribute'
|
|
1401
1494
|
}
|
|
1402
1495
|
},
|
|
1403
1496
|
wave: {
|
|
@@ -1456,6 +1549,10 @@ const groups = {
|
|
|
1456
1549
|
quality: 1,
|
|
1457
1550
|
what: 'element has an aria-selected attribute, which is not allowed'
|
|
1458
1551
|
},
|
|
1552
|
+
270: {
|
|
1553
|
+
quality: 1,
|
|
1554
|
+
what: 'element has an aria-required attribute, which is not allowed'
|
|
1555
|
+
},
|
|
1459
1556
|
281: {
|
|
1460
1557
|
quality: 1,
|
|
1461
1558
|
what: 'element has an aria-expanded attribute, which is not allowed'
|
|
@@ -1468,24 +1565,43 @@ const groups = {
|
|
|
1468
1565
|
quality: 1,
|
|
1469
1566
|
what: 'element has an aria-activedescendant attribute, which is not allowed'
|
|
1470
1567
|
},
|
|
1568
|
+
333: {
|
|
1569
|
+
quality: 1,
|
|
1570
|
+
what: 'element with a textbox role has an aria-owns attribute, which is not allowed'
|
|
1571
|
+
},
|
|
1471
1572
|
1066: {
|
|
1472
1573
|
quality: 1,
|
|
1473
1574
|
what: 'element has an ARIA attribute which is not valid'
|
|
1474
1575
|
}
|
|
1475
1576
|
},
|
|
1476
1577
|
ibm: {
|
|
1477
|
-
|
|
1578
|
+
aria_semantics_attribute: {
|
|
1579
|
+
quality: 1,
|
|
1580
|
+
what: 'ARIA attributes is invalid for the element or ARIA role to which it is assigned'
|
|
1581
|
+
},
|
|
1582
|
+
Rpt_Aria_ValidProperty: {
|
|
1478
1583
|
quality: 1,
|
|
1479
1584
|
what: 'ARIA attribute is invalid for the role'
|
|
1480
1585
|
}
|
|
1481
1586
|
}
|
|
1482
1587
|
}
|
|
1483
1588
|
},
|
|
1589
|
+
ariaRedundant: {
|
|
1590
|
+
weight: 1,
|
|
1591
|
+
packages: {
|
|
1592
|
+
ibm: {
|
|
1593
|
+
aria_attribute_redundant: {
|
|
1594
|
+
quality: 1,
|
|
1595
|
+
what: 'ARIA attribute is used when there is a corresponding HTML attribute'
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
},
|
|
1484
1600
|
ariaReferenceBad: {
|
|
1485
1601
|
weight: 4,
|
|
1486
1602
|
packages: {
|
|
1487
1603
|
ibm: {
|
|
1488
|
-
|
|
1604
|
+
Rpt_Aria_ValidIdRef: {
|
|
1489
1605
|
quality: 1,
|
|
1490
1606
|
what: 'ARIA property does not reference the non-empty unique id of a visible element'
|
|
1491
1607
|
}
|
|
@@ -1531,7 +1647,7 @@ const groups = {
|
|
|
1531
1647
|
}
|
|
1532
1648
|
},
|
|
1533
1649
|
ibm: {
|
|
1534
|
-
|
|
1650
|
+
WCAG21_Input_Autocomplete: {
|
|
1535
1651
|
quality: 1,
|
|
1536
1652
|
what: 'Autocomplete attribute token is not appropriate for the input form field'
|
|
1537
1653
|
}
|
|
@@ -1575,7 +1691,7 @@ const groups = {
|
|
|
1575
1691
|
}
|
|
1576
1692
|
},
|
|
1577
1693
|
ibm: {
|
|
1578
|
-
|
|
1694
|
+
IBMA_Color_Contrast_WCAG2AA: {
|
|
1579
1695
|
quality: 1,
|
|
1580
1696
|
what: 'Contrast ratio of text with background does not meet WCAG 2.1 AA'
|
|
1581
1697
|
}
|
|
@@ -1678,7 +1794,7 @@ const groups = {
|
|
|
1678
1794
|
}
|
|
1679
1795
|
},
|
|
1680
1796
|
ibm: {
|
|
1681
|
-
|
|
1797
|
+
RPT_Header_HasContent: {
|
|
1682
1798
|
quality: 1,
|
|
1683
1799
|
what: 'Heading element provides no descriptive text'
|
|
1684
1800
|
}
|
|
@@ -1712,7 +1828,7 @@ const groups = {
|
|
|
1712
1828
|
}
|
|
1713
1829
|
},
|
|
1714
1830
|
ibm: {
|
|
1715
|
-
|
|
1831
|
+
WCAG20_Img_LinkTextNotRedundant: {
|
|
1716
1832
|
quality: 1,
|
|
1717
1833
|
what: 'Text alternative for the image in a link repeats text of the same or an adjacent link'
|
|
1718
1834
|
}
|
|
@@ -1807,6 +1923,12 @@ const groups = {
|
|
|
1807
1923
|
what: 'Document head element contains no non-empty title element'
|
|
1808
1924
|
}
|
|
1809
1925
|
},
|
|
1926
|
+
ibm: {
|
|
1927
|
+
WCAG20_Doc_HasTitle: {
|
|
1928
|
+
quality: 1,
|
|
1929
|
+
what: 'Page has no subject-identifying title'
|
|
1930
|
+
}
|
|
1931
|
+
},
|
|
1810
1932
|
wave: {
|
|
1811
1933
|
'e:title_invalid': {
|
|
1812
1934
|
quality: 1,
|
|
@@ -2048,6 +2170,10 @@ const groups = {
|
|
|
2048
2170
|
99: {
|
|
2049
2171
|
quality: 1,
|
|
2050
2172
|
what: 'li element has no ul, ol, or list-role parent'
|
|
2173
|
+
},
|
|
2174
|
+
385: {
|
|
2175
|
+
quality: 1,
|
|
2176
|
+
what: 'list item has no ul, ol, or list-role parent or owner'
|
|
2051
2177
|
}
|
|
2052
2178
|
}
|
|
2053
2179
|
}
|
|
@@ -2128,7 +2254,7 @@ const groups = {
|
|
|
2128
2254
|
}
|
|
2129
2255
|
},
|
|
2130
2256
|
ibm: {
|
|
2131
|
-
|
|
2257
|
+
WCAG20_Elem_UniqueAccessKey: {
|
|
2132
2258
|
quality: 1,
|
|
2133
2259
|
what: 'Accesskey attribute value on an element is not unique for the page'
|
|
2134
2260
|
}
|
|
@@ -2151,7 +2277,7 @@ const groups = {
|
|
|
2151
2277
|
weight: 2,
|
|
2152
2278
|
packages: {
|
|
2153
2279
|
ibm: {
|
|
2154
|
-
|
|
2280
|
+
WCAG20_Input_RadioChkInFieldSet: {
|
|
2155
2281
|
quality: 1,
|
|
2156
2282
|
what: 'Input is in a different group than another with the name'
|
|
2157
2283
|
}
|
|
@@ -2190,6 +2316,12 @@ const groups = {
|
|
|
2190
2316
|
what: 'Fieldset has no legend element'
|
|
2191
2317
|
}
|
|
2192
2318
|
},
|
|
2319
|
+
ibm: {
|
|
2320
|
+
WCAG20_Fieldset_HasLegend: {
|
|
2321
|
+
quality: 1,
|
|
2322
|
+
what: 'fieldset element has no single, non-empty legend as a label'
|
|
2323
|
+
}
|
|
2324
|
+
},
|
|
2193
2325
|
wave: {
|
|
2194
2326
|
'a:legend_missing': {
|
|
2195
2327
|
quality: 1,
|
|
@@ -2273,6 +2405,12 @@ const groups = {
|
|
|
2273
2405
|
quality: 1,
|
|
2274
2406
|
what: 'table element contains no th element or element with a rowheader or columnheader role'
|
|
2275
2407
|
}
|
|
2408
|
+
},
|
|
2409
|
+
ibm: {
|
|
2410
|
+
RPT_Table_DataHeadingsAria: {
|
|
2411
|
+
quality: 1,
|
|
2412
|
+
what: 'Data table does not identify headers'
|
|
2413
|
+
}
|
|
2276
2414
|
}
|
|
2277
2415
|
}
|
|
2278
2416
|
},
|
|
@@ -2332,7 +2470,7 @@ const groups = {
|
|
|
2332
2470
|
}
|
|
2333
2471
|
}
|
|
2334
2472
|
},
|
|
2335
|
-
|
|
2473
|
+
controlNoText: {
|
|
2336
2474
|
weight: 4,
|
|
2337
2475
|
packages: {
|
|
2338
2476
|
axe: {
|
|
@@ -2347,6 +2485,12 @@ const groups = {
|
|
|
2347
2485
|
what: 'Form control has no label'
|
|
2348
2486
|
}
|
|
2349
2487
|
},
|
|
2488
|
+
ibm: {
|
|
2489
|
+
WCAG20_Input_ExplicitLabel: {
|
|
2490
|
+
quality: 1,
|
|
2491
|
+
what: 'Form control has no associated label'
|
|
2492
|
+
}
|
|
2493
|
+
},
|
|
2350
2494
|
wave: {
|
|
2351
2495
|
'e:label_missing': {
|
|
2352
2496
|
quality: 1,
|
|
@@ -2397,6 +2541,12 @@ const groups = {
|
|
|
2397
2541
|
quality: 1,
|
|
2398
2542
|
what: 'Visible label is not in the accessible name'
|
|
2399
2543
|
}
|
|
2544
|
+
},
|
|
2545
|
+
ibm: {
|
|
2546
|
+
WCAG21_Label_Accessible: {
|
|
2547
|
+
quality: 1,
|
|
2548
|
+
what: 'Accessible name does not match or contain the visible label text'
|
|
2549
|
+
}
|
|
2400
2550
|
}
|
|
2401
2551
|
}
|
|
2402
2552
|
},
|
|
@@ -2446,11 +2596,17 @@ const groups = {
|
|
|
2446
2596
|
}
|
|
2447
2597
|
},
|
|
2448
2598
|
tabFocusability: {
|
|
2449
|
-
weight:
|
|
2599
|
+
weight: 4,
|
|
2450
2600
|
packages: {
|
|
2601
|
+
ibm: {
|
|
2602
|
+
Rpt_Aria_MissingFocusableChild: {
|
|
2603
|
+
quality: 1,
|
|
2604
|
+
what: 'UI component has no focusable child element for keyboard access'
|
|
2605
|
+
}
|
|
2606
|
+
},
|
|
2451
2607
|
testaro: {
|
|
2452
2608
|
focAll: {
|
|
2453
|
-
quality:
|
|
2609
|
+
quality: 0.5,
|
|
2454
2610
|
what: 'Discrepancy between elements that should be and that are Tab-focusable'
|
|
2455
2611
|
}
|
|
2456
2612
|
}
|
|
@@ -2532,6 +2688,12 @@ const groups = {
|
|
|
2532
2688
|
quality: 1,
|
|
2533
2689
|
what: 'Some page content is not contained by landmarks'
|
|
2534
2690
|
}
|
|
2691
|
+
},
|
|
2692
|
+
ibm: {
|
|
2693
|
+
Rpt_Aria_OrphanedContent_Native_Host_Sematics: {
|
|
2694
|
+
quality: 1,
|
|
2695
|
+
what: 'Content does not reside within an element with a landmark role'
|
|
2696
|
+
}
|
|
2535
2697
|
}
|
|
2536
2698
|
}
|
|
2537
2699
|
},
|
|
@@ -2568,6 +2730,21 @@ const groups = {
|
|
|
2568
2730
|
}
|
|
2569
2731
|
}
|
|
2570
2732
|
},
|
|
2733
|
+
mainConfusion: {
|
|
2734
|
+
weight: 3,
|
|
2735
|
+
packages: {
|
|
2736
|
+
ibm: {
|
|
2737
|
+
Rpt_Aria_MultipleMainsRequireLabel_Implicit_2: {
|
|
2738
|
+
quality: 1,
|
|
2739
|
+
what: 'Element with main role has no unique label among the main-role elements'
|
|
2740
|
+
},
|
|
2741
|
+
Rpt_Aria_MultipleMainsVisibleLabel_Implicit: {
|
|
2742
|
+
quality: 1,
|
|
2743
|
+
what: 'Element with main role has no unique visible label among the main-role elements'
|
|
2744
|
+
}
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
},
|
|
2571
2748
|
mainNot1: {
|
|
2572
2749
|
weight: 2,
|
|
2573
2750
|
packages: {
|
|
@@ -2595,7 +2772,13 @@ const groups = {
|
|
|
2595
2772
|
axe: {
|
|
2596
2773
|
'landmark-no-duplicate-banner': {
|
|
2597
2774
|
quality: 1,
|
|
2598
|
-
what: '
|
|
2775
|
+
what: 'Page has more than 1 banner landmark'
|
|
2776
|
+
}
|
|
2777
|
+
},
|
|
2778
|
+
ibm: {
|
|
2779
|
+
Rpt_Aria_OneBannerInSiblingSet_Implicit: {
|
|
2780
|
+
quality: 1,
|
|
2781
|
+
what: 'Multiple elements with a banner role are on the page'
|
|
2599
2782
|
}
|
|
2600
2783
|
}
|
|
2601
2784
|
}
|
|
@@ -2611,6 +2794,17 @@ const groups = {
|
|
|
2611
2794
|
}
|
|
2612
2795
|
}
|
|
2613
2796
|
},
|
|
2797
|
+
footerConfusion: {
|
|
2798
|
+
weight: 3,
|
|
2799
|
+
packages: {
|
|
2800
|
+
ibm: {
|
|
2801
|
+
Rpt_Aria_MultipleContentinfoLandmarks_Implicit: {
|
|
2802
|
+
quality: 1,
|
|
2803
|
+
what: 'Element with a contentinfo role has no unique purpose label among the contentinfo-role elements'
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
},
|
|
2614
2808
|
footerMultiple: {
|
|
2615
2809
|
weight: 2,
|
|
2616
2810
|
packages: {
|
|
@@ -2630,6 +2824,12 @@ const groups = {
|
|
|
2630
2824
|
quality: 1,
|
|
2631
2825
|
what: 'Landmark has a role and an accessible name that are identical to another'
|
|
2632
2826
|
}
|
|
2827
|
+
},
|
|
2828
|
+
ibm: {
|
|
2829
|
+
landmark_name_unique: {
|
|
2830
|
+
quality: 1,
|
|
2831
|
+
what: 'Landmark has no unique aria-labelledby or aria-label among landmarks in the same parent region'
|
|
2832
|
+
}
|
|
2633
2833
|
}
|
|
2634
2834
|
}
|
|
2635
2835
|
},
|
|
@@ -2644,6 +2844,17 @@ const groups = {
|
|
|
2644
2844
|
}
|
|
2645
2845
|
}
|
|
2646
2846
|
},
|
|
2847
|
+
bannerConfusion: {
|
|
2848
|
+
weight: 3,
|
|
2849
|
+
packages: {
|
|
2850
|
+
ibm: {
|
|
2851
|
+
Rpt_Aria_MultipleBannerLandmarks_Implicit: {
|
|
2852
|
+
quality: 1,
|
|
2853
|
+
what: 'Element with a banner role has no unique purpose label among the banner-role elements'
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
},
|
|
2647
2858
|
navConfusion: {
|
|
2648
2859
|
weight: 3,
|
|
2649
2860
|
packages: {
|
|
@@ -2652,6 +2863,34 @@ const groups = {
|
|
|
2652
2863
|
quality: 1,
|
|
2653
2864
|
what: 'nav element has an accessible name that is non-unique among the nav elements'
|
|
2654
2865
|
}
|
|
2866
|
+
},
|
|
2867
|
+
ibm: {
|
|
2868
|
+
Rpt_Aria_MultipleNavigationLandmarks_Implicit: {
|
|
2869
|
+
quality: 1,
|
|
2870
|
+
what: 'Element with a navigation role has no unique purpose label among the navigation-role elements'
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
},
|
|
2875
|
+
regionConfusion: {
|
|
2876
|
+
weight: 3,
|
|
2877
|
+
packages: {
|
|
2878
|
+
ibm: {
|
|
2879
|
+
Rpt_Aria_MultipleRegionsUniqueLabel_Implicit: {
|
|
2880
|
+
quality: 1,
|
|
2881
|
+
what: 'Element with a region role has no unique label among the region-role elements'
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
},
|
|
2886
|
+
searchConfusion: {
|
|
2887
|
+
weight: 3,
|
|
2888
|
+
packages: {
|
|
2889
|
+
ibm: {
|
|
2890
|
+
Rpt_Aria_MultipleSearchLandmarks: {
|
|
2891
|
+
quality: 1,
|
|
2892
|
+
what: 'Element with a search role has no unique purpose label among the search-role elements'
|
|
2893
|
+
}
|
|
2655
2894
|
}
|
|
2656
2895
|
}
|
|
2657
2896
|
},
|
|
@@ -2666,6 +2905,17 @@ const groups = {
|
|
|
2666
2905
|
}
|
|
2667
2906
|
}
|
|
2668
2907
|
},
|
|
2908
|
+
complementaryNoText: {
|
|
2909
|
+
weight: 1,
|
|
2910
|
+
packages: {
|
|
2911
|
+
ibm: {
|
|
2912
|
+
Rpt_Aria_ComplementaryRequiredLabel_Implicit: {
|
|
2913
|
+
quality: 1,
|
|
2914
|
+
what: 'Element has a complementary role but has no label'
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
},
|
|
2669
2919
|
navNoText: {
|
|
2670
2920
|
weight: 3,
|
|
2671
2921
|
packages: {
|
|
@@ -2677,6 +2927,17 @@ const groups = {
|
|
|
2677
2927
|
}
|
|
2678
2928
|
}
|
|
2679
2929
|
},
|
|
2930
|
+
labelNoText: {
|
|
2931
|
+
weight: 4,
|
|
2932
|
+
packages: {
|
|
2933
|
+
ibm: {
|
|
2934
|
+
Valerie_Label_HasContent: {
|
|
2935
|
+
quality: 1,
|
|
2936
|
+
what: 'label element has no non-empty purpose-descriptive text'
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
},
|
|
2680
2941
|
focusableOperable: {
|
|
2681
2942
|
weight: 3,
|
|
2682
2943
|
packages: {
|
|
@@ -2724,6 +2985,12 @@ const groups = {
|
|
|
2724
2985
|
what: 'Element with an explicit or implicit nonnegative tabindex attribute is directly aria-hidden'
|
|
2725
2986
|
}
|
|
2726
2987
|
},
|
|
2988
|
+
ibm: {
|
|
2989
|
+
aria_hidden_focus_misuse: {
|
|
2990
|
+
quality: 1,
|
|
2991
|
+
what: 'Focusable element is within the subtree of an element with aria-hidden set to true'
|
|
2992
|
+
}
|
|
2993
|
+
},
|
|
2727
2994
|
tenon: {
|
|
2728
2995
|
189: {
|
|
2729
2996
|
quality: 1,
|
|
@@ -3128,6 +3395,12 @@ const groups = {
|
|
|
3128
3395
|
what: 'Skip-link target is not focusable or does not exist'
|
|
3129
3396
|
}
|
|
3130
3397
|
},
|
|
3398
|
+
ibm: {
|
|
3399
|
+
WCAG20_Body_FirstASkips_Native_Host_Sematics: {
|
|
3400
|
+
quality: 0.5,
|
|
3401
|
+
what: 'Page provides no way to skip directly to the main content'
|
|
3402
|
+
}
|
|
3403
|
+
},
|
|
3131
3404
|
wave: {
|
|
3132
3405
|
'e:link_skip_broken': {
|
|
3133
3406
|
quality: 1,
|
|
@@ -3169,7 +3442,7 @@ const groups = {
|
|
|
3169
3442
|
}
|
|
3170
3443
|
}
|
|
3171
3444
|
},
|
|
3172
|
-
|
|
3445
|
+
obsolete: {
|
|
3173
3446
|
weight: 3,
|
|
3174
3447
|
packages: {
|
|
3175
3448
|
alfa: {
|
|
@@ -3179,6 +3452,10 @@ const groups = {
|
|
|
3179
3452
|
}
|
|
3180
3453
|
},
|
|
3181
3454
|
htmlcs: {
|
|
3455
|
+
'e:AA.1_3_1.H49.AlignAttr': {
|
|
3456
|
+
quality: 1,
|
|
3457
|
+
what: 'align attribute is obsolete'
|
|
3458
|
+
},
|
|
3182
3459
|
'e:AA.1_3_1.H49.Center': {
|
|
3183
3460
|
quality: 1,
|
|
3184
3461
|
what: 'center element is obsolete'
|
|
@@ -3187,22 +3464,21 @@ const groups = {
|
|
|
3187
3464
|
quality: 1,
|
|
3188
3465
|
what: 'font element is obsolete'
|
|
3189
3466
|
}
|
|
3190
|
-
}
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
'e:AA.1_3_1.H49.AlignAttr': {
|
|
3467
|
+
},
|
|
3468
|
+
ibm: {
|
|
3469
|
+
combobox_version: {
|
|
3470
|
+
quality: 1,
|
|
3471
|
+
what: 'combobox design pattern is invalid for ARIA 1.2'
|
|
3472
|
+
},
|
|
3473
|
+
element_attribute_deprecated: {
|
|
3198
3474
|
quality: 1,
|
|
3199
|
-
what: '
|
|
3475
|
+
what: 'Element or attribute is obsolete'
|
|
3200
3476
|
}
|
|
3201
3477
|
},
|
|
3202
3478
|
wave: {
|
|
3203
3479
|
'a:longdesc': {
|
|
3204
3480
|
quality: 1,
|
|
3205
|
-
what: '
|
|
3481
|
+
what: 'longdesc attribute is obsolete'
|
|
3206
3482
|
}
|
|
3207
3483
|
}
|
|
3208
3484
|
}
|
|
@@ -3352,10 +3628,10 @@ exports.scorer = async report => {
|
|
|
3352
3628
|
const {items} = result[preferredMode];
|
|
3353
3629
|
if (items && Array.isArray(items)) {
|
|
3354
3630
|
items.forEach(issue => {
|
|
3355
|
-
const {
|
|
3356
|
-
if (
|
|
3631
|
+
const {ruleId, level} = issue;
|
|
3632
|
+
if (ruleId && level) {
|
|
3357
3633
|
// Add 4 per violation, 1 per warning (“recommendation”).
|
|
3358
|
-
addDetail(which,
|
|
3634
|
+
addDetail(which, ruleId, level === 'violation' ? 4 : 1);
|
|
3359
3635
|
}
|
|
3360
3636
|
});
|
|
3361
3637
|
}
|