@jrpool/kilotest 46.0.8 → 46.0.14
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/.devin/config.local.json +4 -1
- package/.github/workflows/{smoke-test.yml → ci.yml} +8 -2
- package/README.md +14 -8
- package/alerts.test.js +137 -0
- package/api/listDiagnoses.test.js +44 -0
- package/api/listIssues.js +1 -1
- package/api/listIssues.test.js +10 -0
- package/api/listReports.test.js +6 -2
- package/api/listViolators.test.js +35 -0
- package/api/requestTest.test.js +7 -0
- package/api/routes.test.js +63 -0
- package/api/util.js +9 -11
- package/api/util.test.js +68 -2
- package/apiIntegration.test.js +13 -3
- package/docs/AI-TOOLS.md +41 -158
- package/docs/{Smoke Test.json → CI.json} +4 -3
- package/index.html +3 -3
- package/index.js +64 -77
- package/index.test.js +1152 -0
- package/indexBalances.test.js +188 -0
- package/indexFallback.test.js +33 -0
- package/indexServe.test.js +59 -0
- package/indexWorkers.test.js +79 -0
- package/lhm.plugin.json +3218 -0
- package/llms-full.txt +6 -7
- package/llms.txt +1 -1
- package/mcp.js +2 -2
- package/mcp.test.js +281 -0
- package/openapi.yaml +1 -1
- package/package.json +10 -5
- package/routing.test.js +13 -3
- package/scripts/generate-openapi.js +1 -1
- package/test/fixtures/buildFixtures.js +83 -0
- package/test/fixtures/db/reports/260101T0008-mul.json +137 -0
- package/test/fixtures/db/reports/260101T0009-brd.json +147 -0
- package/test/setup.cjs +14 -0
- package/util.js +16 -6
- package/util.test.js +886 -3
- package/web/ai0BalanceForm/ai0BalanceForm.test.js +88 -0
- package/web/expungeReportsForm/expungeReportsForm.test.js +114 -0
- package/web/hideReportForm/hideReportForm.test.js +82 -0
- package/web/listDiagnoses/listDiagnoses.test.js +160 -0
- package/web/listIssues/listIssues.test.js +109 -0
- package/web/listReports/listReports.test.js +139 -3
- package/web/listRules/listRules.test.js +103 -0
- package/web/listTopIssues/listTopIssues.test.js +126 -0
- package/web/listViolators/index.js +1 -2
- package/web/listViolators/listViolators.test.js +256 -0
- package/web/pruneReportsForm/pruneReportsForm.test.js +86 -0
- package/web/reannotate/reannotate.test.js +144 -0
- package/web/reannotateForm/reannotateForm.test.js +134 -0
- package/web/renewWCAG/renewWCAG.test.js +124 -0
- package/web/requestRetest/requestRetest.test.js +119 -0
- package/web/requestRetestForm/requestRetestForm.test.js +62 -0
- package/web/requestTest/requestTest.test.js +115 -0
- package/web/requestTestForm/requestTestForm.test.js +40 -0
- package/web/rewindReportsForm/rewindReportsForm.test.js +86 -0
- package/web/tutorial/index.html +3 -3
- package/web/tutorial/tutorial.test.js +97 -0
- package/web/unhideReportForm/unhideReportForm.test.js +135 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/*
|
|
2
|
+
ai0BalanceForm.test.js
|
|
3
|
+
Unit tests for web/ai0BalanceForm/index.js.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// IMPORTS
|
|
7
|
+
|
|
8
|
+
const {test, before, after} = require('node:test');
|
|
9
|
+
const assert = require('node:assert/strict');
|
|
10
|
+
const path = require('node:path');
|
|
11
|
+
const fs = require('fs/promises');
|
|
12
|
+
const {parse} = require('node-html-parser');
|
|
13
|
+
const {answer} = require('./index');
|
|
14
|
+
|
|
15
|
+
// CONSTANTS
|
|
16
|
+
|
|
17
|
+
const balancePath = path.join(__dirname, '..', '..', 'ai0Balance.json');
|
|
18
|
+
|
|
19
|
+
// SETUP AND TEARDOWN
|
|
20
|
+
|
|
21
|
+
const savedAuthCode = process.env.AUTH_CODE;
|
|
22
|
+
let savedBalance;
|
|
23
|
+
|
|
24
|
+
before(async () => {
|
|
25
|
+
process.env.AUTH_CODE = 'test-auth-code';
|
|
26
|
+
savedBalance = await fs.readFile(balancePath, 'utf8').catch(() => null);
|
|
27
|
+
// Ensure the balance file exists so the try block in index.js is covered.
|
|
28
|
+
if (savedBalance === null) {
|
|
29
|
+
await fs.writeFile(balancePath, '{"balance":0}\n');
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
after(async () => {
|
|
34
|
+
if (savedAuthCode !== undefined) {
|
|
35
|
+
process.env.AUTH_CODE = savedAuthCode;
|
|
36
|
+
}
|
|
37
|
+
if (savedBalance !== null) {
|
|
38
|
+
await fs.writeFile(balancePath, savedBalance);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
await fs.unlink(balancePath).catch(() => {});
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// TESTS
|
|
46
|
+
|
|
47
|
+
test('ai0BalanceForm displays the current balance when no newBalance is submitted', async () => {
|
|
48
|
+
const result = await answer(null, '');
|
|
49
|
+
assert.equal(result.status, 'ok');
|
|
50
|
+
assert.ok(result.answerPage);
|
|
51
|
+
const html = parse(result.answerPage);
|
|
52
|
+
assert.ok(html.querySelector('title'));
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('ai0BalanceForm returns an error for an invalid auth code', async () => {
|
|
56
|
+
const result = await answer(null, 'authCode=wrong&newBalance=5.00');
|
|
57
|
+
assert.equal(result.status, 'error');
|
|
58
|
+
assert.equal(result.message, 'Invalid authorization code');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('ai0BalanceForm records a valid new balance with valid auth code', async () => {
|
|
62
|
+
const result = await answer(null, 'authCode=test-auth-code&newBalance=2.50');
|
|
63
|
+
assert.equal(result.status, 'ok');
|
|
64
|
+
assert.ok(result.answerPage.includes('$2.5 is the'));
|
|
65
|
+
// Verify the file was written.
|
|
66
|
+
const data = JSON.parse(await fs.readFile(balancePath, 'utf8'));
|
|
67
|
+
assert.equal(data.balance, 2.50);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('ai0BalanceForm does not record an invalid balance', async () => {
|
|
71
|
+
// 150 is out of range (>= 100).
|
|
72
|
+
const result = await answer(null, 'authCode=test-auth-code&newBalance=150');
|
|
73
|
+
assert.equal(result.status, 'ok');
|
|
74
|
+
// The oldBalance should not show the new value.
|
|
75
|
+
assert.ok(!result.answerPage.includes('$150 is the'));
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('ai0BalanceForm shows no-balance message when balance file is missing', async () => {
|
|
79
|
+
await fs.unlink(balancePath).catch(() => {});
|
|
80
|
+
try {
|
|
81
|
+
const result = await answer(null, '');
|
|
82
|
+
assert.equal(result.status, 'ok');
|
|
83
|
+
assert.ok(result.answerPage.includes('There is no'));
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
await fs.writeFile(balancePath, savedBalance || '{"balance":0}');
|
|
87
|
+
}
|
|
88
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/*
|
|
2
|
+
expungeReportsForm.test.js
|
|
3
|
+
Unit tests for web/expungeReportsForm/index.js.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// IMPORTS
|
|
7
|
+
|
|
8
|
+
const {test, before, after} = require('node:test');
|
|
9
|
+
const assert = require('node:assert/strict');
|
|
10
|
+
const path = require('node:path');
|
|
11
|
+
const fs = require('fs/promises');
|
|
12
|
+
const {parse} = require('node-html-parser');
|
|
13
|
+
const {answer} = require('./index');
|
|
14
|
+
const {reportsPath} = require('../../util');
|
|
15
|
+
|
|
16
|
+
// SETUP AND TEARDOWN
|
|
17
|
+
|
|
18
|
+
const savedAuthCode = process.env.AUTH_CODE;
|
|
19
|
+
const savedDBDir = process.env.DB_DIR;
|
|
20
|
+
const fixtureDBDir = path.join(__dirname, '..', '..', 'test', 'fixtures', 'db');
|
|
21
|
+
|
|
22
|
+
before(() => {
|
|
23
|
+
process.env.AUTH_CODE = 'test-auth-code';
|
|
24
|
+
process.env.DB_DIR = fixtureDBDir;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
after(async () => {
|
|
28
|
+
if (savedAuthCode !== undefined) {
|
|
29
|
+
process.env.AUTH_CODE = savedAuthCode;
|
|
30
|
+
}
|
|
31
|
+
if (savedDBDir !== undefined) {
|
|
32
|
+
process.env.DB_DIR = savedDBDir;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// TESTS
|
|
37
|
+
|
|
38
|
+
test('expungeReportsForm displays a list of reports when no submission', async () => {
|
|
39
|
+
const result = await answer(null, '');
|
|
40
|
+
assert.equal(result.status, 'ok');
|
|
41
|
+
assert.ok(result.answerPage);
|
|
42
|
+
const html = parse(result.answerPage);
|
|
43
|
+
assert.ok(html.querySelector('title'));
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('expungeReportsForm returns an error for an invalid auth code', async () => {
|
|
47
|
+
const result = await answer(null, 'authCode=wrong&report=260101T0001-ct');
|
|
48
|
+
assert.equal(result.status, 'error');
|
|
49
|
+
assert.equal(result.message, 'Invalid authorization code');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('expungeReportsForm returns an error when deleting a nonexistent report', async () => {
|
|
53
|
+
const result = await answer(null, 'authCode=test-auth-code&report=999999T9999-nope');
|
|
54
|
+
assert.equal(result.status, 'error');
|
|
55
|
+
assert.ok(result.message.includes('Deleting sole reports'));
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('expungeReportsForm deletes a sole report with valid auth code', async () => {
|
|
59
|
+
// 260101T0001-ct is the only report for its URL (canttell).
|
|
60
|
+
const reportPath = path.join(reportsPath(), '260101T0001-ct.json');
|
|
61
|
+
const backup = await fs.readFile(reportPath, 'utf8');
|
|
62
|
+
try {
|
|
63
|
+
const result = await answer(null, 'authCode=test-auth-code&report=260101T0001-ct');
|
|
64
|
+
assert.equal(result.status, 'ok');
|
|
65
|
+
await fs.access(reportPath).then(
|
|
66
|
+
() => {throw new Error('Report should have been deleted');},
|
|
67
|
+
() => {}
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
await fs.writeFile(reportPath, backup);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('expungeReportsForm returns an error when a report file is corrupt', async () => {
|
|
76
|
+
const reportPath = path.join(reportsPath(), '260101T0001-ct.json');
|
|
77
|
+
const backup = await fs.readFile(reportPath, 'utf8');
|
|
78
|
+
try {
|
|
79
|
+
await fs.writeFile(reportPath, 'not valid json');
|
|
80
|
+
const result = await answer(null, '');
|
|
81
|
+
assert.equal(result.status, 'error');
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
await fs.writeFile(reportPath, backup);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('expungeReportsForm shows no-deletable message when every URL has at least 2 reports', async () => {
|
|
89
|
+
// Create a temp DB with two reports sharing the same URL.
|
|
90
|
+
const os = require('node:os');
|
|
91
|
+
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'expunge-test-'));
|
|
92
|
+
const tmpReports = path.join(tmpDir, 'reports');
|
|
93
|
+
await fs.mkdir(tmpReports);
|
|
94
|
+
// Copy two reports that share the same URL.
|
|
95
|
+
const mixReport = await fs.readFile(path.join(fixtureDBDir, 'reports', '260101T0000-mix.json'), 'utf8');
|
|
96
|
+
const newReport = await fs.readFile(path.join(fixtureDBDir, 'reports', '260202T0000-new.json'), 'utf8');
|
|
97
|
+
await fs.writeFile(path.join(tmpReports, '260101T0000-mix.json'), mixReport);
|
|
98
|
+
await fs.writeFile(path.join(tmpReports, '260202T0000-new.json'), newReport);
|
|
99
|
+
const savedDBDir = process.env.DB_DIR;
|
|
100
|
+
process.env.DB_DIR = tmpDir;
|
|
101
|
+
try {
|
|
102
|
+
delete require.cache[require.resolve('./index')];
|
|
103
|
+
const {answer} = require('./index');
|
|
104
|
+
const result = await answer(null, '');
|
|
105
|
+
assert.equal(result.status, 'ok');
|
|
106
|
+
assert.ok(result.answerPage.includes('no reports to delete'));
|
|
107
|
+
assert.ok(result.answerPage.includes('disabled'));
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
process.env.DB_DIR = savedDBDir;
|
|
111
|
+
delete require.cache[require.resolve('./index')];
|
|
112
|
+
await fs.rm(tmpDir, {recursive: true}).catch(() => {});
|
|
113
|
+
}
|
|
114
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/*
|
|
2
|
+
hideReportForm.test.js
|
|
3
|
+
Unit tests for web/hideReportForm/index.js.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// IMPORTS
|
|
7
|
+
|
|
8
|
+
const {test, before, after} = require('node:test');
|
|
9
|
+
const assert = require('node:assert/strict');
|
|
10
|
+
const path = require('node:path');
|
|
11
|
+
const fs = require('fs/promises');
|
|
12
|
+
const {parse} = require('node-html-parser');
|
|
13
|
+
const {answer} = require('./index');
|
|
14
|
+
const {reportsPath, hiddenReportsPath} = require('../../util');
|
|
15
|
+
|
|
16
|
+
// SETUP AND TEARDOWN
|
|
17
|
+
|
|
18
|
+
const savedAuthCode = process.env.AUTH_CODE;
|
|
19
|
+
const savedDBDir = process.env.DB_DIR;
|
|
20
|
+
const fixtureDBDir = path.join(__dirname, '..', '..', 'test', 'fixtures', 'db');
|
|
21
|
+
|
|
22
|
+
before(() => {
|
|
23
|
+
process.env.AUTH_CODE = 'test-auth-code';
|
|
24
|
+
process.env.DB_DIR = fixtureDBDir;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
after(async () => {
|
|
28
|
+
if (savedAuthCode !== undefined) {
|
|
29
|
+
process.env.AUTH_CODE = savedAuthCode;
|
|
30
|
+
}
|
|
31
|
+
if (savedDBDir !== undefined) {
|
|
32
|
+
process.env.DB_DIR = savedDBDir;
|
|
33
|
+
}
|
|
34
|
+
// Restore any moved report.
|
|
35
|
+
const reportFile = path.join(hiddenReportsPath(), '260101T0009-brd.json');
|
|
36
|
+
const originalPath = path.join(reportsPath(), '260101T0009-brd.json');
|
|
37
|
+
try {
|
|
38
|
+
await fs.rename(reportFile, originalPath);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Report may already be in place.
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// TESTS
|
|
46
|
+
|
|
47
|
+
test('hideReportForm displays a list of reports when no submission', async () => {
|
|
48
|
+
const result = await answer(null, '');
|
|
49
|
+
assert.equal(result.status, 'ok');
|
|
50
|
+
assert.ok(result.answerPage);
|
|
51
|
+
const html = parse(result.answerPage);
|
|
52
|
+
const radios = html.querySelectorAll('input[type="radio"]');
|
|
53
|
+
assert.ok(radios.length > 0);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('hideReportForm returns an error for an invalid auth code', async () => {
|
|
57
|
+
const result = await answer(null, 'authCode=wrong&report=260101T0009-brd');
|
|
58
|
+
assert.equal(result.status, 'error');
|
|
59
|
+
assert.equal(result.message, 'Invalid authorization code');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('hideReportForm hides a report with valid auth code', async () => {
|
|
63
|
+
const reportPath = path.join(reportsPath(), '260101T0009-brd.json');
|
|
64
|
+
const hiddenPath = path.join(hiddenReportsPath(), '260101T0009-brd.json');
|
|
65
|
+
// Ensure the report exists in reports and not in hidden.
|
|
66
|
+
try {
|
|
67
|
+
await fs.rename(hiddenPath, reportPath);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Report may already be in reports.
|
|
71
|
+
}
|
|
72
|
+
const result = await answer(null, 'authCode=test-auth-code&report=260101T0009-brd');
|
|
73
|
+
assert.equal(result.status, 'ok');
|
|
74
|
+
// The report should now be in the hidden directory.
|
|
75
|
+
await fs.access(hiddenPath);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('hideReportForm returns an error when hiding a nonexistent report', async () => {
|
|
79
|
+
const result = await answer(null, 'authCode=test-auth-code&report=999999T9999-nope');
|
|
80
|
+
assert.equal(result.status, 'error');
|
|
81
|
+
assert.ok(result.message.includes('Hiding report'));
|
|
82
|
+
});
|
|
@@ -8,7 +8,31 @@
|
|
|
8
8
|
const {test, before, after} = require('node:test');
|
|
9
9
|
const assert = require('node:assert/strict');
|
|
10
10
|
const path = require('node:path');
|
|
11
|
+
const fs = require('node:fs/promises');
|
|
11
12
|
const {parse} = require('node-html-parser');
|
|
13
|
+
|
|
14
|
+
// Monkey-patch util functions before requiring index, so that index.js
|
|
15
|
+
// destructures the patched versions.
|
|
16
|
+
const util = require('../../util');
|
|
17
|
+
const realGetReport = util.getReport;
|
|
18
|
+
const realGetPageDataStrings = util.getPageDataStrings;
|
|
19
|
+
let getReportCallCount = 0;
|
|
20
|
+
let failGetReportOnCall = -1;
|
|
21
|
+
let pageDataStringsOverride = null;
|
|
22
|
+
util.getReport = async (...args) => {
|
|
23
|
+
getReportCallCount++;
|
|
24
|
+
if (getReportCallCount === failGetReportOnCall) {
|
|
25
|
+
return {error: 'Report is invalid (test override)'};
|
|
26
|
+
}
|
|
27
|
+
return realGetReport(...args);
|
|
28
|
+
};
|
|
29
|
+
util.getPageDataStrings = async (...args) => {
|
|
30
|
+
if (pageDataStringsOverride !== null) {
|
|
31
|
+
return pageDataStringsOverride;
|
|
32
|
+
}
|
|
33
|
+
return realGetPageDataStrings(...args);
|
|
34
|
+
};
|
|
35
|
+
|
|
12
36
|
const {answer} = require('./index');
|
|
13
37
|
|
|
14
38
|
// SETUP AND TEARDOWN
|
|
@@ -72,3 +96,139 @@ test('listDiagnoses returns ok with no diagnoses for a nonexistent catalog index
|
|
|
72
96
|
const diagnosisTexts = diagnosisItems.map(li => li.text);
|
|
73
97
|
assert.ok(!diagnosisTexts.some(t => t.includes('The link does not have an accessible name')));
|
|
74
98
|
});
|
|
99
|
+
|
|
100
|
+
test('listDiagnoses returns an error when getReport fails after getPageDataStrings succeeds', async () => {
|
|
101
|
+
getReportCallCount = 0;
|
|
102
|
+
failGetReportOnCall = 1;
|
|
103
|
+
try {
|
|
104
|
+
const result = await answer('linkNoText/260101T0000/mix/0');
|
|
105
|
+
assert.equal(result.status, 'error');
|
|
106
|
+
assert.ok(result.message.includes('invalid'));
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
failGetReportOnCall = -1;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('listDiagnoses includes a take-me-there link for a text-linkable catalog item', async () => {
|
|
114
|
+
const dbDir = path.join(__dirname, '..', '..', 'test', 'fixtures', 'db');
|
|
115
|
+
const reportsDir = path.join(dbDir, 'reports');
|
|
116
|
+
const reportPath = path.join(reportsDir, '260101T0003-tlk.json');
|
|
117
|
+
try {
|
|
118
|
+
const report = {
|
|
119
|
+
id: '260101T0003-tlk',
|
|
120
|
+
what: 'Text Linkable Page',
|
|
121
|
+
target: {what: 'Text Linkable Page', url: 'https://example.com/textlinkable'},
|
|
122
|
+
acts: [
|
|
123
|
+
{
|
|
124
|
+
type: 'test',
|
|
125
|
+
which: 'axe',
|
|
126
|
+
result: {
|
|
127
|
+
standardResult: {
|
|
128
|
+
instances: [
|
|
129
|
+
{
|
|
130
|
+
ruleID: 'r11',
|
|
131
|
+
what: 'The link does not have an accessible name',
|
|
132
|
+
outcome: 'failed',
|
|
133
|
+
count: 1,
|
|
134
|
+
catalogIndex: '0',
|
|
135
|
+
issueID: 'linkNoText'
|
|
136
|
+
}
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
],
|
|
142
|
+
jobData: {
|
|
143
|
+
startTime: '26-01-01T00:00',
|
|
144
|
+
endTime: '26-01-01T00:10',
|
|
145
|
+
preventions: {},
|
|
146
|
+
issuelessRules: []
|
|
147
|
+
},
|
|
148
|
+
catalog: {
|
|
149
|
+
'0': {
|
|
150
|
+
tagName: 'A',
|
|
151
|
+
id: '',
|
|
152
|
+
startTag: '<a>',
|
|
153
|
+
text: 'Click here',
|
|
154
|
+
textLinkable: true,
|
|
155
|
+
boxID: '10:20:80:30',
|
|
156
|
+
pathID: '/html/body/a[1]'
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
images: {},
|
|
160
|
+
checkpoints: []
|
|
161
|
+
};
|
|
162
|
+
await fs.writeFile(reportPath, JSON.stringify(report, null, 2));
|
|
163
|
+
const result = await answer('linkNoText/260101T0003/tlk/0');
|
|
164
|
+
assert.equal(result.status, 'ok');
|
|
165
|
+
assert.ok(result.answerPage.includes('Take me there'));
|
|
166
|
+
}
|
|
167
|
+
finally {
|
|
168
|
+
await fs.unlink(reportPath).catch(() => {});
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('listDiagnoses returns an error for an unknown issue with a valid report', async () => {
|
|
173
|
+
const result = await answer('nonexistentIssue/260101T0000/mix/0');
|
|
174
|
+
assert.equal(result.status, 'error');
|
|
175
|
+
assert.equal(result.message, 'Issue not found');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test('listDiagnoses returns an error when report facts are not obtained', async () => {
|
|
179
|
+
pageDataStringsOverride = {
|
|
180
|
+
what: 'Mixed Outcomes Page',
|
|
181
|
+
url: 'https://example.com/mixed',
|
|
182
|
+
urlLink: '<a href="https://example.com/mixed">https://example.com/mixed</a>'
|
|
183
|
+
};
|
|
184
|
+
try {
|
|
185
|
+
const result = await answer('linkNoText/260101T0000/mix/0');
|
|
186
|
+
assert.equal(result.status, 'error');
|
|
187
|
+
assert.equal(result.message, 'Report facts not obtained');
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
pageDataStringsOverride = null;
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test('listDiagnoses handles acts with no standardResult instances gracefully', async () => {
|
|
195
|
+
// Create a temporary report where an act has no standardResult.
|
|
196
|
+
const dbDir = path.join(__dirname, '..', '..', 'test', 'fixtures', 'db');
|
|
197
|
+
const reportPath = path.join(dbDir, 'reports', '260101T0004-nst.json');
|
|
198
|
+
const report = {
|
|
199
|
+
id: '260101T0004-nst',
|
|
200
|
+
target: {what: 'No Standard Result Page', url: 'https://example.com/noresult'},
|
|
201
|
+
acts: [
|
|
202
|
+
{
|
|
203
|
+
type: 'test',
|
|
204
|
+
which: 'axe',
|
|
205
|
+
result: {}
|
|
206
|
+
}
|
|
207
|
+
],
|
|
208
|
+
jobData: {
|
|
209
|
+
startTime: '26-01-01T00:00',
|
|
210
|
+
endTime: '26-01-01T00:10',
|
|
211
|
+
preventions: {},
|
|
212
|
+
issuelessRules: []
|
|
213
|
+
},
|
|
214
|
+
catalog: {
|
|
215
|
+
'0': {
|
|
216
|
+
tagName: 'A',
|
|
217
|
+
id: '',
|
|
218
|
+
startTag: '<a>',
|
|
219
|
+
text: 'Click here',
|
|
220
|
+
pathID: '/html/body/a[1]'
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
images: {},
|
|
224
|
+
checkpoints: []
|
|
225
|
+
};
|
|
226
|
+
await fs.writeFile(reportPath, JSON.stringify(report));
|
|
227
|
+
try {
|
|
228
|
+
const result = await answer('linkNoText/260101T0004/nst/0');
|
|
229
|
+
assert.equal(result.status, 'ok');
|
|
230
|
+
}
|
|
231
|
+
finally {
|
|
232
|
+
await fs.unlink(reportPath).catch(() => {});
|
|
233
|
+
}
|
|
234
|
+
});
|
|
@@ -9,6 +9,29 @@ const {test, before, after} = require('node:test');
|
|
|
9
9
|
const assert = require('node:assert/strict');
|
|
10
10
|
const path = require('node:path');
|
|
11
11
|
const {parse} = require('node-html-parser');
|
|
12
|
+
|
|
13
|
+
// Monkey-patch util functions before requiring index, so that index.js
|
|
14
|
+
// destructures the patched versions.
|
|
15
|
+
const util = require('../../util');
|
|
16
|
+
const realIsHidden = util.isHidden;
|
|
17
|
+
const realGetPageDataStrings = util.getPageDataStrings;
|
|
18
|
+
let isHiddenCallCount = 0;
|
|
19
|
+
let forceHiddenOnCall = -1;
|
|
20
|
+
let pageDataStringsOverride = null;
|
|
21
|
+
util.isHidden = async (timeStamp, jobID) => {
|
|
22
|
+
isHiddenCallCount++;
|
|
23
|
+
if (isHiddenCallCount === forceHiddenOnCall) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
return realIsHidden(timeStamp, jobID);
|
|
27
|
+
};
|
|
28
|
+
util.getPageDataStrings = async (...args) => {
|
|
29
|
+
if (pageDataStringsOverride !== null) {
|
|
30
|
+
return pageDataStringsOverride;
|
|
31
|
+
}
|
|
32
|
+
return realGetPageDataStrings(...args);
|
|
33
|
+
};
|
|
34
|
+
|
|
12
35
|
const {answer} = require('./index');
|
|
13
36
|
|
|
14
37
|
// SETUP AND TEARDOWN
|
|
@@ -78,3 +101,89 @@ test('listIssues for the empty report shows no issue links', async () => {
|
|
|
78
101
|
const violatorLinks = html.querySelectorAll('a[href*="listViolators.html"]');
|
|
79
102
|
assert.equal(violatorLinks.length, 0);
|
|
80
103
|
});
|
|
104
|
+
|
|
105
|
+
test('listIssues returns an error when a report becomes hidden during processing', async () => {
|
|
106
|
+
isHiddenCallCount = 0;
|
|
107
|
+
forceHiddenOnCall = 2;
|
|
108
|
+
try {
|
|
109
|
+
const result = await answer('260101T0000/mix');
|
|
110
|
+
assert.equal(result.status, 'error');
|
|
111
|
+
assert.equal(result.message, 'Report is not available');
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
forceHiddenOnCall = -1;
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('listIssues includes prevention notices for the prevented report', async () => {
|
|
119
|
+
const result = await answer('260101T0006/prv');
|
|
120
|
+
assert.equal(result.status, 'ok');
|
|
121
|
+
assert.ok(result.answerPage.includes('Page not testable by'));
|
|
122
|
+
assert.ok(result.answerPage.includes('page timed out'));
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('listIssues returns an error when getPageDataStrings fails after getData succeeds', async () => {
|
|
126
|
+
pageDataStringsOverride = {error: 'Page data strings error'};
|
|
127
|
+
try {
|
|
128
|
+
const result = await answer('260101T0000/mix');
|
|
129
|
+
assert.equal(result.status, 'error');
|
|
130
|
+
assert.equal(result.message, 'Page data strings error');
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
pageDataStringsOverride = null;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('listIssues returns an error when report facts are not obtained', async () => {
|
|
138
|
+
pageDataStringsOverride = {
|
|
139
|
+
what: 'Mixed Outcomes Page',
|
|
140
|
+
url: 'https://example.com/mixed',
|
|
141
|
+
urlLink: '<a href="https://example.com/mixed">https://example.com/mixed</a>'
|
|
142
|
+
};
|
|
143
|
+
try {
|
|
144
|
+
const result = await answer('260101T0000/mix');
|
|
145
|
+
assert.equal(result.status, 'error');
|
|
146
|
+
assert.equal(result.message, 'Report facts not obtained');
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
pageDataStringsOverride = null;
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test('listIssues shows plural violator count for an issue with multiple violators', async () => {
|
|
154
|
+
// The mul report has linkNoText with 3 violators.
|
|
155
|
+
const result = await answer('260101T0008/mul');
|
|
156
|
+
assert.equal(result.status, 'ok');
|
|
157
|
+
assert.ok(result.answerPage.includes('3 violators were'));
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test('listIssues handles acts with no standardResult instances', async () => {
|
|
161
|
+
// Create a temporary report with an act that has no standardResult.
|
|
162
|
+
const fs = require('node:fs/promises');
|
|
163
|
+
const dbDir = path.join(__dirname, '..', '..', 'test', 'fixtures', 'db');
|
|
164
|
+
const reportPath = path.join(dbDir, 'reports', '260101T0004-nsi.json');
|
|
165
|
+
const report = {
|
|
166
|
+
id: '260101T0004-nsi',
|
|
167
|
+
target: {what: 'No Std Instances Page', url: 'https://example.com/nsi'},
|
|
168
|
+
acts: [
|
|
169
|
+
{type: 'test', which: 'axe', result: {}}
|
|
170
|
+
],
|
|
171
|
+
jobData: {
|
|
172
|
+
startTime: '26-01-01T00:00',
|
|
173
|
+
endTime: '26-01-01T00:10',
|
|
174
|
+
preventions: {},
|
|
175
|
+
issuelessRules: []
|
|
176
|
+
},
|
|
177
|
+
catalog: {},
|
|
178
|
+
images: {},
|
|
179
|
+
checkpoints: []
|
|
180
|
+
};
|
|
181
|
+
await fs.writeFile(reportPath, JSON.stringify(report));
|
|
182
|
+
try {
|
|
183
|
+
const result = await answer('260101T0004/nsi');
|
|
184
|
+
assert.equal(result.status, 'ok');
|
|
185
|
+
}
|
|
186
|
+
finally {
|
|
187
|
+
await fs.unlink(reportPath).catch(() => {});
|
|
188
|
+
}
|
|
189
|
+
});
|