@jrpool/kilotest 57.3.0 → 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 +18 -50
- package/web/listReports/listReports.test.ts +31 -22
- 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 +159 -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,6 +61,7 @@ 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;
|
|
57
67
|
violationCount: number;
|
|
@@ -140,16 +150,37 @@ export const getPathID = (catalog: Catalog, catalogIndex: string, pathID?: strin
|
|
|
140
150
|
}
|
|
141
151
|
return pathID ?? '/html';
|
|
142
152
|
};
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
+
}
|
|
151
176
|
}
|
|
152
|
-
|
|
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.
|
|
153
184
|
const data = {
|
|
154
185
|
description: report.target.what,
|
|
155
186
|
url: report.target.url,
|
|
@@ -159,6 +190,7 @@ export const getReportData = async (timeStamp: string, jobID: string): Promise<R
|
|
|
159
190
|
issueCount: 0,
|
|
160
191
|
engineNames: [] as string[],
|
|
161
192
|
engineCount: 0,
|
|
193
|
+
testedEngineCount: 0,
|
|
162
194
|
reporterNames: [] as string[],
|
|
163
195
|
reporterCount: 0,
|
|
164
196
|
violationCount: 0,
|
|
@@ -167,14 +199,9 @@ export const getReportData = async (timeStamp: string, jobID: string): Promise<R
|
|
|
167
199
|
preventedEngineCount: 0
|
|
168
200
|
};
|
|
169
201
|
const issueIDSet = new Set<string>();
|
|
170
|
-
const engineNameSet = new Set<string>();
|
|
171
202
|
const reporterIDSet = new Set<string>();
|
|
172
203
|
const violatorIndexSet = new Set<string>();
|
|
173
|
-
|
|
174
|
-
getTestActs(report).forEach(act => {
|
|
175
|
-
// Ensure that the rule engine is in the temporary data.
|
|
176
|
-
engineNameSet.add(ruleEngines[act.which!]![0]);
|
|
177
|
-
});
|
|
204
|
+
const {calledIDs, preventedIDs} = getEngineIDs(report);
|
|
178
205
|
// For each violating standard instance of each test act:
|
|
179
206
|
getTestActInstances(report, {violationsOnly: true}).forEach(({act, instance}) => {
|
|
180
207
|
const {catalogIndex, issueID} = instance;
|
|
@@ -196,9 +223,11 @@ export const getReportData = async (timeStamp: string, jobID: string): Promise<R
|
|
|
196
223
|
// Populate the data with the act data.
|
|
197
224
|
data.issueCount = issueIDSet.size;
|
|
198
225
|
data.engineNames = Array
|
|
199
|
-
.from(
|
|
226
|
+
.from(calledIDs)
|
|
227
|
+
.map(id => ruleEngines[id]?.[0] || id)
|
|
200
228
|
.sort((a, b) => a.localeCompare(b, 'en', {sensitivity: 'base'}));
|
|
201
|
-
data.engineCount =
|
|
229
|
+
data.engineCount = calledIDs.length;
|
|
230
|
+
data.testedEngineCount = calledIDs.length - preventedIDs.length;
|
|
202
231
|
data.reporterNames = Array
|
|
203
232
|
.from(reporterIDSet)
|
|
204
233
|
.map(id => ruleEngines[id]![0])
|
|
@@ -206,7 +235,7 @@ export const getReportData = async (timeStamp: string, jobID: string): Promise<R
|
|
|
206
235
|
data.reporterCount = data.reporterNames.length;
|
|
207
236
|
data.violatorCount = violatorIndexSet.size;
|
|
208
237
|
// Add the names of any prevented rule engines to the data.
|
|
209
|
-
data.preventedEngineNames =
|
|
238
|
+
data.preventedEngineNames = preventedIDs
|
|
210
239
|
.map(engineID => ruleEngines[engineID]?.[0] || engineID)
|
|
211
240
|
.sort((a, b) => a.localeCompare(b, 'en', {sensitivity: 'base'}));
|
|
212
241
|
data.preventedEngineCount = data.preventedEngineNames.length;
|
|
@@ -214,14 +243,7 @@ export const getReportData = async (timeStamp: string, jobID: string): Promise<R
|
|
|
214
243
|
return data;
|
|
215
244
|
}
|
|
216
245
|
// Returns page data from an available report.
|
|
217
|
-
export const getPageData =
|
|
218
|
-
// Get the report.
|
|
219
|
-
const report = await getReport(timeStamp, jobID);
|
|
220
|
-
// If this failed:
|
|
221
|
-
if (isReportError(report)) {
|
|
222
|
-
// Return why.
|
|
223
|
-
return report;
|
|
224
|
-
}
|
|
246
|
+
export const getPageData = (report: UsableReport): PageData => {
|
|
225
247
|
const {what: description, url} = report.target;
|
|
226
248
|
// Get the elapsed time in days since the report was completed, using the
|
|
227
249
|
// report content rather than the file system birth time.
|
|
@@ -234,31 +256,122 @@ export const getPageData = async (timeStamp: string, jobID: string): Promise<Pag
|
|
|
234
256
|
};
|
|
235
257
|
};
|
|
236
258
|
// Gets HTML strings for page data from a report.
|
|
237
|
-
export const getPageDataStrings =
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
): Promise<PageDataStrings | {error: string}> => {
|
|
242
|
-
// Get the page data if they were not specified.
|
|
243
|
-
const data = pageData ?? await getPageData(timeStamp, jobID);
|
|
244
|
-
// If the page data are invalid:
|
|
245
|
-
if (data.error !== undefined) {
|
|
246
|
-
// Return why.
|
|
247
|
-
return {
|
|
248
|
-
error: data.error
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
const {daysAgo, url, description} = data;
|
|
252
|
-
// Otherwise, i.e. if they are valid, get a description of the timestamp.
|
|
253
|
-
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));
|
|
254
263
|
// Return the HTML strings.
|
|
255
264
|
return {
|
|
256
265
|
description,
|
|
257
266
|
url,
|
|
258
267
|
urlLink: `<a href="${url}">${url}</a>`,
|
|
259
|
-
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>`
|
|
260
269
|
};
|
|
261
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
|
+
};
|
|
262
375
|
// Gets the descriptions of multi-report pages.
|
|
263
376
|
export const getMultiReportWhats = async (): Promise<string[]> => {
|
|
264
377
|
const reportExtracts = await getReportExtracts();
|