testilo 25.1.2 → 26.0.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
@@ -244,7 +244,7 @@ There are two ways to use the `script` module.
244
244
 
245
245
  ##### By a module
246
246
 
247
- A module can invoke `script` in this way:
247
+ A module can invoke `script()` in this way:
248
248
 
249
249
  ```javaScript
250
250
  const {script} = require('testilo/script');
@@ -293,7 +293,7 @@ There are two ways to use the `merge` module.
293
293
 
294
294
  ##### By a module
295
295
 
296
- A module can invoke `merge` in this way:
296
+ A module can invoke `merge()` in this way:
297
297
 
298
298
  ```javaScript
299
299
  const {merge} = require('testilo/merge');
@@ -426,7 +426,7 @@ Testilo can enhance such a report by:
426
426
  - creating difgests
427
427
  - creating comparisons
428
428
 
429
- ## Scoring
429
+ ### Scoring
430
430
 
431
431
  To add scores to reports, the `score` module of Testilo performs computations on the test results and adds a `score` property to each report.
432
432
 
@@ -436,7 +436,7 @@ The `score()` function of the `score` module takes two arguments:
436
436
 
437
437
  A scoring function defines scoring rules. The Testilo package contains a `procs/score` directory, in which there are modules that export scoring functions. You can use one of those scoring functions, or you can create your own.
438
438
 
439
- ### Scorers
439
+ #### Scorers
440
440
 
441
441
  The built-in scoring functions are named `scorer()` and are exported by files whose names begin with `tsp` (for Testilo scoring proc). Those functions make use of `issues` objects defined in files whose names begin with `tic`. An `issues` object defines an issue classification: a body of data about rules of tools and the tool-agnostic issues that those rules are deemed to belong to.
442
442
 
@@ -482,11 +482,11 @@ The `quality` property is usually 1, but if the test of the rule is known to be
482
482
 
483
483
  Some issue objects (such as `flash` in `tic40.js`) have a `max` property, equal to the maximum possible count of instances. That property allows a scorer to ascribe a greater weight to an instance of that issue.
484
484
 
485
- ### Invocation
485
+ #### Invocation
486
486
 
487
487
  There are two ways to invoke the `score` module.
488
488
 
489
- #### By a module
489
+ ##### By a module
490
490
 
491
491
  A module can invoke `score()` in this way:
492
492
 
@@ -503,7 +503,7 @@ The second argument to `score()` is an array of report objects. They may have be
503
503
 
504
504
  The invoking module can further dispose of the scored reports as needed.
505
505
 
506
- #### By a user
506
+ ##### By a user
507
507
 
508
508
  A user can invoke `score()` in this way:
509
509
 
@@ -519,13 +519,13 @@ When a user invokes `score()` in this example, the `call` module:
519
519
 
520
520
  The optional third argument to `call()` (`75m` in this example) is a report selector. Without the argument, `call()` gets all the reports in the `raw` subdirectory. With the argument, `call()` gets only those reports whose names begin with the argument string.
521
521
 
522
- ### Validation
522
+ #### Validation
523
523
 
524
524
  To test the `score` module, in the project directory you can execute the statement `node validation/score/validate`. If `score` is valid, all logging statements will begin with “Success” and none will begin with “ERROR”.
525
525
 
526
- ## Report digesting
526
+ ### Report digesting
527
527
 
528
- ### Introduction
528
+ #### Introduction
529
529
 
530
530
  Reports from Testaro are JavaScript objects. When represented as JSON, they are human-readable, but not human-friendly. They are basically designed for machine tractability. This is equally true for reports that have been scored by Testilo. But Testilo can _digest_ a scored report, converting it to a human-oriented HTML document, or _digest_.
531
531
 
@@ -538,11 +538,11 @@ The digesting function populates an HTML digest template. A copy of the template
538
538
 
539
539
  The included templates format placeholders with leading and trailing underscore pairs (such as `__issueCount__`).
540
540
 
541
- ### Invocation
541
+ #### Invocation
542
542
 
543
543
  There are two ways to use the `digest` module.
544
544
 
545
- #### By a module
545
+ ##### By a module
546
546
 
547
547
  A module can invoke `digest()` in this way:
548
548
 
@@ -564,7 +564,7 @@ The third argument is the absolute or relative URL of a directory where the repo
564
564
 
565
565
  The `digest()` function returns an array of digested reports. The invoking module can further dispose of the digested reports as needed.
566
566
 
567
- #### By a user
567
+ ##### By a user
568
568
 
569
569
  A user can invoke `digest()` in this way:
570
570
 
@@ -585,9 +585,9 @@ The optional fourth argument to `call()` (`75m` in this example) is a report sel
585
585
 
586
586
  The digests created by `digest()` are HTML files, and they expect a `style.css` file to exist in their directory. The `reports/digested/style.css` file in Testilo is an appropriate stylesheet to be copied into the directory where digested reports are written.
587
587
 
588
- ## Report difgesting
588
+ ### Report difgesting
589
589
 
590
- ### Introduction
590
+ #### Introduction
591
591
 
592
592
  A _difgest_ is a digest that compares two reports. They can be reports of different targets, or reports of the same target from two different times or under two different conditions.
593
593
 
@@ -601,11 +601,11 @@ The `difgest` module difgests two scored reports. Its `difgest()` function takes
601
601
 
602
602
  The difgest template and module operate like the digest ones.
603
603
 
604
- ### Invocation
604
+ #### Invocation
605
605
 
606
606
  There are two ways to use the `difgest` module.
607
607
 
608
- #### By a module
608
+ ##### By a module
609
609
 
610
610
  A module can invoke `difgest()` in this way:
611
611
 
@@ -625,9 +625,9 @@ The difgest will include links to the two digests, which, in turn, contain links
625
625
 
626
626
  `difgest()` returns a difgest. The invoking module can further dispose of the difgest as needed.
627
627
 
628
- #### By a user
628
+ ##### By a user
629
629
 
630
- A user can invoke `difgest` in this way:
630
+ A user can invoke `difgest()` in this way:
631
631
 
632
632
  ```bash
633
633
  node call difgest tfp99 20141215T1200-x7-3 20141215T1200-x7-4
@@ -642,7 +642,7 @@ Difgests include links to the digests of the two reports. The destinations of th
642
642
 
643
643
  Difgests expect a `style.css` file to exist in their directory, as digests do.
644
644
 
645
- ### Validation
645
+ #### Validation
646
646
 
647
647
  To test the `digest` module, in the project directory you can execute the statement `node validation/digest/validate`. If `digest` is valid, all logging statements will begin with “Success” and none will begin with “ERROR”.
648
648
 
@@ -656,13 +656,13 @@ The `compare` module compares the scores in a collection of scored reports. Its
656
656
 
657
657
  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.
658
658
 
659
- ### Invocation
659
+ #### Invocation
660
660
 
661
661
  There are two ways to use the `compare` module.
662
662
 
663
- #### By a module
663
+ ##### By a module
664
664
 
665
- A module can invoke `compare` in this way:
665
+ A module can invoke `compare()` in this way:
666
666
 
667
667
  ```javaScript
668
668
  const {compare} = require('testilo/compare');
@@ -674,9 +674,9 @@ compare(comparer, scoredReports)
674
674
 
675
675
  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.
676
676
 
677
- #### By a user
677
+ ##### By a user
678
678
 
679
- A user can invoke `compare` in this way:
679
+ A user can invoke `compare()` in this way:
680
680
 
681
681
  ```bash
682
682
  node call compare tcp99 legislators
@@ -690,6 +690,10 @@ When a user invokes `compare` in this example, the `call` module:
690
690
 
691
691
  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.
692
692
 
693
+ #### Validation
694
+
695
+ 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”.
696
+
693
697
  ### Tool crediting
694
698
 
695
699
  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_.
@@ -704,13 +708,13 @@ The credit report contains four sections:
704
708
  - `onlies`: a list of the issues that only the tool reported instances of
705
709
  - `mosts`: a list of the issues for which the instance count of the tool was not surpassed by that of any other tool
706
710
 
707
- ### Invocation
711
+ #### Invocation
708
712
 
709
713
  There are two ways to use the `credit` module.
710
714
 
711
- #### By a module
715
+ ##### By a module
712
716
 
713
- A module can invoke `credit` in this way:
717
+ A module can invoke `credit()` in this way:
714
718
 
715
719
  ```javaScript
716
720
  const {credit} = require('testilo/credit');
@@ -720,9 +724,9 @@ credit(scoredReports)
720
724
 
721
725
  The argument to `credit()` is an array of scored report objects. The `credit()` function returns a credit report. The invoking module can further dispose of the credit report as needed.
722
726
 
723
- #### By a user
727
+ ##### By a user
724
728
 
725
- A user can invoke `credit` in this way:
729
+ A user can invoke `credit()` in this way:
726
730
 
727
731
  ```bash
728
732
  node call credit legislators 23pl
@@ -734,9 +738,37 @@ When a user invokes `credit` in this example, the `call` module:
734
738
 
735
739
  The third argument to `call` (`23pl` in this example) is optional. If it is omitted, `call` will get and `credit()` will tabulate all the reports in the `scored` directory.
736
740
 
737
- ### Validation
741
+ ### Summarization
738
742
 
739
- 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”.
743
+ The `summarize` module of Testilo can summarize a collection of scored reports. The summary of each report contains, insofar as they exist in the report, its ID, end time, order ID, target data, and total score.
744
+
745
+ #### Invocation
746
+
747
+ ##### By a module
748
+
749
+ A module can invoke `summarize()` in this way:
750
+
751
+ ```javaScript
752
+ const {summarize} = require('testilo/summarize');
753
+ const reports = […];
754
+ const summary = summarize(reports);
755
+
756
+ ```
757
+
758
+ The `reports` argument is an array of scored reports. The `summary` constant is an object. The module can further dispose of `summary` as needed.
759
+
760
+ ##### By a user
761
+
762
+ A user can invoke `summarize()` in either of these two ways:
763
+
764
+ ```javaScript
765
+ node call summarize divisions
766
+ node call summarize divisions 2411
767
+ ```
768
+
769
+ When a user invokes `summarize` in this example, the `call` module:
770
+ - gets all the reports in the `scored` subdirectory of the `REPORTDIR` directory, or (if the third argument is present) all those whose file names begin with `2411`.
771
+ - writes the summary as a JSON file named `divisions.json` to the `summarized` subdirectory of the `REPORTDIR` directory.
740
772
 
741
773
  ## Origin
742
774
 
package/call.js CHANGED
@@ -34,6 +34,8 @@ const {digest} = require('./digest');
34
34
  const {difgest} = require('./difgest');
35
35
  // Function to compare scores.
36
36
  const {compare} = require('./compare');
37
+ // Function to summarize reports.
38
+ const {summarize} = require('./summarize');
37
39
 
38
40
  // ########## CONSTANTS
39
41
 
@@ -230,7 +232,7 @@ const callCredit = async (tallyID, selector = '') => {
230
232
  const creditDir = `${reportDir}/credit`;
231
233
  await fs.mkdir(creditDir, {recursive: true});
232
234
  await fs.writeFile(`${creditDir}/${tallyID}.json`, JSON.stringify(tally, null, 2));
233
- console.log(`Reports tallied and credit report saved in ${creditDir}`);
235
+ console.log(`Reports tallied and credit report ${tallyID} saved in ${creditDir}`);
234
236
  }
235
237
  // Otherwise, i.e. if no scored reports are to be tallied:
236
238
  else {
@@ -238,6 +240,31 @@ const callCredit = async (tallyID, selector = '') => {
238
240
  console.log('ERROR: No scored reports to be tallied');
239
241
  }
240
242
  };
243
+ // Fulfills a summarize request.
244
+ const callSummarize = async (what, selector = '') => {
245
+ // Get the scored reports to be summarized.
246
+ const reports = await getReports('scored', selector);
247
+ // If any exist:
248
+ if (reports.length) {
249
+ // Summarize them.
250
+ const summary = summarize(what, reports);
251
+ // Add the selector, if any, to the summary.
252
+ if (selector) {
253
+ summary.selector = selector;
254
+ }
255
+ // Save the summary.
256
+ const summaryDir = `${reportDir}/summarized`;
257
+ await fs.mkdir(summaryDir, {recursive: true});
258
+ const filePath = `${summaryDir}/${summary.timeStamp}-${getRandomString(2)}-0.json`;
259
+ await fs.writeFile(filePath, `${JSON.stringify(summary, null, 2)}\n`);
260
+ console.log(`Reports summarized and summary saved as ${filePath}`);
261
+ }
262
+ // Otherwise, i.e. if no scored reports are to be summarized:
263
+ else {
264
+ // Report this.
265
+ console.log('ERROR: No scored reports to be summarized');
266
+ }
267
+ };
241
268
 
242
269
  // ########## OPERATION
243
270
 
@@ -296,6 +323,12 @@ else if (fn === 'credit' && fnArgs.length > 0 && fnArgs.length < 3) {
296
323
  console.log('Execution completed');
297
324
  });
298
325
  }
326
+ else if (fn === 'summarize' && fnArgs.length > 0 && fnArgs.length < 3) {
327
+ callSummarize(... fnArgs)
328
+ .then(() => {
329
+ console.log('Execution completed');
330
+ });
331
+ }
299
332
  else {
300
333
  console.log('ERROR: Invalid statement');
301
334
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testilo",
3
- "version": "25.1.2",
3
+ "version": "26.0.0",
4
4
  "description": "Prepares and processes Testaro reports",
5
5
  "main": "call.js",
6
6
  "scripts": {
@@ -58,7 +58,7 @@ const populateQuery = (reportA, reportB, query) => {
58
58
  const {summary, details} = score;
59
59
  query[`org${suffix}`] = target.what;
60
60
  query[`url${suffix}`] = target.which;
61
- query[`dateTime${suffix}`] = jobData.replace(/-/g, '/').replace('T', ', ');
61
+ query[`dateTime${suffix}`] = jobData.endTime.replace(/-/g, '/').replace('T', ', ');
62
62
  query[`total${suffix}`] = summary.total;
63
63
  query[`digest${suffix}URL`] = process.env.DIGEST_URL.replace('__id__', id);
64
64
  // Get the union of the issues in the reports.
package/procs/util.js CHANGED
@@ -24,7 +24,7 @@ const getTimeStamp
24
24
  = exports.getTimeStamp
25
25
  = date => getTimeString(date).replace(/[-:]/g, '').slice(2, 13);
26
26
  // Returns a time stamp representing the date and time.
27
- exports.getNowStamp = () => getTimeStamp(new Date());
27
+ const getNowStamp = exports.getNowStamp = () => getTimeStamp(new Date());
28
28
  // Inserts a character periodically in a string.
29
29
  const punctuate = (string, insertion, chunkSize) => {
30
30
  const segments = [];
@@ -56,13 +56,19 @@ exports.alphaNumOf = num => {
56
56
  return resultDigits.join('');
57
57
  };
58
58
  // Returns a random string.
59
- exports.getRandomString = length => {
59
+ const getRandomString = exports.getRandomString = length => {
60
60
  const chars = [];
61
61
  for (let i = 0; i < length; i++) {
62
62
  chars.push(alphaNumChars[Math.floor(62 * Math.random())]);
63
63
  }
64
64
  return chars.join('');
65
65
  };
66
+ // Returns a file ID.
67
+ exports.getFileID = randomLength => {
68
+ const timePart = getNowStamp();
69
+ const randomPart = getRandomString(randomLength);
70
+ return `${timePart}-${randomPart}-0`;
71
+ };
66
72
  // Returns a horizontal SVG graph bar.
67
73
  const getSVGBar = (num, max, isRight) => {
68
74
  const widthFrac = 100 * num / max;
package/summarize.js ADDED
@@ -0,0 +1,35 @@
1
+ /*
2
+ summarize.js
3
+ Returns a summary of reports.
4
+ */
5
+
6
+ // ########## IMPORTS
7
+
8
+ // Module to keep secrets.
9
+ require('dotenv').config();
10
+ // Module to perform common operations.
11
+ const {getNowStamp} = require('./procs/util');
12
+
13
+ // ########## FUNCTIONS
14
+
15
+ // Returns a summary.
16
+ exports.summarize = (what, reports) => {
17
+ const data = reports.map(report => {
18
+ const {id, jobData, score, sources} = report;
19
+ const order = sources && sources.order || '';
20
+ const target = sources && sources.target || '';
21
+ return {
22
+ id: id || '',
23
+ endTime: jobData && jobData.endTime || '',
24
+ order: order || '',
25
+ target,
26
+ score: score && score.summary && score.summary.total || null
27
+ };
28
+ });
29
+ const summary = {
30
+ what,
31
+ timeStamp: getNowStamp(),
32
+ data
33
+ };
34
+ return summary;
35
+ };