@jrpool/kilotest 57.2.0 → 57.2.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jrpool/kilotest",
3
- "version": "57.2.0",
3
+ "version": "57.2.3",
4
4
  "description": "Ensemble testing for front-end web quality",
5
5
  "type": "module",
6
6
  "main": "index.ts",
package/util.test.ts CHANGED
@@ -23,36 +23,26 @@ import {
23
23
  annotateReportObject,
24
24
  awaitJob,
25
25
  checkCommentDuplicate,
26
- checkCommentLength,
27
26
  checkLength,
28
27
  createLock,
29
28
  dbPath,
30
29
  describeMax,
31
30
  getAgoDays,
32
- getAgoString,
33
- getCountString,
34
31
  getDateString,
35
32
  getDateTime,
36
- getEngineNamesString,
37
33
  getEnvMax,
38
34
  getIssue,
39
35
  errorMessage,
40
36
  getJSON,
41
- getMultiReportWhats,
42
37
  getNowStamp,
43
38
  getObject,
44
- getPageData,
45
- getPageDataStrings,
46
39
  getPlainText,
47
40
  getRandomString,
48
41
  getReportExtract,
49
42
  getReportExtracts,
50
43
  getReportPath,
51
44
  getReportStats,
52
- getTextFragmentHref,
53
45
  getTimeStamp,
54
- getWCAGLink,
55
- getWeightName,
56
46
  hiddenReportsPath,
57
47
  htmlSafe,
58
48
  isAllowedRedirectTarget,
@@ -64,7 +54,6 @@ import {
64
54
  isURL,
65
55
  isValidAuthCode,
66
56
  jobsPath,
67
- makeBreakable,
68
57
  minifyURL,
69
58
  objectSort,
70
59
  orderJob,
@@ -76,32 +65,6 @@ import {
76
65
 
77
66
  // TESTS
78
67
 
79
- test('checkCommentLength accepts a comment of exactly 20 characters', () => {
80
- const result = checkCommentLength('a'.repeat(20));
81
- assert.deepEqual(result, {status: 'ok'});
82
- });
83
-
84
- test('checkCommentLength accepts a comment of exactly 1000 characters', () => {
85
- const result = checkCommentLength('a'.repeat(1000));
86
- assert.deepEqual(result, {status: 'ok'});
87
- });
88
-
89
- test('checkCommentLength rejects a comment shorter than 20 characters', () => {
90
- const result = checkCommentLength('a'.repeat(19));
91
- assert.deepEqual(result, {
92
- status: 'error',
93
- message: 'Your comment was shorter than 20 characters'
94
- });
95
- });
96
-
97
- test('checkCommentLength rejects a comment longer than 1000 characters', () => {
98
- const result = checkCommentLength('a'.repeat(1001));
99
- assert.deepEqual(result, {
100
- status: 'error',
101
- message: 'Your comment was longer than 1000 characters'
102
- });
103
- });
104
-
105
68
  test('checkLength accepts text at the minimum bound', () => {
106
69
  const result = checkLength('a'.repeat(5), 5, 10, 'reason');
107
70
  assert.deepEqual(result, {status: 'ok'});
@@ -233,28 +196,6 @@ test('getAgoDays returns 0 for the current time', () => {
233
196
  assert.equal(getAgoDays(new Date()), 0);
234
197
  });
235
198
 
236
- test('getAgoString returns "1 day" for a 1-day-old timestamp', () => {
237
- const date = new Date(Date.now() - 1 * 24 * 60 * 60 * 1000);
238
- assert.equal(
239
- getAgoString(date.toISOString().slice(2).replace(/[-:]/g, '').slice(0, 11)), '1 day'
240
- );
241
- });
242
-
243
- test('getAgoString returns "3 days" for a 3-day-old timestamp', () => {
244
- const date = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000);
245
- assert.equal(
246
- getAgoString(date.toISOString().slice(2).replace(/[-:]/g, '').slice(0, 11)), '3 days'
247
- );
248
- });
249
-
250
- test('getCountString returns singular for count 1', () => {
251
- assert.equal(getCountString(1, 'report', 'reports'), '1 report');
252
- });
253
-
254
- test('getCountString returns plural for count 0', () => {
255
- assert.equal(getCountString(0, 'report', 'reports'), '0 reports');
256
- });
257
-
258
199
  test('getDateString returns a valid date string for a valid time stamp', () => {
259
200
  assert.equal(getDateString('260101T0000'), '2026-01-01');
260
201
  });
@@ -359,24 +300,6 @@ test('getRandomString returns a string of the requested length', () => {
359
300
  assert.equal(s.length, 10);
360
301
  });
361
302
 
362
- test('getTextFragmentHref returns a text-fragment URL', () => {
363
- const href = getTextFragmentHref('Hello', 'https://example.com/page');
364
- assert.ok(href.startsWith('https://example.com/page#:~:text='));
365
- });
366
-
367
- test('getWCAGLink returns a URL for a numeric WCAG ID', () => {
368
- const link = getWCAGLink('4.1.2');
369
- assert.ok(link.startsWith('https://www.w3.org/WAI/WCAG22/Understanding/'));
370
- });
371
-
372
- test('getWeightName returns the correct name for each weight', () => {
373
- assert.equal(getWeightName(1), 'lowest');
374
- assert.equal(getWeightName(2), 'low');
375
- assert.equal(getWeightName(3), 'high');
376
- assert.equal(getWeightName(4), 'highest');
377
- assert.equal(getWeightName(5), 'unknown');
378
- });
379
-
380
303
  test('htmlSafe escapes HTML special characters', () => {
381
304
  assert.equal(htmlSafe('<script>alert("x")</script>'), '&lt;script&gt;alert(&quot;x&quot;)&lt;/script&gt;');
382
305
  });
@@ -776,10 +699,6 @@ test('isValidAuthCode returns false for an undefined code', () => {
776
699
  assert.equal(isValidAuthCode(undefined), false);
777
700
  });
778
701
 
779
- test('makeBreakable inserts wbr before non-initial slashes', () => {
780
- assert.equal(makeBreakable('/api/listReports'), '/api<wbr>/listReports');
781
- });
782
-
783
702
  test('minifyURL removes www. and trailing slash and lowercases', () => {
784
703
  assert.equal(minifyURL('https://www.Example.com/'), 'https://example.com');
785
704
  });
@@ -821,50 +740,6 @@ after(() => {
821
740
  }
822
741
  });
823
742
 
824
- test('getPageData returns page data for a valid report', async () => {
825
- const data = await getPageData('260101T0000', 'mix') as any;
826
- assert.equal(data.description, 'Mixed Outcomes Page');
827
- assert.equal(data.url, 'https://example.com/mixed');
828
- assert.equal(typeof data.daysAgo, 'number');
829
- });
830
-
831
- test('getPageData returns an error for a nonexistent report', async () => {
832
- const data = await getPageData('999999T9999', 'xxx') as any;
833
- assert.ok(data.error);
834
- });
835
-
836
- test('getPageDataStrings returns HTML strings for a valid report', async () => {
837
- const strings = await getPageDataStrings('260101T0000', 'mix') as any;
838
- assert.equal(strings.description, 'Mixed Outcomes Page');
839
- assert.equal(strings.url, 'https://example.com/mixed');
840
- assert.equal(strings.urlLink, '<a href="https://example.com/mixed">https://example.com/mixed</a>');
841
- assert.ok(strings.testInfo.includes('by job <code>mix</code>'));
842
- assert.ok(strings.testInfo.includes('2026-01-01 at 00:00'));
843
- });
844
-
845
- test('getPageDataStrings returns different testInfo for a different timeStamp', async () => {
846
- const strings = await getPageDataStrings('260101T0001', 'ct') as any;
847
- assert.equal(strings.description, 'All CantTell Page');
848
- assert.ok(strings.testInfo.includes('by job <code>ct</code>'));
849
- assert.ok(strings.testInfo.includes('2026-01-01 at 00:01'));
850
- });
851
-
852
- test('getPageDataStrings returns an error for a nonexistent report', async () => {
853
- const strings = await getPageDataStrings('999999T9999', 'xxx');
854
- assert.ok(strings.error);
855
- });
856
-
857
- test('getPageDataStrings uses provided pageData instead of reading the report', async () => {
858
- const strings = await getPageDataStrings('260101T0000', 'mix', {
859
- description: 'Custom Page',
860
- url: 'https://custom.com',
861
- daysAgo: 1
862
- }) as any;
863
- assert.equal(strings.description, 'Custom Page');
864
- assert.equal(strings.url, 'https://custom.com');
865
- assert.ok(strings.testInfo.includes('1 day ago'));
866
- });
867
-
868
743
  test('processTestRequest succeeds for a valid new-test request', {timeout: 500}, async () => {
869
744
  // Reset testRequests.json to empty before the test.
870
745
  await fs.writeFile(testRequestsPath(), '{}\n');
@@ -1358,41 +1233,12 @@ test('getReport returns an error for an invalid report', async () => {
1358
1233
  }
1359
1234
  });
1360
1235
 
1361
- test('getReportData returns an error for a nonexistent report', async () => {
1362
- const {getReportData} = await import('./util.ts');
1363
- const result: any = await getReportData('990101T0000', 'xxx');
1364
- assert.ok(result.error);
1365
- });
1366
-
1367
- test('getReportData falls back to the engine ID for an unknown prevented engine', async () => {
1368
- const {getReportData} = await import('./util.ts');
1369
- const prvJSON = await fs.readFile(path.join(reportsPath(), '260101T0006-prv.json'), 'utf8');
1370
- const report = JSON.parse(prvJSON);
1371
- report.jobData.preventions.unknownEngine = 'mystery failure';
1372
- const reportPath = path.join(reportsPath(), '260103T0000-unk.json');
1373
- await fs.writeFile(reportPath, JSON.stringify(report));
1374
- try {
1375
- const result: any = await getReportData('260103T0000', 'unk');
1376
- assert.ok(result.preventedEngineNames.includes('unknownEngine'));
1377
- }
1378
- finally {
1379
- await fs.unlink(reportPath);
1380
- }
1381
- });
1382
-
1383
1236
  // TESTS FOR REMAINING BRANCH COVERAGE IN util.js
1384
1237
 
1385
1238
  test('getAgoDays returns null for an invalid Date object', () => {
1386
1239
  assert.equal(getAgoDays(new Date('invalid')), null);
1387
1240
  });
1388
1241
 
1389
- test('getAgoString returns "1 day" for exactly 1 day ago', () => {
1390
- // Construct a time stamp 1 day and 1 hour ago, so Math.round gives exactly 1.
1391
- const date = new Date(Date.now() - (86400000 + 3600000));
1392
- const stamp = date.toISOString().slice(2).replace(/[-:]/g, '').slice(0, 11);
1393
- assert.equal(getAgoString(stamp), '1 day');
1394
- });
1395
-
1396
1242
  test('getIssue returns null for a known engine with an unknown variable rule', () => {
1397
1243
  assert.equal(getIssue('axe', 'nonexistent-rule-id'), null);
1398
1244
  });
@@ -1403,12 +1249,6 @@ test('getIssue returns an issue ID for a variable rule pattern match', () => {
1403
1249
  assert.equal(result, 'duplicateAttribute');
1404
1250
  });
1405
1251
 
1406
- test('getDateTimeString describes an invalid time stamp as unknown', async () => {
1407
- const {getDateTimeString} = await import('./util.ts');
1408
- const result = getDateTimeString('999999T9999');
1409
- assert.equal(result, 'an unknown date at an unknown time');
1410
- });
1411
-
1412
1252
  test('getPOSTData resolves with parsed JSON for application/json requests', async () => {
1413
1253
  const {Readable} = await import('node:stream');
1414
1254
  const {getPOSTData} = await import('./util.ts');
@@ -1464,35 +1304,6 @@ test('getPOSTData resolves with null for a malformed JSON body', async () => {
1464
1304
  assert.equal(result, null);
1465
1305
  });
1466
1306
 
1467
- test('getEngineNamesString falls back to the engine ID for an unknown engine', async () => {
1468
- const {getEngineNamesString} = await import('./util.ts');
1469
- const result = getEngineNamesString(new Set(['unknownEngine']));
1470
- assert.equal(result, 'unknownEngine');
1471
- });
1472
-
1473
- test('getPathID returns the catalog pathID when catalogIndex is truthy', async () => {
1474
- const {getPathID} = await import('./util.ts');
1475
- const catalog = {'0': {tagName: 'div', pathID: '/html/body/div'}};
1476
- assert.equal(getPathID(catalog, '0', '/fallback'), '/html/body/div');
1477
- });
1478
-
1479
- test('getPathID returns the fallback pathID when catalogIndex is truthy but catalogItem has no pathID', async () => {
1480
- const {getPathID} = await import('./util.ts');
1481
- const catalog = {'0': {tagName: 'div', pathID: ''}};
1482
- assert.equal(getPathID(catalog, '0', '/fallback'), '/fallback');
1483
- });
1484
-
1485
- test('getPathID returns /html when catalogIndex is truthy but catalogItem and pathID are both missing', async () => {
1486
- const {getPathID} = await import('./util.ts');
1487
- const catalog = {};
1488
- assert.equal(getPathID(catalog, '0', null as any), '/html');
1489
- });
1490
-
1491
- test('getPathID returns /html when catalogIndex is falsy and pathID is null', async () => {
1492
- const {getPathID} = await import('./util.ts');
1493
- assert.equal(getPathID({}, null as any, null as any), '/html');
1494
- });
1495
-
1496
1307
  test('isUsableReport returns false for a report with a test act using an unknown engine', async () => {
1497
1308
  const {isUsableReport} = await import('./util.ts');
1498
1309
  const report = {
@@ -1556,17 +1367,6 @@ test('getTimeStamp returns an 11-character stamp from a Date', () => {
1556
1367
  assert.equal(stamp.slice(7), '1430');
1557
1368
  });
1558
1369
 
1559
- test('getEngineNamesString returns a sorted +-delimited list of engine names', () => {
1560
- const result = getEngineNamesString(new Set(['axe', 'wave', 'nuVal']));
1561
- const names = result.split(' + ');
1562
- assert.ok(names.length === 3);
1563
- assert.ok(names.includes('WAVE'));
1564
- });
1565
-
1566
- test('getEngineNamesString falls back to the ID for an unknown engine', () => {
1567
- assert.equal(getEngineNamesString(new Set(['unknownEngine'])), 'unknownEngine');
1568
- });
1569
-
1570
1370
  test('testRequestsLock is a function (the lock returned by createLock)', () => {
1571
1371
  assert.equal(typeof testRequestsLock, 'function');
1572
1372
  });
@@ -1619,11 +1419,6 @@ test('getReportExtracts with onlyLatest returns only the latest report for each
1619
1419
  assert.equal(mixReports[0]!.timeStamp, '260202T0000');
1620
1420
  });
1621
1421
 
1622
- test('getMultiReportWhats returns descriptions that have multiple reports', async () => {
1623
- const whats = await getMultiReportWhats();
1624
- assert.ok(whats.includes('Mixed Outcomes Page'));
1625
- });
1626
-
1627
1422
  test('getExclusionCookieValue is deterministic for a given AUTH_CODE', async () => {
1628
1423
  const savedAuthCode = process.env.AUTH_CODE;
1629
1424
  process.env.AUTH_CODE = 'test-auth-code';
package/util.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  // c8 intermittently reports import lines as uncovered due to a range-merge
10
10
  // artifact in its remapping of Node's type-stripped source.
11
11
  import {sendAlert} from './alerts.ts';
12
- import {issues as issueSpecs, rules as ruleSpecs} from 'testaro-issues';
12
+ import {rules as ruleSpecs} from 'testaro-issues';
13
13
  import type {Act, Catalog, Report, StandardInstance} from 'testaro';
14
14
  import crypto from 'node:crypto';
15
15
  import dns from 'node:dns/promises';
@@ -19,7 +19,6 @@ import net from 'node:net';
19
19
  import path from 'node:path';
20
20
  import querystring from 'node:querystring';
21
21
  /* c8 ignore stop */
22
- import wcagMap from './wcagMap.json' with {type: 'json'};
23
22
 
24
23
  // CONSTANTS
25
24
 
@@ -139,47 +138,10 @@ export type ReportExtract = {
139
138
  superseded?: boolean;
140
139
  };
141
140
 
142
- // Page data from an available report.
143
- export type PageData = {
144
- description: string;
145
- url: string;
146
- daysAgo: number | null;
147
- error?: never;
148
- };
149
-
150
- // HTML strings describing the page data of an available report.
151
- export type PageDataStrings = {
152
- description: string;
153
- url: string;
154
- urlLink: string;
155
- testInfo: string;
156
- error?: never;
157
- };
158
-
159
- // Basics about an available report.
160
- export type ReportData = {
161
- description: string;
162
- url: string;
163
- jobName: unknown;
164
- creationDate: Date | null;
165
- daysAgo: number | null;
166
- issueCount: number;
167
- engineNames: string[];
168
- engineCount: number;
169
- reporterNames: string[];
170
- reporterCount: number;
171
- violatorCount: number;
172
- preventedEngineNames: string[];
173
- preventedEngineCount: number;
174
- error?: never;
175
- };
176
-
177
141
  // MISCELLANEOUS FUNCTIONS
178
142
 
179
143
  // Compares strings alphabetically and case-insensitively.
180
- const alphaCompare = (a: string, b: string) => a.localeCompare(b, 'en', {sensitivity: 'base'});
181
- // Sorts strings alphabetically and case-insensitively.
182
- const alphaSort = (strings: string[]) => strings.sort((a, b) => alphaCompare(a, b));
144
+ export const alphaCompare = (a: string, b: string) => a.localeCompare(b, 'en', {sensitivity: 'base'});
183
145
  // Returns whether a string's length is within the given bounds. Generic length check
184
146
  // shared by any field with a min/max character-count rule (e.g. a request description
185
147
  // or reason), so each caller states its own bounds and label instead of hardcoding an
@@ -198,22 +160,6 @@ export const checkLength = (
198
160
  }
199
161
  return {status: 'ok'};
200
162
  };
201
- // Returns whether a comment's raw length is within the 20-to-1000-character bounds.
202
- export const checkCommentLength = (
203
- content: string
204
- ): {status: 'ok'} | {status: 'error'; message: string} => {
205
- const contentLength = content.length;
206
- // If the length is invalid:
207
- if (contentLength < 20 || contentLength > 1000) {
208
- // Return the applicable error message.
209
- const messageSpec = contentLength < 20 ? 'shorter than 20' : 'longer than 1000';
210
- return {
211
- status: 'error',
212
- message: `Your comment was ${messageSpec} characters`
213
- };
214
- }
215
- return {status: 'ok'};
216
- };
217
163
  // Returns whether a comment repeats, verbatim, one of the given comments, regardless
218
164
  // of when the earlier one was submitted: a legitimate need to resubmit exactly the
219
165
  // same content never arises (the near-term case is a browser "Back" resubmission),
@@ -244,10 +190,6 @@ export const createLock = (): (<T>(fn: () => T | Promise<T>) => Promise<T>) => {
244
190
  return result;
245
191
  };
246
192
  };
247
- // Returns a string encoded for use as a URL fragment.
248
- const fragmentEncode = (string: string) => {
249
- return encodeURIComponent(string).replace(/-/g, '%2D');
250
- };
251
193
  // Returns the time in days since a Date or time stamp, or null if the argument is invalid.
252
194
  export const getAgoDays = (timeArg: string | Date): number | null => {
253
195
  let dateTime;
@@ -274,16 +216,6 @@ export const getAgoDays = (timeArg: string | Date): number | null => {
274
216
  // Otherwise, i.e. if it is valid, return the elapsed days since then.
275
217
  return Math.round((Date.now() - dateTime.getTime()) / (1000 * 60 * 60 * 24));
276
218
  };
277
- // Returns a string describing the time in days since a time stamp.
278
- export const getAgoString = (timeStamp: string): string => {
279
- const agoDays = getAgoDays(timeStamp);
280
- if (agoDays === null) {
281
- return 'an unknown number of days';
282
- }
283
- return agoDays === 1 ? '1 day' : `${agoDays} days`;
284
- };
285
- // Returns a string describing a count.
286
- export const getCountString = (count: number, singular: string, plural: string): string => count === 1 ? `1 ${singular}` : `${count} ${plural}`;
287
219
  // Returns a date string from a time stamp.
288
220
  export const getDateString = (timeStamp: string): string => {
289
221
  const dateString = `20${timeStamp.slice(0, 2)}-${timeStamp.slice(2, 4)}-${timeStamp.slice(4,6)}`;
@@ -435,19 +367,6 @@ export const getTimeStamp = (date: Date): string => {
435
367
  export const getNowStamp = (): string => {
436
368
  return getTimeStamp(new Date());
437
369
  };
438
- // Returns a time string from a time stamp.
439
- const getTimeString = (timeStamp: string) => {
440
- const timeString = `${timeStamp.slice(7, 9)}:${timeStamp.slice(9, 11)}`;
441
- // Return the time string if valid, or null if not.
442
- return (!isNaN(Date.parse(`2000-01-01T${timeString}Z`))) ? timeString : null;
443
- };
444
- // Returns a date-and-time string.
445
- export const getDateTimeString = (timeStamp: string): string => {
446
- const dateString = getDateString(timeStamp) || 'an unknown date';
447
- const timeString = getTimeString(timeStamp) || 'an unknown time';
448
- const dateTimeString = `${dateString} at ${timeString}`;
449
- return dateTimeString;
450
- }
451
370
  // Converts a string to a plain-text 1-line ASCII string.
452
371
  export const getPlainText = (string: string): string => string
453
372
  .replace(/&/g, '+')
@@ -512,26 +431,6 @@ export const getTestRequests = async (): Promise<TestRequests> => {
512
431
  throw new Error('Test-requests file not JSON', {cause: error});
513
432
  }
514
433
  };
515
- // Converts a catalog item text to a text-fragment link destination.
516
- export const getTextFragmentHref = (text: string, url: string): string => {
517
- const fragmentList = text
518
- .split('\n')
519
- .map(fragment => fragmentEncode(fragment))
520
- .join(',');
521
- // Return a text-fragment link.
522
- return `${url}#:~:text=${fragmentList}`;
523
- };
524
- // Returns a +-delimited list of sorted names of rule engines.
525
- export const getEngineNamesString = (engineIDSet: Iterable<string>): string => alphaSort(
526
- Array.from(engineIDSet).map(engineID => ruleEngines[engineID]?.[0] || engineID)
527
- ).join(' + ');
528
- // Gets the WCAG Understanding link for a numeric WCAG standard identifier.
529
- export const getWCAGLink = (numericID: string): string => {
530
- // Return the link.
531
- return `https://www.w3.org/WAI/WCAG22/Understanding/${(wcagMap as Record<string, string>)[numericID]}`;
532
- };
533
- // Gets the name of an issue weight.
534
- export const getWeightName = (weight: number): string => ['lowest', 'low', 'high', 'highest'][weight - 1] ?? 'unknown';
535
434
  // Makes a string HTML-safe.
536
435
  export const htmlSafe = (string: string): string => string ? string
537
436
  .replace(/&/g, '&amp;')
@@ -674,8 +573,6 @@ export const isAllowedRedirectTarget = async (
674
573
  export const isValidAuthCode = (authCode: string | undefined | null): boolean => {
675
574
  return !!authCode && authCode === process.env.AUTH_CODE;
676
575
  };
677
- // Makes a string breakable before non-initial slashes.
678
- export const makeBreakable = (string: string): string => string.replace(/\//g, '<wbr>/').replace(/^<wbr>/, '');
679
576
  // Minifies a URL for duplicate detection.
680
577
  export const minifyURL = (url: string): string => url.replace(/www\.|\/$/g, '').toLowerCase();
681
578
  // Sorts objects by a property value and returns the sorted array.
@@ -770,16 +667,6 @@ export const getTestActInstances = (
770
667
  }
771
668
  return pairs;
772
669
  };
773
- // Returns the path ID of the element of a standard instance.
774
- export const getPathID = (catalog: Catalog, catalogIndex: string, pathID?: string) => {
775
- if (catalogIndex) {
776
- const pathIDFromCatalog = (catalog[catalogIndex] || {}).pathID;
777
- if (pathIDFromCatalog) {
778
- return pathIDFromCatalog;
779
- }
780
- }
781
- return pathID ?? '/html';
782
- };
783
670
  // Returns the path of an available report file.
784
671
  export const getReportPath = (timeStamp: string, jobID: string): string => path
785
672
  .join(reportsPath(), `${timeStamp}-${jobID}.json`);
@@ -866,122 +753,6 @@ export const annotateReportObject = async (report: UsableReport): Promise<void>
866
753
  );
867
754
  }
868
755
  };
869
- // Returns basics about an available report.
870
- export const getReportData = async (timeStamp: string, jobID: string): Promise<ReportData | {error: string}> => {
871
- // Get the report.
872
- const report = await getReport(timeStamp, jobID);
873
- // If this failed:
874
- if (isReportError(report)) {
875
- // Return why.
876
- return {error: report.error};
877
- }
878
- // Otherwise, i.e. if it succeeded, initialize the data.
879
- const data = {
880
- description: report.target.what,
881
- url: report.target.url,
882
- jobName: report.id,
883
- creationDate: getDateTime(timeStamp),
884
- daysAgo: getAgoDays(timeStamp),
885
- issueCount: 0,
886
- engineNames: [] as string[],
887
- engineCount: 0,
888
- reporterNames: [] as string[],
889
- reporterCount: 0,
890
- violatorCount: 0,
891
- preventedEngineNames: [] as string[],
892
- preventedEngineCount: 0
893
- };
894
- const issueIDSet = new Set<string>();
895
- const engineNameSet = new Set<string>();
896
- const reporterIDSet = new Set<string>();
897
- const violatorIndexSet = new Set<string>();
898
- // For each test act of the report:
899
- getTestActs(report).forEach(act => {
900
- // Ensure that the rule engine is in the temporary data.
901
- engineNameSet.add(ruleEngines[act.which!]![0]);
902
- });
903
- // For each violating standard instance of each test act:
904
- getTestActInstances(report, {violationsOnly: true}).forEach(({act, instance}) => {
905
- const {catalogIndex, issueID} = instance;
906
- // If it has a non-ignorable classified issue ID:
907
- if (issueID && issueSpecs[issueID] && issueID !== 'ignorable') {
908
- // Ensure that the rule engine is in the temporary data.
909
- reporterIDSet.add(act.which!);
910
- // Ensure that the issue is in the temporary data.
911
- issueIDSet.add(issueID);
912
- // If the violator has a catalog index:
913
- if (catalogIndex) {
914
- // Ensure that the violator is in the temporary data.
915
- violatorIndexSet.add(String(catalogIndex));
916
- }
917
- }
918
- });
919
- // Populate the data with the act data.
920
- data.issueCount = issueIDSet.size;
921
- data.engineNames = Array
922
- .from(engineNameSet)
923
- .sort((a, b) => a.localeCompare(b, 'en', {sensitivity: 'base'}));
924
- data.engineCount = engineNameSet.size;
925
- data.reporterNames = Array
926
- .from(reporterIDSet)
927
- .map(id => ruleEngines[id]![0])
928
- .sort((a, b) => a.localeCompare(b, 'en', {sensitivity: 'base'}));
929
- data.reporterCount = data.reporterNames.length;
930
- data.violatorCount = violatorIndexSet.size;
931
- // Add the names of any prevented rule engines to the data.
932
- data.preventedEngineNames = Object.keys(report.jobData?.preventions || {})
933
- .map(engineID => ruleEngines[engineID]?.[0] || engineID)
934
- .sort((a, b) => a.localeCompare(b, 'en', {sensitivity: 'base'}));
935
- data.preventedEngineCount = data.preventedEngineNames.length;
936
- // Return the data.
937
- return data;
938
- }
939
- // Returns page data from an available report.
940
- export const getPageData = async (timeStamp: string, jobID: string): Promise<PageData | {error: string}> => {
941
- // Get the report.
942
- const report = await getReport(timeStamp, jobID);
943
- // If this failed:
944
- if (isReportError(report)) {
945
- // Return why.
946
- return report;
947
- }
948
- const {what: description, url} = report.target;
949
- // Get the elapsed time in days since the report was completed, using the
950
- // report content rather than the file system birth time.
951
- const daysAgo = getAgoDays(new Date(`20${report.jobData.endTime}Z`));
952
- // Return the data.
953
- return {
954
- description,
955
- url,
956
- daysAgo
957
- };
958
- };
959
- // Gets HTML strings for page data from a report.
960
- export const getPageDataStrings = async (
961
- timeStamp: string,
962
- jobID: string,
963
- pageData?: PageData | {error: string}
964
- ): Promise<PageDataStrings | {error: string}> => {
965
- // Get the page data if they were not specified.
966
- const data = pageData ?? await getPageData(timeStamp, jobID);
967
- // If the page data are invalid:
968
- if (data.error !== undefined) {
969
- // Return why.
970
- return {
971
- error: data.error
972
- };
973
- }
974
- const {daysAgo, url, description} = data;
975
- // Otherwise, i.e. if they are valid, get a description of the timestamp.
976
- const when = getDateTimeString(timeStamp);
977
- // Return the HTML strings.
978
- return {
979
- description,
980
- url,
981
- urlLink: `<a href="${url}">${url}</a>`,
982
- testInfo: `Tested ${daysAgo === 1 ? '1 day' : `${daysAgo} days`} ago by job <code>${jobID}</code> on ${when}`
983
- };
984
- };
985
756
  // Returns the creation time and size of a report.
986
757
  export const getReportStats = async (timeStamp: string, jobID: string) => {
987
758
  let reportStat;
@@ -1058,15 +829,6 @@ export const isReportAvailable = async (description: string, url: string): Promi
1058
829
  const miniURLs = reportExtracts.map(reportExtract => minifyURL(reportExtract.url));
1059
830
  return descriptions.includes(description) || miniURLs.includes(minifyURL(url));
1060
831
  };
1061
- // Gets the descriptions of multi-report pages.
1062
- export const getMultiReportWhats = async (): Promise<string[]> => {
1063
- const reportExtracts = await getReportExtracts();
1064
- const sortedDescriptions = reportExtracts.map(extract => extract.description).sort();
1065
- const multiReportDescriptions = sortedDescriptions.filter(
1066
- (description, index) => description !== sortedDescriptions[index - 1] && description === sortedDescriptions[index + 1]
1067
- );
1068
- return multiReportDescriptions;
1069
- };
1070
832
  // Returns the property that an approved job in a category has.
1071
833
  const getApprovedJobProperty = async (
1072
834
  description: string, url: string, category: 'claimed' | 'queue'
@@ -7,7 +7,14 @@
7
7
 
8
8
  // IMPORTS
9
9
 
10
- import {errorMessage, getDateTimeString, getJSON, getPlainText, isValidAuthCode, populateTemplate} from '../../util.ts';
10
+ import {
11
+ errorMessage,
12
+ getJSON,
13
+ getPlainText,
14
+ isValidAuthCode,
15
+ populateTemplate
16
+ } from '../../util.ts';
17
+ import {getDateTimeString} from '../util.ts';
11
18
  import fs from 'node:fs/promises';
12
19
  import path from 'node:path';
13
20
 
@@ -6,17 +6,19 @@
6
6
  // IMPORTS
7
7
 
8
8
  import {
9
- getPageDataStrings,
10
9
  getReport,
11
10
  getTestActInstances,
12
- getTextFragmentHref,
13
- getWCAGLink,
14
- getWeightName,
15
11
  htmlSafe,
16
12
  isReportError,
17
13
  populateTemplate,
18
14
  ruleEngines
19
15
  } from '../../util.ts';
16
+ import {
17
+ getPageDataStrings,
18
+ getTextFragmentHref,
19
+ getWCAGLink,
20
+ getWeightName
21
+ } from '../util.ts';
20
22
  import {issues as issueSpecs} from 'testaro-issues';
21
23
 
22
24
  // FUNCTIONS