testilo 7.1.1 → 9.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/call.js CHANGED
@@ -1,204 +1,130 @@
1
1
  /*
2
- do.js
2
+ call.js
3
3
  Invokes Testilo modules with arguments.
4
4
  This is the universal module for use of Testilo from a command line.
5
5
  Arguments:
6
6
  0. function to execute.
7
7
  1+. arguments to pass to the function.
8
8
  Usage examples:
9
- node call aim tp25 https://www.w3c.org/ 'World Wide Web Consortium' w3c developer@w3.org
10
- node call merge script454 webOrgs
11
- node call score sp25a (to score the first raw report)
12
- node call score sp25a 8ep9f (to score the first raw report whose name starts with 8ep9f)
13
- node call multiScore sp25a
14
- node call digest dp25a (to digest the first scored report)
15
- node call digest dp25a 8ep9f (to digest the first scored report whose name starts with 8ep9f)
16
- node call multiDigest dp25a
17
- node call compare cp25a weborgs (to write weborgs.html, comparing all scored reports)
9
+ node call merge ts25 webOrgs user@domain.tld true
10
+ node call score sp25a
11
+ node call digest dp25a
12
+ node call compare cp25a weborgs
18
13
  */
19
14
 
20
15
  // ########## IMPORTS
21
16
 
22
17
  // Module to keep secrets.
23
18
  require('dotenv').config();
24
- // Function to process a script aiming.
19
+ // Function to process files.
25
20
  const fs = require('fs/promises');
26
- const {aim} = require('./aim');
27
- // Function to process a script-batch merger.
21
+ // Function to process a merger.
28
22
  const {merge} = require('./merge');
29
- // Function to score a report.
23
+ // Function to score reports.
30
24
  const {score} = require('./score');
31
- // Function to score multiple reports.
32
- const {multiScore} = require('./multiScore');
33
- // Function to digest a report.
25
+ // Function to digest reports.
34
26
  const {digest} = require('./digest');
35
- // Function to digest multiple reports.
36
- const {multiDigest} = require('./multiDigest');
37
27
  // Function to compare scores.
38
28
  const {compare} = require('./compare');
39
29
 
40
30
  // ########## CONSTANTS
41
31
 
42
- const scriptDir = process.env.SCRIPTDIR;
43
- const scoreProcDir = process.env.SCOREPROCDIR;
44
- const digestProcDir = process.env.DIGESTPROCDIR;
32
+ const specDir = process.env.SPECDIR;
33
+ const functionDir = process.env.FUNCTIONDIR;
45
34
  const jobDir = process.env.JOBDIR;
46
- const rawDir = process.env.REPORTDIR_RAW;
47
- const scoredDir = process.env.REPORTDIR_SCORED;
48
- const digestedDir = process.env.REPORTDIR_DIGESTED;
49
- const comparisonDir = process.env.COMPARISONDIR;
35
+ const reportDir = process.env.REPORTDIR;
50
36
  const fn = process.argv[2];
51
37
  const fnArgs = process.argv.slice(3);
52
38
 
53
39
  // ########## FUNCTIONS
54
40
 
55
- // Fulfills an aiming request.
56
- const callAim = async (scriptName, hostURL, hostName, hostID, requester) => {
57
- const scriptJSON = await fs.readFile(`${scriptDir}/${scriptName}.json`, 'utf8');
41
+ // Fulfills a merging request.
42
+ const callMerge = async (scriptID, batchID, requester, withIsolation = false) => {
43
+ // Get the script and the batch.
44
+ const scriptJSON = await fs.readFile(`${specDir}/scripts/${scriptID}.json`, 'utf8');
58
45
  const script = JSON.parse(scriptJSON);
59
- const job = aim(
60
- script,
61
- {
62
- id: hostID,
63
- which: hostURL,
64
- what: hostName
65
- },
66
- requester
46
+ const batchJSON = await fs.readFile(`${specDir}/batches/${batchID}.json`, 'utf8');
47
+ const batch = JSON.parse(batchJSON);
48
+ // Merge them into an array of jobs.
49
+ const jobs = merge(script, batch, requester, withIsolation);
50
+ // Save the jobs.
51
+ for (const job of jobs) {
52
+ const jobJSON = JSON.stringify(job, null, 2);
53
+ await fs.writeFile(`${jobDir}/todo/${job.id}.json`, jobJSON);
54
+ }
55
+ const {timeStamp} = jobs[0];
56
+ console.log(
57
+ `Script ${scriptID} and batch ${batchID} merged; jobs ${timeStamp}… saved in ${jobDir}/todo`
67
58
  );
68
- const scriptID = job.sources.script;
69
- const jobID = job.id;
70
- await fs.writeFile(`${jobDir}/${jobID}.json`, `${JSON.stringify(job, null, 2)}\n`);
71
- console.log(`Script ${scriptID} aimed at ${hostName} saved as job ${jobID}.json in ${jobDir}`);
72
59
  };
73
- // Fulfills a merger request.
74
- const callMerge = async (scriptName, batchName, requester) => {
75
- await merge(scriptName, batchName, requester);
76
- console.log(`Batch ${batchName}.json merged into script ${scriptName} in ${jobDir}`);
60
+ // Get selected reports.
61
+ const getReports = async (type, selector = '') => {
62
+ const allFileNames = await fs.readdir(`${reportDir}/${type}`);
63
+ const reportIDs = allFileNames
64
+ .filter(fileName => fileName.endsWith('.json'))
65
+ .filter(fileName => fileName.startsWith(selector))
66
+ .map(fileName => fileName.slice(0, -5));
67
+ const reports = [];
68
+ for (const reportID of reportIDs) {
69
+ const reportJSON = await fs.readFile(`${reportDir}/${type}/${reportID}.json`, 'utf8');
70
+ const report = JSON.parse(reportJSON);
71
+ reports.push(report);
72
+ }
73
+ return reports;
77
74
  };
78
75
  // Fulfills a scoring request.
79
- const callScore = async (scoreProcID, reportIDStart = '') => {
80
- // Identify the raw reports.
81
- const rawFileNames = await fs.readdir(rawDir);
82
- const rawReportNames = rawFileNames.filter(fileName => fileName.endsWith('.json'));
76
+ const callScore = async (scorerID, selector = '') => {
77
+ // Get the raw reports to be scored.
78
+ const reports = getReports('raw', selector);
83
79
  // If any exist:
84
- if (rawReportNames.length) {
85
- // Identify the one to be scored.
86
- let rawReportName = '';
87
- if (reportIDStart) {
88
- rawReportName = rawReportNames.find(reportName => reportName.startsWith(reportIDStart));
89
- }
90
- else {
91
- rawReportName = rawReportNames[0];
92
- }
93
- // If it exists:
94
- if (rawReportName) {
95
- // Score it.
96
- const rawReportJSON = await fs.readFile(`${rawDir}/${rawReportName}`, 'utf8');
97
- const rawReport = JSON.parse(rawReportJSON);
98
- const {scorer} = require(`./${scoreProcDir}/${scoreProcID}.js`);
99
- const scoredReport = await score(scorer, rawReport);
100
- // Save it, scored.
80
+ if (reports.length) {
81
+ // Get the scorer.
82
+ const {scorer} = require(`${functionDir}/score/${scorerID}`);
83
+ // Score the reports.
84
+ const scoredReports = score(scorer, reports);
85
+ const scoredReportDir = `${reportDir}/scored`;
86
+ // For each scored report:
87
+ for (const scoredReport of scoredReports) {
88
+ // Save it.
101
89
  await fs.writeFile(
102
- `${scoredDir}/${scoredReport.job.id}.json`, JSON.stringify(scoredReport, null, 2)
90
+ `${scoredReportDir}/${scoredReport.id}.json`, JSON.stringify(scoredReport, null, 2)
103
91
  );
104
- console.log(`Scored report ${rawReport.job.id} saved in ${scoredDir}`);
105
- }
106
- // Otherwise, i.e. if it does not exist:
107
- else {
108
- // Report this.
109
- console.log('ERROR: Specified raw report not found');
110
- }
92
+ };
111
93
  }
112
- // Otherwise, i.e. if no raw report found:
94
+ // Otherwise, i.e. if no raw reports are to be scored:
113
95
  else {
114
96
  // Report this.
115
- console.log('ERROR: No raw report found');
97
+ console.log('ERROR: No raw reports to be scored');
116
98
  }
117
99
  };
118
- // Fulfills a multiple-report scoring request.
119
- const callMultiScore = async scoreProcID => {
120
- // Score all raw reports.
121
- await multiScore(scoreProcID);
122
- };
123
- // Prepares to fulfill a digesting request.
124
- const digestPrep = async digestProcID => {
125
- const {digest} = require('./digest');
126
- const {makeQuery} = require(`./${digestProcDir}/${digestProcID}/index`);
127
- const digestTemplate = await fs.readFile(`${digestProcDir}/${digestProcID}/index.html`, 'utf8');
128
- // Identify the scored reports.
129
- const scoredFileNames = await fs.readdir(scoredDir);
130
- const scoredReportNames = scoredFileNames.filter(fileName => fileName.endsWith('.json'));
131
- // Return the data required for the fulfillment of the request.
132
- return {
133
- anyScoredReports: scoredReportNames.length > 0,
134
- digest,
135
- makeQuery,
136
- digestTemplate,
137
- scoredReportNames
138
- };
139
- };
140
- // Digests and saves a report.
141
- const digestReport = async (scoredReportName, prepData) => {
142
- // Digest the specified report.
143
- const scoredReportJSON = await fs.readFile(`${scoredDir}/${scoredReportName}`, 'utf8');
144
- const scoredReport = JSON.parse(scoredReportJSON);
145
- const digestedReport = digest(prepData.digestTemplate, prepData.makeQuery, scoredReport);
146
- // Save it, digested.
147
- await fs.writeFile(`${digestedDir}/${scoredReport.job.id}.html`, digestedReport);
148
- console.log(`Digested report ${scoredReport.job.id} saved in ${digestedDir}`);
149
- };
150
100
  // Fulfills a digesting request.
151
- const callDigest = async (digestProcID, reportIDStart = '') => {
152
- const prepData = await digestPrep(digestProcID);
153
- // If any scored reports exist:
154
- if (prepData.anyScoredReports) {
155
- // Identify the one to be digested.
156
- let scoredReportName = '';
157
- if (reportIDStart) {
158
- scoredReportName = prepData.scoredReportNames.find(
159
- reportName => reportName.startsWith(reportIDStart)
101
+ const callDigest = async (digesterID, selector = '') => {
102
+ // Get the scored reports to be digested.
103
+ const reports = getReports('scored', selector);
104
+ // If any exist:
105
+ if (reports.length) {
106
+ const digesterDir = `${functionDir}/digest/${digesterID}`;
107
+ // Get the digester.
108
+ const {digester} = require(`${digesterDir}/index`);
109
+ // Get the template.
110
+ const template = await fs.readFile(`${digesterDir}/index.html`);
111
+ // Digest the reports.
112
+ const digestedReports = digest(template, digester, reports);
113
+ const digestedReportDir = `${reportDir}/digested`;
114
+ // For each digested report:
115
+ for (const digestedReport of digestedReports) {
116
+ // Save it.
117
+ await fs.writeFile(
118
+ `${digestedReportDir}/${digestedReport.id}.json`, JSON.stringify(digestedReport, null, 2)
160
119
  );
161
- }
162
- else {
163
- scoredReportName = prepData.scoredReportNames[0];
164
- }
165
- // If it exists:
166
- if (scoredReportName) {
167
- // Digest and save it.
168
- await digestReport(scoredReportName, prepData);
169
- }
170
- // Otherwise, i.e. if it does not exist:
171
- else {
172
- // Report this.
173
- console.log('ERROR: Specified scored report not found');
174
- }
120
+ };
175
121
  }
176
- // Otherwise, i.e. if no scored report exists:
122
+ // Otherwise, i.e. if no scored reports are to be digested:
177
123
  else {
178
124
  // Report this.
179
- console.log('ERROR: No scored report found');
125
+ console.log('ERROR: No scored reports to be digested');
180
126
  }
181
127
  };
182
- // Fulfills a multiple-report digesting request.
183
- const callMultiDigest = async digestProcID => {
184
- const prepData = await digestPrep(digestProcID);
185
- // If any scored reports exist:
186
- if (prepData.anyScoredReports) {
187
- // For each of them:
188
- for (const scoredReportName of prepData.scoredReportNames) {
189
- // Digest and save it.
190
- await digestReport(scoredReportName, prepData);
191
- }
192
- }
193
- // Otherwise, i.e. if no raw report exists:
194
- else {
195
- // Report this.
196
- console.log('ERROR: No raw report found');
197
- }
198
- console.log(
199
- `Digesting completed. Digest proc: ${digestProcID}. Report count: ${prepData.scoredReportNames.length}. Directory: ${digestedDir}.`
200
- );
201
- };
202
128
  // Fulfills a comparison request.
203
129
  const callCompare = async (compareProcID, comparisonNameBase) => {
204
130
  await compare(compareProcID, comparisonNameBase);
@@ -216,7 +142,7 @@ if (fn === 'aim' && fnArgs.length === 5) {
216
142
  console.log('Execution completed');
217
143
  });
218
144
  }
219
- else if (fn === 'merge' && fnArgs.length > 1 && fnArgs.length < 4) {
145
+ else if (fn === 'merge' && fnArgs.length > 2 && fnArgs.length < 5) {
220
146
  callMerge(... fnArgs)
221
147
  .then(() => {
222
148
  console.log('Execution completed');
package/compare.js CHANGED
@@ -1,39 +1,22 @@
1
1
  /*
2
2
  compare.js
3
- Creates comparisons from scored reports.
4
- Reads reports in process.env.REPORTDIR_SCORED and outputs into process.env.COMPARISONDIR.
5
- Arguments:
6
- 0. Base of name of compare proc located in procs/compare.
7
- 1. Base of name of comparative report to be written.
8
- Usage example: node compare cp18a usFedExec
9
- Note: Compares all reports in process.env.REPORTDIR_SCORED (but not in its subdirectories).
3
+ Creates a comparative report from scored reports.
10
4
  */
11
5
 
12
- // ########## IMPORTS
13
-
14
- // Module to keep secrets.
15
- require('dotenv').config();
16
- // Module to read and write files.
17
- const fs = require('fs/promises');
18
-
19
- // ########## CONSTANTS
20
-
21
- const comparisonDir = process.env.COMPARISONDIR || 'reports/comparative';
22
-
23
6
  // ########## FUNCTIONS
24
7
 
25
- // Replaces the placeholders in content with eponymous query parameters.
26
- const replaceHolders = (content, query) => content
8
+ // Replaces the placeholders in a template with eponymous query parameters.
9
+ const replaceHolders = (template, query) => template
27
10
  .replace(/__([a-zA-Z]+)__/g, (ph, qp) => query[qp]);
28
- // Creates and saves a web page containing a comparative table.
29
- exports.compare = async (compareProcID, comparisonNameBase) => {
30
- const comparisonDirAbs = `${__dirname}/${comparisonDir}`;
31
- const {getQuery} = require(`./${process.env.COMPAREPROCDIR}/${compareProcID}/index`);
32
- const query = await getQuery();
33
- const pageRaw = await fs.readFile(
34
- `${__dirname}/${process.env.COMPAREPROCDIR}/${compareProcID}/index.html`, 'utf8'
35
- );
36
- const page = replaceHolders(pageRaw, query);
37
- await fs.writeFile(`${comparisonDirAbs}/${comparisonNameBase}.html`, page);
38
- console.log(`Page ${comparisonNameBase}.html created and saved`);
11
+ // Compares the scored reports and returns a comparison.
12
+ exports.compare = (comparisonTemplate, comparer, reports) => {
13
+ // Create a query.
14
+ const query = {};
15
+ // Populate the query.
16
+ comparer(reports, query);
17
+ // Use it to create a comparison.
18
+ const comparison = replaceHolders(comparisonTemplate, query);
19
+ // Return the comparison.
20
+ console.log(`Comparison complete. Report count: ${reports.length}`);
21
+ return comparison;
39
22
  };
package/digest.js CHANGED
@@ -1,10 +1,10 @@
1
1
  /*
2
2
  digest.js
3
- Creates a digest from a scored report.
3
+ Creates digests from a scored reports.
4
4
  Arguments:
5
5
  0. Digest template.
6
- 1. Digest proc.
7
- 2. Scored report.
6
+ 1. Digesting function.
7
+ 2. Array of scored reports.
8
8
  */
9
9
 
10
10
  // ########## FUNCTIONS
@@ -12,14 +12,22 @@
12
12
  // Replaces the placeholders in content with eponymous query parameters.
13
13
  const replaceHolders = (content, query) => content
14
14
  .replace(/__([a-zA-Z]+)__/g, (ph, qp) => query[qp]);
15
- // Creates a digest.
16
- exports.digest = (digestTemplate, digestProc, scoredReport) => {
15
+ // Digests the scored reports and returns them, digested.
16
+ exports.digest = (digestTemplate, digester, reports) => {
17
+ const digests = [];
17
18
  // Create a query.
18
19
  const query = {};
19
- digestProc(scoredReport, query);
20
- // Use it to replace the placeholders in the template.
21
- const digestedReport = replaceHolders(digestTemplate, query);
22
- // Return the digest.
23
- console.log(`Report ${scoredReport.job.id} digested`);
24
- return digestedReport;
20
+ // For each report:
21
+ for (const report of reports) {
22
+ // Populate the query.
23
+ digester(report, query);
24
+ // Use it to create a digest.
25
+ const digestedReport = replaceHolders(digestTemplate, query);
26
+ // Add the digest to the array of digests.
27
+ digests.push(digestedReport);
28
+ console.log(`Report ${report.id} digested`);
29
+ };
30
+ // Return the digests.
31
+ console.log(`Digesting complete. Report count: ${reports.length}`);
32
+ return digests;
25
33
  };
package/merge.js CHANGED
@@ -1,53 +1,128 @@
1
1
  /*
2
2
  merge.js
3
- Merges a batch and a script to produce jobs.
3
+ Merges a script and a batch and returns jobs.
4
4
  Arguments:
5
- 0. base of name of script located in process.env.SCRIPTDIR.
6
- 1. base of name of batch located in process.env.BATCHDIR.
7
- 2. email address to be notified of completion.
8
- Usage examples:
9
- node call merge ts25 weborgs developer@w3.org
10
- node call merge ts25 weborgs
11
- Note: The subdirectory for jobs will be created if it does not exist.
5
+ 0. script
6
+ 1. batch
7
+ 2. whether to provide test isolation (no if omitted)
12
8
  */
13
9
 
14
10
  // ########## IMPORTS
15
11
 
16
12
  // Module to keep secrets.
17
13
  require('dotenv').config();
18
- // Module to read and write files.
19
- const fs = require('fs/promises');
20
- // Module to aim a script at a host.
21
- const {aim} = require('./aim');
22
14
 
23
15
  // ########## CONSTANTS
24
16
 
25
- const scriptDir = process.env.SCRIPTDIR || 'scripts';
26
- const batchDir = process.env.BATCHDIR || 'batches';
27
- const jobDir = process.env.JOBDIR || 'watch';
28
17
  const stdRequester = process.env.REQUESTER;
18
+ const contaminantNames = new Set([
19
+ 'axe',
20
+ 'continuum',
21
+ 'focAll',
22
+ 'focInd',
23
+ 'focOp',
24
+ 'hover',
25
+ 'htmlcs',
26
+ 'ibm',
27
+ 'menuNav',
28
+ 'textNodes',
29
+ 'wave'
30
+ ]);
29
31
 
30
32
  // ########## FUNCTIONS
31
33
 
32
- // Merges a batch into a script and writes jobs.
33
- exports.merge = async (scriptName, batchName, requester = stdRequester) => {
34
- // Get the script and the batch.
35
- const scriptJSON = await fs.readFile(`${scriptDir}/${scriptName}.json`, 'utf8');
36
- const batchJSON = await fs.readFile(`${batchDir}/${batchName}.json`, 'utf8');
37
- const script = JSON.parse(scriptJSON);
38
- const batch = JSON.parse(batchJSON);
34
+ // Merges a script and a batch and returns jobs.
35
+ exports.merge = (script, batch, requester, isolate = false) => {
36
+ if (isolate === 'false') {
37
+ isolate = false;
38
+ }
39
+ // If the requester is unspecified, make it the standard requester.
40
+ requester ||= stdRequester;
41
+ // Create a timestamp.
39
42
  const timeStamp = Math.floor((Date.now() - Date.UTC(2022, 1)) / 2000).toString(36);
40
- // Create the job directory if it does not exist.
41
- await fs.mkdir(jobDir, {recursive: true});
42
- // For each host in the batch:
43
- const {hosts} = batch;
44
- for (const host of hosts) {
45
- // Aim the script at the host.
46
- const job = await aim(script, host, requester, timeStamp);
47
- // Add the batch name to the job.
48
- job.sources.batch = batchName;
49
- // Save the job.
50
- await fs.writeFile(`${jobDir}/${job.id}.json`, JSON.stringify(job, null, 2));
43
+ // Create a time description.
44
+ const creationTime = (new Date()).toISOString().slice(0, 19);
45
+ // Initialize a target-independent job.
46
+ const protoJob = JSON.parse(JSON.stringify(script));
47
+ // Add the timestamp to the ID of the job.
48
+ protoJob.id = `${timeStamp}-${protoJob.id}-`;
49
+ // Add a sources property to the job.
50
+ protoJob.sources = {
51
+ script: script.id,
52
+ batch: batch.id,
53
+ target: {
54
+ id: '',
55
+ what: ''
56
+ },
57
+ requester
51
58
  };
52
- console.log(`Merger completed. Job count: ${hosts.length}. Time stamp ${timeStamp}.`);
59
+ // Add time properties to the job.
60
+ protoJob.creationTime = creationTime;
61
+ protoJob.timeStamp = timeStamp;
62
+ // If isolation was requested:
63
+ if (isolate) {
64
+ // Append a copy of the previous placeholder to each eligible contaminating test in the script.
65
+ let {acts} = protoJob;
66
+ let lastPlaceholder = {};
67
+ for (const actIndexString in acts) {
68
+ const actIndex = Number.parseInt(actIndexString);
69
+ const act = acts[actIndex];
70
+ if (act.type === 'placeholder') {
71
+ lastPlaceholder = act;
72
+ }
73
+ else if (
74
+ act.type === 'test'
75
+ && contaminantNames.has(act.which)
76
+ && actIndex < acts.length - 1
77
+ && acts[actIndex + 1].type !== 'placeholder'
78
+ ) {
79
+ acts[actIndex] = JSON.parse(JSON.stringify([act, lastPlaceholder]));
80
+ }
81
+ };
82
+ protoJob.acts = acts.flat();
83
+ }
84
+ // Initialize an array of jobs.
85
+ const jobs = [];
86
+ // For each target in the batch:
87
+ const {targets} = batch;
88
+ for (const target of targets) {
89
+ // Initialize a job.
90
+ const job = JSON.parse(JSON.stringify(protoJob));
91
+ // Add the target ID to the job ID.
92
+ job.id += target.id;
93
+ // Add data on the target to the sources property of the job.
94
+ job.sources.target.id = target.id;
95
+ job.sources.target.what = target.what;
96
+ // Replace each placeholder object in the job with the named replacer array of the target.
97
+ let {acts} = job;
98
+ for (const actIndex in acts) {
99
+ const act = acts[actIndex];
100
+ if (act.type === 'placeholder') {
101
+ const replacerName = act.which;
102
+ if (replacerName && target.acts) {
103
+ let replacerActs = target.acts[replacerName];
104
+ if (replacerActs) {
105
+ // Add a which property to any launch act in the replacer.
106
+ replacerActs = JSON.parse(JSON.stringify(replacerActs));
107
+ for (const replacerAct of replacerActs) {
108
+ if (replacerAct.type === 'launch') {
109
+ replacerAct.which = act.launch;
110
+ }
111
+ }
112
+ acts[actIndex] = replacerActs;
113
+ }
114
+ else {
115
+ console.log(`ERROR: Target ${target.id} has no ${replacerName} replacer`);
116
+ }
117
+ }
118
+ else {
119
+ console.log(`ERROR: Placeholder for target ${target.id} not replaceable`);
120
+ }
121
+ }
122
+ }
123
+ job.acts = acts.flat();
124
+ // Append the job to the array of jobs.
125
+ jobs.push(job);
126
+ };
127
+ return jobs;
53
128
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testilo",
3
- "version": "7.1.1",
3
+ "version": "9.0.0",
4
4
  "description": "Client that scores and digests Testaro reports",
5
5
  "main": "aim.js",
6
6
  "scripts": {
@@ -0,0 +1,48 @@
1
+ <!DOCTYPE HTML>
2
+ <html lang="en-US">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <meta name="author" content="Testilo">
7
+ <meta name="creator" content="Testilo">
8
+ <meta name="publisher" name="Testilo">
9
+ <meta name="description" content="comparison of accessibility scores">
10
+ <meta name="keywords" content="accessibility a11y web testing">
11
+ <title>Accessibility score comparison</title>
12
+ <link rel="icon" href="favicon.png">
13
+ <link rel="stylesheet" href="style.css">
14
+ </head>
15
+ <body>
16
+ <main>
17
+ <header>
18
+ <h1>Accessibility score comparison</h1>
19
+ </header>
20
+ <h2>Introduction</h2>
21
+ <p>The table below compares __pageCount__ web pages on <a href="https://www.w3.org/WAI/fundamentals/accessibility-intro/">accessibility</a>. The page names are links to the pages on the web. The scores are links to digests that explain in detail how the scores were computed.</p>
22
+ <p>The pages were:</p>
23
+ <ol id="summary">
24
+ <li>Tested by <a href="https://www.npmjs.com/package/testaro">Testaro</a> with script <code>ts20</code></li>
25
+ <li>Scored by <a href="https://www.npmjs.com/package/testilo">Testilo</a> with procedure <code>sp20b</code></li>
26
+ <li>Digested by Testilo with procedure <code>dp20d</code></li>
27
+ <li>Compared by Testilo with procedure <code>cp20a</code></li>
28
+ </ol>
29
+ <p>The script made Testaro perform 1230 automated accessibility tests drawn from nine different packages: Alfa, Axe, Continuum, Equal Access, HTML CodeSniffer, Nu Html Checker, Tenon, Testaro, and WAVE. The scoring procedure made Testilo assign a score to each page, with 0 being perfect.</p>
30
+ <h2>Comparison</h2>
31
+ <table class="allBorder">
32
+ <caption>Accessibility scores of web pages</caption>
33
+ <thead>
34
+ <tr><th scope="col">Page</th><th scope="col" colspan="2">Score (lower is better)</tr>
35
+ </thead>
36
+ <tbody class="linkSmaller secondCellRight">
37
+ __tableBody__
38
+ </tbody>
39
+ </table>
40
+ <h2>Discussion</h2>
41
+ <p>The widths of the bars in the above table are proportional to the square roots of the scores that the bars represent. For example, if score A is 4 times score B, then bar A is 2 times as wide as bar B.</p>
42
+ <p>Tests and scoring formulae are fallible and subjective. The reported faults merit investigation as potential opportunities for improved accessibility. But some may not actually harm accessibility, and some other accessibility faults are not detectable with these tests. Different reasonable procedures could yield different test results and different scores. Testaro and Testilo can be customized to fit different definitions and weightings of types of accessibility.</p>
43
+ <footer>
44
+ <p class="date">Produced <time itemprop="datePublished" datetime="__dateISO__">__dateSlash__</time></p>
45
+ </footer>
46
+ </main>
47
+ </body>
48
+ </html>