@jrpool/kilotest 46.0.5 → 46.0.7
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/getReport.test.js +62 -0
- package/api/listDiagnoses.test.js +88 -0
- package/api/listIssues.test.js +98 -0
- package/api/listReports.test.js +104 -0
- package/api/listViolators.test.js +73 -0
- package/api/requestFeature.test.js +54 -0
- package/api/requestRetest.test.js +76 -0
- package/api/requestTest.test.js +81 -0
- package/index.js +25 -19
- package/package.json +2 -2
- package/routing.test.js +77 -0
- package/util.js +5 -5
- package/web/listDiagnoses/listDiagnoses.test.js +74 -0
- package/web/listIssues/index.js +3 -3
- package/web/listIssues/listIssues.test.js +80 -0
- package/web/listReports/listReports.test.js +84 -0
- package/web/listViolators/listViolators.test.js +66 -0
- package/api/testSuite.js +0 -311
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/*
|
|
2
|
+
getReport.test.js
|
|
3
|
+
Tests for api/getReport.js using the fixture corpus.
|
|
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 {response} = require('./getReport');
|
|
12
|
+
|
|
13
|
+
// SETUP AND TEARDOWN
|
|
14
|
+
|
|
15
|
+
const savedDBDir = process.env.DB_DIR;
|
|
16
|
+
|
|
17
|
+
before(() => {
|
|
18
|
+
process.env.DB_DIR = path.join(__dirname, '..', 'test', 'fixtures', 'db');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
after(() => {
|
|
22
|
+
if (savedDBDir !== undefined) {
|
|
23
|
+
process.env.DB_DIR = savedDBDir;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
delete process.env.DB_DIR;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// TESTS
|
|
31
|
+
|
|
32
|
+
test('getReport returns the full report for a valid report', async () => {
|
|
33
|
+
const body = await response(['260101T0000', 'mix']);
|
|
34
|
+
const content = body['response content'];
|
|
35
|
+
assert.equal(typeof content['size of the report in bytes'], 'number');
|
|
36
|
+
assert.ok(content['size of the report in bytes'] > 0);
|
|
37
|
+
const report = content['full report'];
|
|
38
|
+
assert.equal(report.id, '260101T0000-mix');
|
|
39
|
+
assert.equal(report.target.what, 'Mixed Outcomes Page');
|
|
40
|
+
assert.equal(report.target.url, 'https://example.com/mixed');
|
|
41
|
+
assert.equal(report.acts.length, 2);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('getReport returns an error for a nonexistent report', async () => {
|
|
45
|
+
const body = await response(['999999T9999', 'xyz']);
|
|
46
|
+
const content = body['response content'];
|
|
47
|
+
assert.equal(content['full report'], null);
|
|
48
|
+
assert.equal(typeof content['size of the report in bytes'], 'string');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('getReport returns an error for the hidden report', async () => {
|
|
52
|
+
const body = await response(['260101T0007', 'hid']);
|
|
53
|
+
const content = body['response content'];
|
|
54
|
+
assert.equal(content['full report'], null);
|
|
55
|
+
assert.equal(typeof content['size of the report in bytes'], 'string');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('getReport includes tool name and metadata', async () => {
|
|
59
|
+
const body = await response(['260101T0000', 'mix']);
|
|
60
|
+
assert.equal(body['tool name'], 'getReport');
|
|
61
|
+
assert.ok(body['response metadata'].identifier);
|
|
62
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/*
|
|
2
|
+
listDiagnoses.test.js
|
|
3
|
+
Tests for api/listDiagnoses.js using the fixture corpus, with emphasis on outcome handling.
|
|
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 {response} = require('./listDiagnoses');
|
|
12
|
+
|
|
13
|
+
// SETUP AND TEARDOWN
|
|
14
|
+
|
|
15
|
+
const savedDBDir = process.env.DB_DIR;
|
|
16
|
+
|
|
17
|
+
before(() => {
|
|
18
|
+
process.env.DB_DIR = path.join(__dirname, '..', 'test', 'fixtures', 'db');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
after(() => {
|
|
22
|
+
if (savedDBDir !== undefined) {
|
|
23
|
+
process.env.DB_DIR = savedDBDir;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
delete process.env.DB_DIR;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// TESTS
|
|
31
|
+
|
|
32
|
+
test('listDiagnoses returns 2 diagnoses for catalogIndex 0, linkNoText, in the mixed report (axe and alfa, excluding cantTell)', async () => {
|
|
33
|
+
const body = await response(['0', 'linkNoText', '260101T0000', 'mix']);
|
|
34
|
+
const diagnoses = body['response content']['diagnoses of how the element exhibited the issue'];
|
|
35
|
+
assert.equal(diagnoses.length, 2);
|
|
36
|
+
const rules = diagnoses.map(d => d['identifier of the violated rule']).sort();
|
|
37
|
+
assert.deepEqual(rules, ['r11', 'r11']);
|
|
38
|
+
const descriptions = diagnoses.map(d => d['description of the violation']);
|
|
39
|
+
assert.ok(descriptions.every(d => d === 'The link does not have an accessible name'));
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('listDiagnoses returns 0 diagnoses when all instances are cantTell', async () => {
|
|
43
|
+
const body = await response(['0', 'focusIndicationBad', '260101T0001', 'ct']);
|
|
44
|
+
const diagnoses = body['response content']['diagnoses of how the element exhibited the issue'];
|
|
45
|
+
assert.equal(diagnoses.length, 0);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('listDiagnoses treats missing outcome as a violation', async () => {
|
|
49
|
+
const body = await response(['0', 'linkNoText', '260101T0002', 'no']);
|
|
50
|
+
const diagnoses = body['response content']['diagnoses of how the element exhibited the issue'];
|
|
51
|
+
assert.equal(diagnoses.length, 1);
|
|
52
|
+
assert.equal(diagnoses[0]['description of the violation'], 'Button has no accessible name');
|
|
53
|
+
assert.equal(diagnoses[0]['severity of the violation on a 0-to-3 scale'], 3);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('listDiagnoses returns element basics from the catalog', async () => {
|
|
57
|
+
const body = await response(['0', 'linkNoText', '260101T0000', 'mix']);
|
|
58
|
+
const elementBasics = body['response content']['basics about the element'];
|
|
59
|
+
assert.equal(elementBasics.identifier, '0');
|
|
60
|
+
assert.equal(elementBasics['tag name'], 'A');
|
|
61
|
+
assert.equal(elementBasics['inner text'], 'About Us');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('listDiagnoses returns element details from the catalog', async () => {
|
|
65
|
+
const body = await response(['0', 'linkNoText', '260101T0000', 'mix']);
|
|
66
|
+
const elementDetails = body['response content']['details about the element'];
|
|
67
|
+
assert.equal(elementDetails['start tag'], '<a>');
|
|
68
|
+
assert.equal(elementDetails['XPath'], '/html/body/a[1]');
|
|
69
|
+
assert.equal(elementDetails['x, y, width, and height of bounding box'], '10:20:80:30');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('listDiagnoses returns an error for an unknown issue ID', async () => {
|
|
73
|
+
const body = await response(['0', 'nonexistentIssue', '260101T0000', 'mix']);
|
|
74
|
+
const basics = body['response content']['basics about the issue'];
|
|
75
|
+
assert.ok(basics.error);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('listDiagnoses returns an error for a nonexistent catalog index', async () => {
|
|
79
|
+
const body = await response(['999', 'linkNoText', '260101T0000', 'mix']);
|
|
80
|
+
const basics = body['response content']['basics about the element'];
|
|
81
|
+
assert.ok(basics.error);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('listDiagnoses returns an error for a nonexistent report', async () => {
|
|
85
|
+
const body = await response(['0', 'linkNoText', '999999T9999', 'xyz']);
|
|
86
|
+
const basics = body['response content']['basics about the report'];
|
|
87
|
+
assert.ok(basics.error);
|
|
88
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/*
|
|
2
|
+
listIssues.test.js
|
|
3
|
+
Tests for api/listIssues.js using the fixture corpus, with emphasis on outcome handling.
|
|
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 {response} = require('./listIssues');
|
|
12
|
+
|
|
13
|
+
// SETUP AND TEARDOWN
|
|
14
|
+
|
|
15
|
+
const savedDBDir = process.env.DB_DIR;
|
|
16
|
+
|
|
17
|
+
before(() => {
|
|
18
|
+
process.env.DB_DIR = path.join(__dirname, '..', 'test', 'fixtures', 'db');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
after(() => {
|
|
22
|
+
if (savedDBDir !== undefined) {
|
|
23
|
+
process.env.DB_DIR = savedDBDir;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
delete process.env.DB_DIR;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// TESTS
|
|
31
|
+
|
|
32
|
+
test('listIssues returns 2 issues for the mixed-outcomes report, excluding cantTell', async () => {
|
|
33
|
+
const body = await response(['260101T0000', 'mix']);
|
|
34
|
+
const issues = body['response content']['basics about all issues reported in the report'];
|
|
35
|
+
assert.equal(issues.length, 2);
|
|
36
|
+
const summaries = issues.map(i => i.summary).sort();
|
|
37
|
+
assert.deepEqual(summaries, ['all-capital text', 'link not named']);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('listIssues returns 0 issues for the all-cantTell report', async () => {
|
|
41
|
+
const body = await response(['260101T0001', 'ct']);
|
|
42
|
+
const issues = body['response content']['basics about all issues reported in the report'];
|
|
43
|
+
assert.equal(issues.length, 0);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('listIssues treats missing outcome as a violation, not cantTell', async () => {
|
|
47
|
+
const body = await response(['260101T0002', 'no']);
|
|
48
|
+
const issues = body['response content']['basics about all issues reported in the report'];
|
|
49
|
+
assert.equal(issues.length, 1);
|
|
50
|
+
assert.equal(issues[0].identifier, 'linkNoText');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('listIssues returns 0 issues for the empty report', async () => {
|
|
54
|
+
const body = await response(['260101T0005', 'emp']);
|
|
55
|
+
const issues = body['response content']['basics about all issues reported in the report'];
|
|
56
|
+
assert.equal(issues.length, 0);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('listIssues reports prevented rule engines for the prevented report', async () => {
|
|
60
|
+
const body = await response(['260101T0006', 'prv']);
|
|
61
|
+
const details = body['response content']['details about the report'];
|
|
62
|
+
const preventions = details['test results']['rule engines that could not test the page'];
|
|
63
|
+
assert.equal(preventions.length, 1);
|
|
64
|
+
assert.equal(preventions[0].name, 'Alfa');
|
|
65
|
+
assert.equal(preventions[0]['reason for failure'], 'page timed out');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('listIssues includes reporter names for each issue in the mixed report', async () => {
|
|
69
|
+
const body = await response(['260101T0000', 'mix']);
|
|
70
|
+
const issues = body['response content']['basics about all issues reported in the report'];
|
|
71
|
+
const linkIssue = issues.find(i => i.identifier === 'linkNoText');
|
|
72
|
+
assert.deepEqual(linkIssue['rule engines with any violations belonging to the issue'], ['Alfa', 'Axe']);
|
|
73
|
+
const allCapsIssue = issues.find(i => i.identifier === 'allCaps');
|
|
74
|
+
assert.deepEqual(allCapsIssue['rule engines with any violations belonging to the issue'], ['Alfa']);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('listIssues counts issues by priority correctly for the mixed report', async () => {
|
|
78
|
+
const body = await response(['260101T0000', 'mix']);
|
|
79
|
+
const details = body['response content']['details about the report'];
|
|
80
|
+
const counts = details['test results']['counts of issues by priority'];
|
|
81
|
+
// linkNoText weight 4 (highest), allCaps weight 1 (lowest).
|
|
82
|
+
assert.equal(counts.highest, 1);
|
|
83
|
+
assert.equal(counts.high, 0);
|
|
84
|
+
assert.equal(counts.low, 0);
|
|
85
|
+
assert.equal(counts.lowest, 1);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('listIssues reports the superseded status for the mixed report', async () => {
|
|
89
|
+
const body = await response(['260101T0000', 'mix']);
|
|
90
|
+
const basics = body['response content']['basics about the report'];
|
|
91
|
+
assert.equal(basics['whether a later report about the same page exists'], true);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('listIssues returns an error for a nonexistent report', async () => {
|
|
95
|
+
const body = await response(['999999T9999', 'xyz']);
|
|
96
|
+
const basics = body['response content']['basics about the report'];
|
|
97
|
+
assert.ok(basics.error);
|
|
98
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/*
|
|
2
|
+
listReports.test.js
|
|
3
|
+
Tests for api/listReports.js using the fixture corpus.
|
|
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 {response} = require('./listReports');
|
|
12
|
+
|
|
13
|
+
// CONSTANTS
|
|
14
|
+
|
|
15
|
+
// Fixture report identifiers in the reports/ directory (excluding the hidden one).
|
|
16
|
+
const fixtureIds = [
|
|
17
|
+
['260101T0000', 'mix'],
|
|
18
|
+
['260101T0001', 'ct'],
|
|
19
|
+
['260101T0002', 'no'],
|
|
20
|
+
['260101T0005', 'emp'],
|
|
21
|
+
['260101T0006', 'prv'],
|
|
22
|
+
['260202T0000', 'new']
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
// SETUP AND TEARDOWN
|
|
26
|
+
|
|
27
|
+
const savedDBDir = process.env.DB_DIR;
|
|
28
|
+
|
|
29
|
+
before(() => {
|
|
30
|
+
process.env.DB_DIR = path.join(__dirname, '..', 'test', 'fixtures', 'db');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
after(() => {
|
|
34
|
+
if (savedDBDir !== undefined) {
|
|
35
|
+
process.env.DB_DIR = savedDBDir;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
delete process.env.DB_DIR;
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// TESTS
|
|
43
|
+
|
|
44
|
+
test('listReports returns basics about all 6 non-hidden reports', async () => {
|
|
45
|
+
const body = await response();
|
|
46
|
+
const reportsBasics = body['response content']['basics about all available reports'];
|
|
47
|
+
assert.equal(reportsBasics.length, 6);
|
|
48
|
+
const ids = reportsBasics.map(b => b.identifier).sort();
|
|
49
|
+
const expectedIds = fixtureIds.map(([ts, jid]) => `${ts}-${jid}`).sort();
|
|
50
|
+
assert.deepEqual(ids, expectedIds);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('listReports does not include the hidden report', async () => {
|
|
54
|
+
const body = await response();
|
|
55
|
+
const reportsBasics = body['response content']['basics about all available reports'];
|
|
56
|
+
const ids = reportsBasics.map(b => b.identifier);
|
|
57
|
+
assert.ok(!ids.includes('260101T0007-hid'), 'hidden report must not appear in listReports');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('listReports sorts reports by page description and then by completion time', async () => {
|
|
61
|
+
const body = await response();
|
|
62
|
+
const reportsBasics = body['response content']['basics about all available reports'];
|
|
63
|
+
const descriptions = reportsBasics.map(b => b['tested web page'].description);
|
|
64
|
+
// All CantTell Page, Empty Results Page, Mixed Outcomes Page, Mixed Outcomes Page, No Outcomes Page, Prevented Page.
|
|
65
|
+
assert.deepEqual(
|
|
66
|
+
descriptions,
|
|
67
|
+
[
|
|
68
|
+
'All CantTell Page',
|
|
69
|
+
'Empty Results Page',
|
|
70
|
+
'Mixed Outcomes Page',
|
|
71
|
+
'Mixed Outcomes Page',
|
|
72
|
+
'No Outcomes Page',
|
|
73
|
+
'Prevented Page'
|
|
74
|
+
]
|
|
75
|
+
);
|
|
76
|
+
// The two Mixed Outcomes Page reports should be sorted by completion time (older first).
|
|
77
|
+
const mixedReports = reportsBasics.filter(b => b['tested web page'].description === 'Mixed Outcomes Page');
|
|
78
|
+
assert.equal(mixedReports[0].identifier, '260101T0000-mix');
|
|
79
|
+
assert.equal(mixedReports[1].identifier, '260202T0000-new');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('listReports marks the superseded report correctly', async () => {
|
|
83
|
+
const body = await response();
|
|
84
|
+
const reportsBasics = body['response content']['basics about all available reports'];
|
|
85
|
+
const mix = reportsBasics.find(b => b.identifier === '260101T0000-mix');
|
|
86
|
+
const newer = reportsBasics.find(b => b.identifier === '260202T0000-new');
|
|
87
|
+
assert.equal(mix['whether a later report about the same page exists'], true);
|
|
88
|
+
assert.equal(newer['whether a later report about the same page exists'], false);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('listReports includes request-test instructions', async () => {
|
|
92
|
+
const body = await response();
|
|
93
|
+
const content = body['response content'];
|
|
94
|
+
assert.ok(content['how to request that a page with no report be tested']);
|
|
95
|
+
assert.ok(content['how a web user can request that the page be tested']);
|
|
96
|
+
assert.equal(content['how to request that a page with no report be tested'].method, 'POST');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('listReports includes tool name and metadata', async () => {
|
|
100
|
+
const body = await response();
|
|
101
|
+
assert.equal(body['tool name'], 'listReports');
|
|
102
|
+
assert.ok(body['response metadata'].identifier);
|
|
103
|
+
assert.ok(body['response metadata']['date and time']);
|
|
104
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/*
|
|
2
|
+
listViolators.test.js
|
|
3
|
+
Tests for api/listViolators.js using the fixture corpus, with emphasis on outcome handling.
|
|
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 {response} = require('./listViolators');
|
|
12
|
+
|
|
13
|
+
// SETUP AND TEARDOWN
|
|
14
|
+
|
|
15
|
+
const savedDBDir = process.env.DB_DIR;
|
|
16
|
+
|
|
17
|
+
before(() => {
|
|
18
|
+
process.env.DB_DIR = path.join(__dirname, '..', 'test', 'fixtures', 'db');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
after(() => {
|
|
22
|
+
if (savedDBDir !== undefined) {
|
|
23
|
+
process.env.DB_DIR = savedDBDir;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
delete process.env.DB_DIR;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// TESTS
|
|
31
|
+
|
|
32
|
+
test('listViolators returns 1 violator for linkNoText in the mixed report, excluding cantTell', async () => {
|
|
33
|
+
const body = await response(['linkNoText', '260101T0000', 'mix']);
|
|
34
|
+
const violators = body['response content']['basics about all elements exhibiting the issue'];
|
|
35
|
+
assert.equal(violators.length, 1);
|
|
36
|
+
assert.equal(violators[0].identifier, '0');
|
|
37
|
+
assert.equal(violators[0]['tag name'], 'A');
|
|
38
|
+
assert.equal(violators[0]['inner text'], 'About Us');
|
|
39
|
+
assert.equal(violators[0]['count of rule engines reporting that the element exhibited the issue'], 2);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('listViolators returns 0 violators for an issue when all instances are cantTell', async () => {
|
|
43
|
+
const body = await response(['focusIndicationBad', '260101T0001', 'ct']);
|
|
44
|
+
const violators = body['response content']['basics about all elements exhibiting the issue'];
|
|
45
|
+
assert.equal(violators.length, 0);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('listViolators treats missing outcome as a violation', async () => {
|
|
49
|
+
const body = await response(['linkNoText', '260101T0002', 'no']);
|
|
50
|
+
const violators = body['response content']['basics about all elements exhibiting the issue'];
|
|
51
|
+
assert.equal(violators.length, 1);
|
|
52
|
+
assert.equal(violators[0].identifier, '0');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('listViolators returns an error for an unknown issue ID', async () => {
|
|
56
|
+
const body = await response(['nonexistentIssue', '260101T0000', 'mix']);
|
|
57
|
+
const basics = body['response content']['basics about the issue'];
|
|
58
|
+
assert.ok(basics.error);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('listViolators returns an error for a nonexistent report', async () => {
|
|
62
|
+
const body = await response(['linkNoText', '999999T9999', 'xyz']);
|
|
63
|
+
const basics = body['response content']['basics about the report'];
|
|
64
|
+
assert.ok(basics.error);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('listViolators includes reporter facts for the issue', async () => {
|
|
68
|
+
const body = await response(['linkNoText', '260101T0000', 'mix']);
|
|
69
|
+
const details = body['response content']['details about the issue'];
|
|
70
|
+
const reporters = details['rule engines reporting violations belonging to the issue'];
|
|
71
|
+
const names = reporters.map(r => r.name).sort();
|
|
72
|
+
assert.deepEqual(names, ['Alfa', 'Axe']);
|
|
73
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/*
|
|
2
|
+
requestFeature.test.js
|
|
3
|
+
Tests for api/requestFeature.js with mocked side effects.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// IMPORTS
|
|
7
|
+
|
|
8
|
+
const {test, before, after} = require('node:test');
|
|
9
|
+
const assert = require('node:assert/strict');
|
|
10
|
+
const alerts = require('../alerts');
|
|
11
|
+
|
|
12
|
+
// SETUP AND TEARDOWN
|
|
13
|
+
|
|
14
|
+
let sendAlertCalls = [];
|
|
15
|
+
|
|
16
|
+
before(() => {
|
|
17
|
+
sendAlertCalls = [];
|
|
18
|
+
// @ts-expect-error: Replacing the real function with a mock for testing.
|
|
19
|
+
alerts.sendAlert = async (subject, body) => {
|
|
20
|
+
sendAlertCalls.push({subject, body});
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Require requestFeature after the mock is in place, so it captures the mocked sendAlert.
|
|
25
|
+
const {response} = require('./requestFeature');
|
|
26
|
+
|
|
27
|
+
after(() => {
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// TESTS
|
|
31
|
+
|
|
32
|
+
test('requestFeature rejects an empty feature request', async () => {
|
|
33
|
+
const body = await response(['']);
|
|
34
|
+
const details = body['response content']['details about your request'];
|
|
35
|
+
assert.ok(details.error);
|
|
36
|
+
assert.equal(sendAlertCalls.length, 0);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('requestFeature accepts a non-empty feature request and notifies the manager', async () => {
|
|
40
|
+
const body = await response(['Add a dark mode toggle']);
|
|
41
|
+
const details = body['response content']['details about your request'];
|
|
42
|
+
assert.equal(details.error, undefined);
|
|
43
|
+
assert.ok(details['date and time received']);
|
|
44
|
+
assert.equal(details.disposition, 'received and logged; manager notified');
|
|
45
|
+
assert.equal(sendAlertCalls.length, 1);
|
|
46
|
+
assert.equal(sendAlertCalls[0].subject, 'MCP feature request received');
|
|
47
|
+
assert.equal(sendAlertCalls[0].body, 'Add a dark mode toggle');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('requestFeature includes tool name and metadata', async () => {
|
|
51
|
+
const body = await response(['Some feature']);
|
|
52
|
+
assert.equal(body['tool name'], 'requestFeature');
|
|
53
|
+
assert.ok(body['response metadata'].identifier);
|
|
54
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/*
|
|
2
|
+
requestRetest.test.js
|
|
3
|
+
Tests for api/requestRetest.js using the fixture corpus, with mocked side effects.
|
|
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 apiUtil = require('./util');
|
|
12
|
+
|
|
13
|
+
// SETUP AND TEARDOWN
|
|
14
|
+
|
|
15
|
+
const savedDBDir = process.env.DB_DIR;
|
|
16
|
+
let processTestRequestCalls = [];
|
|
17
|
+
|
|
18
|
+
before(() => {
|
|
19
|
+
process.env.DB_DIR = path.join(__dirname, '..', 'test', 'fixtures', 'db');
|
|
20
|
+
processTestRequestCalls = [];
|
|
21
|
+
// @ts-expect-error: Replacing the real function with a mock for testing.
|
|
22
|
+
apiUtil.processTestRequest = async (testType, what, url, reason) => {
|
|
23
|
+
processTestRequestCalls.push({testType, what, url, reason});
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Require requestRetest after the mock is in place, so it captures the mocked processTestRequest.
|
|
28
|
+
const {response} = require('./requestRetest');
|
|
29
|
+
|
|
30
|
+
after(() => {
|
|
31
|
+
if (savedDBDir !== undefined) {
|
|
32
|
+
process.env.DB_DIR = savedDBDir;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
delete process.env.DB_DIR;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// TESTS
|
|
40
|
+
|
|
41
|
+
test('requestRetest rejects a nonexistent report', async () => {
|
|
42
|
+
const body = await response(['999999T9999', 'xyz', 'A reason that is long enough.']);
|
|
43
|
+
assert.ok(body['response content']['details about your request'].error);
|
|
44
|
+
assert.equal(processTestRequestCalls.length, 0);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('requestRetest rejects a superseded report', async () => {
|
|
48
|
+
const body = await response(['260101T0000', 'mix', 'A reason that is long enough.']);
|
|
49
|
+
const details = body['response content']['details about your request'];
|
|
50
|
+
assert.ok(details.error.includes('later report'));
|
|
51
|
+
assert.equal(processTestRequestCalls.length, 0);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('requestRetest rejects a reason shorter than 20 characters', async () => {
|
|
55
|
+
const body = await response(['260101T0001', 'ct', 'short']);
|
|
56
|
+
const details = body['response content']['details about your request'];
|
|
57
|
+
assert.ok(details.error.includes('reason'));
|
|
58
|
+
assert.equal(processTestRequestCalls.length, 0);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('requestRetest rejects a reason longer than 100 characters', async () => {
|
|
62
|
+
const longReason = 'x'.repeat(101);
|
|
63
|
+
const body = await response(['260101T0001', 'ct', longReason]);
|
|
64
|
+
const details = body['response content']['details about your request'];
|
|
65
|
+
assert.ok(details.error.includes('reason'));
|
|
66
|
+
assert.equal(processTestRequestCalls.length, 0);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('requestRetest accepts a valid retest request for the latest report of a page', async () => {
|
|
70
|
+
const body = await response(['260202T0000', 'new', 'A reason that is long enough.']);
|
|
71
|
+
const details = body['response content']['details about your request'];
|
|
72
|
+
assert.equal(details.error, undefined);
|
|
73
|
+
assert.equal(details['page to be retested'].description, 'Mixed Outcomes Page');
|
|
74
|
+
assert.equal(processTestRequestCalls.length, 1);
|
|
75
|
+
assert.equal(processTestRequestCalls[0].testType, 'retest');
|
|
76
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/*
|
|
2
|
+
requestTest.test.js
|
|
3
|
+
Tests for api/requestTest.js using the fixture corpus, with mocked side effects.
|
|
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 apiUtil = require('./util');
|
|
12
|
+
|
|
13
|
+
// SETUP AND TEARDOWN
|
|
14
|
+
|
|
15
|
+
const savedDBDir = process.env.DB_DIR;
|
|
16
|
+
let processTestRequestCalls = [];
|
|
17
|
+
|
|
18
|
+
before(() => {
|
|
19
|
+
process.env.DB_DIR = path.join(__dirname, '..', 'test', 'fixtures', 'db');
|
|
20
|
+
processTestRequestCalls = [];
|
|
21
|
+
// @ts-expect-error: Replacing the real function with a mock for testing.
|
|
22
|
+
apiUtil.processTestRequest = async (testType, what, url, reason) => {
|
|
23
|
+
processTestRequestCalls.push({testType, what, url, reason});
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Require requestTest after the mock is in place, so it captures the mocked processTestRequest.
|
|
28
|
+
const {response} = require('./requestTest');
|
|
29
|
+
|
|
30
|
+
after(() => {
|
|
31
|
+
if (savedDBDir !== undefined) {
|
|
32
|
+
process.env.DB_DIR = savedDBDir;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
delete process.env.DB_DIR;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// TESTS
|
|
40
|
+
|
|
41
|
+
test('requestTest rejects an empty description', async () => {
|
|
42
|
+
const body = await response(['', 'https://example.com/test', 'A reason that is long enough.']);
|
|
43
|
+
assert.ok(body['response content']['details about your request'].error);
|
|
44
|
+
assert.equal(processTestRequestCalls.length, 0);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('requestTest rejects a description longer than 100 characters', async () => {
|
|
48
|
+
const longWhat = 'x'.repeat(101);
|
|
49
|
+
const body = await response([longWhat, 'https://example.com/test', 'A reason that is long enough.']);
|
|
50
|
+
assert.ok(body['response content']['details about your request'].error);
|
|
51
|
+
assert.equal(processTestRequestCalls.length, 0);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('requestTest rejects a URL shorter than 12 characters', async () => {
|
|
55
|
+
const body = await response(['Test Page', 'short', 'A reason that is long enough.']);
|
|
56
|
+
assert.ok(body['response content']['details about your request'].error);
|
|
57
|
+
assert.equal(processTestRequestCalls.length, 0);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('requestTest rejects an already-tested page', async () => {
|
|
61
|
+
const body = await response(['Mixed Outcomes Page', 'https://example.com/mixed', 'A reason that is long enough.']);
|
|
62
|
+
const details = body['response content']['details about your request'];
|
|
63
|
+
assert.ok(details.error.includes('already been tested'));
|
|
64
|
+
assert.equal(processTestRequestCalls.length, 0);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('requestTest accepts a valid new page request', async () => {
|
|
68
|
+
const body = await response(['Brand New Page', 'https://example.com/brandnew', 'A reason that is long enough.']);
|
|
69
|
+
const details = body['response content']['details about your request'];
|
|
70
|
+
assert.equal(details.error, undefined);
|
|
71
|
+
assert.equal(details['page to be tested'].description, 'Brand New Page');
|
|
72
|
+
assert.equal(processTestRequestCalls.length, 1);
|
|
73
|
+
assert.equal(processTestRequestCalls[0].testType, 'test');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('requestTest includes disposition information for a valid request', async () => {
|
|
77
|
+
const body = await response(['Brand New Page', 'https://example.com/brandnew', 'A reason that is long enough.']);
|
|
78
|
+
const disposition = body['response content']['disposition of your request'];
|
|
79
|
+
assert.ok(disposition);
|
|
80
|
+
assert.ok(disposition['what happens next']);
|
|
81
|
+
});
|