testilo 11.0.7 → 12.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
@@ -35,15 +35,13 @@ Testaro executes _jobs_. In a job, Testaro performs _acts_ (tests and other oper
35
35
 
36
36
  You can create a job for Testaro directly, without using Testilo.
37
37
 
38
- Testilo can, however, make job preparation more efficient in two scenarios:
39
- - A common use case is to define a battery of tests and to have those same tests performed on multiple targets. In that case, it is efficient to create multiple jobs, one per target. The jobs are identical, except for the target-specific acts (including navigating to targets). If you tell Testilo about the tests and the targets, Testilo can create such collections of jobs for you.
40
- - Some tests operate on a copy of a target and modify that copy. Usually, one wants test isolation: The results of a test do not depend on any previously performed tests. To ensure test isolation, a job containing such target-modifying test acts must follow them with acts that restore the target to its pre-test state. Testilo can insert the required target-restoring acts into each job after target-modifying test act.
41
-
42
- The `batch` and `merge` modules performs these services.
38
+ Testilo can, however, make job preparation more efficient in these scenarios:
39
+ - You want to perform a battery of tests on multiple targets.
40
+ - You want to test targets for particular issues, using whichever tools happen to have tests for those issues.
43
41
 
44
42
  ### Target lists
45
43
 
46
- The simplest version of a list of targets is _target list_. It is stored as a tab-delimited text file, with one line per target. Each line contains 3 items, with tabs between them:
44
+ The simplest version of a list of targets is a _target list_. It is stored as a tab-delimited text file, with one line per target. Each line contains 3 items, with tabs between them:
47
45
  - An ID for the target
48
46
  - A description of the target
49
47
  - The URL of the target
@@ -125,7 +123,7 @@ Here is a script:
125
123
 
126
124
  ```javaScript
127
125
  {
128
- id: 'ts25',
126
+ id: 'ts99',
129
127
  what: 'Axe and QualWeb on account page',
130
128
  strict: true,
131
129
  timeLimit: 60,
@@ -152,7 +150,7 @@ Here is a script:
152
150
  }
153
151
  ```
154
152
 
155
- This script has 2 acts. The first is a placeholder act. The above batch can be merged with this script to create jobs. In that case, the first job would launch a Chromium browser, navigate to the Acme login page, complete and submit the login form, wait for the account page to load, and then run the Axe tests. If the batch contained additional targets, additional jobs would be created, with the login actions for each target specified in the `private` array of the `acts` object of that target.
153
+ This script has 3 acts. The first is a placeholder act. The above batch can be merged with this script to create jobs. In that case, the first job would launch a Chromium browser, navigate to the Acme login page, complete and submit the login form, wait for the account page to load, run the Axe tests, and then run the QualWeb tests. If the batch contained additional targets, additional jobs would be created, with the login actions for each target specified in the `private` array of the `acts` object of that target.
156
154
 
157
155
  As shown in this example, when a browser is launched by placeholder substitution, the script can determine the browser type (`chromium`, `firefox`, or `webkit`) by assigning a value to a `launch` property of the placeholder. That is useful, because sometimes it is the actions specified in a script that dictate which browser type is appropriate.
158
156
 
@@ -191,7 +189,7 @@ The `call` module will save the batch as a JSON file in the `batches` subdirecto
191
189
 
192
190
  ### Merge
193
191
 
194
- Testilo merges batches with scripts by means of a `merge` module.
192
+ Testilo merges batches with scripts, producing jobs, by means of the `merge` module.
195
193
 
196
194
  The `merge` module needs to be given a batch and a script. In addition, `merge` offers an isolation option. If you exercise it, the `merge` module will act as if the latest placeholder were **again** inserted after each target-modifying test act, except where that test act is the last act or where the next act after it is a placeholder.
197
195
 
@@ -203,7 +201,7 @@ Suppose you ask for a merger of the above batch and script, **without** the isol
203
201
 
204
202
  ```javaScript
205
203
  {
206
- id: '7is3i-ts25-acme',
204
+ id: '7is3i-ts99-acme',
207
205
  what: 'Axe on account page',
208
206
  strict: true,
209
207
  timeLimit: 60,
@@ -252,7 +250,7 @@ Suppose you ask for a merger of the above batch and script, **without** the isol
252
250
  }
253
251
  ],
254
252
  sources: {
255
- script: 'ts25',
253
+ script: 'ts99',
256
254
  batch: 'clothing-stores',
257
255
  target: {
258
256
  id: 'acme',
@@ -311,7 +309,7 @@ A user can invoke `merge` in this way:
311
309
  - `node call merge s b e false`
312
310
  - `node call merge s b e`
313
311
 
314
- In these statements, replace `s` and `b` with the base names of the script and batch files, respectively. For example, if the script file is named `ts25.json`, then replace `s` with `ts25`. Replace `e` with an email address, or with an empty string if the environment variable `process.env.REQUESTER` exists and you want to use it.
312
+ In these statements, replace `s` and `b` with the base names of the script and batch files, respectively. For example, if the script file is named `ts99.json`, then replace `s` with `ts99`. Replace `e` with an email address, or with an empty string if the environment variable `process.env.REQUESTER` exists and you want to use it.
315
313
 
316
314
  The first statement will cause a merger **with** isolation.
317
315
  The second and third statements will cause a merger **without* isolation.
@@ -324,6 +322,44 @@ The `call` module will save the jobs as JSON files in the `todo` subdirectory of
324
322
 
325
323
  To test the `merge` module, in the project directory you can execute the statement `node validation/merge/validate`. If `merge` is valid, all logging statements will begin with “Success” and none will begin with “ERROR”.
326
324
 
325
+ ### Issues to script
326
+
327
+ Testilo classifies issues. The built-in issue classifications are located in the `procs/score` directory, in files whose names begin with `tic` (for “Testilo issue classification”). You can create additional `tic` files with custom issue classifications.
328
+
329
+ If you want Testaro to test targets for particular issues, you can name those issues and use the Testilo `script` module to create a script.
330
+
331
+ #### Invocation
332
+
333
+ There are two ways to use the `script` module.
334
+
335
+ ##### By a module
336
+
337
+ A module can invoke `script` in this way:
338
+
339
+ ```javaScript
340
+ const {script} = require('testilo/script');
341
+ const scriptObj = script(issueClasses, issueIDs);
342
+ ```
343
+
344
+ This invocation references `issueClasses` and `issueIDs` variables. The `issueClasses` variable is an object that classifies issues, such as the `issueClasses` object in a `tic` file. The `issueIDs` variable is an array of strings such as `'regionNoText'`, that are the names of properties of the `issueClasses` object. The `script()` function of the `script` module generates a script and returns it as an object. The invoking module can further dispose of the script as needed.
345
+
346
+ ##### By a user
347
+
348
+ A user can invoke `script` in this way: In the Testilo project directory, execute the statement `node call script s c i0 i1 i2 i3 …`.
349
+
350
+ In this statement:
351
+ - Replace `s` with an ID for the script, such as `headings`.
352
+ - Replace `c` with the base name, such as `tic99`, of an issue classification file in the `score` subdirectory of the `process.env.FUNCTIONDIR` directory.
353
+ - Replace the remaining arguments (`i0` etc.) with issue IDs from that classification file.
354
+
355
+ The `call` module will retrieve the named classification from its directory.
356
+ The `script` module will create a script.
357
+ The `call` module will save the script as a JSON file in the `scripts` subdirectory of the `process.env.SPECDIR` directory.
358
+
359
+ #### Options
360
+
361
+ When the `script` module creates a script for you, it does not ask you for all of the options that the script may require. Instead, it chooses options. After you invoke `script`, you can edit the script that it creates to revise options.
362
+
327
363
  ## Report scoring
328
364
 
329
365
  ### Introduction
@@ -366,7 +402,7 @@ A module can invoke `score` in this way:
366
402
 
367
403
  ```javaScript
368
404
  const {score} = require('testilo/score');
369
- const {scorer} = require('testilo/procs/score/tsp25');
405
+ const {scorer} = require('testilo/procs/score/tsp99');
370
406
  score(scorer, reports);
371
407
  ```
372
408
 
@@ -377,12 +413,12 @@ The first argument to `score()` is a scoring function. In this example, it has b
377
413
  A user can invoke `score` in this way:
378
414
 
379
415
  ```bash
380
- node call score tsp25
381
- node call score tsp25 75m
416
+ node call score tsp99
417
+ node call score tsp99 75m
382
418
  ```
383
419
 
384
420
  When a user invokes `score` in this example, the `call` module:
385
- - gets the scoring module `tsp25` from its JSON file `tsp25.json` in the `score` subdirectory of the `process.env.FUNCTIONDIR` directory.
421
+ - gets the scoring module `tsp99` from its JSON file `tsp99.json` in the `score` subdirectory of the `process.env.FUNCTIONDIR` directory.
386
422
  - gets the reports from the `raw` subdirectory of the `process.env.REPORTDIR` directory.
387
423
  - writes the scored reports in JSON format to the `scored` subdirectory of the `process.env.REPORTDIR` directory.
388
424
 
@@ -415,7 +451,7 @@ A module can invoke `digest` in this way:
415
451
 
416
452
  ```javaScript
417
453
  const {digest} = require('testilo/digest');
418
- const digesterDir = `${process.env.FUNCTIONDIR}/digest/dp25a`;
454
+ const digesterDir = `${process.env.FUNCTIONDIR}/digest/dp99a`;
419
455
  fs.readFile(`${digesterDir}/index.html`, 'utf8')
420
456
  .then(template => {
421
457
  const {digester} = require(`${digesterDir}/index`);
@@ -430,12 +466,12 @@ The first two arguments to `digest()` are a digest template and a digesting func
430
466
  A user can invoke `digest` in this way:
431
467
 
432
468
  ```bash
433
- node call digest tdp25
434
- node call digest tdp25 75m
469
+ node call digest tdp99
470
+ node call digest tdp99 75m
435
471
  ```
436
472
 
437
473
  When a user invokes `digest` in this example, the `call` module:
438
- - gets the template and the digesting module from subdirectory `tdp25` in the `digest` subdirectory of the `process.env.FUNCTIONDIR` directory.
474
+ - gets the template and the digesting module from subdirectory `tdp99` in the `digest` subdirectory of the `process.env.FUNCTIONDIR` directory.
439
475
  - gets the reports from the `scored` subdirectory of the `process.env.REPORTDIR` directory.
440
476
  - writes the digested reports to the `digested` subdirectory of the `process.env.REPORTDIR` directory.
441
477
 
@@ -469,7 +505,7 @@ A module can invoke `compare` in this way:
469
505
  ```javaScript
470
506
  const fs = require('fs/promises);
471
507
  const {compare} = require('testilo/compare');
472
- const comparerDir = `${process.env.FUNCTIONDIR}/compare/tcp25`;
508
+ const comparerDir = `${process.env.FUNCTIONDIR}/compare/tcp99`;
473
509
  fs.readFile(`${comparerDir}/index.html`)
474
510
  .then(template => {
475
511
  const {comparer} = require(`${comparerDir}/index`);
@@ -484,11 +520,11 @@ The first two arguments to `compare()` are a template and a comparison function.
484
520
  A user can invoke `compare` in this way:
485
521
 
486
522
  ```bash
487
- node call compare tcp25 legislators
523
+ node call compare tcp99 legislators
488
524
  ```
489
525
 
490
526
  When a user invokes `compare` in this example, the `call` module:
491
- - gets the template and the comparison module from subdirectory `tcp25` of the subdirectory `compare` in the `process.env.FUNCTIONDIR` directory.
527
+ - gets the template and the comparison module from subdirectory `tcp99` of the subdirectory `compare` in the `process.env.FUNCTIONDIR` directory.
492
528
  - gets all the reports in the `scored` subdirectory of the `process.env.REPORTDIR` directory.
493
529
  - writes the comparative report as an HTML file named `legislators.html` to the `comparative` subdirectory of the `process.env.REPORTDIR` directory.
494
530
 
package/call.js CHANGED
@@ -20,6 +20,8 @@ require('dotenv').config();
20
20
  const fs = require('fs/promises');
21
21
  // Function to process a list-to-batch conversion.
22
22
  const {batch} = require('./batch');
23
+ // Function to create a script from rule specifications.
24
+ const {script} = require('./script');
23
25
  // Function to process a merger.
24
26
  const {merge} = require('./merge');
25
27
  // Function to score reports.
@@ -52,6 +54,17 @@ const callBatch = async (listID, what) => {
52
54
  await fs.writeFile(`${specDir}/batches/${listID}.json`, `${batchJSON}\n`);
53
55
  console.log(`Target list ${listID} converted to a batch and saved in ${specDir}/batches`);
54
56
  };
57
+ // Fulfills a script-creation request.
58
+ const callScript = async (scriptID, classificationID, ... issueIDs) => {
59
+ // Get the issue classification.
60
+ const {issueClasses} = require(`${functionDir}/score/${classificationID}`);
61
+ // Create a script.
62
+ const scriptObj = script(scriptID, issueClasses, ... issueIDs);
63
+ // Save the script.
64
+ const scriptJSON = JSON.stringify(scriptObj, null, 2);
65
+ await fs.writeFile(`${specDir}/scripts/${scriptID}.json`, scriptJSON);
66
+ console.log(`Script ${scriptID} created and saved in ${specDir}/scripts`);
67
+ };
55
68
  // Fulfills a merging request.
56
69
  const callMerge = async (scriptID, batchID, requester, withIsolation = false) => {
57
70
  // Get the script and the batch.
@@ -163,6 +176,12 @@ else if (fn === 'batch' && fnArgs.length === 2) {
163
176
  console.log('Execution completed');
164
177
  });
165
178
  }
179
+ else if (fn === 'script' && fnArgs.length > 2) {
180
+ callScript(... fnArgs)
181
+ .then(() => {
182
+ console.log('Execution completed');
183
+ });
184
+ }
166
185
  else if (fn === 'merge' && fnArgs.length > 2 && fnArgs.length < 5) {
167
186
  callMerge(... fnArgs)
168
187
  .then(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testilo",
3
- "version": "11.0.7",
3
+ "version": "12.0.0",
4
4
  "description": "Client that scores and digests Testaro reports",
5
5
  "main": "aim.js",
6
6
  "scripts": {
@@ -166,7 +166,7 @@ exports.issueClasses = {
166
166
  }
167
167
  }
168
168
  },
169
- headingNoText: {
169
+ headingImageNoText: {
170
170
  wcag: '1.1.1',
171
171
  weight: 4,
172
172
  tools: {
@@ -2588,7 +2588,7 @@ exports.issueClasses = {
2588
2588
  testaro: {
2589
2589
  'role-bad': {
2590
2590
  variable: false,
2591
- quality: 1,
2591
+ quality: 0.5,
2592
2592
  what: 'Nonexistent or implicit-overriding role'
2593
2593
  }
2594
2594
  }
@@ -3453,7 +3453,7 @@ exports.issueClasses = {
3453
3453
  }
3454
3454
  },
3455
3455
  qualWeb: {
3456
- 'QW-ACT-R33': {
3456
+ 'QW-ACT-R35': {
3457
3457
  variable: false,
3458
3458
  quality: 1,
3459
3459
  what: 'Heading has no accessible name'
@@ -6847,6 +6847,11 @@ exports.issueClasses = {
6847
6847
  quality: 1,
6848
6848
  what: 'Element is closed while an element within it is unclosed'
6849
6849
  },
6850
+ '^End tag .+ seen, but there were open elements.*$': {
6851
+ variable: true,
6852
+ quality: 1,
6853
+ what: 'Element is closed while an element within it is unclosed'
6854
+ },
6850
6855
  '^Unclosed element .+$': {
6851
6856
  variable: true,
6852
6857
  quality: 1,
package/script.js ADDED
@@ -0,0 +1,126 @@
1
+ /*
2
+ script.js
3
+ Creates and returns a script for specified issues.
4
+ Arguments:
5
+ 0. issue classification
6
+ 1. issue IDs
7
+ */
8
+
9
+ // ########## IMPORTS
10
+
11
+ // Module to keep secrets.
12
+ require('dotenv').config();
13
+
14
+ // ########## FUNCTIONS
15
+
16
+ // Creates and returns a script.
17
+ exports.script = (id, issueClasses, ... issueIDs) => {
18
+ // Initialize data on the tests for the specified issues.
19
+ const neededTools = {};
20
+ // For each specified issue:
21
+ issueIDs.forEach(issueID => {
22
+ console.log(`Processing ${issueID}`);
23
+ // If it exists in the classification:
24
+ const issueData = issueClasses[issueID];
25
+ if (issueData) {
26
+ console.log('It is in the classification');
27
+ // For each tool that tests for the issue:
28
+ const issueToolIDs = Object.keys(issueData.tools);
29
+ console.log(`Tools with tests for it are ${issueToolIDs}`);
30
+ issueToolIDs.forEach(issueToolID => {
31
+ // For each of the tests of the tool for the issue:
32
+ if (! neededTools[issueToolID]) {
33
+ neededTools[issueToolID] = [];
34
+ }
35
+ Object.keys(issueData.tools[issueToolID]).forEach(ruleID => {
36
+ // Add data on the test.
37
+ const ruleData = issueData.tools[issueToolID][ruleID];
38
+ if (issueToolID === 'nuVal') {
39
+ if (ruleData.variable) {
40
+ neededTools[issueToolID].push(`~${ruleID}`);
41
+ }
42
+ else {
43
+ neededTools[issueToolID].push(`=${ruleID}`);
44
+ }
45
+ }
46
+ else {
47
+ neededTools[issueToolID].push(ruleID);
48
+ }
49
+ });
50
+ });
51
+ }
52
+ // Otherwise, i.e. if it does not exist in the classification:
53
+ else {
54
+ // Report this.
55
+ console.log(`ERROR: Issue ${issueID} not in issue classification`);
56
+ return {};
57
+ }
58
+ });
59
+ // If any tests have been identified:
60
+ const toolIDs = Object.keys(neededTools);
61
+ if (toolIDs.length) {
62
+ // Initialize a script.
63
+ const scriptObj = {
64
+ id,
65
+ what: `accessibility tests`,
66
+ strict: true,
67
+ timeLimit: 30 + 2 * issueIDs.length,
68
+ acts: [
69
+ {
70
+ "type": "placeholder",
71
+ "which": "main",
72
+ "launch": "chromium"
73
+ }
74
+ ]
75
+ };
76
+ // If Tenon is one of the identified tools:
77
+ if (toolIDs.includes('tenon')) {
78
+ // Add a Tenon request act to the script.
79
+ scriptObj.acts.push({
80
+ type: 'tenonRequest',
81
+ id: 'a',
82
+ withNewContent: false,
83
+ what: 'Tenon API version 2 test request, with URL'
84
+ });
85
+ }
86
+ // For each identified tool:
87
+ toolIDs.forEach(toolID => {
88
+ // Initialize a test act for it.
89
+ const toolAct = {
90
+ type: 'test',
91
+ which: toolID,
92
+ rules: neededTools[toolID]
93
+ };
94
+ // Add option specifications if necessary.
95
+ if (toolID === 'axe') {
96
+ toolAct.detailLevel = 2;
97
+ }
98
+ else if (toolID === 'ibm') {
99
+ toolAct.withItems = true;
100
+ toolAct.withNewContent = false;
101
+ }
102
+ else if (toolID === 'qualWeb') {
103
+ toolAct.withNewContent = false;
104
+ }
105
+ else if (toolID === 'tenon') {
106
+ toolAct.id = 'a';
107
+ }
108
+ else if (toolID === 'testaro') {
109
+ toolAct.withItems = true;
110
+ }
111
+ else if (toolID === 'wave') {
112
+ toolAct.reportType = 4;
113
+ }
114
+ // Add the act to the script.
115
+ scriptObj.acts.push(toolAct);
116
+ });
117
+ // Return the script.
118
+ return scriptObj;
119
+ }
120
+ // Otherwise, i.e. if no tests have been identified:
121
+ else {
122
+ // Report this.
123
+ console.log(`ERROR: No tests for the specified issues found`);
124
+ return {};
125
+ }
126
+ };