testilo 21.4.1 → 21.5.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
@@ -137,9 +137,13 @@ Here is a script:
137
137
  ```javaScript
138
138
  {
139
139
  id: 'ts99',
140
- what: 'Axe and QualWeb on account page',
140
+ what: 'aside mislocation',
141
141
  strict: true,
142
+ isolate: true,
142
143
  timeLimit: 60,
144
+ standard: 'also',
145
+ observe: false,
146
+ timeStamp: '240115T1200',
143
147
  acts: [
144
148
  {
145
149
  type: 'placeholder',
@@ -149,27 +153,38 @@ Here is a script:
149
153
  {
150
154
  type: 'test',
151
155
  which: 'axe',
152
- detailLevel: 1,
153
- rules: [],
154
- what: 'Axe core, all rules'
156
+ detailLevel: 2,
157
+ rules: ['landmark-complementary-is-top-level'],
158
+ what: 'Axe'
155
159
  },
156
160
  {
157
161
  type: 'test',
158
162
  which: 'qualWeb',
159
163
  withNewContent: false,
160
- what: 'QualWeb, all rules'
164
+ rules: ['QW-BP25', 'QW-BP26']
165
+ what: 'QualWeb'
161
166
  }
162
167
  ]
163
168
  }
164
169
  ```
165
170
 
166
- 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.
171
+ A script has several properties that specify facts about the jobs to be created. They include:
172
+ - `id`: an ID that uniquely distinguishes the script from other scripts.
173
+ - `what`: a description of the script.
174
+ - `strict`: You decide whether Testaro is to throw an error on an attempt to navigate to a URL if the server redirects the request to a URL differing substantially from the specified URL. All differences are considered substantial unless the URLs differ only in the presence and absence of a trailing slash.
175
+ - `isolate`: You decide whether to isolate test acts, as needed, from effects of previous test acts. If `true`, the `merge` module will add a copy of the latest placeholder after each target-modifying test act before the immediately following test act.
176
+ - `standard`: You choose how the reports of test acts should be standardized. The alternatives are `'no'` (do not standardize), `'also'` (standardize and report both the original and the standardized results), and `'only'` (standardize and report only the standardized results).
177
+ - `observe`: You decide how granularly Testaro will allow a server to observe job progress. If `true`, Testaro sends a message to the server to announce each test act (identifying the tool), and the `testaro` tool sends a message to the server to announce each rule when its test is performed. The server can further update the requesting client on the basis of these messages. It is generally user-friendly to make `observe` `true` if the user application makes the user wait while the job is assigned and performed. If the application allows the user to leave and sends the user a message when the job has been completed, `observe` can be set to `false`.
178
+ - `timeStamp`: You can specify the date and time that the job is to wait for before it is performed. This specification is a string with the format `yymmddThhMM`.
179
+ - `acts`: an array of acts.
180
+
181
+ The first act in this example script is a placeholder, whose `which` property is `'private'`. If the above batch were merged with this script, in each job the placeholder would be replaced with the `private` acts of a target. For example, the first act of 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.
167
182
 
168
- 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.
183
+ 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.
169
184
 
170
185
  ### Target list to batch
171
186
 
172
- If you have a target list, the `batch` module of Testilo can convert it to a batch. The batch will contain, for each target, one array of acts named `main`, containing a `launch` act (depending on the script to specify the browser type) and a `url` act.
187
+ If you have a target list, the `batch` module of Testilo can convert it to a batch. The batch will contain, for each target, one array of acts named `main`, containing a `launch` act (depending on the script to specify the browser type and the target to specify the URL).
173
188
 
174
189
  #### Invocation
175
190
 
@@ -203,11 +218,13 @@ The `call` module will save the batch as a JSON file in the `batches` subdirecto
203
218
 
204
219
  ### Issues to script
205
220
 
206
- 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.
221
+ Testilo classifies tool rules into _issues_. The built-in 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 classifications.
222
+
223
+ For example, one of the issues in the `tic38.js` file is `mainNot1`. Four rules are classified as belonging to that issue: rule `main_element_only_one` of `aslint` and 3 more rules defined by 3 other tools.
207
224
 
208
- 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.
225
+ If you want Testaro to test targets for only particular issues, you can name those issues and use the Testilo `script` module to create a script for that purpose. The only tools called by the script will be tools that define rules that are classified as belonging to one or more issues named.
209
226
 
210
- If you want Testaro to test targets for **all** the rules of all the available tools, without regard to any issue classification, you can use the `script` module to create a script that does not impose any issue restrictions.
227
+ If you want Testaro to test targets for **all** the rules of all the available tools, you can use the `script` module to create a script that does not impose any issue restrictions.
211
228
 
212
229
  #### Invocation
213
230
 
@@ -223,13 +240,13 @@ const scriptObj = script(scriptID, issues, issueID0, issueID1, …);
223
240
  ```
224
241
 
225
242
  This invocation references `scriptID`, `issues`, and `issueID` variables.
226
- - The `scriptID` variable is an alphanumeric string.
227
- - The `issues` variable is an object that classifies issues, such as the `issues` object in a `tic` file.
228
- - The `issueID` variables are strings, such as `'regionNoText'`, that name of properties of the `issues` object.
243
+ - The `scriptID` variable is an arbitrary alphanumeric string.
244
+ - The `issues` variable (if present) is an object that classifies issues, such as the `issues` object in a `tic` file.
245
+ - The `issueID` variables (if any) are strings, such as `'regionNoText'`, that name properties of the `issues` object.
229
246
 
230
- 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.
247
+ The `script()` function of the `script` module generates a script and returns it as an object. The invoking module can further modify and use the script as needed.
231
248
 
232
- To create a script without issue restrictions, a module can use this invocation:
249
+ To create a script **without** issue restrictions, a module can use this invocation:
233
250
 
234
251
  ```javaScript
235
252
  const {script} = require('testilo/script');
@@ -241,11 +258,11 @@ const scriptObj = script(scriptID);
241
258
  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 …`.
242
259
 
243
260
  In this statement:
244
- - Replace `s` with an ID for the script, such as `headings`.
261
+ - Replace `s` with an arbitrary ID for the script, such as `headings`.
245
262
  - Replace `c` with the base name, such as `tic99`, of an issue classification file in the `score` subdirectory of the `process.env.FUNCTIONDIR` directory.
246
263
  - Replace the remaining arguments (`i0` etc.) with issue IDs from that classification file.
247
264
 
248
- The `call` module will retrieve the named classification from its directory.
265
+ The `call` module will retrieve the named classification.
249
266
  The `script` module will create a script.
250
267
  The `call` module will save the script as a JSON file in the `scripts` subdirectory of the `process.env.SPECDIR` directory.
251
268
 
@@ -259,7 +276,7 @@ When the `script` module creates a script for you, it does not ask you for all o
259
276
 
260
277
  Testilo merges batches with scripts, producing jobs, by means of the `merge` module.
261
278
 
262
- 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.
279
+ The `merge` module needs to be given a batch and a script.
263
280
 
264
281
  #### Output
265
282
 
@@ -362,7 +379,7 @@ A module can invoke `merge` in this way:
362
379
 
363
380
  ```javaScript
364
381
  const {merge} = require('testilo/merge');
365
- const jobs = merge(script, batch, requester, true, 'only', false);
382
+ const jobs = merge(script, batch, requester, true, 'only', false, '240115T1200');
366
383
  ```
367
384
 
368
385
  This invocation references `script`, `batch`, and `requester` variables that the module must have already defined. The `script` and `batch` variables are a script object and a batch object, respectively. The `requester` variable is an email address. The fourth argument is a boolean, specifying whether to perform test isolation. The fifth argument is a string that specifies the Testaro standardization option ('also', 'only', or 'no'). The sixth argument is a boolean, specifying whether Testaro will allow granular network watching of the job. The `merge()` function of the `merge` module generates jobs and returns them in an array. The invoking module can further dispose of the jobs as needed.
package/batch.js CHANGED
@@ -1,10 +1,6 @@
1
1
  /*
2
2
  batch.js
3
3
  Converts a target list to a batch.
4
- Arguments:
5
- 0. batch ID
6
- 1. batch description
7
- 2. target list
8
4
  */
9
5
 
10
6
  // ########## FUNCTIONS
package/merge.js CHANGED
@@ -8,6 +8,7 @@
8
8
  3. whether to provide test isolation
9
9
  4. value of the standard property
10
10
  5. whether reporting is to be granular
11
+ 6. date and time as a compact timestamp for job execution, if not now
11
12
  */
12
13
 
13
14
  // ########## IMPORTS
@@ -17,34 +18,85 @@ require('dotenv').config();
17
18
 
18
19
  // ########## CONSTANTS
19
20
 
20
- const stdRequester = process.env.REQUESTER;
21
+ // Standard requester.
22
+ const stdRequester = process.env.REQUESTER || 'nobody@nodomain.tld';
23
+ // Length of the random part of a job ID, as a string.
24
+ const randomIDLength = process.env.RANDOM_ID_LENGTH || '3';
21
25
  // Tools that alter the page.
22
26
  const contaminantNames = new Set([
23
27
  'alfa',
24
28
  'aslint',
25
29
  'axe',
26
30
  'htmlcs',
27
- 'ibm',
28
31
  'testaro'
29
32
  ]);
33
+ const randomIDChars = (() => {
34
+ const digits = Array(10).fill('').map((digit, index) => index.toString());
35
+ const uppers = Array(26).fill('').map((letter, index) => String.fromCodePoint(65 + index));
36
+ const lowers = Array(26).fill('').map((letter, index) => String.fromCodePoint(97 + index));
37
+ return digits.concat(uppers, lowers);
38
+ })();
39
+
30
40
 
31
41
  // ########## FUNCTIONS
32
42
 
43
+ // Inserts a character periodically in a string.
44
+ const punctuate = (string, insertion, chunkSize) => {
45
+ const segments = [];
46
+ let startIndex = 0;
47
+ while (startIndex < string.length) {
48
+ segments.push(string.slice(startIndex, startIndex + chunkSize));
49
+ startIndex += chunkSize;
50
+ }
51
+ return segments.join(insertion);
52
+ };
53
+ // Converts a compact timestamp to a date.
54
+ const dateOf = timeStamp => {
55
+ if (/^\d{6}T\d{4}$/.test(timeStamp)) {
56
+ const dateString = punctuate(timeStamp.slice(0, 6), '-', 2);
57
+ const timeString = punctuate(timeStamp.slice(7, 11), ':', 2);
58
+ return new Date(`20${dateString}T${timeString}Z`);
59
+ } else {
60
+ return null;
61
+ }
62
+ };
63
+ // Converts a date and time to a compact timestamp.
64
+ const stampTime = date => date.toISOString().replace(/[-:]/g, '').slice(2, 13);
65
+ // Generates a random string.
66
+ const getRandomID = length => {
67
+ const chars = [];
68
+ for (let i = 0; i < length; i++) {
69
+ chars.push(randomIDChars[Math.floor(62 * Math.random())]);
70
+ }
71
+ return chars.join('');
72
+ };
33
73
  // Merges a script and a batch and returns jobs.
34
- exports.merge = (script, batch, requester, isolate, standard, isGranular) => {
74
+ exports.merge = (script, batch, requester, isolate, standard, observe, timeStamp) => {
35
75
  if (isolate === 'false') {
36
76
  isolate = false;
37
77
  }
38
- // If the requester is unspecified, make it the standard requester.
78
+ // If a timestamp was specified:
79
+ if (timeStamp) {
80
+ // If it is invalid:
81
+ if (! dateOf(timeStamp)) {
82
+ // Report this and quit.
83
+ console.log(`ERROR: Timestamp invalid`);
84
+ return [];
85
+ }
86
+ }
87
+ // Otherwise, i.e. if no timestamp was specified:
88
+ else {
89
+ // Create one for the job.
90
+ timeStamp = stampTime(new Date());
91
+ }
92
+ // If the requester is blank or unspecified, make it the standard requester.
39
93
  requester ||= stdRequester;
40
- // Create a timestamp.
41
- const timeStamp = new Date().toISOString().replace(/[-:]/g, '').slice(2, 15);
42
- // Create a time description.
43
- const creationTime = (new Date()).toISOString().slice(0, 19);
94
+ // Create a creation-time description.
95
+ const creationTime = (new Date()).toISOString().slice(0, 16);
44
96
  // Initialize a target-independent job.
45
97
  const protoJob = JSON.parse(JSON.stringify(script));
46
- // Add the timestamp to the ID of the job.
47
- protoJob.id = `${timeStamp}-${protoJob.id}-`;
98
+ // Make the timestamp and a random string the ID of the job.
99
+ protoJob.id = `${timeStamp}-${getRandomID(Number.parseInt(randomIDLength, 10))}`;
48
100
  // Add a sources property to the job.
49
101
  protoJob.sources = {
50
102
  script: script.id,
@@ -61,10 +113,10 @@ exports.merge = (script, batch, requester, isolate, standard, isGranular) => {
61
113
  protoJob.creationTime = creationTime;
62
114
  protoJob.timeStamp = timeStamp;
63
115
  protoJob.standard = standard || 'only';
64
- protoJob.observe = isGranular || false;
116
+ protoJob.observe = observe || false;
65
117
  // If isolation was requested:
66
118
  if (isolate) {
67
- // Perform it.
119
+ // Configure the job for it.
68
120
  let {acts} = protoJob;
69
121
  let lastPlaceholder = {};
70
122
  for (const actIndexString in acts) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testilo",
3
- "version": "21.4.1",
3
+ "version": "21.5.0",
4
4
  "description": "Prepares and processes Testaro reports",
5
5
  "main": "aim.js",
6
6
  "scripts": {
@@ -26,13 +26,15 @@
26
26
  <tr><th>Tested by</th><td>Testaro, procedure <code>__ts__</code></td></tr>
27
27
  <tr><th>Scored by</th><td>Testilo, procedure <code>__sp__</code></td></tr>
28
28
  <tr><th>Digested by</th><td>Testilo, procedure <code>__dp__</code></td></tr>
29
- <tr><th>Full report</th><td><a href="__reportURL__"><code>__reportURL__</code></a></td></tr>
29
+ <tr>
30
+ <th>Full report</th><td><a href="__reportURL__"><code>__reportURL__</code></a></td>
31
+ </tr>
30
32
  </table>
31
33
  </header>
32
34
  <h2>Introduction</h2>
33
35
  <p>How <a href="https://www.w3.org/WAI/">accessible</a> is the __org__ web page at <a href="__url__"><code>__url__</code></a>?</p>
34
36
  <p>This digest can help answer that question. Nine different tools (Alfa, ASLint, Axe, Equal Access, HTML CodeSniffer, Nu Html Checker, QualWeb, Testaro, and WAVE) tested the page to check its compliance with their accessibility rules. In all, the tools define about 915 rules, which are classified here into about 300 accessibility issues.</p>
35
- <p>The results were interpreted to yield a score, with 0 being ideal. The score for this page was __total__, the sum of __issueCount__ for the count of issues, __issue__ for specific issues, __tool__ for tool-by-tool ratings, __prevention__ for the page preventing tools from running, __log__ for browser warnings, and __latency__ for delayed page responses.</p>
37
+ <p>The results were interpreted to yield a score, with 0 being ideal. The score for this page was __total__, the sum of __issueCount__ for the count of issues, __issue__ for specific issues, __solo__ for unclassified rule violations, __tool__ for tool-by-tool ratings, __prevention__ for the page preventing tools from running, __log__ for browser warnings, and __latency__ for delayed page responses.</p>
36
38
  <h2>Issue summary</h2>
37
39
  <table class="allBorder thirdCellRight">
38
40
  <caption>Summary of issues</caption>
@@ -43,7 +43,8 @@ const populateQuery = (report, query) => {
43
43
  summaryRows: [],
44
44
  issueRows: []
45
45
  };
46
- ['total', 'issueCount', 'issue', 'tool', 'prevention', 'log', 'latency'].forEach(sumItem => {
46
+ ['total', 'issueCount', 'issue', 'solo', 'tool', 'prevention', 'log', 'latency']
47
+ .forEach(sumItem => {
47
48
  query[sumItem] = summary[sumItem];
48
49
  rows.summaryRows.push(getScoreRow(sumItem, query[sumItem]));
49
50
  });
@@ -1,6 +1,6 @@
1
1
  /*
2
- tic37
3
- Testilo issue classification 38
2
+ tic39
3
+ Testilo issue classification 39
4
4
 
5
5
  Classifies about 900 rules of the tools of Testaro into about 300 issues.
6
6
 
@@ -163,7 +163,7 @@ exports.issues = {
163
163
  'QW-WCAG-T20': {
164
164
  variable: false,
165
165
  quality: 0,
166
- what: 'Link text is not supplemented with a title attribute [invalid]'
166
+ what: 'Link title may fail to describe the link correctly [speculative]'
167
167
  },
168
168
  'QW-WCAG-T23': {
169
169
  variable: false,
@@ -4363,28 +4363,12 @@ exports.issues = {
4363
4363
  }
4364
4364
  }
4365
4365
  },
4366
- h1Multiple: {
4367
- summary: 'multiple h1 headings',
4366
+ h1Not1: {
4367
+ summary: 'not exactly 1 h1 heading',
4368
4368
  why: 'User cannot understand the topic of the document',
4369
4369
  wcag: '1.3.1',
4370
4370
  weight: 2,
4371
4371
  max: 1,
4372
- tools: {
4373
- nuVal: {
4374
- 'Consider using the h1 element as a top-level heading only (all h1 elements are treated as top-level headings by many screen readers and other tools).': {
4375
- variable: false,
4376
- quality: 1,
4377
- what: 'Page contains more than 1 h1 element'
4378
- }
4379
- }
4380
- }
4381
- },
4382
- h1Missing: {
4383
- summary: 'h1 missing',
4384
- why: 'User cannot understand the topic of the document',
4385
- wcag: '1.3.1',
4386
- weight: 3,
4387
- max: 1,
4388
4372
  tools: {
4389
4373
  aslint: {
4390
4374
  'h1_must_be': {
@@ -4400,6 +4384,20 @@ exports.issues = {
4400
4384
  what: 'Document contains no level-one heading'
4401
4385
  }
4402
4386
  },
4387
+ nuVal: {
4388
+ 'Consider using the h1 element as a top-level heading only (all h1 elements are treated as top-level headings by many screen readers and other tools).': {
4389
+ variable: false,
4390
+ quality: 1,
4391
+ what: 'Page contains more than 1 h1 element'
4392
+ }
4393
+ },
4394
+ qualWeb: {
4395
+ 'QW-BP28': {
4396
+ variable: false,
4397
+ quality: 1,
4398
+ what: 'h1 element missing or used more than once'
4399
+ }
4400
+ },
4403
4401
  wave: {
4404
4402
  'h1_missing': {
4405
4403
  variable: false,
@@ -4696,6 +4694,13 @@ exports.issues = {
4696
4694
  quality: 1,
4697
4695
  what: 'Element is not contained by a ul or ol element'
4698
4696
  }
4697
+ },
4698
+ qualWeb: {
4699
+ 'QW-BP23': {
4700
+ variable: false,
4701
+ quality: 0.1,
4702
+ what: 'Element is not in a true list'
4703
+ }
4699
4704
  }
4700
4705
  }
4701
4706
  },
@@ -7,12 +7,12 @@
7
7
 
8
8
  // IMPORTS
9
9
 
10
- const {issues} = require('./tic38');
10
+ const {issues} = require('./tic39');
11
11
 
12
12
  // CONSTANTS
13
13
 
14
14
  // ID of this proc.
15
- const scoreProcID = 'tsp38';
15
+ const scoreProcID = 'tsp39';
16
16
  // Latency weight (how much each second of excess latency adds to the score).
17
17
  const latencyWeight = 1;
18
18
  // Normal latency (6 visits, with 1.5 second per visit).
package/script.js CHANGED
@@ -1,9 +1,6 @@
1
1
  /*
2
2
  script.js
3
- Creates and returns a script.
4
- Arguments:
5
- 0. issue classification
6
- 1. issue IDs
3
+ Creates and returns a script to perform the tests for issues.
7
4
  */
8
5
 
9
6
  // ########## IMPORTS
@@ -13,6 +10,7 @@ require('dotenv').config();
13
10
 
14
11
  // ########## VARIABLES
15
12
 
13
+ // List of presumptively needed tools.
16
14
  let toolIDs = [
17
15
  'alfa', 'aslint', 'axe', 'htmlcs', 'ibm', 'nuVal', 'qualWeb', 'testaro', 'wave'
18
16
  ];
@@ -21,7 +19,7 @@ let toolIDs = [
21
19
 
22
20
  // Creates and returns a script.
23
21
  exports.script = (id, issues = null, ... issueIDs) => {
24
- // Initialize data on the tools and rules for the specified issues.
22
+ // Initialize data on the tools and their rules for the specified issues, if any.
25
23
  const neededTools = {};
26
24
  // If an issue classification and any issues were specified:
27
25
  if (issues && issueIDs.length) {
@@ -53,42 +51,47 @@ exports.script = (id, issues = null, ... issueIDs) => {
53
51
  }
54
52
  });
55
53
  });
54
+ // Remove unneeded tools from the tool list.
56
55
  toolIDs = Object.keys(neededTools);
57
56
  }
58
57
  // Otherwise, i.e. if it does not exist in the classification:
59
58
  else {
60
- // Report this.
59
+ // Report this and quit.
61
60
  console.log(`ERROR: Issue ${issueID} not in issue classification`);
62
61
  return {};
63
62
  }
64
63
  });
65
64
  }
66
- // If any rules have been identified:
65
+ // If, after any issue-based pruning, any needed tools remain:
67
66
  if (toolIDs.length) {
68
67
  // Initialize a script.
69
68
  const scriptObj = {
70
69
  id,
71
70
  what: `accessibility tests`,
72
71
  strict: true,
72
+ isolate: true,
73
73
  timeLimit: 30 + (10 * issueIDs.length || 30 * toolIDs.length),
74
+ standard: 'only',
75
+ observe: true,
76
+ timeStamp: '',
74
77
  acts: [
75
78
  {
76
79
  "type": "placeholder",
77
80
  "which": "main",
78
- "launch": "chromium"
81
+ "launch": "webkit"
79
82
  }
80
83
  ]
81
84
  };
82
- // For each identified tool:
85
+ // For each needed tool:
83
86
  toolIDs.forEach(toolID => {
84
87
  // Initialize a test act for it.
85
88
  const toolAct = {
86
89
  type: 'test',
87
90
  which: toolID
88
91
  };
89
- // If rules were specified:
92
+ // If issues were specified:
90
93
  if (issues && issueIDs.length) {
91
- // Add a rules property to the act.
94
+ // Add a rules array as a property to the act.
92
95
  toolAct.rules = neededTools[toolID];
93
96
  // If the tool is Testaro:
94
97
  if (toolID === 'testaro') {
@@ -96,13 +99,13 @@ exports.script = (id, issues = null, ... issueIDs) => {
96
99
  toolAct.rules.unshift('y');
97
100
  }
98
101
  }
99
- // Add option specifications if necessary.
102
+ // Add any needed option specifications to the act.
100
103
  if (toolID === 'axe') {
101
104
  toolAct.detailLevel = 2;
102
105
  }
103
106
  else if (toolID === 'ibm') {
104
107
  toolAct.withItems = true;
105
- toolAct.withNewContent = false;
108
+ toolAct.withNewContent = true;
106
109
  }
107
110
  else if (toolID === 'qualWeb') {
108
111
  toolAct.withNewContent = false;
@@ -1,8 +1,12 @@
1
1
  {
2
- "id": "ts36",
2
+ "id": "ts39",
3
3
  "what": "comprehensive accessibility tests with webkit and redirection permitted",
4
4
  "strict": false,
5
- "timeLimit": 330,
5
+ "isolate": true,
6
+ "timeLimit": 600,
7
+ "standard": "only",
8
+ "observe": true,
9
+ "timeStamp": "",
6
10
  "acts": [
7
11
  {
8
12
  "type": "placeholder",
@@ -30,7 +34,7 @@
30
34
  "type": "test",
31
35
  "which": "ibm",
32
36
  "withItems": true,
33
- "withNewContent": false
37
+ "withNewContent": true
34
38
  },
35
39
  {
36
40
  "type": "test",
@@ -1,300 +0,0 @@
1
- /*
2
- tsp37
3
- Testilo score proc 37
4
-
5
- Computes target score data and adds them to a ts37 report.
6
- */
7
-
8
- // IMPORTS
9
-
10
- const {issues} = require('./tic38');
11
-
12
- // CONSTANTS
13
-
14
- // ID of this proc.
15
- const scoreProcID = 'tsp38';
16
- // Latency weight (how much each second of excess latency adds to the score).
17
- const latencyWeight = 1;
18
- // Normal latency (6 visits, with 1.5 second per visit).
19
- const normalLatency = 9;
20
- // Prevention weight (how much each prevention adds to the score).
21
- const preventionWeight = 300;
22
- // Maximum instance count addition weight (divisor of max).
23
- const maxWeight = 30;
24
- // Other weights.
25
- const severityWeights = [1, 2, 3, 4];
26
- const toolWeight = 0.1;
27
- const logWeights = {
28
- logCount: 0.1,
29
- logSize: 0.002,
30
- errorLogCount: 0.2,
31
- errorLogSize: 0.004,
32
- prohibitedCount: 3,
33
- visitRejectionCount: 2
34
- };
35
- // Initialize a table of tool rules.
36
- const issueIndex = {};
37
- // Initialize an array of variably named tool rules.
38
- const issueMatcher = [];
39
- // For each issue:
40
- Object.keys(issues).forEach(issueName => {
41
- // For each tool with rules belonging to that issue:
42
- Object.keys(issues[issueName].tools).forEach(toolName => {
43
- // For each of those rules:
44
- Object.keys(issues[issueName].tools[toolName]).forEach(ruleID => {
45
- // Add it to the table of tool rules.
46
- if (! issueIndex[toolName]) {
47
- issueIndex[toolName] = {};
48
- }
49
- issueIndex[toolName][ruleID] = issueName;
50
- // If it is variably named:
51
- if (issues[issueName].tools[toolName][ruleID].variable) {
52
- // Add it to the array of variably named tool rules.
53
- issueMatcher.push(ruleID);
54
- }
55
- })
56
- });
57
- });
58
-
59
- // FUNCTIONS
60
-
61
- // Scores a report.
62
- exports.scorer = report => {
63
- // If there are any acts in the report:
64
- const {acts} = report;
65
- if (Array.isArray(acts) && acts.length) {
66
- // If any of them are test acts:
67
- const testActs = acts.filter(act => act.type === 'test');
68
- if (testActs.length) {
69
- // Initialize the score data.
70
- const score = {
71
- scoreProcID,
72
- weights: {
73
- severities: severityWeights,
74
- tool: toolWeight,
75
- log: logWeights,
76
- latency: latencyWeight,
77
- prevention: preventionWeight,
78
- maxInstanceCount: maxWeight
79
- },
80
- normalLatency,
81
- summary: {
82
- total: 0,
83
- issue: 0,
84
- solo: 0,
85
- tool: 0,
86
- prevention: 0,
87
- log: 0,
88
- latency: 0
89
- },
90
- details: {
91
- severity: {
92
- total: [0, 0, 0, 0],
93
- byTool: {}
94
- },
95
- prevention: {},
96
- issue: {},
97
- solo: {},
98
- tool: {}
99
- }
100
- };
101
- const {summary, details} = score;
102
- // For each test act:
103
- testActs.forEach(act => {
104
- // If the page prevented the tool from operating:
105
- const {which, standardResult} = act;
106
- if (! standardResult || standardResult.prevented) {
107
- // Add this to the score.
108
- details.prevention[which] = preventionWeight;
109
- }
110
- // Otherwise, if a valid standard result exists:
111
- else if (
112
- standardResult
113
- && standardResult.totals
114
- && standardResult.totals.length === 4
115
- && standardResult.instances
116
- ) {
117
- // Add the severity totals of the tool to the score.
118
- const {totals} = standardResult;
119
- details.severity.byTool[which] = totals;
120
- // Add the severity-weighted tool totals to the score.
121
- details.tool[which] = totals.reduce(
122
- (sum, current, index) => sum + severityWeights[index] * current, 0
123
- );
124
- // For each instance of the tool:
125
- standardResult.instances.forEach(instance => {
126
- // Get the rule ID.
127
- const {ruleID, ordinalSeverity, count} = instance;
128
- // If it is not in the table of tool rules:
129
- let canonicalRuleID = ruleID;
130
- if (! issueIndex[which][ruleID]) {
131
- // Convert it to the variably named tool rule that it matches, if any.
132
- canonicalRuleID = issueMatcher.find(pattern => {
133
- const patternRE = new RegExp(pattern);
134
- return patternRE.test(instance.ruleID);
135
- });
136
- }
137
- // If the rule ID belongs to an issue:
138
- if (canonicalRuleID) {
139
- // Get the issue.
140
- const issueName = issueIndex[which][canonicalRuleID];
141
- // Add the instance to the issue details of the score data.
142
- if (! details.issue[issueName]) {
143
- details.issue[issueName] = {
144
- summary: issues[issueName].summary,
145
- score: 0,
146
- maxCount: 0,
147
- weight: issues[issueName].weight,
148
- countLimit: issues[issueName].max,
149
- tools: {}
150
- };
151
- if (! details.issue[issueName].countLimit) {
152
- delete details.issue[issueName].countLimit;
153
- }
154
- }
155
- if (! details.issue[issueName].tools[which]) {
156
- details.issue[issueName].tools[which] = {};
157
- }
158
- if (! details.issue[issueName].tools[which][canonicalRuleID]) {
159
- const ruleData = issues[issueName].tools[which][canonicalRuleID];
160
- details.issue[issueName].tools[which][canonicalRuleID] = {
161
- quality: ruleData.quality,
162
- what: ruleData.what,
163
- complaints: {
164
- countTotal: 0,
165
- texts: []
166
- }
167
- };
168
- }
169
- details
170
- .issue[issueName]
171
- .tools[which][canonicalRuleID]
172
- .complaints
173
- .countTotal += instance.count || 1;
174
- if (
175
- ! details
176
- .issue[issueName]
177
- .tools[which][canonicalRuleID]
178
- .complaints
179
- .texts
180
- .includes(instance.what)
181
- ) {
182
- details
183
- .issue[issueName]
184
- .tools[which][canonicalRuleID]
185
- .complaints
186
- .texts
187
- .push(instance.what);
188
- }
189
- }
190
- // Otherwise, i.e. if the rule ID belongs to no issue:
191
- else {
192
- // Add the instance to the solo details of the score data.
193
- if (! details.solo[which]) {
194
- details.solo[which] = {};
195
- }
196
- if (! details.solo[which][ruleID]) {
197
- details.solo[which][ruleID] = 0;
198
- }
199
- details.solo[which][ruleID] += (count || 1) * (ordinalSeverity + 1);
200
- // Report this.
201
- console.log(`ERROR: ${instance.ruleID} of ${which} not found in issues`);
202
- }
203
- });
204
- }
205
- // Otherwise, i.e. if a failed standard result exists:
206
- else {
207
- // Add an inferred prevention to the score.
208
- details.prevention[which] = preventionWeight;
209
- }
210
- });
211
- // For each issue with any complaints:
212
- Object.keys(details.issue).forEach(issueName => {
213
- const issueData = details.issue[issueName];
214
- // For each tool with any complaints for the issue:
215
- Object.keys(issueData.tools).forEach(toolID => {
216
- // Get the sum of the quality-weighted counts of its issue rules.
217
- let weightedCount = 0;
218
- Object.values(issueData.tools[toolID]).forEach(ruleData => {
219
- weightedCount += ruleData.quality * ruleData.complaints.countTotal;
220
- });
221
- // If the sum exceeds the existing maximum weighted count for the issue:
222
- if (weightedCount > issueData.maxCount) {
223
- // Change the maximum count for the issue to the sum.
224
- issueData.maxCount = weightedCount;
225
- }
226
- });
227
- // Get the score for the issue, including any addition for the instance count limit.
228
- const maxAddition = issueData.countLimit ? maxWeight / issueData.countLimit : 0;
229
- issueData.score = Math.round(issueData.weight * issueData.maxCount * (1 + maxAddition));
230
- });
231
- // Add the severity detail totals to the score.
232
- details.severity.total = Object.keys(details.severity.byTool).reduce((severityTotals, toolID) => {
233
- details.severity.byTool[toolID].forEach((severityScore, index) => {
234
- severityTotals[index] += severityScore;
235
- });
236
- return severityTotals;
237
- }, details.severity.total);
238
- // Add the summary issue total to the score.
239
- summary.issue = Object
240
- .values(details.issue)
241
- .reduce((total, current) => total + current.score, 0);
242
- // Add the summary solo total to the score.
243
- Object.keys(details.solo).forEach(tool => {
244
- summary.solo += Object
245
- .values(details.solo[tool])
246
- .reduce((total, current) => total + current);
247
- });
248
- // Add the summary tool total to the score.
249
- summary.tool = toolWeight * details.severity.total.reduce(
250
- (total, current, index) => total + severityWeights[index] * current, 0
251
- );
252
- // Add the summary prevention total to the score.
253
- summary.prevention = Object.values(details.prevention).reduce(
254
- (total, current) => total + current, 0
255
- );
256
- // Add the summary log score to the score.
257
- const {jobData} = report;
258
- if (jobData) {
259
- summary.log = Math.max(0, Math.round(
260
- logWeights.logCount * jobData.logCount
261
- + logWeights.logSize * jobData.logSize +
262
- + logWeights.errorLogCount * jobData.errorLogCount
263
- + logWeights.errorLogSize * jobData.errorLogSize
264
- + logWeights.prohibitedCount * jobData.prohibitedCount +
265
- + logWeights.visitRejectionCount * jobData.visitRejectionCount
266
- ));
267
- // Add the summary latency score to the score.
268
- summary.latency = Math.round(
269
- latencyWeight * (Math.max(0, jobData.visitLatency - normalLatency))
270
- );
271
- }
272
- // Round the unrounded scores.
273
- Object.keys(summary).forEach(summaryTypeName => {
274
- summary[summaryTypeName] = Math.round(summary[summaryTypeName]);
275
- });
276
- details.severity.total.forEach((severityTotal, index) => {
277
- details.severity.total[index] = Math.round(severityTotal);
278
- });
279
- // Add the summary total score to the score.
280
- summary.total = summary.issue
281
- + summary.solo
282
- + summary.tool
283
- + summary.prevention
284
- + summary.log
285
- + summary.latency;
286
- // Add the score to the report or replace the existing score of the report.
287
- report.score = score;
288
- }
289
- // Otherwise, i.e. if none of them is a test act:
290
- else {
291
- // Report this.
292
- console.log('ERROR: No test acts');
293
- }
294
- }
295
- // Otherwise, i.e. if there are no acts in the report:
296
- else {
297
- // Report this.
298
- console.log('ERROR: No acts');
299
- }
300
- };
package/scripts/ts31.json DELETED
@@ -1,56 +0,0 @@
1
- {
2
- "id": "ts31",
3
- "what": "accessibility tests",
4
- "strict": true,
5
- "timeLimit": 330,
6
- "acts": [
7
- {
8
- "type": "placeholder",
9
- "which": "main",
10
- "launch": "chromium"
11
- },
12
- {
13
- "type": "test",
14
- "which": "alfa"
15
- },
16
- {
17
- "type": "test",
18
- "which": "axe",
19
- "detailLevel": 2
20
- },
21
- {
22
- "type": "test",
23
- "which": "continuum"
24
- },
25
- {
26
- "type": "test",
27
- "which": "htmlcs"
28
- },
29
- {
30
- "type": "test",
31
- "which": "ibm",
32
- "withItems": true,
33
- "withNewContent": false
34
- },
35
- {
36
- "type": "test",
37
- "which": "nuVal"
38
- },
39
- {
40
- "type": "test",
41
- "which": "qualWeb",
42
- "withNewContent": false
43
- },
44
- {
45
- "type": "test",
46
- "which": "testaro",
47
- "withItems": true,
48
- "stopOnFail": false
49
- },
50
- {
51
- "type": "test",
52
- "which": "wave",
53
- "reportType": 4
54
- }
55
- ]
56
- }
package/scripts/ts33.json DELETED
@@ -1,56 +0,0 @@
1
- {
2
- "id": "ts33",
3
- "what": "comprehensive accessibility tests",
4
- "strict": true,
5
- "timeLimit": 330,
6
- "acts": [
7
- {
8
- "type": "placeholder",
9
- "which": "main",
10
- "launch": "chromium"
11
- },
12
- {
13
- "type": "test",
14
- "which": "alfa"
15
- },
16
- {
17
- "type": "test",
18
- "which": "axe",
19
- "detailLevel": 2
20
- },
21
- {
22
- "type": "test",
23
- "which": "continuum"
24
- },
25
- {
26
- "type": "test",
27
- "which": "htmlcs"
28
- },
29
- {
30
- "type": "test",
31
- "which": "ibm",
32
- "withItems": true,
33
- "withNewContent": false
34
- },
35
- {
36
- "type": "test",
37
- "which": "nuVal"
38
- },
39
- {
40
- "type": "test",
41
- "which": "qualWeb",
42
- "withNewContent": false
43
- },
44
- {
45
- "type": "test",
46
- "which": "testaro",
47
- "withItems": true,
48
- "stopOnFail": false
49
- },
50
- {
51
- "type": "test",
52
- "which": "wave",
53
- "reportType": 4
54
- }
55
- ]
56
- }
package/scripts/ts34.json DELETED
@@ -1,77 +0,0 @@
1
- {
2
- "id": "ts34",
3
- "what": "comprehensive accessibility tests",
4
- "strict": true,
5
- "timeLimit": 330,
6
- "acts": [
7
- {
8
- "type": "placeholder",
9
- "which": "main",
10
- "launch": "chromium"
11
- },
12
- {
13
- "type": "test",
14
- "which": "alfa"
15
- },
16
- {
17
- "type": "test",
18
- "which": "axe",
19
- "detailLevel": 2
20
- },
21
- {
22
- "type": "test",
23
- "which": "continuum"
24
- },
25
- {
26
- "type": "test",
27
- "which": "htmlcs"
28
- },
29
- {
30
- "type": "test",
31
- "which": "ibm",
32
- "withItems": true,
33
- "withNewContent": false
34
- },
35
- {
36
- "type": "test",
37
- "which": "nuVal"
38
- },
39
- {
40
- "type": "test",
41
- "which": "qualWeb",
42
- "withNewContent": false
43
- },
44
- {
45
- "type": "test",
46
- "which": "wave",
47
- "reportType": 4
48
- },
49
- {
50
- "type": "test",
51
- "which": "testaro",
52
- "withItems": true,
53
- "stopOnFail": false,
54
- "rules": [
55
- "n",
56
- "hover",
57
- "motion"
58
- ]
59
- },
60
- {
61
- "type": "placeholder",
62
- "which": "main",
63
- "launch": "webkit"
64
- },
65
- {
66
- "type": "test",
67
- "which": "testaro",
68
- "withItems": true,
69
- "stopOnFail": false,
70
- "rules": [
71
- "y",
72
- "hover",
73
- "motion"
74
- ]
75
- }
76
- ]
77
- }