@jrpool/kilotest 57.2.3 → 58.2.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/api/orderNewTest.test.ts +1 -1
- package/api/orderNewTest.ts +1 -1
- package/api/requestNewTest.test.ts +1 -1
- package/api/requestNewTest.ts +1 -1
- package/index.test.ts +1 -1
- package/index.ts +11 -20
- package/package.json +1 -1
- package/test/fixtures/buildFixtures.ts +10 -1
- package/test/fixtures/db/reports/260101T0006-prv.json +10 -0
- package/util.test.ts +46 -0
- package/util.ts +16 -18
- package/web/listDiagnoses/index.html +6 -19
- package/web/listDiagnoses/index.ts +21 -43
- package/web/listDiagnoses/listDiagnoses.test.ts +14 -2
- package/web/listIssues/index.html +12 -12
- package/web/listIssues/index.ts +112 -168
- package/web/listIssues/listIssues.test.ts +33 -10
- package/web/listReports/index.ts +21 -50
- package/web/listReports/listReports.test.ts +82 -0
- package/web/listViolators/index.html +4 -13
- package/web/listViolators/index.ts +30 -64
- package/web/listViolators/listViolators.test.ts +16 -4
- package/web/reportDeletion.ts +8 -4
- package/web/requestNewTest/index.ts +2 -2
- package/web/requestNewTest/requestNewTest.test.ts +1 -1
- package/web/tutorialAI/index.html +6 -6
- package/web/util.test.ts +123 -27
- package/web/util.ts +163 -46
package/web/util.ts
CHANGED
|
@@ -13,15 +13,15 @@ import {
|
|
|
13
13
|
getAgoDays,
|
|
14
14
|
getDateString,
|
|
15
15
|
getDateTime,
|
|
16
|
-
getReport,
|
|
17
16
|
getReportExtracts,
|
|
18
17
|
getTestActInstances,
|
|
19
18
|
getTestActs,
|
|
20
|
-
|
|
19
|
+
htmlSafe,
|
|
21
20
|
ruleEngines
|
|
22
21
|
} from '../util.ts';
|
|
23
22
|
import {issues as issueSpecs} from 'testaro-issues';
|
|
24
23
|
import type {Catalog} from 'testaro';
|
|
24
|
+
import type {UsableReport} from '../util.ts';
|
|
25
25
|
/* c8 ignore stop */
|
|
26
26
|
import wcagMap from '../wcagMap.json' with {type: 'json'};
|
|
27
27
|
|
|
@@ -42,6 +42,15 @@ export type PageDataStrings = {
|
|
|
42
42
|
testInfo: string;
|
|
43
43
|
error?: never;
|
|
44
44
|
};
|
|
45
|
+
// Counts summarizing the results of an available report.
|
|
46
|
+
export type ResultsSummary = {
|
|
47
|
+
engineCount: number;
|
|
48
|
+
testedEngineCount: number;
|
|
49
|
+
reporterCount: number;
|
|
50
|
+
violationCount: number;
|
|
51
|
+
violatorCount: number;
|
|
52
|
+
issueCount: number;
|
|
53
|
+
};
|
|
45
54
|
// Basics about an available report.
|
|
46
55
|
export type ReportData = {
|
|
47
56
|
description: string;
|
|
@@ -52,8 +61,10 @@ export type ReportData = {
|
|
|
52
61
|
issueCount: number;
|
|
53
62
|
engineNames: string[];
|
|
54
63
|
engineCount: number;
|
|
64
|
+
testedEngineCount: number;
|
|
55
65
|
reporterNames: string[];
|
|
56
66
|
reporterCount: number;
|
|
67
|
+
violationCount: number;
|
|
57
68
|
violatorCount: number;
|
|
58
69
|
preventedEngineNames: string[];
|
|
59
70
|
preventedEngineCount: number;
|
|
@@ -139,16 +150,37 @@ export const getPathID = (catalog: Catalog, catalogIndex: string, pathID?: strin
|
|
|
139
150
|
}
|
|
140
151
|
return pathID ?? '/html';
|
|
141
152
|
};
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
153
|
+
// Identifies the rule engines called and the rule engines prevented from testing, by a report.
|
|
154
|
+
// Prevented engines have test acts. The nuVal and nuVnu engines are 2 implementations of one
|
|
155
|
+
// engine, so they count as one. A nuVal prevention is ignored if nuVnu then ran successfully.
|
|
156
|
+
export const getEngineIDs = (report: any) => {
|
|
157
|
+
const preventions = report.jobData?.preventions ?? {};
|
|
158
|
+
const calledIDs = new Set<string>([
|
|
159
|
+
...getTestActs(report).map((act: any) => act.which as string),
|
|
160
|
+
...Object.keys(preventions)
|
|
161
|
+
]);
|
|
162
|
+
const preventedIDs = new Set<string>(Object.keys(preventions));
|
|
163
|
+
// If both implementations were called:
|
|
164
|
+
if (calledIDs.has('nuVal') && calledIDs.has('nuVnu')) {
|
|
165
|
+
// Count them as one engine.
|
|
166
|
+
calledIDs.delete('nuVnu');
|
|
167
|
+
// If nuVnu was not prevented from testing:
|
|
168
|
+
if (!preventedIDs.has('nuVnu')) {
|
|
169
|
+
// Ignore any nuVal prevention.
|
|
170
|
+
preventedIDs.delete('nuVal');
|
|
171
|
+
}
|
|
172
|
+
// Otherwise, i.e. if nuVnu was prevented, count both preventions as one.
|
|
173
|
+
else {
|
|
174
|
+
preventedIDs.delete('nuVnu');
|
|
175
|
+
}
|
|
150
176
|
}
|
|
151
|
-
|
|
177
|
+
return {calledIDs: Array.from(calledIDs), preventedIDs: Array.from(preventedIDs)};
|
|
178
|
+
};
|
|
179
|
+
// Returns basics about an available report.
|
|
180
|
+
export const getReportData = (report: UsableReport): ReportData => {
|
|
181
|
+
// Identify the report by the time stamp at the start of its job name.
|
|
182
|
+
const timeStamp = report.id.slice(0, 11);
|
|
183
|
+
// Initialize the data.
|
|
152
184
|
const data = {
|
|
153
185
|
description: report.target.what,
|
|
154
186
|
url: report.target.url,
|
|
@@ -158,21 +190,18 @@ export const getReportData = async (timeStamp: string, jobID: string): Promise<R
|
|
|
158
190
|
issueCount: 0,
|
|
159
191
|
engineNames: [] as string[],
|
|
160
192
|
engineCount: 0,
|
|
193
|
+
testedEngineCount: 0,
|
|
161
194
|
reporterNames: [] as string[],
|
|
162
195
|
reporterCount: 0,
|
|
196
|
+
violationCount: 0,
|
|
163
197
|
violatorCount: 0,
|
|
164
198
|
preventedEngineNames: [] as string[],
|
|
165
199
|
preventedEngineCount: 0
|
|
166
200
|
};
|
|
167
201
|
const issueIDSet = new Set<string>();
|
|
168
|
-
const engineNameSet = new Set<string>();
|
|
169
202
|
const reporterIDSet = new Set<string>();
|
|
170
203
|
const violatorIndexSet = new Set<string>();
|
|
171
|
-
|
|
172
|
-
getTestActs(report).forEach(act => {
|
|
173
|
-
// Ensure that the rule engine is in the temporary data.
|
|
174
|
-
engineNameSet.add(ruleEngines[act.which!]![0]);
|
|
175
|
-
});
|
|
204
|
+
const {calledIDs, preventedIDs} = getEngineIDs(report);
|
|
176
205
|
// For each violating standard instance of each test act:
|
|
177
206
|
getTestActInstances(report, {violationsOnly: true}).forEach(({act, instance}) => {
|
|
178
207
|
const {catalogIndex, issueID} = instance;
|
|
@@ -182,6 +211,8 @@ export const getReportData = async (timeStamp: string, jobID: string): Promise<R
|
|
|
182
211
|
reporterIDSet.add(act.which!);
|
|
183
212
|
// Ensure that the issue is in the temporary data.
|
|
184
213
|
issueIDSet.add(issueID);
|
|
214
|
+
// Increment the violation count.
|
|
215
|
+
data.violationCount++;
|
|
185
216
|
// If the violator has a catalog index:
|
|
186
217
|
if (catalogIndex) {
|
|
187
218
|
// Ensure that the violator is in the temporary data.
|
|
@@ -192,9 +223,11 @@ export const getReportData = async (timeStamp: string, jobID: string): Promise<R
|
|
|
192
223
|
// Populate the data with the act data.
|
|
193
224
|
data.issueCount = issueIDSet.size;
|
|
194
225
|
data.engineNames = Array
|
|
195
|
-
.from(
|
|
226
|
+
.from(calledIDs)
|
|
227
|
+
.map(id => ruleEngines[id]?.[0] || id)
|
|
196
228
|
.sort((a, b) => a.localeCompare(b, 'en', {sensitivity: 'base'}));
|
|
197
|
-
data.engineCount =
|
|
229
|
+
data.engineCount = calledIDs.length;
|
|
230
|
+
data.testedEngineCount = calledIDs.length - preventedIDs.length;
|
|
198
231
|
data.reporterNames = Array
|
|
199
232
|
.from(reporterIDSet)
|
|
200
233
|
.map(id => ruleEngines[id]![0])
|
|
@@ -202,7 +235,7 @@ export const getReportData = async (timeStamp: string, jobID: string): Promise<R
|
|
|
202
235
|
data.reporterCount = data.reporterNames.length;
|
|
203
236
|
data.violatorCount = violatorIndexSet.size;
|
|
204
237
|
// Add the names of any prevented rule engines to the data.
|
|
205
|
-
data.preventedEngineNames =
|
|
238
|
+
data.preventedEngineNames = preventedIDs
|
|
206
239
|
.map(engineID => ruleEngines[engineID]?.[0] || engineID)
|
|
207
240
|
.sort((a, b) => a.localeCompare(b, 'en', {sensitivity: 'base'}));
|
|
208
241
|
data.preventedEngineCount = data.preventedEngineNames.length;
|
|
@@ -210,14 +243,7 @@ export const getReportData = async (timeStamp: string, jobID: string): Promise<R
|
|
|
210
243
|
return data;
|
|
211
244
|
}
|
|
212
245
|
// Returns page data from an available report.
|
|
213
|
-
export const getPageData =
|
|
214
|
-
// Get the report.
|
|
215
|
-
const report = await getReport(timeStamp, jobID);
|
|
216
|
-
// If this failed:
|
|
217
|
-
if (isReportError(report)) {
|
|
218
|
-
// Return why.
|
|
219
|
-
return report;
|
|
220
|
-
}
|
|
246
|
+
export const getPageData = (report: UsableReport): PageData => {
|
|
221
247
|
const {what: description, url} = report.target;
|
|
222
248
|
// Get the elapsed time in days since the report was completed, using the
|
|
223
249
|
// report content rather than the file system birth time.
|
|
@@ -230,31 +256,122 @@ export const getPageData = async (timeStamp: string, jobID: string): Promise<Pag
|
|
|
230
256
|
};
|
|
231
257
|
};
|
|
232
258
|
// Gets HTML strings for page data from a report.
|
|
233
|
-
export const getPageDataStrings =
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
): Promise<PageDataStrings | {error: string}> => {
|
|
238
|
-
// Get the page data if they were not specified.
|
|
239
|
-
const data = pageData ?? await getPageData(timeStamp, jobID);
|
|
240
|
-
// If the page data are invalid:
|
|
241
|
-
if (data.error !== undefined) {
|
|
242
|
-
// Return why.
|
|
243
|
-
return {
|
|
244
|
-
error: data.error
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
const {daysAgo, url, description} = data;
|
|
248
|
-
// Otherwise, i.e. if they are valid, get a description of the timestamp.
|
|
249
|
-
const when = getDateTimeString(timeStamp);
|
|
259
|
+
export const getPageDataStrings = (report: UsableReport): PageDataStrings => {
|
|
260
|
+
const {daysAgo, url, description} = getPageData(report);
|
|
261
|
+
// Get a description of the time stamp at the start of the job name.
|
|
262
|
+
const when = getDateTimeString(report.id.slice(0, 11));
|
|
250
263
|
// Return the HTML strings.
|
|
251
264
|
return {
|
|
252
265
|
description,
|
|
253
266
|
url,
|
|
254
267
|
urlLink: `<a href="${url}">${url}</a>`,
|
|
255
|
-
testInfo: `Tested ${daysAgo === 1 ? '1 day' : `${daysAgo} days`} ago by job <code>${
|
|
268
|
+
testInfo: `Tested ${daysAgo === 1 ? '1 day' : `${daysAgo} days`} ago on ${when} by job <code>${report.id}</code>`
|
|
256
269
|
};
|
|
257
270
|
};
|
|
271
|
+
// Returns the lines of a list of facts about the page of an available report.
|
|
272
|
+
export const getPageFactsLines = (
|
|
273
|
+
strings: PageDataStrings,
|
|
274
|
+
summary: ResultsSummary,
|
|
275
|
+
margin: string
|
|
276
|
+
): string[] => [
|
|
277
|
+
`${margin}<ul>`,
|
|
278
|
+
`${margin} <li>URL: ${strings.urlLink}</li>`,
|
|
279
|
+
`${margin} <li>${strings.testInfo}</li>`,
|
|
280
|
+
`${margin} <li>Summary of results:`,
|
|
281
|
+
`${margin} <ul>`,
|
|
282
|
+
`${margin} <li>Rule engines:`,
|
|
283
|
+
`${margin} <ul>`,
|
|
284
|
+
`${margin} <li>Called: ${summary.engineCount}</li>`,
|
|
285
|
+
`${margin} <li>Were able to test: ${summary.testedEngineCount}</li>`,
|
|
286
|
+
`${margin} <li>Reported any rule violations: ${summary.reporterCount}</li>`,
|
|
287
|
+
`${margin} </ul>`,
|
|
288
|
+
`${margin} </li>`,
|
|
289
|
+
`${margin} <li>Violations: ${summary.violationCount}</li>`,
|
|
290
|
+
`${margin} <li>Violators: ${summary.violatorCount}</li>`,
|
|
291
|
+
`${margin} <li>Issues: ${summary.issueCount}</li>`,
|
|
292
|
+
`${margin} </ul>`,
|
|
293
|
+
`${margin} </li>`,
|
|
294
|
+
`${margin}</ul>`
|
|
295
|
+
];
|
|
296
|
+
// Returns the lines of a list of facts about an issue in an available report.
|
|
297
|
+
export const getIssueFactsLines = (
|
|
298
|
+
report: UsableReport,
|
|
299
|
+
issueID: string,
|
|
300
|
+
margin: string,
|
|
301
|
+
listClass?: string
|
|
302
|
+
): string[] => {
|
|
303
|
+
// Get the issue classification.
|
|
304
|
+
const {wcag, weight, why} = issueSpecs[issueID]!;
|
|
305
|
+
// Initialize the issue counts.
|
|
306
|
+
const reporters = new Set<string>();
|
|
307
|
+
const violatorIndexes = new Set<string>();
|
|
308
|
+
let violationCount = 0;
|
|
309
|
+
// For each violating standard instance of the issue:
|
|
310
|
+
getTestActInstances(report, {violationsOnly: true, issueID}).forEach(({act, instance}) => {
|
|
311
|
+
// Increment the counts.
|
|
312
|
+
violationCount++;
|
|
313
|
+
reporters.add(act.which!);
|
|
314
|
+
violatorIndexes.add(String(instance.catalogIndex || '0'));
|
|
315
|
+
});
|
|
316
|
+
const reporterString = getCountString(reporters.size, 'rule engine', 'rule engines');
|
|
317
|
+
// Return the lines.
|
|
318
|
+
return [
|
|
319
|
+
`${margin}<ul${listClass ? ` class="${listClass}"` : ''}>`,
|
|
320
|
+
`${margin} <li>Why it matters: ${why}</li>`,
|
|
321
|
+
`${margin} <li>Priority: ${getWeightName(weight)}</li>`,
|
|
322
|
+
`${margin} <li>Related WCAG standard: <a href="${getWCAGLink(wcag)}">${wcag}</a></li>`,
|
|
323
|
+
`${margin} <li>Reported by ${reporterString} (${getEngineNamesString(reporters)})</li>`,
|
|
324
|
+
`${margin} <li>Violations: ${violationCount}</li>`,
|
|
325
|
+
`${margin} <li>Violators: ${violatorIndexes.size}</li>`,
|
|
326
|
+
`${margin}</ul>`
|
|
327
|
+
];
|
|
328
|
+
};
|
|
329
|
+
// Returns the lines of a list of facts about a violator of an issue in an available report.
|
|
330
|
+
export const getViolatorFactsLines = (
|
|
331
|
+
report: UsableReport,
|
|
332
|
+
issueID: string,
|
|
333
|
+
catalogIndex: string,
|
|
334
|
+
pathID: string | null,
|
|
335
|
+
margin: string,
|
|
336
|
+
listClass?: string
|
|
337
|
+
): string[] => {
|
|
338
|
+
const {catalog} = report;
|
|
339
|
+
const catalogItem = catalog[catalogIndex];
|
|
340
|
+
const boxID = catalogItem?.boxID;
|
|
341
|
+
const startTag = catalogItem?.startTag;
|
|
342
|
+
const tagName = catalogItem?.tagName;
|
|
343
|
+
const text = catalogItem?.text;
|
|
344
|
+
// Identify the rule engines that reported the violator for the issue.
|
|
345
|
+
const reporters = new Set<string>();
|
|
346
|
+
getTestActInstances(report, {violationsOnly: true, issueID, catalogIndex}).forEach(({act}) => {
|
|
347
|
+
reporters.add(act.which!);
|
|
348
|
+
});
|
|
349
|
+
const lines: string[] = [
|
|
350
|
+
`${margin}<ul${listClass ? ` class="${listClass}"` : ''}>`,
|
|
351
|
+
`${margin} <li>Tag name: <code>${tagName || 'HTML'}</code></li>`
|
|
352
|
+
];
|
|
353
|
+
// Text is not applicable for container elements.
|
|
354
|
+
if (text && !['HTML', 'BODY', 'HEAD', 'SCRIPT', 'STYLE', 'NOSCRIPT'].includes(tagName as string)) {
|
|
355
|
+
const textString = text.split('\n').join(' … ');
|
|
356
|
+
lines.push(`${margin} <li>Text: <q>${htmlSafe(textString)}</q></li>`);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
lines.push(`${margin} <li>Text: [not applicable]</li>`);
|
|
360
|
+
}
|
|
361
|
+
lines.push(`${margin} <li>Start tag: <code>${htmlSafe(startTag ?? '') || '[not obtained]'}</code></li>`);
|
|
362
|
+
lines.push(`${margin} <li>XPath: <code>${makeBreakable(pathID || '[not obtained]')}</code></li>`);
|
|
363
|
+
// If the catalog item has a bounding box, add its dimensions.
|
|
364
|
+
if (boxID) {
|
|
365
|
+
const dims = boxID.split(':');
|
|
366
|
+
lines.push(`${margin} <li>Bounding box: x = ${dims[0]}, y = ${dims[1]}, width = ${dims[2]}, height = ${dims[3]}</li>`);
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
lines.push(`${margin} <li>Bounding box: [not obtained]</li>`);
|
|
370
|
+
}
|
|
371
|
+
lines.push(`${margin} <li>Reported by ${getEngineNamesString(reporters)}</li>`);
|
|
372
|
+
lines.push(`${margin}</ul>`);
|
|
373
|
+
return lines;
|
|
374
|
+
};
|
|
258
375
|
// Gets the descriptions of multi-report pages.
|
|
259
376
|
export const getMultiReportWhats = async (): Promise<string[]> => {
|
|
260
377
|
const reportExtracts = await getReportExtracts();
|