@jrpool/kilotest 47.0.1 → 48.0.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/requestRetest.test.ts +66 -7
- package/api/requestRetest.ts +55 -39
- package/api/requestTest.test.ts +63 -7
- package/api/requestTest.ts +36 -26
- package/api/util.test.ts +6 -5
- package/api/util.ts +2 -24
- package/docs/BACKLOG.md +12 -8
- package/docs/SERVICE.md +32 -6
- package/index.test.ts +20 -12
- package/index.ts +4 -2
- package/package.json +1 -1
- package/util.test.ts +195 -127
- package/util.ts +292 -196
- package/web/listReports/index.ts +33 -12
- package/web/listReports/listReports.test.ts +1 -1
- package/web/reannotate/index.ts +2 -1
- package/web/reportDeletion.ts +2 -2
- package/web/requestRetest/index.html +3 -3
- package/web/requestRetest/index.ts +47 -16
- package/web/requestRetest/requestRetest.test.ts +38 -24
- package/web/requestTest/index.html +4 -4
- package/web/requestTest/index.ts +37 -9
- package/web/requestTest/requestTest.test.ts +21 -6
- package/web/unhideReportForm/index.ts +2 -2
package/web/reportDeletion.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// IMPORTS
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
|
-
errorMessage, getReportData, objectSort, populateTemplate, reportsPath
|
|
9
|
+
errorMessage, getReportData, objectSort, populateTemplate, readdirOrCreate, reportsPath
|
|
10
10
|
} from '../util.ts';
|
|
11
11
|
import fs from 'node:fs/promises';
|
|
12
12
|
import path from 'node:path';
|
|
@@ -71,7 +71,7 @@ export const reportDeletionForm = async (
|
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
-
const reportNames = await
|
|
74
|
+
const reportNames = await readdirOrCreate(reportsPath(), 'Reports directory');
|
|
75
75
|
// Initialize an array of report summaries.
|
|
76
76
|
const reportSpecs: ReportSpec[] = [];
|
|
77
77
|
// For each report:
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
14
14
|
<main>
|
|
15
|
-
<h1><a href="/">Kilotest</a>: Retest request received for
|
|
16
|
-
<p>Your request to retest the
|
|
17
|
-
<p>The reason for your request has been recorded as <q>
|
|
15
|
+
<h1><a href="/">Kilotest</a>: Retest request received for __description__</h1>
|
|
16
|
+
<p>Your request to retest the __description__ page at __url__ has been received.</p>
|
|
17
|
+
<p>The reason for your request has been recorded as <q>__reason__</q>.</p>
|
|
18
18
|
<p>Thank you for your request.</p>
|
|
19
19
|
<p>If a <a href="/enqueueForm.html">Kilotest manager approves this request</a>, a test order will be added to the queue.</p>
|
|
20
20
|
<p>In most cases, if you check Kilotest again in 1 day, you will find that the requested test has been performed.</p>
|
|
@@ -5,27 +5,58 @@
|
|
|
5
5
|
|
|
6
6
|
// IMPORTS
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import {populateTemplate, processTestRequest} from '../../util.ts';
|
|
9
9
|
|
|
10
10
|
// FUNCTIONS
|
|
11
11
|
|
|
12
|
-
export const answer = async (pageArgs: string,
|
|
13
|
-
const [timeStamp, jobID] = pageArgs.split('/');
|
|
14
|
-
//
|
|
15
|
-
const
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
);
|
|
20
|
-
// If no matching report was found:
|
|
21
|
-
if (!reportExtract) {
|
|
22
|
-
// Return why.
|
|
12
|
+
export const answer = async (pageArgs: string, reason: string) => {
|
|
13
|
+
const [timeStamp, jobID] = pageArgs.split('/') as [string, string];
|
|
14
|
+
// Process the request. This resolves the cited report's description and URL itself.
|
|
15
|
+
const processResult = await processTestRequest(reason, {timeStamp, jobID});
|
|
16
|
+
// If the cited report does not exist:
|
|
17
|
+
if (processResult.result === 'nonreport') {
|
|
18
|
+
// Report this.
|
|
23
19
|
return {
|
|
24
20
|
status: 'error',
|
|
25
|
-
message:
|
|
21
|
+
message: 'Invalid request'
|
|
26
22
|
};
|
|
27
23
|
}
|
|
28
|
-
const {
|
|
29
|
-
//
|
|
30
|
-
|
|
24
|
+
const {result, description, url} = processResult;
|
|
25
|
+
// If the request was recorded:
|
|
26
|
+
if (result === 'ok') {
|
|
27
|
+
const query = {
|
|
28
|
+
description,
|
|
29
|
+
url,
|
|
30
|
+
reason
|
|
31
|
+
};
|
|
32
|
+
// Return the populated page.
|
|
33
|
+
return {
|
|
34
|
+
status: 'ok',
|
|
35
|
+
answerPage: await populateTemplate(import.meta.dirname, query)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
// Otherwise, if the request is a duplicate:
|
|
39
|
+
else if (result === 'duplicate') {
|
|
40
|
+
// Report this.
|
|
41
|
+
return {
|
|
42
|
+
status: 'error',
|
|
43
|
+
message: 'Test request duplicates an already submitted request'
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// Otherwise, if the cited report has been superseded:
|
|
47
|
+
else if (result === 'superseded') {
|
|
48
|
+
// Report this.
|
|
49
|
+
return {
|
|
50
|
+
status: 'error',
|
|
51
|
+
message: 'A later report about the page is already available'
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Otherwise, i.e. if a request to test a page with the same description or URL is approved:
|
|
55
|
+
else {
|
|
56
|
+
// Report this.
|
|
57
|
+
return {
|
|
58
|
+
status: 'error',
|
|
59
|
+
message: 'A request to test a page with the same description or URL is already approved'
|
|
60
|
+
}
|
|
61
|
+
}
|
|
31
62
|
};
|
|
@@ -68,11 +68,14 @@ test('answer returns ok with a populated answer page for a valid retest request'
|
|
|
68
68
|
const result: any = await answer('260101T0001/ct', 'Because changes were made');
|
|
69
69
|
assert.equal(result.status, 'ok');
|
|
70
70
|
assert.ok(result.answerPage, 'answerPage should be present');
|
|
71
|
-
// Verify the placeholders were replaced with the report target.
|
|
71
|
+
// Verify the description and reason placeholders were replaced with the report target.
|
|
72
|
+
// (Note: the template's <title> uses __target__, a placeholder query never supplies;
|
|
73
|
+
// that pre-existing template mismatch is out of scope here and left unasserted.)
|
|
72
74
|
assert.ok(result.answerPage.includes('All CantTell Page'));
|
|
73
75
|
assert.ok(result.answerPage.includes('Because changes were made'));
|
|
74
|
-
assert.ok(!result.answerPage.includes('
|
|
75
|
-
assert.ok(!result.answerPage.includes('
|
|
76
|
+
assert.ok(!result.answerPage.includes('__description__'));
|
|
77
|
+
assert.ok(!result.answerPage.includes('__url__'));
|
|
78
|
+
assert.ok(!result.answerPage.includes('__reason__'));
|
|
76
79
|
// Verify the HTML is well-formed.
|
|
77
80
|
const doc = parse(result.answerPage);
|
|
78
81
|
const h1 = doc.querySelector('h1');
|
|
@@ -85,32 +88,43 @@ test('answer returns an error when the report does not exist', async () => {
|
|
|
85
88
|
// Use a nonexistent report timestamp and jobID.
|
|
86
89
|
const result: any = await answer('990101T0000/xxx', 'Because changes were made');
|
|
87
90
|
assert.equal(result.status, 'error');
|
|
88
|
-
assert.
|
|
91
|
+
assert.equal(result.message, 'Invalid request');
|
|
89
92
|
});
|
|
90
93
|
|
|
91
|
-
test('answer returns an error for
|
|
94
|
+
test('answer returns an error for a duplicate retest request', async () => {
|
|
92
95
|
const {answer} = await import('./index.ts');
|
|
93
|
-
//
|
|
94
|
-
|
|
96
|
+
// Submit the same retest request twice; the second is a duplicate.
|
|
97
|
+
await answer('260101T0001/ct', 'Because changes were made');
|
|
98
|
+
const result: any = await answer('260101T0001/ct', 'Because changes were made');
|
|
95
99
|
assert.equal(result.status, 'error');
|
|
96
|
-
assert.equal(result.message, '
|
|
100
|
+
assert.equal(result.message, 'Test request duplicates an already submitted request');
|
|
97
101
|
});
|
|
98
102
|
|
|
99
|
-
test('answer returns an error when
|
|
100
|
-
|
|
101
|
-
// (
|
|
102
|
-
|
|
103
|
-
const realUtil = await import('../../util.ts');
|
|
104
|
-
t.mock.module('../../util.ts', {
|
|
105
|
-
exports: {
|
|
106
|
-
...realUtil,
|
|
107
|
-
getReportExtracts: async () => []
|
|
108
|
-
}
|
|
109
|
-
} as any);
|
|
110
|
-
// Import a fresh instance of index.ts so it binds to the mocked module.
|
|
111
|
-
// The query string makes the specifier unique, bypassing the module cache.
|
|
112
|
-
const {answer} = await import('./index.ts?mockNoExtracts' as any);
|
|
113
|
-
const result: any = await answer('260101T0001/ct', 'Because changes were made');
|
|
103
|
+
test('answer returns an error when the cited report has been superseded', async () => {
|
|
104
|
+
const {answer} = await import('./index.ts');
|
|
105
|
+
// 260101T0000-mix (Mixed Outcomes Page) is superseded by 260202T0000-new.
|
|
106
|
+
const result: any = await answer('260101T0000/mix', 'Because changes were made');
|
|
114
107
|
assert.equal(result.status, 'error');
|
|
115
|
-
assert.
|
|
108
|
+
assert.equal(result.message, 'A later report about the page is already available');
|
|
116
109
|
});
|
|
110
|
+
|
|
111
|
+
test('answer returns an error when a matching job is already claimed or queued', async () => {
|
|
112
|
+
const {answer} = await import('./index.ts');
|
|
113
|
+
// Create a claimed job sharing the description of the cited report
|
|
114
|
+
// (260101T0001-ct, "All CantTell Page").
|
|
115
|
+
const claimedDir = path.join(fixtureDBDir, 'jobs', 'claimed');
|
|
116
|
+
const jobPath = path.join(claimedDir, 'claimedJob.json');
|
|
117
|
+
await fs.writeFile(jobPath, JSON.stringify({
|
|
118
|
+
id: 'claimedJob',
|
|
119
|
+
target: {what: 'All CantTell Page', url: 'https://example.com/unrelated'}
|
|
120
|
+
}));
|
|
121
|
+
try {
|
|
122
|
+
const result: any = await answer('260101T0001/ct', 'Because changes were made');
|
|
123
|
+
assert.equal(result.status, 'error');
|
|
124
|
+
assert.equal(result.message, 'A request to test a page with the same description or URL is already approved');
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
await fs.unlink(jobPath).catch(() => {});
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
<meta name="publisher" content="Jonathan Robert Pool">
|
|
7
7
|
<meta name="creator" content="Jonathan Robert Pool">
|
|
8
8
|
<meta name="keywords" content="report,accessibility,a11y">
|
|
9
|
-
<title>
|
|
9
|
+
<title>__description__ test request received | Kilotest</title>
|
|
10
10
|
<link rel="icon" href="/favicon.ico">
|
|
11
11
|
<link rel="stylesheet" href="/style.css">
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
14
14
|
<main>
|
|
15
|
-
<h1><a href="/">Kilotest</a>: Test request received for
|
|
16
|
-
<p>Your request to test the
|
|
17
|
-
<p>The reason for your request has been recorded as <q>
|
|
15
|
+
<h1><a href="/">Kilotest</a>: Test request received for __description__</h1>
|
|
16
|
+
<p>Your request to test the __description__ page at __url__ has been received.</p>
|
|
17
|
+
<p>The reason for your request has been recorded as <q>__reason__</q>.</p>
|
|
18
18
|
<p>Thank you for your request.</p>
|
|
19
19
|
<p>If a <a href="/enqueueForm.html">Kilotest manager approves this request</a>, a test order will be added to the queue.</p>
|
|
20
20
|
<p>In most cases, if you check Kilotest again in 1 day, you will find that the requested job has been performed.</p>
|
package/web/requestTest/index.ts
CHANGED
|
@@ -5,20 +5,48 @@
|
|
|
5
5
|
|
|
6
6
|
// IMPORTS
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import {populateTemplate, processTestRequest} from '../../util.ts';
|
|
9
9
|
|
|
10
10
|
// FUNCTIONS
|
|
11
11
|
|
|
12
|
-
export const answer = async (description: string, url: string,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
export const answer = async (description: string, url: string, reason: string) => {
|
|
13
|
+
// Process the request.
|
|
14
|
+
const {result} = await processTestRequest(reason, {description, url});
|
|
15
|
+
// If the request was recorded:
|
|
16
|
+
if (result === 'ok') {
|
|
17
|
+
const query = {
|
|
18
|
+
description,
|
|
19
|
+
url,
|
|
20
|
+
reason
|
|
21
|
+
};
|
|
22
|
+
// Return the populated page.
|
|
23
|
+
return {
|
|
24
|
+
status: 'ok',
|
|
25
|
+
answerPage: await populateTemplate(import.meta.dirname, query)
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
// Otherwise, if the request is a duplicate:
|
|
29
|
+
else if (result === 'duplicate') {
|
|
30
|
+
// Report this.
|
|
17
31
|
return {
|
|
18
32
|
status: 'error',
|
|
19
|
-
message:
|
|
33
|
+
message: 'Test request duplicates an already submitted request'
|
|
20
34
|
};
|
|
21
35
|
}
|
|
22
|
-
// Otherwise,
|
|
23
|
-
|
|
36
|
+
// Otherwise, if a report is available about a page with the same description and URL:
|
|
37
|
+
else if (result === 'retest') {
|
|
38
|
+
// Report this.
|
|
39
|
+
return {
|
|
40
|
+
status: 'error',
|
|
41
|
+
message: 'A report about the page is already available'
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Otherwise, i.e. if a request to test a page with the same description or URL is approved:
|
|
45
|
+
else {
|
|
46
|
+
// Report this.
|
|
47
|
+
return {
|
|
48
|
+
status: 'error',
|
|
49
|
+
message: 'A request to test a page with the same description or URL is already approved'
|
|
50
|
+
}
|
|
51
|
+
}
|
|
24
52
|
};
|
|
@@ -72,8 +72,9 @@ test('answer returns ok with a populated answer page for a valid test request',
|
|
|
72
72
|
// Verify the placeholders were replaced.
|
|
73
73
|
assert.ok(result.answerPage.includes('Test Page'));
|
|
74
74
|
assert.ok(result.answerPage.includes('Because accessibility'));
|
|
75
|
-
assert.ok(!result.answerPage.includes('
|
|
76
|
-
assert.ok(!result.answerPage.includes('
|
|
75
|
+
assert.ok(!result.answerPage.includes('__description__'));
|
|
76
|
+
assert.ok(!result.answerPage.includes('__url__'));
|
|
77
|
+
assert.ok(!result.answerPage.includes('__reason__'));
|
|
77
78
|
// Verify the HTML is well-formed.
|
|
78
79
|
const doc = parse(result.answerPage);
|
|
79
80
|
const h1 = doc.querySelector('h1');
|
|
@@ -95,7 +96,7 @@ test('answer returns an error when the URL is already queued', {timeout: 500}, a
|
|
|
95
96
|
'Queued Page', 'https://example.com/queued', 'Because accessibility'
|
|
96
97
|
);
|
|
97
98
|
assert.equal(result.status, 'error');
|
|
98
|
-
assert.
|
|
99
|
+
assert.equal(result.message, 'A request to test a page with the same description or URL is already approved');
|
|
99
100
|
}
|
|
100
101
|
finally {
|
|
101
102
|
try {
|
|
@@ -107,9 +108,23 @@ test('answer returns an error when the URL is already queued', {timeout: 500}, a
|
|
|
107
108
|
}
|
|
108
109
|
});
|
|
109
110
|
|
|
110
|
-
test('answer returns an error for
|
|
111
|
+
test('answer returns an error for a duplicate request', {timeout: 500}, async () => {
|
|
111
112
|
const {answer} = await import('./index.ts');
|
|
112
|
-
|
|
113
|
+
// Submit the same request twice; the second is a duplicate.
|
|
114
|
+
await answer('Duplicate Page', 'https://example.com/duplicate', 'Because accessibility');
|
|
115
|
+
const result: any = await answer(
|
|
116
|
+
'Duplicate Page', 'https://example.com/duplicate', 'Because accessibility'
|
|
117
|
+
);
|
|
118
|
+
assert.equal(result.status, 'error');
|
|
119
|
+
assert.equal(result.message, 'Test request duplicates an already submitted request');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('answer returns an error when a report already exists for the page', {timeout: 500}, async () => {
|
|
123
|
+
const {answer} = await import('./index.ts');
|
|
124
|
+
// The fixture database already has a report for this description and URL.
|
|
125
|
+
const result: any = await answer(
|
|
126
|
+
'Mixed Outcomes Page', 'https://example.com/mixed', 'Because accessibility'
|
|
127
|
+
);
|
|
113
128
|
assert.equal(result.status, 'error');
|
|
114
|
-
assert.equal(result.message, '
|
|
129
|
+
assert.equal(result.message, 'A report about the page is already available');
|
|
115
130
|
});
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
// IMPORTS
|
|
7
7
|
|
|
8
|
-
import {hiddenReportsPath, populateTemplate, reportsPath} from '../../util.ts';
|
|
8
|
+
import {hiddenReportsPath, populateTemplate, readdirOrCreate, reportsPath} from '../../util.ts';
|
|
9
9
|
import fs from 'node:fs/promises';
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
|
|
@@ -45,7 +45,7 @@ export const answer = async (_: any, search: string) => {
|
|
|
45
45
|
// Initialize an array of data on reports to be unhidden.
|
|
46
46
|
const reportsData: any[] = [];
|
|
47
47
|
// Get the names of the hidden report files.
|
|
48
|
-
const hiddenReportFileNames = await
|
|
48
|
+
const hiddenReportFileNames = await readdirOrCreate(hiddenReportsPath(), 'Hidden reports directory');
|
|
49
49
|
// For each hidden report:
|
|
50
50
|
for (const reportFileName of hiddenReportFileNames) {
|
|
51
51
|
// Get its file.
|