testilo 3.12.2 → 3.12.3

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.
@@ -0,0 +1,328 @@
1
+ /*
2
+ sp16a
3
+ Testilo score proc hover
4
+
5
+ Computes scores from Testaro script hover and adds them to a report.
6
+ Usage examples:
7
+ node score spHover 35k1r
8
+ node score spHover
9
+
10
+ This proc applies specified weights to the component scores before summing them. An issue reported
11
+ by a test is given a score. That score is determined by:
12
+ Whether the issue is reported as an error or a warning.
13
+ How important the issue is, if the test package is pre-weighted (axe, tenon, and testaro)
14
+ Whether the test belongs to a group or is a solo test.
15
+ How heavily the group is weighted, if the test package is not pre-weighted and the test belongs
16
+ to a group
17
+
18
+ The scores of solo tests are added together, multiplied by the soloWeight multiplier, and
19
+ contributed to the total score.
20
+
21
+ The scores of grouped tests are aggregated into a group score before being contributed to the
22
+ total score. The group score is the sum of (1) an absolute score, assigned because the group has
23
+ at least one test with a non-zero score, (2) the largest score among the tests of the group
24
+ multiplied by a multiplier, and (3) the sum of the scores from the other tests of the group
25
+ multiplied by a smaller multiplier. These three amounts are given by the groupWeights object.
26
+
27
+ Browser logging and navigation statistics produce a log score, and the prevention of tests
28
+ produces a prevention score. They, too, are added to the total score.
29
+
30
+ Each grouped test has a quality property, typically set to 1. The value of this property can be
31
+ modified when the test is found to be higher or lower in quality than usual.
32
+ */
33
+
34
+ // CONSTANTS
35
+
36
+ // ID of this proc.
37
+ const scoreProcID = 'spHover';
38
+ // Configuration disclosures.
39
+ const logWeights = {
40
+ logCount: 0.5,
41
+ logSize: 0.01,
42
+ errorLogCount: 1,
43
+ errorLogSize: 0.02,
44
+ prohibitedCount: 15,
45
+ visitTimeoutCount: 10,
46
+ visitRejectionCount: 10,
47
+ visitLatency: 1
48
+ };
49
+ // Normal latency (1 second per visit).
50
+ const normalLatency = 13;
51
+ // How much each solo issue adds to the score.
52
+ const soloWeight = 2;
53
+ // How much grouped issues add to the score.
54
+ const groupWeights = {
55
+ // Added per issue group.
56
+ absolute: 2,
57
+ // Added per issue reported by the package with the largest count in the group.
58
+ largest: 1,
59
+ // Added per issue in the group reported by each other package.
60
+ smaller: 0.4
61
+ };
62
+ // How much each prevention adds to the score.
63
+ const preventionWeights = {
64
+ testaro: 50,
65
+ other: 100
66
+ };
67
+ // Test groups.
68
+ const groups = {
69
+ hoverSurprise: {
70
+ weight: 2,
71
+ packages: {
72
+ testaro: {
73
+ hover: {
74
+ variable: false,
75
+ quality: 1,
76
+ what: 'Hovering is mis-indicated or changes content'
77
+ }
78
+ }
79
+ }
80
+ }
81
+ };
82
+
83
+ // VARIABLES
84
+
85
+ let packageDetails = {};
86
+ let groupDetails = {};
87
+ let summary = {};
88
+ let preventionScores = {};
89
+
90
+ // FUNCTIONS
91
+
92
+ // Initialize the variables.
93
+ const init = () => {
94
+ packageDetails = {};
95
+ groupDetails = {
96
+ groups: {},
97
+ solos: {}
98
+ };
99
+ summary = {
100
+ total: 0,
101
+ log: 0,
102
+ preventions: 0,
103
+ solos: 0,
104
+ groups: []
105
+ };
106
+ preventionScores = {};
107
+ };
108
+
109
+ // Adds a score to the package details.
110
+ const addDetail = (actWhich, testID, addition = 1) => {
111
+ if (addition) {
112
+ if (!packageDetails[actWhich]) {
113
+ packageDetails[actWhich] = {};
114
+ }
115
+ if (!packageDetails[actWhich][testID]) {
116
+ packageDetails[actWhich][testID] = 0;
117
+ }
118
+ packageDetails[actWhich][testID] += Math.round(addition);
119
+ }
120
+ };
121
+ // Scores a report.
122
+ exports.scorer = async report => {
123
+ // Initialize the variables.
124
+ init();
125
+ // If there are any acts in the report:
126
+ const {acts} = report;
127
+ if (Array.isArray(acts)) {
128
+ // If any of them are test acts:
129
+ const testActs = acts.filter(act => act.type === 'test');
130
+ if (testActs.length) {
131
+ // For each test act:
132
+ testActs.forEach(test => {
133
+ const {which} = test;
134
+ // Add scores to the package details.
135
+ if (which === 'hover') {
136
+ const issues = test.result && test.result.totals;
137
+ if (issues) {
138
+ const {
139
+ impactTriggers,
140
+ additions,
141
+ removals,
142
+ opacityChanges,
143
+ opacityImpact,
144
+ unhoverables,
145
+ noCursors,
146
+ badCursors,
147
+ noIndicators,
148
+ badIndicators
149
+ } = issues;
150
+ // Add score with weights on hover-impact types.
151
+ const score = 2 * impactTriggers
152
+ + 0.3 * additions
153
+ + removals
154
+ + 0.2 * opacityChanges
155
+ + 0.1 * opacityImpact
156
+ + unhoverables
157
+ + 3 * noCursors
158
+ + 2 * badCursors
159
+ + noIndicators
160
+ + badIndicators;
161
+ if (score) {
162
+ addDetail('testaro', which, score);
163
+ }
164
+ }
165
+ }
166
+ });
167
+ // Get the prevention scores and add them to the summary.
168
+ const actsPrevented = testActs.filter(test => test.result.prevented);
169
+ actsPrevented.forEach(act => {
170
+ if (otherPackages.includes(act.which)) {
171
+ preventionScores[act.which] = preventionWeights.other;
172
+ }
173
+ else {
174
+ preventionScores[`testaro-${act.which}`] = preventionWeights.testaro;
175
+ }
176
+ });
177
+ const preventionScore = Object.values(preventionScores).reduce(
178
+ (sum, current) => sum + current,
179
+ 0
180
+ );
181
+ const roundedPreventionScore = Math.round(preventionScore);
182
+ summary.preventions = roundedPreventionScore;
183
+ summary.total += roundedPreventionScore;
184
+ // Initialize a table of the groups to which tests belong.
185
+ const testGroups = {
186
+ testaro: {}
187
+ };
188
+ // Initialize a table of the regular expressions of variably named tests of packages.
189
+ const testMatchers = {};
190
+ Object.keys(groups).forEach(groupName => {
191
+ Object.keys(groups[groupName].packages).forEach(packageName => {
192
+ Object.keys(groups[groupName].packages[packageName]).forEach(testID => {
193
+ // Update the group table.
194
+ testGroups[packageName][testID] = groupName;
195
+ // If the test is variably named:
196
+ if (groups[groupName].packages[packageName][testID].variable) {
197
+ // Add its regular expression, as multiline, to the variably-named-test table.
198
+ if (! testMatchers[packageName]) {
199
+ testMatchers[packageName] = [];
200
+ }
201
+ testMatchers[packageName].push(new RegExp(testID, 's'));
202
+ }
203
+ });
204
+ });
205
+ });
206
+ // For each package with any scores:
207
+ Object.keys(packageDetails).forEach(packageName => {
208
+ const matchers = testMatchers[packageName];
209
+ // For each test with any scores in the package:
210
+ Object.keys(packageDetails[packageName]).forEach(testMessage => {
211
+ // Initialize the test ID as the reported test message.
212
+ let testID = testMessage;
213
+ // Get the group of the test, if it has a fixed name and is in a group.
214
+ let groupName = testGroups[packageName][testMessage];
215
+ // If the test has a variable name or is a solo test:
216
+ if (! groupName) {
217
+ // Determine whether the package has variably named tests and the test is among them.
218
+ testRegExp = matchers && matchers.find(matcher => matcher.test(testMessage));
219
+ // If so:
220
+ if (testRegExp) {
221
+ // Make the matching regular expression the test ID.
222
+ testID = testRegExp.source;
223
+ // Get the group of the test.
224
+ groupName = testGroups[packageName][testID];
225
+ }
226
+ }
227
+ // If the test is in a group:
228
+ if (groupName) {
229
+ // Initialize its score as its score in the package details.
230
+ if (! groupDetails.groups[groupName]) {
231
+ groupDetails.groups[groupName] = {};
232
+ }
233
+ if (! groupDetails.groups[groupName][packageName]) {
234
+ groupDetails.groups[groupName][packageName] = {};
235
+ }
236
+ let weightedScore = packageDetails[packageName][testMessage];
237
+ // Weight that by the group weight and normalize it to a 1–4 scale per instance.
238
+ weightedScore *= groups[groupName].weight / 4;
239
+ // Adjust the score for the quality of the test.
240
+ weightedScore *= groups[groupName].packages[packageName][testID].quality;
241
+ // Round the score, but not to less than 1.
242
+ const roundedScore = Math.max(Math.round(weightedScore), 1);
243
+ // Add the rounded score and the test description to the group details.
244
+ groupDetails.groups[groupName][packageName][testID] = {
245
+ score: roundedScore,
246
+ what: groups[groupName].packages[packageName][testID].what
247
+ };
248
+ }
249
+ // Otherwise, i.e. if the test is solo:
250
+ else {
251
+ if (! groupDetails.solos[packageName]) {
252
+ groupDetails.solos[packageName] = {};
253
+ }
254
+ const roundedScore = Math.round(packageDetails[packageName][testID]);
255
+ groupDetails.solos[packageName][testID] = roundedScore;
256
+ }
257
+ });
258
+ });
259
+ // Determine the group scores and add them to the summary.
260
+ const groupNames = Object.keys(groupDetails.groups);
261
+ const {absolute, largest, smaller} = groupWeights;
262
+ // For each group with any scores:
263
+ groupNames.forEach(groupName => {
264
+ const scores = [];
265
+ // For each package with any scores in the group:
266
+ const groupPackageData = Object.values(groupDetails.groups[groupName]);
267
+ groupPackageData.forEach(packageObj => {
268
+ // Get the sum of the scores of the tests of the package in the group.
269
+ const scoreSum = Object.values(packageObj).reduce(
270
+ (sum, current) => sum + current.score,
271
+ 0
272
+ );
273
+ // Add the sum to the list of package scores in the group.
274
+ scores.push(scoreSum);
275
+ });
276
+ // Sort the scores in descending order.
277
+ scores.sort((a, b) => b - a);
278
+ // Compute the sum of the absolute score and the weighted largest and other scores.
279
+ const groupScore = absolute
280
+ + largest * scores[0]
281
+ + smaller * scores.slice(1).reduce((sum, current) => sum + current, 0);
282
+ const roundedGroupScore = Math.round(groupScore);
283
+ summary.groups.push({
284
+ groupName,
285
+ score: roundedGroupScore
286
+ });
287
+ summary.total += roundedGroupScore;
288
+ });
289
+ summary.groups.sort((a, b) => b.score - a.score);
290
+ // Determine the solo score and add it to the summary.
291
+ const soloPackageNames = Object.keys(groupDetails.solos);
292
+ soloPackageNames.forEach(packageName => {
293
+ const testIDs = Object.keys(groupDetails.solos[packageName]);
294
+ testIDs.forEach(testID => {
295
+ const score = soloWeight * groupDetails.solos[packageName][testID];
296
+ summary.solos += score;
297
+ summary.total += score;
298
+ });
299
+ });
300
+ summary.solos = Math.round(summary.solos);
301
+ summary.total = Math.round(summary.total);
302
+ }
303
+ }
304
+ // Get the log score.
305
+ const logScore = logWeights.logCount * report.logCount
306
+ + logWeights.logSize * report.logSize +
307
+ + logWeights.errorLogCount * report.errorLogCount
308
+ + logWeights.errorLogSize * report.errorLogSize
309
+ + logWeights.prohibitedCount * report.prohibitedCount +
310
+ + logWeights.visitTimeoutCount * report.visitTimeoutCount +
311
+ + logWeights.visitRejectionCount * report.visitRejectionCount
312
+ + logWeights.visitLatency * (report.visitLatency - normalLatency);
313
+ const roundedLogScore = Math.round(logScore);
314
+ summary.log = roundedLogScore;
315
+ summary.total += roundedLogScore;
316
+ // Add the score facts to the report.
317
+ report.score = {
318
+ scoreProcID,
319
+ logWeights,
320
+ soloWeight,
321
+ groupWeights,
322
+ preventionWeights,
323
+ packageDetails,
324
+ groupDetails,
325
+ preventionScores,
326
+ summary
327
+ };
328
+ };