testilo 3.5.0 → 3.6.2
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/README.md +17 -56
- package/digest.js +9 -6
- package/package.json +1 -1
- package/procs/digest/dp11a/index.js +15 -15
- package/procs/digest/dp12a/index.html +78 -0
- package/procs/digest/dp12a/index.js +126 -0
- package/procs/score/sp12a.js +2207 -0
- package/score.js +14 -8
- package/reports/comparative/railsites.html +0 -47
- package/reports/digested/35k1r-railpass.html +0 -9588
- package/reports/digested/3aee6-eurail.html +0 -36233
- package/reports/digested/dp10a/35k1r-railpass.html +0 -9302
- package/reports/digested/dp10a/3aee6-eurail.html +0 -35782
- package/reports/old/2q4h4-greinerabout.json +0 -10190
- package/reports/old/2q4h4-greinercol.json +0 -4965
- package/reports/old/2q4h4-greinercontact.json +0 -7804
- package/reports/old/2q4h4-greinerhome.json +0 -4277
- package/reports/old/2q4h4-greinermasks.json +0 -18805
- package/reports/old/2q4h4-greinermeasure.json +0 -4408
- package/reports/raw/35k1r-railpass.json +0 -8792
- package/reports/raw/3aee6-eurail.json +0 -35250
- package/reports/scored/35k1r-railpass.json +0 -9366
- package/reports/scored/3aee6-eurail.json +0 -35957
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@ Scorer and digester of Testaro reports
|
|
|
3
3
|
|
|
4
4
|
## Introduction
|
|
5
5
|
|
|
6
|
-
This application enriches [Testaro](https://www.npmjs.com/package/testaro) reports. Testaro performs digital accessibility tests on
|
|
6
|
+
This application enriches [Testaro](https://www.npmjs.com/package/testaro) reports. Testaro performs digital accessibility tests on web artifacts and creates reports in JSON format. To make those reports more useful, this application, Testilo, computes scores, converts the scored reports to human-readable web pages (_digests_), and compiles human-readable web pages comparing the scores of multiple artifacts.
|
|
7
7
|
|
|
8
8
|
## Dependencies
|
|
9
9
|
|
|
@@ -11,11 +11,13 @@ The `dotenv` dependency lets you set environment variables in an untracked `.env
|
|
|
11
11
|
|
|
12
12
|
## Architecture
|
|
13
13
|
|
|
14
|
-
The routines that perform scoring and
|
|
14
|
+
The routines that perform scoring, digesting, and comparing are _procs_ and are located in the `procs` directory.
|
|
15
15
|
|
|
16
|
-
To score
|
|
16
|
+
To score reports, Testilo needs to be told which reports to score and which scoring procedure (or _score proc_) to use.
|
|
17
17
|
|
|
18
|
-
Similarly, once
|
|
18
|
+
Similarly, once reports have been scored, Testilo can digest them. For this purpose, Testilo needs to be told which scored reports to digest and which _digest proc_ to use.
|
|
19
|
+
|
|
20
|
+
Likewise, Testilo can compare scored reports. It needs to be told which reports to compare and which _comparison proc_ to use.
|
|
19
21
|
|
|
20
22
|
Testilo includes some score procs, digest procs, and comparison procs. You can add others.
|
|
21
23
|
|
|
@@ -25,74 +27,33 @@ Testilo includes some score procs, digest procs, and comparison procs. You can a
|
|
|
25
27
|
|
|
26
28
|
#### Process
|
|
27
29
|
|
|
28
|
-
To score Testaro reports, execute the statement `node score
|
|
29
|
-
|
|
30
|
-
If you replace `abc` with the entire name base, Testilo will score one report. If you replace `abc` with a prefix (such as `35k1r-`), Testilo will score all the reports whose names begin with that prefix.
|
|
30
|
+
To score Testaro reports, execute the statement `node score procID reportNameStart`. Replace (here and below) `procID` with the base of the name of the score proc. An environment variable named `REPORTDIR_RAW` defines a directory (i.e. a filesystem path, relative to the project directory of Testilo) where Testilo will look for the raw (i.e. not-yet-scored) reports. If you want Testilo to score all of the reports in that directory, you can omit the `reportNameStart` argument. If, instead, you want Testilo to score only the reports in that directory whose names begin with a certain string, replace `reportNameStart` with that string. So, for example, suppose the raw reports are in the `reports/raw` directory of a project, `testing`, that sits alongside of Testilo in your filesystem. Then you would assign the value `'../testing/reports/raw'` to the environment variable `REPORTDIR_RAW`. Now, to score all of the reports in that directory with score proc `sp11a`, execute `node score sp11a`. Or, to score only the reports in that directory whose filenames begin with `4rper`, execute node score sp11a `4rper`.
|
|
31
31
|
|
|
32
|
-
This procedure has
|
|
33
|
-
- The score proc is compatible with the script that produced the report(s).
|
|
34
|
-
-
|
|
35
|
-
- Testilo can find the report file(s) in the directory whose relative path (relative to the project directory of Testilo) is the value of the `REPORTDIR_RAW` environment variable.
|
|
36
|
-
- Testilo can read in the `REPORTDIR_RAW` directory.
|
|
37
|
-
- There is a `REPORTDIR_SCORED` environment variable, whose value is the relative path of a directory that Testilo can write to.
|
|
38
|
-
- The `procs/score` directory contains a file named `xyz.js`.
|
|
32
|
+
This procedure has two preconditions:
|
|
33
|
+
- The score proc is compatible with the script (or _test proc_) that produced the report(s).
|
|
34
|
+
- You have defined environment variables `REPORTDIR_RAW` and (for the scored reports that Testilo will write) `REPORTDIR_SCORED`.
|
|
39
35
|
|
|
40
|
-
|
|
36
|
+
The scored report file has the same name as the original. The scored report has the same content as the original, plus new properties named `score` and `scoreProcID`.
|
|
41
37
|
|
|
42
38
|
#### Procedures
|
|
43
39
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
Score proc `sp10a` implements an algorithm similar to `tsp09a`, except for Testaro sample script `tp10`, which, unlike `tsp09`, includes the Tenon package.
|
|
47
|
-
|
|
48
|
-
Score proc `sp10b` likewise scores results from Testaro script `tp10`, but, unlike score proc `sp10a`, bases scores on _issues_ rather than tests. Here, an issue is a case of a fault or a suspected fault of a particular kind. For example, if there are 7 informative images without text alternatives, the count of issues of that kind is 7.
|
|
49
|
-
|
|
50
|
-
Because packages differ in how they identify the locations of issues, score proc `sp10b` does not try to establish whether an issue discovered by one test is the same as an issue discovered by another test. Instead, its algorithm is based on the presumption that the issues of a kind discovered by different tests are typically, but not always, subsets or supersets of one another. For example, if test A discovers 4 issues of kind X and test B discovers 6 issues of kind X, it is likely that the 4 discovered by A are also among the 6 discovered by B.
|
|
51
|
-
|
|
52
|
-
Reflecting this presumption, the contribution of any issue kind to a total score is based substantially on the largest count of issues of that kind discovered by any test. The first issue of a kind is weighted more heavily than each additional issue, because the existence of any issues of a particular kind, regardless of how many, tends to create usability barriers, remediation costs, and liability risks.
|
|
53
|
-
|
|
54
|
-
To a smaller extent, the total score is also affected by the counts of issues of the same kind discovered by other tests. This is because, when multiple tests discover a kind of issue:
|
|
55
|
-
- we can be more confident that there really are issues of that kind.
|
|
56
|
-
- the issues discovered by different tests might not fully overlap.
|
|
57
|
-
- there is less excuse for allowing issues of that kind to appear.
|
|
58
|
-
- the website owner is more likely to receive complaints or claims about issues of that kind.
|
|
40
|
+
The score procs included with Testilo represent milestones in the refinement of a scoring methodology.
|
|
59
41
|
|
|
60
|
-
|
|
42
|
+
One development has been an expansion of the set of packages. The progression from score proc `sp09a` to `sp10a` included the addition of the Tenon package.
|
|
61
43
|
|
|
62
|
-
|
|
63
|
-
- to treat them as a group of 1, because they alone discover issues of some kind.
|
|
64
|
-
- to insert them into an existing group.
|
|
44
|
+
The main development has been a change from package-based to issue-based scoring. With package-based scoring, each package yielded a score, and the scores were summed into a total score. With issue-based scoring, the individual tests in each package are classified into groups, such that the tests from various packages in any particular group all seek to discover approximately the same type of accessibility issue. An artifact gets a score on each group, and the group scores are summed into a total score. The change from package-based to issue-based scoring took place in the progression from score proc `sp10a` to `sp10b`.
|
|
65
45
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
The contribution also depends on how serious each kind of issue is deemed to be. Score proc `sp10b` assigns a weight to each test group. Weighting could be even more granular: The Axe-core package attributes a seriousness to each issue, so that two issues of the same kind can differ in seriousness. But score proc `sp10b` ignores that Axe-core rating.
|
|
46
|
+
The problem of combining partly similar tests from different packages and producing an appropriate accessibility score has no perfect solution, and each successive score proc embodies efforts to make the result more appropriate.
|
|
69
47
|
|
|
70
48
|
#### Prevention
|
|
71
49
|
|
|
72
|
-
Testaro reports an error when it is unable to perform a test on a host. A score proc can take such errors into account.
|
|
73
|
-
|
|
74
|
-
One motivation for penalizing prevention is the assumption that measurement of success is an essential contributor to success, so preventing measurement of accessibility interferes with the achievement of accessibility.
|
|
75
|
-
|
|
76
|
-
A second motivation is the presumtion that assistive technologies are unpredictable, changing, and not fundamentally different from testing technologies. Therefore, a host that prevents a testing technology is at risk for preventing an assistive technology, too, and thereby interfering with accessibility.
|
|
77
|
-
|
|
78
|
-
Users who prefer to disregard prevention in scoring can create score procs that do that. It is not obvious what it means to disregard a test that could not be performed. Treating it as contributing 0 to a score arguably is not neutral, but rather rewards prevention.
|
|
50
|
+
Testaro reports an error when it is unable to perform a test on a host. A score proc can take such errors into account. The score procs included with Testilo do that by estimating scores when a package cannot be run.
|
|
79
51
|
|
|
80
52
|
### Digesting
|
|
81
53
|
|
|
82
54
|
To make scored Testaro reports more useful for humans, Testilo can create digests of scored reports. A digest is an HTML document (a web page) summarizing and explaining the findings, with the scored report appended to it.
|
|
83
55
|
|
|
84
|
-
To make Testilo digest reports, execute the statement `node digest
|
|
85
|
-
|
|
86
|
-
This procedure has some preconditions:
|
|
87
|
-
- The digest proc is compatible with the score proc that scored the report.
|
|
88
|
-
- The filename extension is `.json`.
|
|
89
|
-
- Testilo can find the scored report file(s) in the directory whose relative path (relative to the project directory of Testilo) is the value of the `REPORTDIR_SCORED` environment variable.
|
|
90
|
-
- Testilo can read in the `REPORTDIR_SCORED` directory.
|
|
91
|
-
- There is a `REPORTDIR_DIGESTED` environment variable, whose value is the relative path of a directory that Testilo can write to.
|
|
92
|
-
- The `procs/digest` directory contains a subdirectory named `xyz`, which in turn contains files named `index.html` and `index.js`.
|
|
93
|
-
- You have copied the `reports/digested/style.css` file into the `REPORTDIR_DIGESTED` directory.
|
|
94
|
-
|
|
95
|
-
When Testilo digests a report, Testilo saves the digest in the directory whose relative path is the value of the `REPORTDIR_DIGESTED` environment variable. The digest has the same name as the report on which it is based, except with `.html` as the extension.
|
|
56
|
+
To make Testilo digest reports, execute the statement `node digest procID reportNameStart`. The rules for this statement are the same as for the `score` statement, except that the directory where Testilo finds the reports in the one referenced by the `REPORTDIR_SCORED` environment variable, and the directory where Testilo will write the digests is the one referenced by `REPORTDIR_DIGESTED`. In order to make the digests appear correct in a browser, you must copy the `reports/digested/style.css` file into the `REPORTDIR_DIGESTED` directory.
|
|
96
57
|
|
|
97
58
|
### Comparing
|
|
98
59
|
|
package/digest.js
CHANGED
|
@@ -15,8 +15,8 @@ const fs = require('fs/promises');
|
|
|
15
15
|
|
|
16
16
|
const reportDirScored = process.env.REPORTDIR_SCORED || 'reports/scored';
|
|
17
17
|
const reportDirDigested = process.env.REPORTDIR_DIGESTED || 'reports/digested';
|
|
18
|
-
const
|
|
19
|
-
const
|
|
18
|
+
const digesterID = process.argv[2];
|
|
19
|
+
const reportIDStart = process.argv[3];
|
|
20
20
|
|
|
21
21
|
// ########## FUNCTIONS
|
|
22
22
|
|
|
@@ -26,11 +26,14 @@ const replaceHolders = (content, query) => content
|
|
|
26
26
|
// Creates a digest.
|
|
27
27
|
const digest = async () => {
|
|
28
28
|
const reportDirScoredAbs = `${__dirname}/${reportDirScored}`;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
let reportFileNames = await fs.readdir(reportDirScoredAbs);
|
|
30
|
+
reportFileNames = reportFileNames.filter(fileName => fileName.endsWith('.json'));
|
|
31
|
+
if (reportIDStart) {
|
|
32
|
+
reportFileNames = reportFileNames
|
|
33
|
+
.filter(fileName => fileName.startsWith(reportIDStart));
|
|
34
|
+
}
|
|
32
35
|
const {makeQuery} = require(`${__dirname}/procs/digest/${digesterID}/index.js`);
|
|
33
|
-
for (const fileName of
|
|
36
|
+
for (const fileName of reportFileNames) {
|
|
34
37
|
const reportJSON = await fs.readFile(`${reportDirScoredAbs}/${fileName}`, 'utf8');
|
|
35
38
|
const report = JSON.parse(reportJSON);
|
|
36
39
|
const query = {};
|
package/package.json
CHANGED
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
// CONSTANTS
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
// Newlines with indentations.
|
|
10
|
+
const joiner = '\n ';
|
|
11
|
+
const innerJoiner = '\n ';
|
|
12
|
+
const specialMessages = {
|
|
13
|
+
log: 'This is based on the amount of browser logging during the tests. Browsers usually log messages only when pages contain erroneous code.',
|
|
14
|
+
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.',
|
|
15
|
+
solos: 'This is based on issues reported by unclassified tests. Details are in the report.'
|
|
16
|
+
};
|
|
17
17
|
|
|
18
18
|
// FUNCTIONS
|
|
19
19
|
|
|
@@ -26,11 +26,11 @@ const htmlEscape = textOrNumber => textOrNumber
|
|
|
26
26
|
const getScoreRow = (component, score) => `<tr><th>${component}</th><td>${score}</td></tr>`;
|
|
27
27
|
// Gets the start of a paragraph about a special score.
|
|
28
28
|
const getSpecialPStart = (summary, scoreID) =>
|
|
29
|
-
`<p><span class="componentID">${scoreID}</span>: Score ${summary[scoreID]}.`;
|
|
29
|
+
`<p><span class="componentID">${scoreID}</span>: Score ${summary[scoreID]}.`;
|
|
30
30
|
// Adds parameters to a query for a digest.
|
|
31
31
|
exports.makeQuery = (report, query) => {
|
|
32
32
|
// Add an HTML-safe copy of the host report to the query to be appended to the digest.
|
|
33
|
-
const {script, host, score} = report;
|
|
33
|
+
const { script, host, score } = report;
|
|
34
34
|
const reportJSON = JSON.stringify(report, null, 2);
|
|
35
35
|
const reportJSONSafe = htmlEscape(reportJSON);
|
|
36
36
|
query.report = reportJSONSafe;
|
|
@@ -52,7 +52,7 @@ exports.makeQuery = (report, query) => {
|
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
-
const {groupDetails, summary} = score;
|
|
55
|
+
const { groupDetails, summary } = score;
|
|
56
56
|
if (typeof summary.total === 'number') {
|
|
57
57
|
query.totalScore = summary.total;
|
|
58
58
|
}
|
|
@@ -69,7 +69,7 @@ exports.makeQuery = (report, query) => {
|
|
|
69
69
|
}
|
|
70
70
|
});
|
|
71
71
|
// Add the group rows of the score-summary table to the query.
|
|
72
|
-
const {groups} = summary;
|
|
72
|
+
const { groups } = summary;
|
|
73
73
|
const groupIDs = Object.keys(groups);
|
|
74
74
|
groupIDs.sort((a, b) => groups[b] - groups[a]);
|
|
75
75
|
groupIDs.forEach(groupID => {
|
|
@@ -89,7 +89,7 @@ exports.makeQuery = (report, query) => {
|
|
|
89
89
|
// Otherwise, i.e. if the score has no special components:
|
|
90
90
|
else {
|
|
91
91
|
// Add a paragraph stating this for the issue summary to the query.
|
|
92
|
-
query.specialSummary = '<p>No special issues contributed to the score.</p>'
|
|
92
|
+
query.specialSummary = '<p>No special issues contributed to the score.</p>';
|
|
93
93
|
}
|
|
94
94
|
// If the score has any classified issues as components:
|
|
95
95
|
if (groupIDs.length) {
|
|
@@ -104,7 +104,7 @@ exports.makeQuery = (report, query) => {
|
|
|
104
104
|
const testIDs = Object.keys(groupData[packageID]);
|
|
105
105
|
testIDs.forEach(testID => {
|
|
106
106
|
const testData = groupData[packageID][testID];
|
|
107
|
-
const {issueCount} = testData;
|
|
107
|
+
const { issueCount } = testData;
|
|
108
108
|
const issueNoun = issueCount !== 1 ? 'issues' : 'issue';
|
|
109
109
|
const listItem = `<li>${issueCount} ${issueNoun} reported by package <code>${packageID}</code>, test <code>${testID}</code> (${testData.what})</li>`;
|
|
110
110
|
groupListItems.push(listItem);
|
|
@@ -122,6 +122,6 @@ exports.makeQuery = (report, query) => {
|
|
|
122
122
|
// Otherwise, i.e. if the score has no classified issues as components:
|
|
123
123
|
else {
|
|
124
124
|
// Add a paragraph stating this for the group summary to the query.
|
|
125
|
-
query.groupSummary = '<p>No classified issues contributed to the score.</p>'
|
|
125
|
+
query.groupSummary = '<p>No classified issues contributed to the score.</p>';
|
|
126
126
|
}
|
|
127
127
|
};
|
|
@@ -0,0 +1,78 @@
|
|
|
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>tp12</code></p>
|
|
25
|
+
<p><strong>Scored by</strong>: Testilo, procedure <code>sp12a</code></p>
|
|
26
|
+
<p><strong>Digested by</strong>: Testilo, procedure <code>dp12a</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>tp12</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 808 tests. Of these, 16 are custom tests defined by Testaro, and the others belong to these six 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>
|
|
35
|
+
<a href="https://www.npmjs.com/package/html_codesniffer">HTML CodeSniffer</a> by Squiz Labs
|
|
36
|
+
</li>
|
|
37
|
+
<li><a href="https://github.com/IBMa/equal-access">Equal Access</a> by IBM</li>
|
|
38
|
+
<li><a href="https://tenon.io/documentation/apiv2.php">Tenon</a> by Level Access</li>
|
|
39
|
+
<li><a href="https://wave.webaim.org/api/">WAVE</a> by WebAIM</li>
|
|
40
|
+
</ul>
|
|
41
|
+
<p>Testaro produced a report enumerating the test results.</p>
|
|
42
|
+
<p><a href="https://www.npmjs.com/package/testilo">Testilo</a> processed the report and used the <code>sp12a</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>
|
|
43
|
+
<p>Finally, Testilo used procedure <code>dp12a</code> to produce this digest, briefly explaining how <code>sp12a</code> computed the scores.</p>
|
|
44
|
+
<h2>Score summary</h2>
|
|
45
|
+
<table class="allBorder secondCellRight">
|
|
46
|
+
<caption>Score components</caption>
|
|
47
|
+
<tbody class="headersLeft">
|
|
48
|
+
__scoreRows__
|
|
49
|
+
</tbody>
|
|
50
|
+
</table>
|
|
51
|
+
<h2>Issue summary</h2>
|
|
52
|
+
<h3>Special issues</h3>
|
|
53
|
+
__specialSummary__
|
|
54
|
+
<h3>Classified issues</h3>
|
|
55
|
+
__groupSummary__
|
|
56
|
+
<h2>Discussion</h2>
|
|
57
|
+
<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>sp12a</code>. You can modify and extend Testaro and Testilo to fit other theories and priorities.</p>
|
|
58
|
+
<p>Here, in brief, is how <code>sp12a</code> computes a score for a page.</p>
|
|
59
|
+
<ul>
|
|
60
|
+
<li>It finds all the defects and warnings (let’s call them <q>issues</q>) recorded in the report.</li>
|
|
61
|
+
<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>
|
|
62
|
+
<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>sp12a</code> assigns a severity weight to the issue type and uses that weight.)</li>
|
|
63
|
+
<li>It assigns quality ratings to particular tests that are judged abnormally reliable or unreliable.</li>
|
|
64
|
+
<li>It assigns a score to each issue reported by each test of each package.</li>
|
|
65
|
+
<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>
|
|
66
|
+
<li>It assigns a score for issues in the page logged by the browser.</li>
|
|
67
|
+
<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>
|
|
68
|
+
<li>It adds the scores together to obtain a total score.</li>
|
|
69
|
+
</ul>
|
|
70
|
+
<p>The precise rules of <code>sp12a</code> are found in the <a href="https://github.com/jrpool/testilo/blob/main/procs/score/sp12a.js">code itself</a>.</p>
|
|
71
|
+
<h2>Report</h2>
|
|
72
|
+
<pre>__report__</pre>
|
|
73
|
+
<footer>
|
|
74
|
+
<p class="date">Produced <time itemprop="datePublished" datetime="__dateISO__">__dateSlash__</time></p>
|
|
75
|
+
</footer>
|
|
76
|
+
</main>
|
|
77
|
+
</body>
|
|
78
|
+
</html>
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/*
|
|
2
|
+
index: digester for scoring procedure sp12a.
|
|
3
|
+
Creator of parameters for substitution into index.html.
|
|
4
|
+
Usage example for selected files: node digest dp12a 35k1r
|
|
5
|
+
Usage example for all files in REPORTDIR_SCORED: node digest dp12a
|
|
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
|
+
};
|