@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/api/orderNewTest.test.ts
CHANGED
|
@@ -121,7 +121,7 @@ test('orderNewTest rejects an already-tested page', async () => {
|
|
|
121
121
|
const disposition = body['response content']['disposition of your order'] as any;
|
|
122
122
|
assert.ok(
|
|
123
123
|
disposition['what happens next']
|
|
124
|
-
.includes('a report about a page with the same description
|
|
124
|
+
.includes('a report about a page with the same description or URL is available.')
|
|
125
125
|
);
|
|
126
126
|
assert.equal(disposition['report identifier'], null);
|
|
127
127
|
});
|
package/api/orderNewTest.ts
CHANGED
|
@@ -100,7 +100,7 @@ export const response = async (args: string[]) => {
|
|
|
100
100
|
requestDisposition = {
|
|
101
101
|
'what happens next': `Your order will not be processed, because ${getRequestFailureReason(
|
|
102
102
|
orderResult.result,
|
|
103
|
-
{result: 'reportExists', reason: 'a report about a page with the same description
|
|
103
|
+
{result: 'reportExists', reason: 'a report about a page with the same description or URL is available.'}
|
|
104
104
|
)}`,
|
|
105
105
|
'report identifier': null,
|
|
106
106
|
'how you can check for completion': 'Not applicable.',
|
|
@@ -118,7 +118,7 @@ test('requestNewTest rejects an already-tested page', async () => {
|
|
|
118
118
|
const disposition = body['response content']['disposition of your request'] as any;
|
|
119
119
|
assert.ok(
|
|
120
120
|
disposition['what happens next']
|
|
121
|
-
.includes('a report about a page with the same description
|
|
121
|
+
.includes('a report about a page with the same description or URL is available.')
|
|
122
122
|
);
|
|
123
123
|
assert.ok(!logged.some(line => line.includes('request for a new test awaits approval')));
|
|
124
124
|
});
|
package/api/requestNewTest.ts
CHANGED
|
@@ -84,7 +84,7 @@ export const response = async (args: string[]) => {
|
|
|
84
84
|
howToCheck: 'You can call the listReports tool to learn whether the page has been tested and a report is available.',
|
|
85
85
|
howWebUserChecks: `A web user can visit ${thisHost}/listReports.html to learn whether the page has been tested and a report is available.`
|
|
86
86
|
},
|
|
87
|
-
{result: 'reportExists', reason: 'a report about a page with the same description
|
|
87
|
+
{result: 'reportExists', reason: 'a report about a page with the same description or URL is available.'}
|
|
88
88
|
);
|
|
89
89
|
}
|
|
90
90
|
// Create the response content.
|
package/index.test.ts
CHANGED
|
@@ -476,7 +476,7 @@ test('POST /requestNewTest.html with an already-tested URL returns an error', as
|
|
|
476
476
|
why: 'Because accessibility matters'
|
|
477
477
|
});
|
|
478
478
|
assert.equal(res.statusCode, 400);
|
|
479
|
-
assert.ok(res.body.includes('already
|
|
479
|
+
assert.ok(res.body.includes('same description or URL is already available'));
|
|
480
480
|
});
|
|
481
481
|
|
|
482
482
|
test('POST /requestNewTest.html with invalid data returns an error', async () => {
|
package/index.ts
CHANGED
|
@@ -23,7 +23,6 @@ import {
|
|
|
23
23
|
isReportError,
|
|
24
24
|
getReportPath,
|
|
25
25
|
hiddenReportsPath,
|
|
26
|
-
isReportAvailable,
|
|
27
26
|
isTimeStamp,
|
|
28
27
|
isJobID,
|
|
29
28
|
isURL,
|
|
@@ -949,27 +948,19 @@ const handleRequest = async (request: IncomingMessage, response: ServerResponse)
|
|
|
949
948
|
const {description, url, why} = postData as {description?: string; url: string; why?: string};
|
|
950
949
|
// If the request is valid:
|
|
951
950
|
if (description && isURL(url) && why && await isAllowedTarget(url)) {
|
|
952
|
-
//
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
951
|
+
// Serve headers for a response.
|
|
952
|
+
setHeaders('text/html', pathname, 'ultra');
|
|
953
|
+
// Get the answer data.
|
|
954
|
+
const answerData = await answer.requestNewTest(description, url, why);
|
|
955
|
+
// If they are valid:
|
|
956
|
+
if (answerData.status === 'ok') {
|
|
957
|
+
// Serve the answer page.
|
|
958
|
+
response.end(answerData.answerPage);
|
|
956
959
|
}
|
|
957
|
-
// Otherwise, i.e. if
|
|
960
|
+
// Otherwise, i.e. if they are invalid:
|
|
958
961
|
else {
|
|
959
|
-
//
|
|
960
|
-
|
|
961
|
-
// Get the answer data.
|
|
962
|
-
const answerData = await answer.requestNewTest(description, url, why);
|
|
963
|
-
// If they are valid:
|
|
964
|
-
if (answerData.status === 'ok') {
|
|
965
|
-
// Serve the answer page.
|
|
966
|
-
response.end(answerData.answerPage);
|
|
967
|
-
}
|
|
968
|
-
// Otherwise, i.e. if they are invalid:
|
|
969
|
-
else {
|
|
970
|
-
// Report the error.
|
|
971
|
-
await serveError({message: answerData.message}, response, true);
|
|
972
|
-
}
|
|
962
|
+
// Report the error.
|
|
963
|
+
await serveError({message: answerData.message}, response, true);
|
|
973
964
|
}
|
|
974
965
|
}
|
|
975
966
|
// Otherwise, i.e. if the request is invalid:
|
package/package.json
CHANGED
|
@@ -203,11 +203,20 @@ const main = async () => {
|
|
|
203
203
|
const preventedCatalog = {
|
|
204
204
|
'0': catalogItem('IMG', 'An image', '/html/body/img[1]', '0:0:200:100')
|
|
205
205
|
};
|
|
206
|
+
// As in real reports, the prevented engine has a test act, with no standard result.
|
|
207
|
+
const preventedAlfaAct = {
|
|
208
|
+
type: 'test',
|
|
209
|
+
which: engineAlfa,
|
|
210
|
+
startTime: '26-01-01T00:00',
|
|
211
|
+
data: {prevented: true, error: 'page timed out'},
|
|
212
|
+
endTime: '26-01-01T00:01'
|
|
213
|
+
};
|
|
206
214
|
const preventedActs = [
|
|
207
215
|
testAct(engineAxe, [
|
|
208
216
|
instance('r11', 'The image has no alt text', 'failed',
|
|
209
217
|
issueLinkNoText, 0, 2, 1)
|
|
210
|
-
])
|
|
218
|
+
]),
|
|
219
|
+
preventedAlfaAct
|
|
211
220
|
];
|
|
212
221
|
const preventedReport = report('260101T0006-prv', 'Prevented Page',
|
|
213
222
|
'https://example.com/prevented', preventedActs, preventedCatalog);
|
package/util.test.ts
CHANGED
|
@@ -773,6 +773,21 @@ test('processTestRequest returns a reportExists error when a report already exis
|
|
|
773
773
|
assert.equal(result, 'reportExists');
|
|
774
774
|
});
|
|
775
775
|
|
|
776
|
+
test('processTestRequest returns a reportExists error when a report exists about a page with the same description', async () => {
|
|
777
|
+
const {result} = await processTestRequest(
|
|
778
|
+
'because accessibility', {description: 'Mixed Outcomes Page', url: 'https://example.com/drifted'}
|
|
779
|
+
);
|
|
780
|
+
assert.equal(result, 'reportExists');
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
test('processTestRequest returns a reportExists error when a report exists about a page with an equivalent URL', async () => {
|
|
784
|
+
// The URL differs from that of the report only by a www prefix, a trailing slash, and case.
|
|
785
|
+
const {result} = await processTestRequest(
|
|
786
|
+
'because accessibility', {description: 'Drifted Page', url: 'https://www.Example.com/mixed/'}
|
|
787
|
+
);
|
|
788
|
+
assert.equal(result, 'reportExists');
|
|
789
|
+
});
|
|
790
|
+
|
|
776
791
|
test('processTestRequest returns a queueFull error once the pending-request queue is full', {timeout: 500}, async () => {
|
|
777
792
|
// Fill testRequests.json with 20 pending requests, all for the same URL, so the total
|
|
778
793
|
// pending count (summed across all URLs' request arrays) reaches the cap directly,
|
|
@@ -972,6 +987,23 @@ test('processTestRequest returns "url" for a page matching a queued job by URL',
|
|
|
972
987
|
}
|
|
973
988
|
});
|
|
974
989
|
|
|
990
|
+
test('processTestRequest returns "url" for a page matching a queued job by an equivalent URL', async () => {
|
|
991
|
+
const queuedPath = path.join(jobsPath(), 'queue', 'que.json');
|
|
992
|
+
await fs.writeFile(queuedPath, getJSON({
|
|
993
|
+
target: {what: 'Some Queued Page', url: 'https://example.com/queued-url'}
|
|
994
|
+
}));
|
|
995
|
+
try {
|
|
996
|
+
// The URL differs from that of the job only by a www prefix, a trailing slash, and case.
|
|
997
|
+
const {result} = await processTestRequest(
|
|
998
|
+
'because', {description: 'A Different Page', url: 'https://www.Example.com/queued-url/'}
|
|
999
|
+
);
|
|
1000
|
+
assert.equal(result, 'url');
|
|
1001
|
+
}
|
|
1002
|
+
finally {
|
|
1003
|
+
await fs.unlink(queuedPath);
|
|
1004
|
+
}
|
|
1005
|
+
});
|
|
1006
|
+
|
|
975
1007
|
test('processTestRequest returns "nonreport" when the cited report does not exist', async () => {
|
|
976
1008
|
// Cite a timeStamp/jobID that does not correspond to any available report.
|
|
977
1009
|
const {result} = await processTestRequest('because', {timeStamp: '999999T9999', jobID: 'xxx'});
|
|
@@ -1682,6 +1714,20 @@ test('orderJob returns a reportExists error when a report already exists for the
|
|
|
1682
1714
|
assert.equal(result, 'reportExists');
|
|
1683
1715
|
});
|
|
1684
1716
|
|
|
1717
|
+
test('orderJob returns a reportExists error when a report exists about a page with the same description', async () => {
|
|
1718
|
+
const {result} = await orderJob(
|
|
1719
|
+
{description: 'Mixed Outcomes Page', url: 'https://example.com/drifted'}, 'because accessibility matters here'
|
|
1720
|
+
);
|
|
1721
|
+
assert.equal(result, 'reportExists');
|
|
1722
|
+
});
|
|
1723
|
+
|
|
1724
|
+
test('orderJob returns a reportExists error when a report exists about a page with the same URL', async () => {
|
|
1725
|
+
const {result} = await orderJob(
|
|
1726
|
+
{description: 'Drifted Page', url: 'https://example.com/mixed'}, 'because accessibility matters here'
|
|
1727
|
+
);
|
|
1728
|
+
assert.equal(result, 'reportExists');
|
|
1729
|
+
});
|
|
1730
|
+
|
|
1685
1731
|
test('orderJob returns "description" for a page matching a claimed job by description', async () => {
|
|
1686
1732
|
const claimedPath = path.join(jobsPath(), 'claimed', 'clm.json');
|
|
1687
1733
|
await fs.writeFile(claimedPath, getJSON({
|
package/util.ts
CHANGED
|
@@ -77,8 +77,8 @@ export type TestRequest = {
|
|
|
77
77
|
// Test requests by URL.
|
|
78
78
|
export type TestRequests = Record<string, TestRequest[]>;
|
|
79
79
|
|
|
80
|
-
// Test request addition result. 'reportExists' means a report already exists
|
|
81
|
-
// page a newTest request or order named (an outcome), distinct from the 'retest'
|
|
80
|
+
// Test request addition result. 'reportExists' means a report already exists about a
|
|
81
|
+
// page with the description or URL that a newTest request or order named (an outcome), distinct from the 'retest'
|
|
82
82
|
// requestType discriminant elsewhere in this file, which means the caller asked to
|
|
83
83
|
// retest a page that already has a report (a request kind); the two must not be
|
|
84
84
|
// confused with one another.
|
|
@@ -122,6 +122,7 @@ export type AnnotatedAct = Act & {
|
|
|
122
122
|
// and the else/after-return branch is narrowed to UsableReport.
|
|
123
123
|
export type UsableReport = Omit<Report, 'target' | 'catalog' | 'jobData' | 'acts'> & {
|
|
124
124
|
error?: never;
|
|
125
|
+
id: string;
|
|
125
126
|
target: {what: string; url: string};
|
|
126
127
|
catalog: Catalog;
|
|
127
128
|
jobData: NonNullable<Report['jobData']> & {endTime: string; issuelessRules?: string[]};
|
|
@@ -841,8 +842,9 @@ const getApprovedJobProperty = async (
|
|
|
841
842
|
// Return this.
|
|
842
843
|
return 'description';
|
|
843
844
|
}
|
|
844
|
-
// Otherwise, if a job with
|
|
845
|
-
|
|
845
|
+
// Otherwise, if a job with an equivalent URL is in the category:
|
|
846
|
+
const miniURL = minifyURL(url);
|
|
847
|
+
if (jobsData.some(job => minifyURL(job.url) === miniURL)) {
|
|
846
848
|
// Return this.
|
|
847
849
|
return 'url';
|
|
848
850
|
}
|
|
@@ -951,13 +953,11 @@ export const processTestRequest = (
|
|
|
951
953
|
// Return this.
|
|
952
954
|
return {result: 'superseded', description, url};
|
|
953
955
|
}
|
|
954
|
-
// Otherwise, if the request is to test a new page
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
return {result: 'reportExists', description, url};
|
|
960
|
-
}
|
|
956
|
+
// Otherwise, if the request is to test a new page and a report already exists about a
|
|
957
|
+
// page with the same description or URL:
|
|
958
|
+
if (requestType === 'newTest' && await isReportAvailable(description, url)) {
|
|
959
|
+
// Return this.
|
|
960
|
+
return {result: 'reportExists', description, url};
|
|
961
961
|
}
|
|
962
962
|
// Otherwise, i.e. if the request is genuinely new, if the queue of requests awaiting
|
|
963
963
|
// approval is already full (a cap of 0 means no limit, so the queue is never full):
|
|
@@ -1070,13 +1070,11 @@ export const orderJob = (
|
|
|
1070
1070
|
// Return this.
|
|
1071
1071
|
return {result: 'superseded', description, url};
|
|
1072
1072
|
}
|
|
1073
|
-
// Otherwise, if the order is to test a new page
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
return {result: 'reportExists', description, url};
|
|
1079
|
-
}
|
|
1073
|
+
// Otherwise, if the order is to test a new page and a report already exists about a
|
|
1074
|
+
// page with the same description or URL:
|
|
1075
|
+
if (!('timeStamp' in target) && await isReportAvailable(description, url)) {
|
|
1076
|
+
// Return this.
|
|
1077
|
+
return {result: 'reportExists', description, url};
|
|
1080
1078
|
}
|
|
1081
1079
|
// Otherwise, if the job queue is already full (a cap of 0 means no limit, so the
|
|
1082
1080
|
// queue is never full):
|
|
@@ -14,28 +14,15 @@
|
|
|
14
14
|
<main>
|
|
15
15
|
<h1><a href="/">Kilotest</a>: Diagnoses of <q>__issue__</q> violation by HTML element __catalogIndex__ on __target__ page</h1>
|
|
16
16
|
<h2>Basics</h2>
|
|
17
|
-
<h3>About the
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
</
|
|
22
|
-
|
|
23
|
-
<ul>
|
|
24
|
-
<li>Tag name: <code>__tagName__</code></li>
|
|
25
|
-
<li>Text: __text__</li>
|
|
26
|
-
<li>Start tag: <code>__startTag__</code></li>
|
|
27
|
-
<li>XPath: <code>__pathID__</code></li>
|
|
28
|
-
<li>Bounding box: __box__</li>
|
|
29
|
-
</ul>
|
|
17
|
+
<h3>About the page</h3>
|
|
18
|
+
__pageFacts__
|
|
19
|
+
<h3>About the issue</h3>
|
|
20
|
+
__issueFacts__
|
|
21
|
+
<h3>About the violator</h3>
|
|
22
|
+
__violatorFacts__
|
|
30
23
|
<ul class="nav">
|
|
31
24
|
__takeMeThere__
|
|
32
25
|
</ul>
|
|
33
|
-
<h3>About the <q>__issue__</q> issue</h3>
|
|
34
|
-
<ul>
|
|
35
|
-
<li>Why it matters: __why__</li>
|
|
36
|
-
<li>Priority: __priority__</li>
|
|
37
|
-
<li>Related WCAG standard: __wcag__</li>
|
|
38
|
-
</ul>
|
|
39
26
|
<h2>Diagnoses</h2>
|
|
40
27
|
<p>Here is how rule engines diagnose the <q>__issue__</q> issue for HTML element __catalogIndex__ of the __target__ page.</p>
|
|
41
28
|
<ol>
|
|
@@ -14,10 +14,12 @@ import {
|
|
|
14
14
|
ruleEngines
|
|
15
15
|
} from '../../util.ts';
|
|
16
16
|
import {
|
|
17
|
+
getIssueFactsLines,
|
|
17
18
|
getPageDataStrings,
|
|
19
|
+
getPageFactsLines,
|
|
20
|
+
getReportData,
|
|
18
21
|
getTextFragmentHref,
|
|
19
|
-
|
|
20
|
-
getWeightName
|
|
22
|
+
getViolatorFactsLines
|
|
21
23
|
} from '../util.ts';
|
|
22
24
|
import {issues as issueSpecs} from 'testaro-issues';
|
|
23
25
|
|
|
@@ -32,37 +34,33 @@ const populateQuery = async (
|
|
|
32
34
|
pathID: string | null,
|
|
33
35
|
query: Record<string, any>
|
|
34
36
|
) => {
|
|
35
|
-
// Get
|
|
36
|
-
const
|
|
37
|
+
// Get the report.
|
|
38
|
+
const report = await getReport(timeStamp, jobID);
|
|
37
39
|
// If this failed:
|
|
38
|
-
if (
|
|
40
|
+
if (isReportError(report)) {
|
|
39
41
|
// Populate the query with the reason.
|
|
40
|
-
query.error =
|
|
42
|
+
query.error = report.error;
|
|
41
43
|
// Stop populating the query.
|
|
42
44
|
return;
|
|
43
45
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const report = await getReport(timeStamp, jobID);
|
|
46
|
+
// Otherwise, i.e. if it succeeded, get descriptions of the page facts.
|
|
47
|
+
const pageDataStrings = getPageDataStrings(report);
|
|
47
48
|
// If this failed:
|
|
48
|
-
if (
|
|
49
|
+
if (pageDataStrings.error !== undefined) {
|
|
49
50
|
// Populate the query with the reason.
|
|
50
|
-
query.error =
|
|
51
|
+
query.error = pageDataStrings.error;
|
|
51
52
|
// Stop populating the query.
|
|
52
53
|
return;
|
|
53
54
|
}
|
|
55
|
+
const {testInfo, url, description} = pageDataStrings;
|
|
54
56
|
const {catalog} = report;
|
|
55
|
-
// Otherwise, i.e. if it succeeded, get the catalog item of the specified violator.
|
|
56
|
-
const catalogItem = catalog[catalogIndex];
|
|
57
|
-
const boxID = catalogItem?.boxID;
|
|
58
|
-
const startTag = catalogItem?.startTag;
|
|
59
|
-
const tagName = catalogItem?.tagName;
|
|
60
|
-
const text = catalogItem?.text;
|
|
61
57
|
query.catalogIndex = catalogIndex;
|
|
62
58
|
const lines: string[] = [];
|
|
63
59
|
const margin = ' '.repeat(6);
|
|
60
|
+
const catalogItem = catalog[catalogIndex];
|
|
61
|
+
// If the violator has a linkable text item, add a take-me-there link.
|
|
64
62
|
if (catalogIndex && catalogItem?.textLinkable) {
|
|
65
|
-
const href = getTextFragmentHref(text as string, url);
|
|
63
|
+
const href = getTextFragmentHref(catalogItem.text as string, url);
|
|
66
64
|
const label = `Take me to element ${catalogIndex} on the page (in a new tab)`;
|
|
67
65
|
const link = `<a href="${href}" target="_blank" aria-label="${label}">Take me there</a>`;
|
|
68
66
|
query.takeMeThere = `${margin} <p>${link}</p>`;
|
|
@@ -70,10 +68,10 @@ const populateQuery = async (
|
|
|
70
68
|
else {
|
|
71
69
|
query.takeMeThere = '';
|
|
72
70
|
}
|
|
73
|
-
// Add facts about the
|
|
71
|
+
// Add facts about the page to the query.
|
|
74
72
|
query.target = description;
|
|
75
|
-
query.urlLink = urlLink;
|
|
76
73
|
query.testInfo = testInfo;
|
|
74
|
+
query.pageFacts = getPageFactsLines(pageDataStrings, getReportData(report), margin).join('\n');
|
|
77
75
|
query.issue = issueSpecs[issueID]?.summary;
|
|
78
76
|
// If adding the issue summary failed:
|
|
79
77
|
if (!query.issue) {
|
|
@@ -82,29 +80,9 @@ const populateQuery = async (
|
|
|
82
80
|
// Stop populating the query.
|
|
83
81
|
return;
|
|
84
82
|
}
|
|
85
|
-
// Otherwise, i.e. if it succeeded,
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
query.why = why;
|
|
89
|
-
query.priority = getWeightName(weight);
|
|
90
|
-
query.wcag = `<a href="${getWCAGLink(wcag)}">${wcag}</a>`;
|
|
91
|
-
query.tagName = tagName || 'HTML';
|
|
92
|
-
if (text && !['HTML', 'BODY', 'HEAD', 'SCRIPT', 'STYLE', 'NOSCRIPT'].includes(tagName as string)) {
|
|
93
|
-
const textString = text.split('\n').join(' … ');
|
|
94
|
-
query.text = `<q>${htmlSafe(textString)}</q>`;
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
query.text = '[not applicable]';
|
|
98
|
-
}
|
|
99
|
-
query.startTag = htmlSafe(startTag ?? '') || '[not obtained]';
|
|
100
|
-
query.pathID = pathID || '[not obtained]';
|
|
101
|
-
if (boxID) {
|
|
102
|
-
const dims = boxID.split(':');
|
|
103
|
-
query.box = `x = ${dims[0]}, y = ${dims[1]}, width = ${dims[2]}, height = ${dims[3]}`;
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
query.box = '[not obtained]';
|
|
107
|
-
}
|
|
83
|
+
// Otherwise, i.e. if it succeeded, add the issue-facts and violator-facts lists to the query.
|
|
84
|
+
query.issueFacts = getIssueFactsLines(report, issueID, margin).join('\n');
|
|
85
|
+
query.violatorFacts = getViolatorFactsLines(report, issueID, catalogIndex, pathID, margin).join('\n');
|
|
108
86
|
// Initialize an array of diagnoses.
|
|
109
87
|
const diagnoses: any[] = [];
|
|
110
88
|
// For each violating standard instance that pertains to this combination of issue and violator:
|
|
@@ -33,7 +33,7 @@ mock.module('../../util.ts', {
|
|
|
33
33
|
mock.module('../util.ts', {
|
|
34
34
|
exports: {
|
|
35
35
|
...realWebUtil,
|
|
36
|
-
getPageDataStrings:
|
|
36
|
+
getPageDataStrings: (...args: any[]) => {
|
|
37
37
|
if (pageDataStringsOverride !== null) {
|
|
38
38
|
return pageDataStringsOverride;
|
|
39
39
|
}
|
|
@@ -108,7 +108,7 @@ test('listDiagnoses returns ok with no diagnoses for a nonexistent catalog index
|
|
|
108
108
|
assert.ok(!diagnosisTexts.some(t => t.includes('The link does not have an accessible name')));
|
|
109
109
|
});
|
|
110
110
|
|
|
111
|
-
test('listDiagnoses returns an error when getReport fails
|
|
111
|
+
test('listDiagnoses returns an error when getReport fails', async () => {
|
|
112
112
|
getReportCallCount = 0;
|
|
113
113
|
failGetReportOnCall = 1;
|
|
114
114
|
try {
|
|
@@ -121,6 +121,18 @@ test('listDiagnoses returns an error when getReport fails after getPageDataStrin
|
|
|
121
121
|
}
|
|
122
122
|
});
|
|
123
123
|
|
|
124
|
+
test('listDiagnoses returns an error when getPageDataStrings fails after getReport succeeds', async () => {
|
|
125
|
+
pageDataStringsOverride = {error: 'Page data strings error'};
|
|
126
|
+
try {
|
|
127
|
+
const result = await answer('linkNoText/260101T0000/mix/0');
|
|
128
|
+
assert.equal(result.status, 'error');
|
|
129
|
+
assert.equal(result.message, 'Page data strings error');
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
pageDataStringsOverride = null;
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
124
136
|
test('listDiagnoses includes a take-me-there link for a text-linkable catalog item', async () => {
|
|
125
137
|
const dbDir = (await import('../../test/dbFixture.ts')).fixtureDBDir;
|
|
126
138
|
const reportsDir = path.join(dbDir, 'reports');
|
|
@@ -13,20 +13,20 @@
|
|
|
13
13
|
<body>
|
|
14
14
|
<main>
|
|
15
15
|
<h1><a href="/">Kilotest</a>: Issues reported for __target__</h1>
|
|
16
|
-
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
|
|
17
|
+
<h2>About the tests</h2>
|
|
18
|
+
<details>
|
|
19
|
+
<summary>How to deal with the issues</summary>
|
|
20
|
+
<h3>Do it yourself</h3>
|
|
21
|
+
<p>Explore the issues below in as much detail as you want. Decide which ones are actual defects. Plan and implement corrections.</p>
|
|
22
|
+
<h3>Or use AI</h3>
|
|
23
|
+
<p>Make Kilotest available to your AI assistant. There are <a href="/tutorialAI.html">step-by-step instructions</a> for this.</p>
|
|
24
|
+
<p>After that, you can ask:</p>
|
|
25
|
+
<blockquote>Kilotest reports defects on the __target__ web page. Summarize them and propose a plan to resolve the genuine ones.</blockquote>
|
|
26
|
+
</details>
|
|
27
|
+
__pageFacts__
|
|
22
28
|
<p><a href="/requestRetestForm.html/__timeStamp__/__jobID__">Should Kilotest retest the page?</a></p>
|
|
23
29
|
<h2>Issues reported</h2>
|
|
24
|
-
<h3>Summary</h3>
|
|
25
|
-
<p>__reporterCount__ reported issues (__reporters__)</p>
|
|
26
|
-
<p>__issueCount__ reported (__highestCount__ highest, __highCount__ high, __lowCount__ low, __lowestCount__ lowest priority)</p>
|
|
27
|
-
<p>__violatorCount__ reported</p>
|
|
28
|
-
<p>Download the <a href="/fullReport.json/__timeStamp__/__jobID__">full technical report</a></p>
|
|
29
|
-
<h3>Details</h3>
|
|
30
30
|
<details>
|
|
31
31
|
<summary>Highest priority: __highestCount__</summary>
|
|
32
32
|
<ul class="headed">
|