testilo 11.0.8 → 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 +59 -23
- package/call.js +19 -0
- package/package.json +1 -1
- package/procs/score/tic27.js +3 -3
- package/script.js +126 -0
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
|
|
39
|
-
-
|
|
40
|
-
-
|
|
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: '
|
|
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
|
|
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
|
|
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-
|
|
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: '
|
|
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 `
|
|
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/
|
|
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
|
|
381
|
-
node call score
|
|
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 `
|
|
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/
|
|
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
|
|
434
|
-
node call digest
|
|
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 `
|
|
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/
|
|
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
|
|
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 `
|
|
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
package/procs/score/tic27.js
CHANGED
|
@@ -166,7 +166,7 @@ exports.issueClasses = {
|
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
},
|
|
169
|
-
|
|
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:
|
|
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-
|
|
3456
|
+
'QW-ACT-R35': {
|
|
3457
3457
|
variable: false,
|
|
3458
3458
|
quality: 1,
|
|
3459
3459
|
what: 'Heading has no accessible name'
|
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
|
+
};
|