testilo 29.0.1 → 30.1.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
@@ -643,7 +643,7 @@ To test the `digest` module, in the project directory you can execute the statem
643
643
 
644
644
  ### Summarization
645
645
 
646
- The `summarize` module of Testilo can summarize a scored report. The summary contains, insofar as they exist in the report, its ID, end time, order ID, target data, and total score.
646
+ The `summarize` module of Testilo can summarize a scored report. The summary contains, insofar as they exist in the report, its ID, end time, `sources` property, and total score.
647
647
 
648
648
  #### Invocation
649
649
 
@@ -680,12 +680,14 @@ When a user invokes `summarize` in this example, the `call` module:
680
680
 
681
681
  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 _comparison_.
682
682
 
683
- The `compare` module compares the scores in a summary of scored reports. Its `compare()` function takes two arguments:
683
+ The `compare` module compares the scores in a summary report. Its `compare()` function takes two arguments:
684
684
  - a comparison function
685
685
  - a summary report
686
686
 
687
687
  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.
688
688
 
689
+ Summary reports suitable for comparisons are those that contain one result per target. If a summary report contains results from multiple times per target, tracking (described below) is appropriate, rather than comparison.
690
+
689
691
  #### Invocation
690
692
 
691
693
  There are two ways to use the `compare` module.
@@ -715,7 +717,7 @@ node call compare 'state legislators' tcp99 240813
715
717
 
716
718
  When a user invokes `compare` in this example, the `call` module:
717
719
  - gets the comparison module from subdirectory `tcp99` of the subdirectory `compare` in the `FUNCTIONDIR` directory.
718
- - gets the summary report whose file name begins with `'240813'` from the `summarized` subdirectory of the `REPORTDIR` directory.
720
+ - gets the first summary report whose file name begins with `'240813'` from the `summarized` subdirectory of the `REPORTDIR` directory.
719
721
  - creates an ID for the comparison.
720
722
  - creates the comparison as an HTML document.
721
723
  - writes the comparison in the `comparative` subdirectory of the `REPORTDIR` directory, with `state legislators` as a description and the ID as the base of the file name.
@@ -726,6 +728,47 @@ The comparative report created by `compare` is an HTML file, and it expects a `s
726
728
 
727
729
  To test the `compare` module, in the project directory you can execute the statement `node validation/compare/validate`. If `compare` is valid, all logging statements will begin with “Success” and none will begin with “ERROR”.
728
730
 
731
+ ### Track
732
+
733
+ The `track` module of Testilo selects, organizes, and presents data from summaries to show changes over time in total scores. The module produces a web page, showing changes in a table and a line graph. The line graph contains a line for each target (namely, each value of the `sources.target.what` property).
734
+
735
+ A typical use case for tracking is monitoring, i.e. periodic auditing of one or more web pages.
736
+
737
+ #### Invocation
738
+
739
+ ##### By a module
740
+
741
+ A module can invoke `track()` in this way:
742
+
743
+ ```javaScript
744
+ const {track} = require('testilo/track');
745
+ const trackerDir = `${process.env.FUNCTIONDIR}/track/ttp99a`;
746
+ const {tracker} = require(`${trackerDir}/index`);
747
+ const summaryReport = …;
748
+ const [reportID, trackReport] = track(tracker, summaryReport);
749
+ ```
750
+
751
+ The `track()` function returns an ID and an HTML tracking report that shows data for all of the results in the summary report. The invoking module can further dispose of the tracking report as needed.
752
+
753
+ ##### By a user
754
+
755
+ A user can invoke `track()` in one of these ways:
756
+
757
+ ```javaScript
758
+ node call track ttp99a
759
+ node call track ttp99a 241016
760
+ node call track ttp99a 241016 'ABC Foundation'
761
+ ```
762
+
763
+ When a user invokes `track()` in this example, the `call` module:
764
+ - gets the summary report from the last file in the `summarized` subdirectory of the `REPORTDIR` directory, or if the third argument to `call()` exists the last one whose name begins with `'241016'`.
765
+ - selects the summarized data for all results in the summary report, or if the fourth argument to `call()` exists from all results whose `target.what` property has the value `'ABC Foundation'`.
766
+ - uses tracker `ttp99a` to create a tracking report.
767
+ - assigns an ID to the tracking report.
768
+ - writes the tracking report to the `tracking` subdirectory of the `REPORTDIR` directory, with the ID as the base of its file name.
769
+
770
+ The tracking reports created by `track()` are HTML files, and they expect a `style.css` file to exist in their directory. The `reports/tracking/style.css` file in Testilo is an appropriate stylesheet to be copied into the directory where tracking reports are written.
771
+
729
772
  ### Tool crediting
730
773
 
731
774
  If you use Testaro to perform all the tests of all the tools on multiple targets and score the reports with a score proc that maps tool rules onto tool-agnostic issues, you may want to tabulate the comparative efficacy of each tool in discovering instances of issues. Testilo can help you do this by producing a _credit report_.
@@ -772,42 +815,6 @@ When a user invokes `credit` in this example, the `call` module:
772
815
  - creates an ID for the credit report.
773
816
  - writes the credit report as a JSON file, with the ID as the base of its file name and `legislators` as its description, to the `credit` subdirectory of the `REPORTDIR` directory.
774
817
 
775
- ### Track
776
-
777
- The `track` module of Testilo selects, organizes, and presents data from summaries to show changes over time in total scores. The module produces a web page, showing changes in a table and (in the future) also in a line graph.
778
-
779
- #### Invocation
780
-
781
- ##### By a module
782
-
783
- A module can invoke `track()` in this way:
784
-
785
- ```javaScript
786
- const {track} = require('testilo/track');
787
- const trackerDir = `${process.env.FUNCTIONDIR}/track/ttp99a`;
788
- const {tracker} = require(`${trackerDir}/index`);
789
- const summary = …;
790
- const [reportID, trackReport] = track(tracker, summary);
791
- ```
792
-
793
- The `track()` function returns an ID and an HTML tracking report that shows data for all of the results in the summary. The invoking module can further dispose of the report as needed.
794
-
795
- ##### By a user
796
-
797
- A user can invoke `track()` in this way:
798
-
799
- ```javaScript
800
- node call track ttp99a 241016T2045-Uf-0 4 'ABC Foundation'
801
- ```
802
-
803
- When a user invokes `track()` in this example, the `call` module:
804
- - gets the summary from the `241016T2045-Uf-0.json` file in the `summarized` subdirectory of the `REPORTDIR` directory.
805
- - selects the summarized data for all results with the `order` value of `'4'` and the `target.what` value of `'ABC Foundation'`. If the third or fourth argument to `call()` is `null` (or omitted), then `call()` does not select results by `order` or by `target.what`, respectively.
806
- - uses tracker `ttp99a` to create a tracking report.
807
- - writes the tracking report to the `tracking` subdirectory of the `REPORTDIR` directory.
808
-
809
- The tracking reports created by `track()` are HTML files, and they expect a `style.css` file to exist in their directory. The `reports/tracking/style.css` file in Testilo is an appropriate stylesheet to be copied into the directory where tracking reports are written.
810
-
811
818
  ## Origin
812
819
 
813
820
  Work on the functionalities of Testaro and Testilo began in 2017. It was named [Autotest](https://github.com/jrpool/autotest) in early 2021 and then partitioned into the more single-purpose packages Testaro and Testilo in January 2022.
package/call.js CHANGED
@@ -38,7 +38,7 @@ const {compare} = require('./compare');
38
38
  const {credit} = require('./credit');
39
39
  // Function to summarize reports.
40
40
  const {summarize} = require('./summarize');
41
- // Function to track audits.
41
+ // Function to track results.
42
42
  const {track} = require('./track');
43
43
 
44
44
  // ########## CONSTANTS
@@ -52,11 +52,17 @@ const fnArgs = process.argv.slice(3);
52
52
 
53
53
  // ########## FUNCTIONS
54
54
 
55
- // Gets a summary report.
55
+ // Gets the last matching summary report.
56
56
  const getSummaryReport = async selector => {
57
57
  const summaryDir = `${reportDir}/summarized`;
58
58
  const summaryReportNames = await fs.readdir(summaryDir);
59
- const summaryReportName = summaryReportNames.find(reportName => reportName.startsWith(selector));
59
+ let summaryReportName;
60
+ if (summaryReportNames && summaryReportNames.length) {
61
+ summaryReportName = summaryReportNames.findLast(reportName => reportName.startsWith(selector));
62
+ }
63
+ else {
64
+ summaryReportName = summaryReportNames.pop();
65
+ }
60
66
  if (summaryReportName) {
61
67
  const summaryReportJSON = await fs.readFile(`${summaryDir}/${summaryReportName}`, 'utf8');
62
68
  const summaryReport = JSON.parse(summaryReportJSON);
@@ -255,7 +261,7 @@ const callSummarize = async (what, selector = '') => {
255
261
  const summaryReport = {
256
262
  id: getFileID(2),
257
263
  what,
258
- data: []
264
+ summaries: []
259
265
  };
260
266
  // For each report to be summarized:
261
267
  for (const reportID of reportIDs) {
@@ -263,7 +269,7 @@ const callSummarize = async (what, selector = '') => {
263
269
  const report = await getReport('scored', reportID);
264
270
  // Add a summary of it to the summary report.
265
271
  const summary = summarize(report);
266
- summaryReport.data.push(summary);
272
+ summaryReport.summaries.push(summary);
267
273
  };
268
274
  // Save the summary report.
269
275
  const summaryDir = `${reportDir}/summarized`;
@@ -302,6 +308,40 @@ const callCompare = async (what, compareProcID, selector) => {
302
308
  }
303
309
  }
304
310
  };
311
+ // Fulfills a tracking request.
312
+ const callTrack = async (trackerID, selector, targetWhat) => {
313
+ // Get the summary report.
314
+ try {
315
+ const summaryReport = await getSummaryReport(selector);
316
+ // Remove unwanted results from it.
317
+ summaryReport.summaries = summaryReport.summaries.filter(
318
+ result => targetWhat
319
+ ? result.sources
320
+ && result.sources.target
321
+ && result.sources.target.what === targetWhat
322
+ : true
323
+ );
324
+ // If any results remain:
325
+ if (summaryReport.summaries.length) {
326
+ // Get the tracker.
327
+ const {tracker} = require(`${functionDir}/track/${trackerID}/index`);
328
+ // Track the results.
329
+ const [reportID, trackingReport] = await track(tracker, summaryReport);
330
+ // Save the tracking report.
331
+ await fs.mkdir(`${reportDir}/tracking`, {recursive: true});
332
+ const reportPath = `${reportDir}/tracking/${reportID}.html`;
333
+ await fs.writeFile(reportPath, trackingReport);
334
+ console.log(`Tracking report saved in ${reportPath}`);
335
+ }
336
+ // Otherwise, i.e. if no results remain:
337
+ else {
338
+ console.log('ERROR: No results match the request');
339
+ }
340
+ }
341
+ catch(error) {
342
+ console.log(`ERROR: Tracking request invalid (${error.message})`);
343
+ }
344
+ };
305
345
  // Fulfills a credit request.
306
346
  const callCredit = async (what, selector = '') => {
307
347
  // Get the IDs of the scored reports to be credited.
@@ -331,43 +371,6 @@ const callCredit = async (what, selector = '') => {
331
371
  console.log('ERROR: No scored reports to be credited');
332
372
  }
333
373
  };
334
- // Fulfills a tracking request.
335
- const callTrack = async (trackerID, summaryID, orderID, targetWhat) => {
336
- // Get the summary.
337
- try {
338
- const summaryJSON = await fs.readFile(`${reportDir}/summarized/${summaryID}.json`, 'utf8');
339
- const summary = JSON.parse(summaryJSON);
340
- // Remove unwanted audits from it.
341
- summary.data = summary.data.filter(result => {
342
- if (orderID && result.order !== orderID) {
343
- return false;
344
- }
345
- if (targetWhat && result.target && result.target.what !== targetWhat) {
346
- return false;
347
- }
348
- return true;
349
- });
350
- // If any results remain:
351
- if (summary.data.length) {
352
- // Get the tracker.
353
- const {tracker} = require(`${functionDir}/track/${trackerID}/index`);
354
- // Track the results.
355
- const [reportID, trackingReport] = await track(tracker, summary);
356
- // Save the tracking report.
357
- await fs.mkdir(`${reportDir}/tracking`, {recursive: true});
358
- const reportPath = `${reportDir}/tracking/${reportID}.html`;
359
- await fs.writeFile(reportPath, trackingReport);
360
- console.log(`Tracking report saved in ${reportPath}`);
361
- }
362
- // Otherwise, i.e. if no audits remain:
363
- else {
364
- console.log('ERROR: No audits match the request');
365
- }
366
- }
367
- catch(error) {
368
- console.log(`ERROR: Tracking request invalid (${error.message})`);
369
- }
370
- };
371
374
 
372
375
  // ########## OPERATION
373
376
 
package/compare.js CHANGED
@@ -11,6 +11,6 @@
11
11
  // Compares the summarized reports and returns a comparison.
12
12
  exports.compare = async (id, what, comparer, summaryReport) => {
13
13
  // Return the comparison.
14
- console.log(`Comparison complete. Report count: ${summaryReport.data.length}`);
14
+ console.log(`Comparison complete. Report count: ${summaryReport.summaries.length}`);
15
15
  return comparer(id, what, summaryReport);
16
16
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testilo",
3
- "version": "29.0.1",
3
+ "version": "30.1.0",
4
4
  "description": "Prepares and processes Testaro reports",
5
5
  "main": "call.js",
6
6
  "scripts": {
@@ -17,17 +17,17 @@ const innestJoiner = '\n ';
17
17
  // ########## FUNCTIONS
18
18
 
19
19
  // Returns the maximum score.
20
- const getMaxScore = summaryReport => summaryReport.data.reduce(
20
+ const getMaxScore = summaryReport => summaryReport.summaries.reduce(
21
21
  (max, result) => Math.max(max, result.score), 0
22
22
  );
23
23
  // Converts summary report data to a table body.
24
24
  const getTableBody = async summaryReport => {
25
25
  const maxScore = getMaxScore(summaryReport);
26
- const rows = summaryReport.data
26
+ const rows = summaryReport.summaries
27
27
  .sort((a, b) => a.score - b.score)
28
28
  .map(result => {
29
- const {id, target, score} = result;
30
- const {what, which} = target;
29
+ const {id, sources, score} = result;
30
+ const {what, which} = sources.target;
31
31
  const pageCell = `<th scope="row"><a href="${which}">${what}</a></th>`;
32
32
  const scoreDestination = process.env.DIGEST_URL.replace('__id__', id);
33
33
  const numCell = `<td><a href="${scoreDestination}">${score}</a></td>`;
@@ -42,7 +42,7 @@ const getTableBody = async summaryReport => {
42
42
  const populateQuery = async (id, what, summaryReport, query) => {
43
43
  query.id = id;
44
44
  query.what = what;
45
- query.pageCount = summaryReport.data.length;
45
+ query.pageCount = summaryReport.summaries.length;
46
46
  query.tableBody = await getTableBody(summaryReport);
47
47
  query.dateISO = getNowDate();
48
48
  query.dateSlash = getNowDateSlash();
@@ -36,7 +36,7 @@
36
36
  </table>
37
37
  </header>
38
38
  <h2>Introduction</h2>
39
- <p>This <q>difgest</q> summarizes the differences between the scores from the two accessibility audits referenced in the synopsis above. A perfect score would be 0.</p>
39
+ <p>This <q>difgest</q> summarizes the differences between the scores from the two accessibility test results referenced in the synopsis above. A perfect score would be 0.</p>
40
40
  <h2>Issue summary</h2>
41
41
  <p>Issues are ordered from the one on which B was most superior to the one on which A was most superior.</p>
42
42
  <table class="allBorder redBar">
@@ -28,12 +28,12 @@
28
28
  <script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
29
29
  <script src="https://cdn.jsdelivr.net/npm/@observablehq/plot@0.6"></script>
30
30
  <script type="module">
31
- const summaryJSON = '__summaryJSON__';
32
- const summary = JSON.parse(summaryJSON);
31
+ const summaryReportJSON = '__summaryReportJSON__';
32
+ const summaryReport = JSON.parse(summaryReportJSON);
33
33
  const graphData = [];
34
- summary.data.forEach(result => {
34
+ summaryReport.summaries.forEach(result => {
35
35
  graphData.push({
36
- target: result.target.what,
36
+ target: result.sources.target.what,
37
37
  time: new Date(`20${result.endTime}Z`),
38
38
  score: result.score
39
39
  });
@@ -21,29 +21,30 @@ const digestURL = process.env.DIGEST_URL;
21
21
  // FUNCTIONS
22
22
 
23
23
  // Adds parameters to a query for a tracking report.
24
- const populateQuery = async (id, summary, query) => {
24
+ const populateQuery = async (id, summaryReport, query) => {
25
25
  // General parameters.
26
26
  query.id = id;
27
27
  query.tp = trackerID;
28
28
  query.dateISO = getNowDate();
29
29
  query.dateSlash = getNowDateSlash();
30
- // JSON of pruned summary.
31
- summary.data.forEach(result => {
32
- delete result.target.id;
30
+ // JSON of pruned summary report.
31
+ const {summaries} = summaryReport;
32
+ summaries.forEach(result => {
33
+ delete result.sources.target.id;
33
34
  });
34
- query.summaryJSON = JSON.stringify(summary);
35
+ query.summaryReportJSON = JSON.stringify(summaryReport);
35
36
  // For each score:
36
37
  const rows = [];
37
- const results = summary.data;
38
- const targetWhats = Array.from(new Set(results.map(result => result.target.what))).sort();
39
- summary.data.forEach(result => {
38
+ const targetWhats = Array.from(new Set(summaries.map(result => result.sources.target.what))).sort();
39
+ summaries.forEach(result => {
40
40
  // Create an HTML table row for it.
41
41
  const timeCell = `<td>${result.endTime}</td>`;
42
42
  const digestLinkDestination = digestURL.replace('__id__', result.id);
43
43
  const scoreCell = `<td><a href=${digestLinkDestination}>${result.score}</a></td>`;
44
44
  const orderCell = `<td class="center">${result.order}</td>`;
45
- const targetID = alphaNumOf(targetWhats.indexOf(result.target.what));
46
- const targetLink = `<a href="${result.target.which}">${result.target.what}</a>`;
45
+ const {target} = result.sources;
46
+ const targetID = alphaNumOf(targetWhats.indexOf(target.what));
47
+ const targetLink = `<a href="${target.which}">${target.what}</a>`;
47
48
  const targetCell = `<td>${targetID}: ${targetLink}</td>`;
48
49
  const row = `<tr>${[timeCell, scoreCell, orderCell, targetCell].join('')}</tr>`;
49
50
  // Add the row to the array of rows.
@@ -53,10 +54,10 @@ const populateQuery = async (id, summary, query) => {
53
54
  query.scoreRows = rows.join(innerJoiner);
54
55
  };
55
56
  // Returns a tracking report.
56
- exports.tracker = async (id, summary) => {
57
+ exports.tracker = async (id, summaryReport) => {
57
58
  // Create a query to replace placeholders.
58
59
  const query = {};
59
- await populateQuery(id, summary, query);
60
+ await populateQuery(id, summaryReport, query);
60
61
  // Get the template.
61
62
  let template = await fs.readFile(`${__dirname}/index.html`, 'utf8');
62
63
  // Replace its placeholders.
package/summarize.js CHANGED
@@ -13,13 +13,10 @@ require('dotenv').config();
13
13
  // Returns a report summary.
14
14
  exports.summarize = report => {
15
15
  const {id, jobData, score, sources} = report;
16
- const order = sources && sources.order || '';
17
- const target = sources && sources.target || '';
18
16
  const summary = {
19
- id: id || '',
20
- endTime: jobData && jobData.endTime || '',
21
- order: order || '',
22
- target,
17
+ id: id || null,
18
+ endTime: jobData && jobData.endTime || null,
19
+ sources: sources || null,
23
20
  score: score && score.summary && score.summary.total || null
24
21
  };
25
22
  return summary;
package/track.js CHANGED
@@ -14,10 +14,9 @@ const {getFileID} = require('./procs/util');
14
14
  // ########## FUNCTIONS
15
15
 
16
16
  // Creates and returns a tracking report from a summary.
17
- exports.track = async (tracker, summary) => {
17
+ exports.track = async (tracker, summaryReport) => {
18
18
  // Use the tracker to create a tracking report.
19
19
  const id = getFileID(2);
20
- const trackingReport = await tracker(id, summary);
21
- console.log(`Tracking report ${id} created`);
20
+ const trackingReport = await tracker(id, summaryReport);
22
21
  return [id, trackingReport];
23
22
  };