testilo 13.3.2 → 13.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -469,15 +469,13 @@ A module can invoke `digest` in this way:
469
469
 
470
470
  ```javaScript
471
471
  const {digest} = require('testilo/digest');
472
- const digesterDir = `${process.env.FUNCTIONDIR}/digest/dp99a`;
473
- fs.readFile(`${digesterDir}/index.html`, 'utf8')
474
- .then(template => {
475
- const {digester} = require(`${digesterDir}/index`);
476
- const digestedReports = digest(template, digester, scoredReports);
477
- });
472
+ const digesterDir = `${process.env.FUNCTIONDIR}/digest/tdp99a`;
473
+ const {digester} = require(`${digesterDir}/index`);
474
+ digest(digester, scoredReports)
475
+ .then(digestedReports => {});
478
476
  ```
479
477
 
480
- The first two arguments to `digest()` are a digest template and a digesting function. In this example, they have been obtained from files in the Testilo package, but they could be custom-made. The third argument to `digest()` is an array of scored report objects. The `digest()` function returns an array of digested reports. The invoking module can further dispose of the digested reports as needed.
478
+ The first argument to `digest()` is a digesting function. In this example, it has been obtained from a files in the Testilo package, but it could be custom-made. The second argument to `digest()` is an array of scored report objects. The `digest()` function returns an array of digested reports. The invoking module can further dispose of the digested reports as needed.
481
479
 
482
480
  #### By a user
483
481
 
@@ -505,12 +503,11 @@ To test the `digest` module, in the project directory you can execute the statem
505
503
 
506
504
  If you use Testilo to perform a battery of tests on multiple targets, you may want a single report that compares the total scores received by the targets. Testilo can produce such a _comparative report_.
507
505
 
508
- The `compare` module compares the scores in a collection of scored reports. Its `compare()` function takes three arguments:
509
- - a comparison template
506
+ The `compare` module compares the scores in a collection of scored reports. Its `compare()` function takes two arguments:
510
507
  - a comparison function
511
508
  - an array of scored reports
512
509
 
513
- The comparison template is an HTML document containing placeholders. A copy of the template, with its placeholders replaced by computed values, becomes the comparative report. The comparison function defines the rules for replacing the placeholders with values. The Testilo package contains a `procs/compare` directory, in which there are subdirectories containing pairs of templates and modules that export comparison functions. You can use one of those pairs, or you can create your own.
510
+ The comparison function defines the rules for generating an HTML file comparing the scored reports. The Testilo package contains a `procs/compare` directory, in which there are subdirectories containing modules that export comparison functions. You can use one of those functions, or you can create your own.
514
511
 
515
512
  ### Invocation
516
513
 
@@ -521,31 +518,30 @@ There are two ways to use the `compare` module.
521
518
  A module can invoke `compare` in this way:
522
519
 
523
520
  ```javaScript
524
- const fs = require('fs/promises);
525
521
  const {compare} = require('testilo/compare');
526
522
  const comparerDir = `${process.env.FUNCTIONDIR}/compare/tcp99`;
527
- fs.readFile(`${comparerDir}/index.html`)
528
- .then(template => {
529
- const {comparer} = require(`${comparerDir}/index`);
530
- const comparativeReport = compare(template, comparer, scoredReports);
531
- });
523
+ const {comparer} = require(`${comparerDir}/index`);
524
+ compare(comparer, scoredReports)
525
+ .then(comparison => {});
532
526
  ```
533
527
 
534
- The first two arguments to `compare()` are a template and a comparison function. In this example, they have been obtained from files in the Testilo package, but they could be custom-made. The third argument to `compare()` is an array of report objects. The `compare()` function returns a comparative report. The invoking module can further dispose of the comparative report as needed.
528
+ The first argument to `compare()` is a comparison function. In this example, it been obtained from a file in the Testilo package, but it could be custom-made. The second argument to `compare()` is an array of report objects. The `compare()` function returns a comparative report. The invoking module can further dispose of the comparative report as needed.
535
529
 
536
530
  #### By a user
537
531
 
538
532
  A user can invoke `compare` in this way:
539
533
 
540
534
  ```bash
541
- node call compare tcp99 legislators
535
+ node call compare tcp99 legislators 23pl
542
536
  ```
543
537
 
544
538
  When a user invokes `compare` in this example, the `call` module:
545
- - gets the template and the comparison module from subdirectory `tcp99` of the subdirectory `compare` in the `process.env.FUNCTIONDIR` directory.
546
- - gets all the reports in the `scored` subdirectory of the `process.env.REPORTDIR` directory.
539
+ - gets the comparison module from subdirectory `tcp99` of the subdirectory `compare` in the `process.env.FUNCTIONDIR` directory.
540
+ - gets all the reports in the `scored` subdirectory of the `process.env.REPORTDIR` directory whose file names begin with `23pl`.
547
541
  - writes the comparative report as an HTML file named `legislators.html` to the `comparative` subdirectory of the `process.env.REPORTDIR` directory.
548
542
 
543
+ The fourth argument to `call` (`23pl` in this example) is optional. If it is omitted, `call` will get and `comparer` will compare all the reports in the `scored` directory.
544
+
549
545
  The comparative report created by `compare` is an HTML file, and it expects a `style.css` file to exist in its directory. The `reports/comparative/style.css` file in Testilo is an appropriate stylesheet to be copied into the directory where comparative reports are written.
550
546
 
551
547
  ### Validation
package/call.js CHANGED
@@ -131,8 +131,8 @@ const callDigest = async (digesterID, selector = '') => {
131
131
  const reports = await getReports('scored', selector);
132
132
  // If any exist:
133
133
  if (reports.length) {
134
- const digesterDir = `${functionDir}/digest/${digesterID}`;
135
134
  // Get the digester.
135
+ const digesterDir = `${functionDir}/digest/${digesterID}`;
136
136
  const {digester} = require(`${digesterDir}/index`);
137
137
  // Digest the reports.
138
138
  const digestedReports = await digest(digester, reports);
@@ -152,15 +152,20 @@ const callDigest = async (digesterID, selector = '') => {
152
152
  };
153
153
  // Fulfills a comparison request.
154
154
  // Get the scored reports to be scored.
155
- const callCompare = async (compareProcID, comparisonNameBase) => {
156
- const reports = await getReports('scored');
157
- const comparerDir = `${functionDir}/compare/${compareProcID}`;
158
- const comparisonTemplate = await fs.readFile(`${comparerDir}/index.html`, 'utf8');
159
- const comparer = require(`${comparerDir}/index`).getQuery;
160
- const comparison = await compare(comparisonTemplate, comparer, reports);
161
- const comparisonDir = `${reportDir}/comparative`;
162
- await fs.writeFile(`${comparisonDir}/${comparisonNameBase}.html`, comparison);
163
- console.log(`Comparison completed. Proc: ${compareProcID}. Directory: ${comparisonDir}.`);
155
+ const callCompare = async (compareProcID, comparisonNameBase, selector = '') => {
156
+ const reports = await getReports('scored', selector);
157
+ // If any exist:
158
+ if (reports.length) {
159
+ // Get the comparer.
160
+ const comparerDir = `${functionDir}/compare/${compareProcID}`;
161
+ const {comparer} = require(`${comparerDir}/index`);
162
+ // Compare the reports.
163
+ const comparison = await compare(comparer, reports);
164
+ // Save the comparison.
165
+ const comparisonDir = `${reportDir}/comparative`;
166
+ await fs.writeFile(`${comparisonDir}/${comparisonNameBase}.html`, comparison);
167
+ console.log(`Comparison completed and saved in ${comparisonDir}`);
168
+ }
164
169
  };
165
170
 
166
171
  // ########## OPERATION
@@ -214,7 +219,7 @@ else if (fn === 'multiDigest' && fnArgs.length === 1) {
214
219
  console.log('Execution completed');
215
220
  });
216
221
  }
217
- else if (fn === 'compare' && fnArgs.length === 2) {
222
+ else if (fn === 'compare' && fnArgs.length > 1 && fnArgs.length < 4) {
218
223
  callCompare(... fnArgs)
219
224
  .then(() => {
220
225
  console.log('Execution completed');
package/compare.js CHANGED
@@ -1,22 +1,16 @@
1
1
  /*
2
2
  compare.js
3
- Creates a comparative report from scored reports.
3
+ Creates a comparison from scored reports.
4
+ Arguments:
5
+ 0. Comparing function.
6
+ 1. Array of scored reports.
4
7
  */
5
8
 
6
9
  // ########## FUNCTIONS
7
10
 
8
- // Replaces the placeholders in a template with eponymous query parameters.
9
- const replaceHolders = (template, query) => template
10
- .replace(/__([a-zA-Z]+)__/g, (ph, qp) => query[qp]);
11
11
  // Compares the scored reports and returns a comparison.
12
- exports.compare = async (comparisonTemplate, comparer, reports) => {
13
- // Create a query.
14
- const query = {};
15
- // Populate the query.
16
- await comparer(reports, query);
17
- // Use it to create a comparison.
18
- const comparison = replaceHolders(comparisonTemplate, query);
12
+ exports.compare = async (comparer, scoredReports) => {
19
13
  // Return the comparison.
20
- console.log(`Comparison complete. Report count: ${reports.length}`);
21
- return comparison;
14
+ console.log(`Comparison complete. Report count: ${scoredReports.length}`);
15
+ return comparer(scoredReports);
22
16
  };
package/digest.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  digest.js
3
- Creates digests from a scored reports.
3
+ Creates digests from scored reports.
4
4
  Arguments:
5
5
  0. Digesting function.
6
6
  1. Array of scored reports.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testilo",
3
- "version": "13.3.2",
3
+ "version": "13.4.0",
4
4
  "description": "Client that scores and digests Testaro reports",
5
5
  "main": "aim.js",
6
6
  "scripts": {
@@ -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 are links to the pages on the web. The scores are links 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>__scriptID__</code></li>
25
+ <li>Scored by <a href="https://www.npmjs.com/package/testilo">Testilo</a> with procedure <code>__scorer__</code></li>
26
+ <li>Digested by Testilo with procedure <code>__digester__</code></li>
27
+ <li>Compared by Testilo with procedure <code>__comparer__</code></li>
28
+ </ol>
29
+ <p>Testaro used ten tools (Alfa, Axe, Continuum, Equal Access, HTML CodeSniffer, Nu Html Checker, Tenon, Testaro, and WAVE) to perform about 1350 automated accessibility tests. Testilo used its scoring procedure to assign a score to each page, with 0 being perfect.</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 (from best to worst)</tr>
35
+ </thead>
36
+ <tbody class="linkSmaller secondCellRight">
37
+ __tableBody__
38
+ </tbody>
39
+ </table>
40
+ <h2>Disclaimer</h2>
41
+ <p>Other tests and scoring formulae would produce different results. The algorithms underlying these results can be inspected in the Testaro and Testilo packages and can be revised 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,83 @@
1
+ // index: comparer for scoring procedure tsp27
2
+
3
+ // ########## IMPORTS
4
+
5
+ // Module to access files.
6
+ const fs = require('fs/promises');
7
+
8
+ // CONSTANTS
9
+
10
+ // Digester ID.
11
+ const id = 'tcp27';
12
+ // Newlines with indentations.
13
+ const joiner = '\n ';
14
+ const innerJoiner = '\n ';
15
+ const innestJoiner = '\n ';
16
+
17
+ // ########## FUNCTIONS
18
+
19
+ // Returns data on the targets.
20
+ const getData = async scoredReports => {
21
+ const bodyData = [];
22
+ for (const report of scoredReports) {
23
+ const {id, sources, score} = report;
24
+ bodyData.push({
25
+ id,
26
+ org: sources.target.what,
27
+ url: sources.target.which,
28
+ score: score.summary.total
29
+ });
30
+ };
31
+ return {
32
+ pageCount: scoredReports.length,
33
+ script: scoredReports[0].sources.script,
34
+ bodyData
35
+ }
36
+ };
37
+ // Returns the maximum score.
38
+ const getMaxScore = tableData => tableData.reduce((max, item) => Math.max(max, item.score), 0);
39
+ // Converts report data to a table body.
40
+ const getTableBody = async bodyData => {
41
+ const maxScore = getMaxScore(bodyData);
42
+ const rows = bodyData
43
+ .sort((a, b) => a.score - b.score)
44
+ .map(item => {
45
+ const {id, org, url, score} = item;
46
+ const pageCell = `<th scope="row"><a href="${url}">${org}</a></th>`;
47
+ const numCell = `<td><a href="digests/${id}.html">${score}</a></td>`;
48
+ // Make the bar width proportional.
49
+ const barWidth = 100 * score / maxScore;
50
+ const bar = `<rect height="100%" width="${barWidth}%" fill="red"></rect>`;
51
+ const barCell = `<td aria-hidden="true"><svg width="100%" height="0.7em">${bar}</svg></td>`;
52
+ const row = `<tr>${pageCell}${numCell}${barCell}</tr>`;
53
+ return row;
54
+ });
55
+ return rows.join(innestJoiner);
56
+ };
57
+ // Populates a query for a comparative table.
58
+ const populateQuery = async (scoredReports, query) => {
59
+ const data = await getData(scoredReports);
60
+ query.pageCount = data.pageCount;
61
+ query.scriptID = scoredReports[0].sources.script;
62
+ query.scorer = 'tsp27';
63
+ query.digester = 'tdp27';
64
+ query.comparer = 'tcp27';
65
+ query.tableBody = await getTableBody(data.bodyData);
66
+ const date = new Date();
67
+ query.dateISO = date.toISOString().slice(0, 10);
68
+ query.dateSlash = query.dateISO.replace(/-/g, '/');
69
+ };
70
+ // Returns a digested report.
71
+ exports.comparer = async scoredReports => {
72
+ // Create a query to replace placeholders.
73
+ const query = {};
74
+ populateQuery(scoredReports, query);
75
+ // Get the template.
76
+ let template = await fs.readFile(`${__dirname}/index.html`, 'utf8');
77
+ // Replace its placeholders.
78
+ Object.keys(query).forEach(param => {
79
+ template = template.replace(new RegExp(`__${param}__`, 'g'), query[param]);
80
+ });
81
+ // Return the digest.
82
+ return template;
83
+ };
@@ -29,24 +29,27 @@
29
29
  </table>
30
30
  </header>
31
31
  <h2>Introduction</h2>
32
- <p>This is a digest of results from a battery of accessibility tests.</p>
33
- <p>The battery includes about 1350 tests drawn from ten different packages: Alfa, Axe, Continuum, Equal Access, HTML CodeSniffer, Nu Html Checker, QualWeb, Tenon, Testaro, and WAVE.</p>
34
- <p>These tests were run on the web page named above and gave the page a score of __total__, where 0 would be <q>perfect</q>.</p>
35
- <h2>Scores</h2>
32
+ <p>This is a digest of results from a battery of <a href="https://www.w3.org/WAI/">web accessibility</a> tests.</p>
33
+ <p>Ten different <dfn>tools</dfn> (Alfa, Axe, Continuum, Equal Access, HTML CodeSniffer, Nu Html Checker, QualWeb, Tenon, Testaro, and WAVE) tested the web page of __org__ at __url__ to check its compliance with various <dfn>rules</dfn>. There were a total of about 1350 rules, classified into about 250 accessibility <dfn>issues</dfn>.</p>
34
+ <p>The results were interpreted to yield an aggregate score of __total__, where 0 would be <q>perfect</q>.</p>
35
+ <h2>Total score</h2>
36
+ <p>The total score is the sum of five components.</p>
36
37
  <table class="allBorder secondCellRight">
37
38
  <caption>Score summary</caption>
38
39
  <thead>
39
- <tr><th>Component</th><th>Score</th></tr>
40
+ <tr><th>Component</th><th>Score</th><th>Description</th></tr>
40
41
  </thead>
41
42
  <tbody class="headersLeft">
42
- <tr><th>total</th><td>__total__</td></tr>
43
- <tr><th>issue</th><td>__issue__</td></tr>
44
- <tr><th>tool</th><td>__tool__</td></tr>
45
- <tr><th>prevention</th><td>__prevention__</td></tr>
46
- <tr><th>log</th><td>__log__</td></tr>
47
- <tr><th>latency</th><td>__latency__</td></tr>
43
+ <tr><th>total</th><td>__total__</td><td>Sum of the component scores</td></tr>
44
+ <tr><th>issue</th><td>__issue__</td><td>Severity and number of reported defects</td></tr>
45
+ <tr><th>tool</th><td>__tool__</td><td>Tool-by-tool defect ratings</td></tr>
46
+ <tr><th>prevention</th><td>__prevention__</td><td>Failed attempts by tools to test the page</td></tr>
47
+ <tr><th>log</th><td>__log__</td><td>Browser warnings about the page</td></tr>
48
+ <tr><th>latency</th><td>__latency__</td><td>Abnormal delay in page responses</td></tr>
48
49
  </tbody>
49
50
  </table>
51
+ <h2>Issue scores</h2>
52
+ <p>The score of an issue depends on the <dfn>severity</dfn> (user impact and certainty) of the issue and on how many instances were reported (by one or more tools).</p>
50
53
  <table class="allBorder secondCellRight">
51
54
  <caption>Issue scores</caption>
52
55
  <thead>
@@ -56,8 +59,10 @@
56
59
  __issueRows__
57
60
  </tbody>
58
61
  </table>
59
- <h2>Issues</h2>
62
+ <h2>Itemized issues</h2>
63
+ <p>The reported rule violations are itemized below, issue by issue. Additional details can be inspected in the complete report at the end of this page.</p>
60
64
  __issueDetailRows__
65
+ <h2>Complete report</h2>
61
66
  <pre>__report__</pre>
62
67
  <footer>
63
68
  <p class="date">Produced <time itemprop="datePublished" datetime="__dateISO__">__dateSlash__</time></p>
@@ -62,7 +62,7 @@ const populateQuery = (report, query) => {
62
62
  // Add paragraph groups about the issue details to the query.
63
63
  const issueDetailRows = [];
64
64
  issueIDs.forEach(issueID => {
65
- issueDetailRows.push(`<h3>Issue <code>${issueID}</code></h3>`);
65
+ issueDetailRows.push(`<h3 class="bars">Issue <code>${issueID}</code></h3>`);
66
66
  issueDetailRows.push(`<p>WCAG: ${issueClasses[issueID].wcag || 'N/A'}</p>`);
67
67
  const issueData = details.issue[issueID];
68
68
  issueDetailRows.push(`<p>Score: ${issueData.score}</p>`);
@@ -47,6 +47,9 @@ fieldset {
47
47
  form {
48
48
  margin-top: 1rem;
49
49
  }
50
+ h1, h2, h3, h4, h5, h6 {
51
+ margin-bottom: 0;
52
+ }
50
53
  h2 {
51
54
  font-size: 1.8rem;
52
55
  margin: 1rem auto 0.5rem auto;
@@ -55,6 +58,10 @@ h2 {
55
58
  h3 {
56
59
  font-size: 1.6rem;
57
60
  }
61
+ h3.bars {
62
+ border-top: solid black 0.1rem;
63
+ padding-top: 0.3rem;
64
+ }
58
65
  h4 {
59
66
  font-size: 1.4rem;
60
67
  }