testilo 12.3.0 → 13.1.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 +12 -2
- package/call.js +1 -1
- package/digest.js +6 -14
- package/package.json +1 -1
- package/procs/digest/tdp27/index.html +17 -9
- package/procs/digest/tdp27/index.js +33 -20
- package/procs/score/tic27.js +9 -106
- package/procs/score/tsp27.js +83 -40
package/README.md
CHANGED
|
@@ -181,8 +181,7 @@ The `batch()` function of the `batch` module generates a batch and returns it as
|
|
|
181
181
|
A user can invoke `batch` in this way:
|
|
182
182
|
|
|
183
183
|
- Create a target list and save it as a text file (with tab-delimited items in newline-delimited lines) in the `targetLists` subdirectory of the `process.env.SPECDIR` directory. Name the file `x.tsv`, where `x` is the list ID.
|
|
184
|
-
- In the Testilo project directory, execute
|
|
185
|
-
- `node call batch i w`
|
|
184
|
+
- In the Testilo project directory, execute the statement `node call batch i w`.
|
|
186
185
|
|
|
187
186
|
In this statement, replace `i` with the list ID and `w` with a description of the batch.
|
|
188
187
|
|
|
@@ -196,6 +195,8 @@ Testilo classifies issues. The built-in issue classifications are located in the
|
|
|
196
195
|
|
|
197
196
|
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.
|
|
198
197
|
|
|
198
|
+
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.
|
|
199
|
+
|
|
199
200
|
#### Invocation
|
|
200
201
|
|
|
201
202
|
There are two ways to use the `script` module.
|
|
@@ -216,6 +217,13 @@ This invocation references `scriptID`, `issueClasses`, and `issueID` variables.
|
|
|
216
217
|
|
|
217
218
|
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.
|
|
218
219
|
|
|
220
|
+
To create a script without issue restrictions, a module can use this invocation:
|
|
221
|
+
|
|
222
|
+
```javaScript
|
|
223
|
+
const {script} = require('testilo/script');
|
|
224
|
+
const scriptObj = script(scriptID);
|
|
225
|
+
```
|
|
226
|
+
|
|
219
227
|
##### By a user
|
|
220
228
|
|
|
221
229
|
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 …`.
|
|
@@ -229,6 +237,8 @@ The `call` module will retrieve the named classification from its directory.
|
|
|
229
237
|
The `script` module will create a script.
|
|
230
238
|
The `call` module will save the script as a JSON file in the `scripts` subdirectory of the `process.env.SPECDIR` directory.
|
|
231
239
|
|
|
240
|
+
To create a script without any issue restrictions, a user can execute the statement `node call script s`.
|
|
241
|
+
|
|
232
242
|
#### Options
|
|
233
243
|
|
|
234
244
|
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.
|
package/call.js
CHANGED
|
@@ -58,7 +58,7 @@ const callBatch = async (listID, what) => {
|
|
|
58
58
|
const callScript = async (scriptID, classificationID = null, ... issueIDs) => {
|
|
59
59
|
// Get any issue classification.
|
|
60
60
|
const issueClasses = classificationID
|
|
61
|
-
? require(`${functionDir}/score/${classificationID}`)
|
|
61
|
+
? require(`${functionDir}/score/${classificationID}`).issueClasses
|
|
62
62
|
: null;
|
|
63
63
|
// Create a script.
|
|
64
64
|
const scriptObj = script(scriptID, issueClasses, ... issueIDs);
|
package/digest.js
CHANGED
|
@@ -2,31 +2,23 @@
|
|
|
2
2
|
digest.js
|
|
3
3
|
Creates digests from a scored reports.
|
|
4
4
|
Arguments:
|
|
5
|
-
0.
|
|
6
|
-
1.
|
|
7
|
-
2. Array of scored reports.
|
|
5
|
+
0. Digesting function.
|
|
6
|
+
1. Array of scored reports.
|
|
8
7
|
*/
|
|
9
8
|
|
|
10
9
|
// ########## FUNCTIONS
|
|
11
10
|
|
|
12
|
-
// Replaces the placeholders in content with eponymous query parameters.
|
|
13
|
-
const replaceHolders = (content, query) => content
|
|
14
|
-
.replace(/__([a-zA-Z]+)__/g, (ph, qp) => query[qp]);
|
|
15
11
|
// Digests the scored reports and returns them, digested.
|
|
16
|
-
exports.digest = (
|
|
12
|
+
exports.digest = (digester, reports) => {
|
|
17
13
|
const digests = {};
|
|
18
|
-
// Create a query.
|
|
19
|
-
const query = {};
|
|
20
14
|
// For each report:
|
|
21
|
-
|
|
22
|
-
// Populate the query.
|
|
23
|
-
digester(report, query);
|
|
15
|
+
reports.forEach(report => {
|
|
24
16
|
// Use it to create a digest.
|
|
25
|
-
const digestedReport =
|
|
17
|
+
const digestedReport = digester(report);
|
|
26
18
|
// Add the digest to the array of digests.
|
|
27
19
|
digests[report.id] = digestedReport;
|
|
28
20
|
console.log(`Report ${report.id} digested`);
|
|
29
|
-
};
|
|
21
|
+
});
|
|
30
22
|
// Return the digests.
|
|
31
23
|
console.log(`Digesting complete. Report count: ${reports.length}`);
|
|
32
24
|
return digests;
|
package/package.json
CHANGED
|
@@ -29,21 +29,29 @@
|
|
|
29
29
|
</table>
|
|
30
30
|
</header>
|
|
31
31
|
<h2>Introduction</h2>
|
|
32
|
-
<p>This is a digest of results from a battery of accessibility
|
|
33
|
-
<p>The battery includes
|
|
32
|
+
<p>This is a digest of results from a battery of accessibility tests.</p>
|
|
33
|
+
<p>The battery includes about 1350 tests drawn from ten different packages: Alfa, Axe, Continuum, Equal Access, HTML CodeSniffer, Nu Html Checker, QualWeb, Tenon, Testaro, and WAVE.</p>
|
|
34
34
|
<p>These tests were run on the web page named above and gave the page a score of __totalScore__, where 0 would be <q>perfect</q>.</p>
|
|
35
35
|
<h2>Score summary</h2>
|
|
36
36
|
<table class="allBorder secondCellRight">
|
|
37
|
-
<caption>Score
|
|
37
|
+
<caption>Score summary</caption>
|
|
38
|
+
<thead>
|
|
39
|
+
<tr><th>Component</th><th>Score</th></tr>
|
|
40
|
+
</thead>
|
|
38
41
|
<tbody class="headersLeft">
|
|
39
|
-
|
|
42
|
+
__summaryRows__
|
|
43
|
+
</tbody>
|
|
44
|
+
</table>
|
|
45
|
+
<h2>Issue scores</h2>
|
|
46
|
+
<table class="allBorder secondCellRight">
|
|
47
|
+
<caption>Issue scores</caption>
|
|
48
|
+
<thead>
|
|
49
|
+
<tr><th>Issue</th><th>Score</th></tr>
|
|
50
|
+
</thead>
|
|
51
|
+
<tbody class="headersLeft">
|
|
52
|
+
__issueRows__
|
|
40
53
|
</tbody>
|
|
41
54
|
</table>
|
|
42
|
-
<h2>Issue summary</h2>
|
|
43
|
-
<h3>Special issues</h3>
|
|
44
|
-
__specialSummary__
|
|
45
|
-
<h3>Classified issues</h3>
|
|
46
|
-
__issueSummary__
|
|
47
55
|
<h2>Complete report</h2>
|
|
48
56
|
<pre>__report__</pre>
|
|
49
57
|
<footer>
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
index: digester for scoring procedure tsp24.
|
|
3
|
-
Creator of parameters for substitution into index.html.
|
|
4
|
-
*/
|
|
1
|
+
// index: digester for scoring procedure tsp27.
|
|
5
2
|
|
|
6
3
|
// IMPORTS
|
|
7
4
|
|
|
@@ -21,20 +18,14 @@ const innerJoiner = '\n ';
|
|
|
21
18
|
const htmlEscape = textOrNumber => textOrNumber
|
|
22
19
|
.toString()
|
|
23
20
|
.replace(/&/g, '&')
|
|
24
|
-
.replace(/</g, '<')
|
|
21
|
+
.replace(/</g, '<')
|
|
22
|
+
.replace(/"/g, '"');
|
|
25
23
|
// Gets a row of the score-summary table.
|
|
26
24
|
const getScoreRow = (componentName, score) => `<tr><th>${componentName}</th><td>${score}</td></tr>`;
|
|
27
|
-
// Gets the start of a paragraph about a special score.
|
|
28
|
-
const getSpecialPStart = (summary, scoreID) =>
|
|
29
|
-
`<p><span class="componentID">${scoreID}</span>: Score ${summary[scoreID]}.`;
|
|
30
25
|
// Adds parameters to a query for a digest.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const {acts, sources, jobData, score} = report;
|
|
26
|
+
const makeQuery = (report, query) => {
|
|
27
|
+
const {sources, jobData, score} = report;
|
|
34
28
|
const {script, target, requester} = sources;
|
|
35
|
-
const reportJSON = JSON.stringify(report, null, 2);
|
|
36
|
-
const reportJSONSafe = htmlEscape(reportJSON);
|
|
37
|
-
query.report = reportJSONSafe;
|
|
38
29
|
const {scoreProcID, summary, issues} = score;
|
|
39
30
|
const {total} = summary;
|
|
40
31
|
query.ts = script;
|
|
@@ -55,20 +46,25 @@ exports.makeQuery = (report, query) => {
|
|
|
55
46
|
return;
|
|
56
47
|
}
|
|
57
48
|
// Get rows for a score-summary table.
|
|
58
|
-
const
|
|
49
|
+
const rows = {
|
|
50
|
+
summaryRows: [],
|
|
51
|
+
issueRows: []
|
|
52
|
+
};
|
|
59
53
|
const componentIDs = ['issues', 'tools', 'preventions', 'log', 'latency'];
|
|
60
54
|
['total'].concat(componentIDs).forEach(itemID => {
|
|
61
55
|
if (summary[itemID]) {
|
|
62
|
-
|
|
56
|
+
rows.summaryRows.push(getScoreRow(itemID, summary[itemID]));
|
|
63
57
|
}
|
|
64
58
|
});
|
|
65
|
-
//
|
|
59
|
+
// Get rows for an issue-score table.
|
|
66
60
|
Object.keys(issues).forEach(issueID => {
|
|
67
|
-
|
|
61
|
+
rows.issueRows.push(getScoreRow(issueID, issues[issueID]));
|
|
68
62
|
});
|
|
69
63
|
// Add the rows to the query.
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
['summaryRows', 'issueRows'].forEach(rowType => {
|
|
65
|
+
query[rowType] = rows[rowType].join(innerJoiner);
|
|
66
|
+
});
|
|
67
|
+
// Add paragraphs about the issues to the query.
|
|
72
68
|
const issueSummaryItems = [];
|
|
73
69
|
Object.keys(issues).forEach(issueID => {
|
|
74
70
|
const issueHeading = `<h4>Issue ${issueID}</h4>`;
|
|
@@ -96,4 +92,21 @@ exports.makeQuery = (report, query) => {
|
|
|
96
92
|
issueSummaryItems.push(issueHeading, wcagP, scoreP, issueIntroP, issueList);
|
|
97
93
|
});
|
|
98
94
|
query.issueSummary = issueSummaryItems.join(joiner);
|
|
95
|
+
// Add an HTML-safe copy of the report to the query to be appended to the digest.
|
|
96
|
+
const reportJSON = JSON.stringify(report, null, 2);
|
|
97
|
+
const reportJSONSafe = htmlEscape(reportJSON);
|
|
98
|
+
query.report = reportJSONSafe;
|
|
99
|
+
};
|
|
100
|
+
// Returns a digested report.
|
|
101
|
+
exports.digester = async report => {
|
|
102
|
+
// Create a query to replace plateholders.
|
|
103
|
+
const query = makeQuery(report, {});
|
|
104
|
+
// Get the template.
|
|
105
|
+
let template = await fs.readFile(`${__dirname}/index.html`, 'utf8');
|
|
106
|
+
// Replace its placeholders.
|
|
107
|
+
Object.keys(query).forEach(param => {
|
|
108
|
+
template = template.replace(new RegExp(`__${param}__`, 'g'), query[param]);
|
|
109
|
+
});
|
|
110
|
+
// Return the digest.
|
|
111
|
+
return template;
|
|
99
112
|
};
|
package/procs/score/tic27.js
CHANGED
|
@@ -2577,13 +2577,6 @@ exports.issueClasses = {
|
|
|
2577
2577
|
quality: 1,
|
|
2578
2578
|
what: 'role attribute has an invalid value'
|
|
2579
2579
|
}
|
|
2580
|
-
},
|
|
2581
|
-
testaro: {
|
|
2582
|
-
'role-bad': {
|
|
2583
|
-
variable: false,
|
|
2584
|
-
quality: 0.5,
|
|
2585
|
-
what: 'Nonexistent or implicit-overriding role'
|
|
2586
|
-
}
|
|
2587
2580
|
}
|
|
2588
2581
|
}
|
|
2589
2582
|
},
|
|
@@ -2621,10 +2614,10 @@ exports.issueClasses = {
|
|
|
2621
2614
|
}
|
|
2622
2615
|
},
|
|
2623
2616
|
testaro: {
|
|
2624
|
-
'role
|
|
2617
|
+
'role': {
|
|
2625
2618
|
variable: false,
|
|
2626
2619
|
quality: 1,
|
|
2627
|
-
what: '
|
|
2620
|
+
what: 'Invalid, native-replacing, or redundant role'
|
|
2628
2621
|
}
|
|
2629
2622
|
}
|
|
2630
2623
|
}
|
|
@@ -4696,13 +4689,6 @@ exports.issueClasses = {
|
|
|
4696
4689
|
what: 'Form control has no accessible name'
|
|
4697
4690
|
}
|
|
4698
4691
|
},
|
|
4699
|
-
testaro: {
|
|
4700
|
-
'labClash-unlabeled': {
|
|
4701
|
-
variable: false,
|
|
4702
|
-
quality: 1,
|
|
4703
|
-
what: 'Button, input, select, or textarea is unlabeled'
|
|
4704
|
-
}
|
|
4705
|
-
},
|
|
4706
4692
|
wave: {
|
|
4707
4693
|
'label_missing': {
|
|
4708
4694
|
variable: false,
|
|
@@ -4967,23 +4953,10 @@ exports.issueClasses = {
|
|
|
4967
4953
|
}
|
|
4968
4954
|
},
|
|
4969
4955
|
testaro: {
|
|
4970
|
-
'focInd
|
|
4956
|
+
'focInd': {
|
|
4971
4957
|
variable: false,
|
|
4972
4958
|
quality: 1,
|
|
4973
|
-
what: 'Focused element displays no focus indicator'
|
|
4974
|
-
}
|
|
4975
|
-
}
|
|
4976
|
-
}
|
|
4977
|
-
},
|
|
4978
|
-
focusIndicationBad: {
|
|
4979
|
-
wcag: '2.4.7',
|
|
4980
|
-
weight: 3,
|
|
4981
|
-
tools: {
|
|
4982
|
-
testaro: {
|
|
4983
|
-
'focInd-nonoutline': {
|
|
4984
|
-
variable: false,
|
|
4985
|
-
quality: 1,
|
|
4986
|
-
what: 'Focused element displays a nostandard focus indicator'
|
|
4959
|
+
what: 'Focused element displays a nonstandard or no focus indicator'
|
|
4987
4960
|
}
|
|
4988
4961
|
}
|
|
4989
4962
|
}
|
|
@@ -5438,15 +5411,10 @@ exports.issueClasses = {
|
|
|
5438
5411
|
weight: 3,
|
|
5439
5412
|
tools: {
|
|
5440
5413
|
testaro: {
|
|
5441
|
-
'focOp
|
|
5414
|
+
'focOp': {
|
|
5442
5415
|
variable: false,
|
|
5443
5416
|
quality: 1,
|
|
5444
|
-
what: 'Tab-focusable elements that are inoperable'
|
|
5445
|
-
},
|
|
5446
|
-
'focOp-operable-nonfocusable': {
|
|
5447
|
-
variable: false,
|
|
5448
|
-
quality: 1,
|
|
5449
|
-
what: 'Operable elements that cannot be Tab-focused'
|
|
5417
|
+
what: 'Tab-focusable elements that are inoperable or operable elements that are not focusable'
|
|
5450
5418
|
}
|
|
5451
5419
|
}
|
|
5452
5420
|
}
|
|
@@ -5645,12 +5613,12 @@ exports.issueClasses = {
|
|
|
5645
5613
|
}
|
|
5646
5614
|
}
|
|
5647
5615
|
},
|
|
5648
|
-
|
|
5616
|
+
hoverSurprise: {
|
|
5649
5617
|
wcag: '1.4.13',
|
|
5650
5618
|
weight: 3,
|
|
5651
5619
|
tools: {
|
|
5652
5620
|
testaro: {
|
|
5653
|
-
'hover
|
|
5621
|
+
'hover': {
|
|
5654
5622
|
variable: false,
|
|
5655
5623
|
quality: 1,
|
|
5656
5624
|
what: 'Hovering over element has unexpected effects'
|
|
@@ -5658,71 +5626,6 @@ exports.issueClasses = {
|
|
|
5658
5626
|
}
|
|
5659
5627
|
}
|
|
5660
5628
|
},
|
|
5661
|
-
unhoverable: {
|
|
5662
|
-
wcag: '1.4.13',
|
|
5663
|
-
weight: 3,
|
|
5664
|
-
tools: {
|
|
5665
|
-
testaro: {
|
|
5666
|
-
'hover-unhoverables': {
|
|
5667
|
-
variable: false,
|
|
5668
|
-
quality: 1,
|
|
5669
|
-
what: 'Operable element cannot be hovered over'
|
|
5670
|
-
}
|
|
5671
|
-
}
|
|
5672
|
-
}
|
|
5673
|
-
},
|
|
5674
|
-
hoverNoCursor: {
|
|
5675
|
-
wcag: '1.4.13',
|
|
5676
|
-
weight: 3,
|
|
5677
|
-
tools: {
|
|
5678
|
-
testaro: {
|
|
5679
|
-
'hover-noCursors': {
|
|
5680
|
-
variable: false,
|
|
5681
|
-
quality: 1,
|
|
5682
|
-
what: 'Hoverable element hides the mouse cursor'
|
|
5683
|
-
}
|
|
5684
|
-
}
|
|
5685
|
-
}
|
|
5686
|
-
},
|
|
5687
|
-
hoverBadCursor: {
|
|
5688
|
-
wcag: '1.4.13',
|
|
5689
|
-
weight: 2,
|
|
5690
|
-
tools: {
|
|
5691
|
-
testaro: {
|
|
5692
|
-
'hover-badCursors': {
|
|
5693
|
-
variable: false,
|
|
5694
|
-
quality: 1,
|
|
5695
|
-
what: 'Link or button makes the hovering mouse cursor nonstandard'
|
|
5696
|
-
}
|
|
5697
|
-
}
|
|
5698
|
-
}
|
|
5699
|
-
},
|
|
5700
|
-
hoverNoIndicator: {
|
|
5701
|
-
wcag: '1.4.13',
|
|
5702
|
-
weight: 3,
|
|
5703
|
-
tools: {
|
|
5704
|
-
testaro: {
|
|
5705
|
-
'hover-noCursors': {
|
|
5706
|
-
variable: false,
|
|
5707
|
-
quality: 1,
|
|
5708
|
-
what: 'Button shows no indication of being hovered over'
|
|
5709
|
-
}
|
|
5710
|
-
}
|
|
5711
|
-
}
|
|
5712
|
-
},
|
|
5713
|
-
hoverBadIndicator: {
|
|
5714
|
-
wcag: '1.4.13',
|
|
5715
|
-
weight: 2,
|
|
5716
|
-
tools: {
|
|
5717
|
-
testaro: {
|
|
5718
|
-
'hover-noCursors': {
|
|
5719
|
-
variable: false,
|
|
5720
|
-
quality: 1,
|
|
5721
|
-
what: 'List item changes when hovered over'
|
|
5722
|
-
}
|
|
5723
|
-
}
|
|
5724
|
-
}
|
|
5725
|
-
},
|
|
5726
5629
|
labelClash: {
|
|
5727
5630
|
wcag: '1.3.1',
|
|
5728
5631
|
weight: 2,
|
|
@@ -5735,7 +5638,7 @@ exports.issueClasses = {
|
|
|
5735
5638
|
}
|
|
5736
5639
|
},
|
|
5737
5640
|
testaro: {
|
|
5738
|
-
'labClash
|
|
5641
|
+
'labClash': {
|
|
5739
5642
|
variable: false,
|
|
5740
5643
|
quality: 1,
|
|
5741
5644
|
what: 'Incompatible label types'
|
package/procs/score/tsp27.js
CHANGED
|
@@ -63,18 +63,22 @@ exports.scorer = report => {
|
|
|
63
63
|
scoreProcID,
|
|
64
64
|
summary: {
|
|
65
65
|
total: 0,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
issue: 0,
|
|
67
|
+
tool: 0,
|
|
68
|
+
prevention: 0,
|
|
69
69
|
log: 0,
|
|
70
70
|
latency: 0
|
|
71
71
|
},
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
details: {
|
|
73
|
+
severity: {
|
|
74
|
+
total: [0, 0, 0, 0],
|
|
75
|
+
byTool: {}
|
|
76
|
+
},
|
|
77
|
+
prevention: {},
|
|
78
|
+
issue: {}
|
|
79
|
+
}
|
|
76
80
|
};
|
|
77
|
-
const {summary,
|
|
81
|
+
const {summary, details} = score;
|
|
78
82
|
// For each test act:
|
|
79
83
|
testActs.forEach(act => {
|
|
80
84
|
// If a successful standard result exists:
|
|
@@ -87,16 +91,11 @@ exports.scorer = report => {
|
|
|
87
91
|
) {
|
|
88
92
|
// Add the severity totals of the tool to the score.
|
|
89
93
|
const {totals} = standardResult;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
toolTotals[index] += totals[index];
|
|
93
|
-
});
|
|
94
|
-
// Update the issue totals for the tool.
|
|
95
|
-
const issueTotals = {};
|
|
96
|
-
let ruleID;
|
|
94
|
+
details.severity.byTool[which] = totals;
|
|
95
|
+
// Add the instance data of the tool to the score.
|
|
97
96
|
standardResult.instances.forEach(instance => {
|
|
98
|
-
ruleID =
|
|
99
|
-
if (! ruleID) {
|
|
97
|
+
let {ruleID} = instance;
|
|
98
|
+
if (! issueIndex[which][ruleID]) {
|
|
100
99
|
ruleID = issueMatcher.find(pattern => {
|
|
101
100
|
const patternRE = new RegExp(pattern);
|
|
102
101
|
return patternRE.test(instance.ruleID);
|
|
@@ -104,22 +103,46 @@ exports.scorer = report => {
|
|
|
104
103
|
}
|
|
105
104
|
if (ruleID) {
|
|
106
105
|
const issueID = issueIndex[which][ruleID];
|
|
107
|
-
if (!
|
|
108
|
-
|
|
106
|
+
if (! details.issue[issueID]) {
|
|
107
|
+
details.issue[issueID] = {
|
|
108
|
+
weight: issueClasses[issueID].weight,
|
|
109
|
+
tools: {}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (! details.issue[issueID].tools[which]) {
|
|
113
|
+
details.issue[issueID].tools[which] = {};
|
|
114
|
+
}
|
|
115
|
+
if (! details.issue[issueID].tools[which][ruleID]) {
|
|
116
|
+
details.issue[issueID].tools[which][ruleID] = {
|
|
117
|
+
quality: issueClasses[issueID].tools[which][ruleID].quality,
|
|
118
|
+
complaints: {
|
|
119
|
+
countTotal: 0,
|
|
120
|
+
texts: []
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
details
|
|
125
|
+
.issue[issueID]
|
|
126
|
+
.tools[which][ruleID]
|
|
127
|
+
.complaints
|
|
128
|
+
.countTotal += instance.count || 1;
|
|
129
|
+
if (
|
|
130
|
+
! details
|
|
131
|
+
.issue[issueID]
|
|
132
|
+
.tools[which][ruleID]
|
|
133
|
+
.complaints
|
|
134
|
+
.texts
|
|
135
|
+
.includes(instance.what)
|
|
136
|
+
) {
|
|
137
|
+
details.issue[issueID].tools[which][ruleID].complaints.texts.push(instance.what);
|
|
109
138
|
}
|
|
110
|
-
issueTotals[issueID] += instance.count || 1;
|
|
111
139
|
}
|
|
112
140
|
else {
|
|
113
141
|
console.log(`ERROR: ${instance.ruleID} of ${which} not found in issueClasses`);
|
|
114
142
|
}
|
|
115
143
|
});
|
|
116
|
-
// Update the issue totals in the score.
|
|
117
|
-
Object.keys(issueTotals).forEach(issueID => {
|
|
118
|
-
issues[issueID] = Math.max(issues[id] || 0, issueTotals[issueID]);
|
|
119
|
-
});
|
|
120
|
-
summary.issues = Object.values(issues).reduce((total, current) => total + current);
|
|
121
144
|
}
|
|
122
|
-
// Otherwise, i.e. if no
|
|
145
|
+
// Otherwise, i.e. if no successful standard result exists:
|
|
123
146
|
else {
|
|
124
147
|
// Add a prevented result to the act if not already there.
|
|
125
148
|
if (! act.result) {
|
|
@@ -129,15 +152,35 @@ exports.scorer = report => {
|
|
|
129
152
|
act.result.prevented = true;
|
|
130
153
|
};
|
|
131
154
|
// Add the tool and the prevention score to the score.
|
|
132
|
-
|
|
133
|
-
summary.preventions += preventionWeight;
|
|
155
|
+
details.prevention[which] = preventionWeight;
|
|
134
156
|
}
|
|
135
157
|
});
|
|
136
|
-
// Add the
|
|
137
|
-
|
|
138
|
-
(
|
|
158
|
+
// Add the severity detail totals to the score.
|
|
159
|
+
details.severity.total = Object.keys(details.severity.byTool).reduce((severityTotals, toolID) => {
|
|
160
|
+
details.severity.byTool[toolID].forEach((severityScore, index) => {
|
|
161
|
+
severityTotals[index] += severityScore;
|
|
162
|
+
});
|
|
163
|
+
return severityTotals;
|
|
164
|
+
}, details.severity.total);
|
|
165
|
+
// Add the summary issue total to the score.
|
|
166
|
+
Object.keys(details.issue).forEach(issueID => {
|
|
167
|
+
Object.keys(details.issue[issueID].tools).forEach(toolID => {
|
|
168
|
+
Object.keys(details.issue[issueID].tools[toolID]).forEach(ruleID => {
|
|
169
|
+
summary.issue += details.issue[issueID].weight
|
|
170
|
+
* details.issue[issueID].tools[toolID][ruleID].quality
|
|
171
|
+
* details.issue[issueID].tools[toolID][ruleID].complaints.countTotal;
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
// Add the summary tool total to the score.
|
|
176
|
+
summary.tool = toolWeight * details.severity.total.reduce(
|
|
177
|
+
(total, current, index) => total + severityWeights[index] * current, 0
|
|
178
|
+
);
|
|
179
|
+
// Add the summary prevention total to the score.
|
|
180
|
+
summary.prevention = Object.values(details.prevention).reduce(
|
|
181
|
+
(total, current) => total + current, 0
|
|
139
182
|
);
|
|
140
|
-
// Add the log score to the score.
|
|
183
|
+
// Add the summary log score to the score.
|
|
141
184
|
const {jobData} = report;
|
|
142
185
|
summary.log = Math.max(0, Math.round(
|
|
143
186
|
logWeights.logCount * jobData.logCount
|
|
@@ -147,21 +190,21 @@ exports.scorer = report => {
|
|
|
147
190
|
+ logWeights.prohibitedCount * jobData.prohibitedCount +
|
|
148
191
|
+ logWeights.visitRejectionCount * jobData.visitRejectionCount
|
|
149
192
|
));
|
|
150
|
-
// Add the latency score to the score.
|
|
193
|
+
// Add the summary latency score to the score.
|
|
151
194
|
summary.latency = Math.round(
|
|
152
195
|
latencyWeight * (Math.max(0, jobData.visitLatency - normalLatency))
|
|
153
196
|
);
|
|
154
|
-
// Round the scores.
|
|
197
|
+
// Round the unrounded scores.
|
|
155
198
|
Object.keys(summary).forEach(summaryTypeName => {
|
|
156
199
|
summary[summaryTypeName] = Math.round(summary[summaryTypeName]);
|
|
157
200
|
});
|
|
158
|
-
|
|
159
|
-
|
|
201
|
+
details.severity.total.forEach((severityTotal, index) => {
|
|
202
|
+
details.severity.total[index] = Math.round(severityTotal);
|
|
160
203
|
});
|
|
161
|
-
// Add the total score to the score.
|
|
162
|
-
summary.total = summary.
|
|
163
|
-
+ summary.
|
|
164
|
-
+ summary.
|
|
204
|
+
// Add the summary total score to the score.
|
|
205
|
+
summary.total = summary.issue
|
|
206
|
+
+ summary.tool
|
|
207
|
+
+ summary.prevention
|
|
165
208
|
+ summary.log
|
|
166
209
|
+ summary.latency;
|
|
167
210
|
// Add the score to the report.
|