testilo 39.0.0 → 39.0.1
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 +5 -1
- package/package.json +1 -1
- package/procs/track/ttp40/index.js +1 -1
- package/procs/track/ttp43/index.html +116 -0
- package/procs/track/ttp43/index.js +100 -0
package/README.md
CHANGED
|
@@ -788,8 +788,12 @@ To test the `digest` module, in the project directory you can execute the statem
|
|
|
788
788
|
|
|
789
789
|
The `summarize` module of Testilo can summarize a scored report. The summary is an object that contains these properties from the report: `id`, `endTime`, `targetWhat` (description of the target), `url` (of the target), `sources`, and `score` (only the value of the `score.total` property of the report).
|
|
790
790
|
|
|
791
|
+
Report summaries make some operations more efficient by allowing other modules to get needed data from summaries instead of from reports. The size of a summary tends to be about 0.01% of the size of a report.
|
|
792
|
+
|
|
791
793
|
#### Invocation
|
|
792
794
|
|
|
795
|
+
The `summarize` module summarizes one report when invoked by a module, but the `call` module invoked by a user can call `summarize` multiple times to summarize multiple reports and combine those summaries into a file.
|
|
796
|
+
|
|
793
797
|
##### By a module
|
|
794
798
|
|
|
795
799
|
A module can invoke `summarize()` in this way:
|
|
@@ -829,7 +833,7 @@ The `compare` module compares the scores in a summary report. The `compare()` fu
|
|
|
829
833
|
|
|
830
834
|
The comparison function defines the rules for generating an HTML file comparing the scored reports. The Testilo package contains a `procs/compare` directory, in which there are subdirectories containing modules that export comparison functions. You can use one of those functions, or you can create your own.
|
|
831
835
|
|
|
832
|
-
|
|
836
|
+
The built-in comparison functions compare all of the scores in the summary report. Thus, if the summary report contains multiple scores for the same target, based on tests performed at various times, those scores will all appear in the comparison, labeled identically with the `what` description of the target. If you want only one score per target to appear, you can create a new summary report that includes only one summary per target in its `summaries` array.
|
|
833
837
|
|
|
834
838
|
#### Invocation
|
|
835
839
|
|
package/package.json
CHANGED
|
@@ -56,7 +56,7 @@ const populateQuery = async (id, what, summaryReport, query) => {
|
|
|
56
56
|
// Get an array of target descriptions and assign to each an ID.
|
|
57
57
|
const rows = [];
|
|
58
58
|
const targets = Array
|
|
59
|
-
.from(new Set(summaries.map(result => result.
|
|
59
|
+
.from(new Set(summaries.map(result => result.targetWhat)))
|
|
60
60
|
.sort()
|
|
61
61
|
.map((targetWhat, index) => [alphaNumOf(index), targetWhat]);
|
|
62
62
|
const targetIDs = {};
|
|
@@ -0,0 +1,116 @@
|
|
|
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="report of accessibility testing of web pages">
|
|
10
|
+
<meta name="keywords" content="accessibility a11y web testing">
|
|
11
|
+
<title>Accessibility tracking report</title>
|
|
12
|
+
<link rel="icon" href="favicon.ico">
|
|
13
|
+
<link rel="stylesheet" href="style.css">
|
|
14
|
+
</head>
|
|
15
|
+
<body>
|
|
16
|
+
<main>
|
|
17
|
+
<header>
|
|
18
|
+
<h1>Accessibility tracking report</h1>
|
|
19
|
+
</header>
|
|
20
|
+
<h2>Introduction</h2>
|
|
21
|
+
<p>This is tracking report <code>__id__</code>, for __what__.</p>
|
|
22
|
+
<p>It tracks accessibility scores over time. A perfect score is 0. The tracking was performed by Testilo procedure <code>__tp__</code>.</p>
|
|
23
|
+
<p>The results are presented first as a graph, and then as a table.</p>
|
|
24
|
+
<h2>Results as a graph</h2>
|
|
25
|
+
<h3>Legend</h3>
|
|
26
|
+
<ul id="legendItems">
|
|
27
|
+
__legendItems__
|
|
28
|
+
</ul>
|
|
29
|
+
<h3>Line graph</h3>
|
|
30
|
+
<figure id="graph">
|
|
31
|
+
<figcaption>Accessibility scores</figcaption>
|
|
32
|
+
</figure>
|
|
33
|
+
<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
|
|
34
|
+
<script src="https://cdn.jsdelivr.net/npm/@observablehq/plot@0.6"></script>
|
|
35
|
+
<script type="module" defer>
|
|
36
|
+
const summaryReportJSON = '__summaryReportJSON__';
|
|
37
|
+
const summaryReport = JSON.parse(summaryReportJSON);
|
|
38
|
+
const graphData = [];
|
|
39
|
+
const targetIDs = {};
|
|
40
|
+
Array.from(document.getElementById('legendItems').children).forEach(li => {
|
|
41
|
+
const targetData = li.textContent.split(': ');
|
|
42
|
+
targetIDs[targetData[1]] = targetData[0];
|
|
43
|
+
});
|
|
44
|
+
summaryReport.summaries.forEach(result => {
|
|
45
|
+
const {what} = result.sources.target;
|
|
46
|
+
graphData.push({
|
|
47
|
+
targetID: targetIDs[what],
|
|
48
|
+
targetWhat: what,
|
|
49
|
+
time: new Date(`20${result.endTime}Z`),
|
|
50
|
+
score: result.score
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
const svg = Plot.plot({
|
|
54
|
+
style: 'overflow: visible;',
|
|
55
|
+
height: 600,
|
|
56
|
+
y: {grid: true},
|
|
57
|
+
marks: [
|
|
58
|
+
Plot.ruleY([0]),
|
|
59
|
+
Plot.lineY(graphData, {
|
|
60
|
+
x: 'time',
|
|
61
|
+
y: 'score',
|
|
62
|
+
stroke: 'targetID'
|
|
63
|
+
}),
|
|
64
|
+
Plot.dot(graphData, {
|
|
65
|
+
x: 'time',
|
|
66
|
+
y: 'score',
|
|
67
|
+
z: 'targetID',
|
|
68
|
+
r: 9
|
|
69
|
+
}),
|
|
70
|
+
Plot.text(graphData, {
|
|
71
|
+
x: 'time',
|
|
72
|
+
y: 'score',
|
|
73
|
+
z: 'targetID',
|
|
74
|
+
text: 'targetID',
|
|
75
|
+
textAnchor: 'middle'
|
|
76
|
+
}),
|
|
77
|
+
Plot.text(graphData, Plot.selectFirst({
|
|
78
|
+
x: 'time',
|
|
79
|
+
y: 'score',
|
|
80
|
+
z: 'targetID',
|
|
81
|
+
text: 'targetWhat',
|
|
82
|
+
textAnchor: 'start',
|
|
83
|
+
dx: 15
|
|
84
|
+
})),
|
|
85
|
+
Plot.text(graphData, Plot.selectLast({
|
|
86
|
+
x: 'time',
|
|
87
|
+
y: 'score',
|
|
88
|
+
z: 'targetID',
|
|
89
|
+
text: 'targetWhat',
|
|
90
|
+
textAnchor: 'start',
|
|
91
|
+
dx: 15
|
|
92
|
+
}))
|
|
93
|
+
]
|
|
94
|
+
});
|
|
95
|
+
document.getElementById('graph').insertAdjacentElement('beforeend', svg);
|
|
96
|
+
</script>
|
|
97
|
+
<h2>Results as a table</h2>
|
|
98
|
+
<table class="allBorder secondCellRight">
|
|
99
|
+
<caption>Accessibility scores</caption>
|
|
100
|
+
<thead>
|
|
101
|
+
<tr>
|
|
102
|
+
<th>Date and time</th>
|
|
103
|
+
<th>Score</th>
|
|
104
|
+
<th>Target</th>
|
|
105
|
+
</tr>
|
|
106
|
+
</thead>
|
|
107
|
+
<tbody>
|
|
108
|
+
__scoreRows__
|
|
109
|
+
</tbody>
|
|
110
|
+
</table>
|
|
111
|
+
<footer>
|
|
112
|
+
<p class="date">Produced <time itemprop="datePublished" datetime="__dateISO__">__dateSlash__</time></p>
|
|
113
|
+
</footer>
|
|
114
|
+
</main>
|
|
115
|
+
</body>
|
|
116
|
+
</html>
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/*
|
|
2
|
+
© 2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
// index: tracker for tracking procedure ttp43.
|
|
24
|
+
|
|
25
|
+
// IMPORTS
|
|
26
|
+
|
|
27
|
+
// Module to keep secrets.
|
|
28
|
+
require('dotenv').config();
|
|
29
|
+
// Module to process files.
|
|
30
|
+
const fs = require('fs/promises');
|
|
31
|
+
// Utility module.
|
|
32
|
+
const {alphaNumOf, getNowDate, getNowDateSlash} = require('../../util');
|
|
33
|
+
|
|
34
|
+
// CONSTANTS
|
|
35
|
+
|
|
36
|
+
// Tracker ID.
|
|
37
|
+
const trackerID = 'ttp43';
|
|
38
|
+
// Newline with indentations.
|
|
39
|
+
const innerJoiner = '\n ';
|
|
40
|
+
// Digest URL.
|
|
41
|
+
const digestURL = process.env.DIGEST_URL;
|
|
42
|
+
|
|
43
|
+
// FUNCTIONS
|
|
44
|
+
|
|
45
|
+
// Adds parameters to a query for a tracking report.
|
|
46
|
+
const populateQuery = async (id, what, summaryReport, query) => {
|
|
47
|
+
// General parameters.
|
|
48
|
+
query.id = id;
|
|
49
|
+
query.what = what;
|
|
50
|
+
query.tp = trackerID;
|
|
51
|
+
query.dateISO = getNowDate();
|
|
52
|
+
query.dateSlash = getNowDateSlash();
|
|
53
|
+
// JSON of summary report.
|
|
54
|
+
const {summaries} = summaryReport;
|
|
55
|
+
query.summaryReportJSON = JSON.stringify(summaryReport);
|
|
56
|
+
// Get an array of target descriptions and assign to each an ID.
|
|
57
|
+
const rows = [];
|
|
58
|
+
const targets = Array
|
|
59
|
+
.from(new Set(summaries.map(result => result.targetWhat)))
|
|
60
|
+
.sort()
|
|
61
|
+
.map((targetWhat, index) => [alphaNumOf(index), targetWhat]);
|
|
62
|
+
const targetIDs = {};
|
|
63
|
+
targets.forEach(target => {
|
|
64
|
+
targetIDs[target[1]] = target[0];
|
|
65
|
+
});
|
|
66
|
+
// Add legend items to the query.
|
|
67
|
+
const legendItems = targets.map(target => `<li>${target[0]}: ${target[1]}</li>`);
|
|
68
|
+
query.legendItems = legendItems.join('\n ');
|
|
69
|
+
// For each result:
|
|
70
|
+
summaries.forEach(result => {
|
|
71
|
+
const {endTime, id, score, targetWhat, url} = result;
|
|
72
|
+
// Create a date-time cell.
|
|
73
|
+
const timeCell = `<td>${endTime}</td>`;
|
|
74
|
+
// Create a score cell.
|
|
75
|
+
const digestLinkDestination = digestURL.replace('__id__', id);
|
|
76
|
+
const scoreCell = `<td><a href=${digestLinkDestination}>${score}</a></td>`;
|
|
77
|
+
// Create a target cell.
|
|
78
|
+
const targetLink = `<a href="${url}">${targetWhat}</a>`;
|
|
79
|
+
const targetCell = `<td>${targetIDs[targetWhat]}: ${targetLink}</td>`;
|
|
80
|
+
const row = `<tr>${[timeCell, scoreCell, targetCell].join('')}</tr>`;
|
|
81
|
+
// Add the row to the array of rows.
|
|
82
|
+
rows.push(row);
|
|
83
|
+
});
|
|
84
|
+
// Add the rows to the query.
|
|
85
|
+
query.scoreRows = rows.join(innerJoiner);
|
|
86
|
+
};
|
|
87
|
+
// Returns a tracking report.
|
|
88
|
+
exports.tracker = async (id, what, summaryReport) => {
|
|
89
|
+
// Create a query to replace placeholders.
|
|
90
|
+
const query = {};
|
|
91
|
+
await populateQuery(id, what, summaryReport, query);
|
|
92
|
+
// Get the template.
|
|
93
|
+
let template = await fs.readFile(`${__dirname}/index.html`, 'utf8');
|
|
94
|
+
// Replace its placeholders.
|
|
95
|
+
Object.keys(query).forEach(param => {
|
|
96
|
+
template = template.replace(new RegExp(`__${param}__`, 'g'), query[param]);
|
|
97
|
+
});
|
|
98
|
+
// Return the tracking report.
|
|
99
|
+
return template;
|
|
100
|
+
};
|