testilo 7.0.8 → 7.0.10

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 CHANGED
@@ -55,7 +55,7 @@ const aimScript = async () => {
55
55
  id: w3c
56
56
  };
57
57
  const {aim} = require('testilo/aim');
58
- const scriptJSON = await fs.readFile(`${process.env.SCRIPTDIR}/tp25.json`, 'utf8');
58
+ const scriptJSON = await fs.readFile(`${process.env.SCRIPTDIR}/ts25.json`, 'utf8');
59
59
  const script = JSON.parse(scriptJSON);
60
60
  const aimedScript = aim(script, host, 'developer@w3c.org');
61
61
  await fs.writeFile(
@@ -69,10 +69,10 @@ aimScript();
69
69
  Execution by a user:
70
70
 
71
71
  ```bash
72
- node call aim tp25 https://w3.org/ 'World Wide Web Consortium' w3c developer@w3c.org
72
+ node call aim ts25 https://w3.org/ 'World Wide Web Consortium' w3c developer@w3c.org
73
73
  ```
74
74
 
75
- In these examples, a copy of the script file named `tp25.json` in the `SCRIPTDIR` directory is aimed at the World Wide Web Consortium and then saved in the `JOBDIR` directory.
75
+ In these examples, a copy of the script file named `ts25.json` in the `SCRIPTDIR` directory is aimed at the World Wide Web Consortium and then saved in the `JOBDIR` directory.
76
76
 
77
77
  The `aim` function neither reads nor writes files. Its arguments are a script object, a host object, and a requester-email-address string, and it returns a job object, aimed at the host.
78
78
 
@@ -107,7 +107,7 @@ Execution by a module:
107
107
 
108
108
  ```javaScript
109
109
  const {merge} = require('testilo/merge');
110
- merge('tp25', 'weborgs', 'developer@w3.org')
110
+ merge('ts25', 'weborgs', 'developer@w3.org')
111
111
  .then(() => {
112
112
  console.log('Merger complete');
113
113
  });
@@ -116,10 +116,10 @@ merge('tp25', 'weborgs', 'developer@w3.org')
116
116
  Execution by a user:
117
117
 
118
118
  ```bash
119
- node call merge tp25 weborgs developer@w3.org
119
+ node call merge ts25 weborgs developer@w3.org
120
120
  ```
121
121
 
122
- In these examples, a copy of the script file named `tp25.json` in the `SCRIPTDIR` directory is aimed at all the hosts in the batch file named `weborgs.json` in the `BATCHDIR` directory, and the resulting jobs are saved in the `JOBDIR` directory. Each job has a `sources` property that identifies an email address to be notified after the job has been run.
122
+ In these examples, a copy of the script file named `ts25.json` in the `SCRIPTDIR` directory is aimed at all the hosts in the batch file named `weborgs.json` in the `BATCHDIR` directory, and the resulting jobs are saved in the `JOBDIR` directory. Each job has a `sources` property that identifies an email address to be notified after the job has been run.
123
123
 
124
124
  ### `score`
125
125
 
@@ -144,7 +144,7 @@ const scoreReport = async (scoreProcID, rawReportID) => {
144
144
  );
145
145
  console.log(`Report ${scoredReport.id} scored`);
146
146
  };
147
- scoreReport('sp25a', '756mr-tp25-w3c');
147
+ scoreReport('sp25a', '756mr-ts25-w3c');
148
148
  ```
149
149
 
150
150
  Execution by a user:
@@ -154,7 +154,7 @@ node call score sp25a
154
154
  node call score sp25a 756mr
155
155
  ```
156
156
 
157
- In these examples, the `score` function applies a score proc named `sp25a`, of which a copy is in the file `sp25a.json` in the `SCOREPROCDIR` directory, to a raw report `756mr-tp25-w3c.json` in the `REPORTDIR_RAW` directory and returns the same report with score data added. The scored report is saved in the `REPORTDIR_SCORED` directory.
157
+ In these examples, the `score` function applies a score proc named `sp25a`, of which a copy is in the file `sp25a.json` in the `SCOREPROCDIR` directory, to a raw report `756mr-ts25-w3c.json` in the `REPORTDIR_RAW` directory and returns the same report with score data added. The scored report is saved in the `REPORTDIR_SCORED` directory.
158
158
 
159
159
  The user statement can pass only 2 arguments to `call` if the first report in the `REPORTDIR_RAW` directory is the desired raw report. If there are multiple reports in that directory and the desired one is not the first, the user must pass 3 arguments to `call`, and the third argument must be a string, such that the first report in that directory that begins with that string is the desired report.
160
160
 
@@ -203,17 +203,17 @@ const digestReport = async (digestProcID, scoredReportID) => {
203
203
  await fs.writeFile(`${process.env.REPORTDIR_DIGESTED}/${digestedReport.id}.html`, digestedReport);
204
204
  console.log(`Report ${digestedReport.id} digested`);
205
205
  };
206
- digestReport('dp25a', '756mr-tp25-w3c');
206
+ digestReport('dp25a', '756mr-ts25-w3c');
207
207
  ```
208
208
 
209
209
  Execution by a user:
210
210
 
211
211
  ```bash
212
- node call digest sp25a
213
- node call digest sp25a 756mr
212
+ node call digest dp25a
213
+ node call digest dp25a 756mr
214
214
  ```
215
215
 
216
- In these examples, the `digest` function applies a digest proc named `dp25a`, of which a copy is in the file `dp25a.json` in the `DIGESTPROCDIR` directory, to a scored report `756mr-tp25-w3c.json` in the `REPORTDIR_SCORED` directory and returns an HTML digest for that same report. The digest is saved in the `REPORTDIR_DIGESTED` directory.
216
+ In these examples, the `digest` function applies a digest proc named `dp25a`, of which a copy is in the file `dp25a.json` in the `DIGESTPROCDIR` directory, to a scored report `756mr-ts25-w3c.json` in the `REPORTDIR_SCORED` directory and returns an HTML digest for that same report. The digest is saved in the `REPORTDIR_DIGESTED` directory.
217
217
 
218
218
  The user statement can pass only 2 arguments to `call` if the first report in the `REPORTDIR_SCORED` directory is the desired scored report. If there are multiple reports in that directory and the desired one is not the first, the user must pass 3 arguments to `call`, and the third argument must be a string, such that the first report in that directory that begins with that string is the desired report.
219
219
 
package/call.js CHANGED
@@ -62,7 +62,7 @@ const callAim = async (scriptName, hostURL, hostName, hostID, requester) => {
62
62
  id: hostID,
63
63
  which: hostURL,
64
64
  what: hostName
65
- },
65
+ },
66
66
  requester
67
67
  );
68
68
  const scriptID = job.sources.script;
@@ -95,7 +95,7 @@ const callScore = async (scoreProcID, reportIDStart = '') => {
95
95
  // Score it.
96
96
  const rawReportJSON = await fs.readFile(`${rawDir}/${rawReportName}`, 'utf8');
97
97
  const rawReport = JSON.parse(rawReportJSON);
98
- const {scorer} = require(`${scoreProcDir}/${scoreProcID}.js`);
98
+ const {scorer} = require(`./${scoreProcDir}/${scoreProcID}.js`);
99
99
  const scoredReport = await score(scorer, rawReport);
100
100
  // Save it, scored.
101
101
  await fs.writeFile(
@@ -123,7 +123,7 @@ const callMultiScore = async scoreProcID => {
123
123
  // Prepares to fulfill a digesting request.
124
124
  const digestPrep = async digestProcID => {
125
125
  const {digest} = require('./digest');
126
- const {makeQuery} = require(`${digestProcDir}/${digestProcID}/index`);
126
+ const {makeQuery} = require(`./${digestProcDir}/${digestProcID}/index`);
127
127
  const digestTemplate = await fs.readFile(`${digestProcDir}/${digestProcID}/index.html`, 'utf8');
128
128
  // Identify the scored reports.
129
129
  const scoredFileNames = await fs.readdir(scoredDir);
package/compare.js CHANGED
@@ -28,9 +28,11 @@ const replaceHolders = (content, query) => content
28
28
  // Creates and saves a web page containing a comparative table.
29
29
  exports.compare = async (compareProcID, comparisonNameBase) => {
30
30
  const comparisonDirAbs = `${__dirname}/${comparisonDir}`;
31
- const {getQuery} = require(`./procs/compare/${compareProcID}/index`);
31
+ const {getQuery} = require(`./${process.env.COMPAREPROCDIR}/${compareProcID}/index`);
32
32
  const query = await getQuery();
33
- const pageRaw = await fs.readFile(`${__dirname}/procs/compare/${compareProcID}/index.html`, 'utf8');
33
+ const pageRaw = await fs.readFile(
34
+ `${__dirname}/${process.env.COMPAREPROCDIR}/${compareProcID}/index.html`, 'utf8'
35
+ );
34
36
  const page = replaceHolders(pageRaw, query);
35
37
  await fs.writeFile(`${comparisonDirAbs}/${comparisonNameBase}.html`, page);
36
38
  console.log(`Page ${comparisonNameBase}.html created and saved`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testilo",
3
- "version": "7.0.8",
3
+ "version": "7.0.10",
4
4
  "description": "Client that scores and digests Testaro reports",
5
5
  "main": "aim.js",
6
6
  "scripts": {
@@ -0,0 +1,80 @@
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>tp16</code></p>
25
+ <p><strong>Scored by</strong>: Testilo, procedure <code>sp16c</code></p>
26
+ <p><strong>Digested by</strong>: Testilo, procedure <code>dp16b</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>tp16</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 1229 tests. Of these, 23 are custom tests or <q>quasi-tests</q> by Testaro, and the others belong to these eight 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://github.com/validator/validator">Nu Html Checker</a></li>
40
+ <li><a href="https://tenon.io/documentation/apiv2.php">Tenon</a> by Level Access</li>
41
+ <li><a href="https://wave.webaim.org/api/">WAVE</a> by WebAIM</li>
42
+ </ul>
43
+ <p>Testaro produced a report enumerating the test results.</p>
44
+ <p><a href="https://www.npmjs.com/package/testilo">Testilo</a> processed the report and used the <code>sp16c</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>
45
+ <p>Finally, Testilo used procedure <code>dp16b</code> to produce this digest, briefly explaining how <code>sp16c</code> computed the scores.</p>
46
+ <h2>Score summary</h2>
47
+ <table class="allBorder secondCellRight">
48
+ <caption>Score components</caption>
49
+ <tbody class="headersLeft">
50
+ __scoreRows__
51
+ </tbody>
52
+ </table>
53
+ <h2>Issue summary</h2>
54
+ <h3>Special issues</h3>
55
+ __specialSummary__
56
+ <h3>Classified issues</h3>
57
+ __groupSummary__
58
+ <h2>Discussion</h2>
59
+ <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>sp16c</code>. You can modify and extend Testaro and Testilo to fit other theories and priorities.</p>
60
+ <p>Here, in brief, is how <code>sp16c</code> computes a score for a page.</p>
61
+ <ul>
62
+ <li>It finds all the defects and warnings (let&rsquo;s call them <q>issues</q>) recorded in the report.</li>
63
+ <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>
64
+ <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>sp16c</code> assigns a severity weight to the issue type and uses that weight.)</li>
65
+ <li>It assigns quality ratings to particular tests that are judged abnormally reliable or unreliable.</li>
66
+ <li>It assigns a score to each issue reported by each test of each package.</li>
67
+ <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>
68
+ <li>It assigns a score for issues in the page logged by the browser.</li>
69
+ <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>
70
+ <li>It adds the scores together to obtain a total score.</li>
71
+ </ul>
72
+ <p>The precise rules of <code>sp16c</code> are found in the <a href="https://github.com/jrpool/testilo/blob/main/procs/score/sp16c.js">code itself</a>.</p>
73
+ <h2>Report</h2>
74
+ <pre>__report__</pre>
75
+ <footer>
76
+ <p class="date">Produced <time itemprop="datePublished" datetime="__dateISO__">__dateSlash__</time></p>
77
+ </footer>
78
+ </main>
79
+ </body>
80
+ </html>
@@ -0,0 +1,126 @@
1
+ /*
2
+ index: digester for scoring procedure sp16c.
3
+ Creator of parameters for substitution into index.html.
4
+ Usage example for selected files in REPORTDIR_SCORED: node digest dp16b 35k1r
5
+ Usage example for all files in REPORTDIR_SCORED: node digest dp16b
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, '&amp;')
25
+ .replace(/</g, '&lt;');
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 {job, 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.jobData.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 = job.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
+ };