@jrpool/kilotest 46.1.0 → 47.0.1

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.
Files changed (147) hide show
  1. package/.claude/settings.local.json +13 -1
  2. package/.devin/config.local.json +8 -1
  3. package/.github/workflows/ci.yml +0 -3
  4. package/.github/workflows/periodic-smoke-tests.yml +30 -0
  5. package/.github/workflows/publish-mcp.yml +4 -4
  6. package/{alerts.test.js → alerts.test.ts} +17 -29
  7. package/{alerts.js → alerts.ts} +10 -13
  8. package/api/{getReport.test.js → getReport.test.ts} +9 -10
  9. package/api/{getReport.js → getReport.ts} +23 -24
  10. package/api/{listDiagnoses.test.js → listDiagnoses.test.ts} +26 -27
  11. package/api/{listDiagnoses.js → listDiagnoses.ts} +46 -70
  12. package/api/{listIssues.test.js → listIssues.test.ts} +21 -14
  13. package/api/{listIssues.js → listIssues.ts} +75 -74
  14. package/api/{listReports.test.js → listReports.test.ts} +17 -16
  15. package/api/listReports.ts +87 -0
  16. package/api/{listViolators.test.js → listViolators.test.ts} +19 -20
  17. package/api/{listViolators.js → listViolators.ts} +45 -70
  18. package/api/requestFeature.test.ts +54 -0
  19. package/api/{requestFeature.js → requestFeature.ts} +18 -15
  20. package/api/{requestRetest.test.js → requestRetest.test.ts} +33 -28
  21. package/api/{requestRetest.js → requestRetest.ts} +32 -25
  22. package/api/{requestTest.test.js → requestTest.test.ts} +35 -30
  23. package/api/{requestTest.js → requestTest.ts} +35 -28
  24. package/api/{routes.test.js → routes.test.ts} +5 -5
  25. package/api/{routes.js → routes.ts} +6 -6
  26. package/api/{schemas.js → schemas.ts} +69 -50
  27. package/api/{util.test.js → util.test.ts} +31 -11
  28. package/api/{util.js → util.ts} +75 -46
  29. package/api/version.ts +8 -0
  30. package/{apiIntegration.test.js → apiIntegration.test.ts} +13 -14
  31. package/balances.ts +87 -0
  32. package/{checkinfo.js → checkinfo.ts} +24 -20
  33. package/docs/AI-TOOLS.md +4 -4
  34. package/docs/BACKLOG.md +356 -0
  35. package/docs/SERVICE.md +144 -17
  36. package/docs/decisions/002-coverage-tool.md +40 -0
  37. package/docs/decisions/003-adr-threshold.md +34 -0
  38. package/docs/decisions/004-docs-directory.md +23 -0
  39. package/docs/decisions/005-observability.md +28 -0
  40. package/docs/decisions/006-git-workflow.md +28 -0
  41. package/docs/decisions/007-internal-testing.md +34 -0
  42. package/docs/decisions/008-dependencies.md +28 -0
  43. package/docs/decisions/009-qai-integration.md +40 -0
  44. package/docs/decisions/QAI-HISTORICAL-CONTEXT.md +42 -0
  45. package/eslint.config.mjs +27 -0
  46. package/index.html +3 -3
  47. package/{index.test.js → index.test.ts} +462 -137
  48. package/{index.js → index.ts} +388 -283
  49. package/{indexBalances.test.js → indexBalances.test.ts} +21 -17
  50. package/{indexServe.test.js → indexServe.test.ts} +9 -9
  51. package/linkValidation.test.ts +190 -0
  52. package/{mcp.test.js → mcp.test.ts} +53 -26
  53. package/{mcp.js → mcp.ts} +29 -20
  54. package/memory/MEMORY.md +7 -0
  55. package/memory/user_maintainer_context.md +24 -0
  56. package/openapi.yaml +115 -68
  57. package/package.json +35 -15
  58. package/pm2.config.cjs +29 -0
  59. package/{routes.test.js → routes.test.ts} +8 -7
  60. package/{routing.test.js → routing.test.ts} +11 -12
  61. package/scripts/{generate-openapi.js → generate-openapi.ts} +13 -13
  62. package/serve.ts +15 -0
  63. package/smokeTest.ts +234 -0
  64. package/test/dbFixture.ts +21 -0
  65. package/test/fixtures/{buildFixtures.js → buildFixtures.ts} +14 -13
  66. package/test/{setup.cjs → setup.ts} +2 -2
  67. package/tsconfig.json +16 -0
  68. package/{util.test.js → util.test.ts} +350 -257
  69. package/util.ts +921 -0
  70. package/web/ai0BalanceForm/{ai0BalanceForm.test.js → ai0BalanceForm.test.ts} +15 -15
  71. package/web/ai0BalanceForm/{index.js → index.ts} +10 -18
  72. package/web/enqueue/index.ts +56 -0
  73. package/web/enqueueForm/index.html +9 -9
  74. package/web/enqueueForm/index.ts +43 -0
  75. package/web/expungeReportsForm/{expungeReportsForm.test.js → expungeReportsForm.test.ts} +18 -20
  76. package/web/expungeReportsForm/index.ts +18 -0
  77. package/web/hideReportForm/{hideReportForm.test.js → hideReportForm.test.ts} +14 -14
  78. package/web/hideReportForm/{index.js → index.ts} +17 -21
  79. package/web/listDiagnoses/{index.js → index.ts} +48 -58
  80. package/web/listDiagnoses/{listDiagnoses.test.js → listDiagnoses.test.ts} +39 -34
  81. package/web/listIssues/{index.js → index.ts} +67 -92
  82. package/web/listIssues/{listIssues.test.js → listIssues.test.ts} +45 -51
  83. package/web/listReports/index.html +3 -3
  84. package/web/listReports/{index.js → index.ts} +48 -53
  85. package/web/listReports/{listReports.test.js → listReports.test.ts} +41 -43
  86. package/web/listRules/{index.js → index.ts} +18 -23
  87. package/web/listRules/{listRules.test.js → listRules.test.ts} +26 -19
  88. package/web/listTopIssues/{index.js → index.ts} +36 -47
  89. package/web/listTopIssues/{listTopIssues.test.js → listTopIssues.test.ts} +26 -17
  90. package/web/listViolators/{index.js → index.ts} +51 -71
  91. package/web/listViolators/{listViolators.test.js → listViolators.test.ts} +43 -36
  92. package/web/manage/index.ts +22 -0
  93. package/web/pruneReportsForm/index.ts +18 -0
  94. package/web/pruneReportsForm/{pruneReportsForm.test.js → pruneReportsForm.test.ts} +11 -11
  95. package/web/reannotate/index.ts +70 -0
  96. package/web/reannotate/{reannotate.test.js → reannotate.test.ts} +19 -22
  97. package/web/reannotateForm/{index.js → index.ts} +41 -50
  98. package/web/reannotateForm/{reannotateForm.test.js → reannotateForm.test.ts} +13 -16
  99. package/web/renewWCAG/{index.js → index.ts} +9 -9
  100. package/web/renewWCAG/{renewWCAG.test.js → renewWCAG.test.ts} +17 -20
  101. package/web/renewWCAGForm/index.ts +22 -0
  102. package/web/{expungeReportsForm/index.js → reportDeletion.ts} +56 -38
  103. package/web/requestRetest/index.html +7 -7
  104. package/web/requestRetest/index.ts +31 -0
  105. package/web/requestRetest/requestRetest.test.ts +116 -0
  106. package/web/requestRetestForm/{index.js → index.ts} +10 -16
  107. package/web/requestRetestForm/{requestRetestForm.test.js → requestRetestForm.test.ts} +11 -12
  108. package/web/requestTest/index.html +7 -7
  109. package/web/requestTest/{index.js → index.ts} +5 -5
  110. package/web/requestTest/{requestTest.test.js → requestTest.test.ts} +21 -21
  111. package/web/requestTestForm/index.html +4 -4
  112. package/web/requestTestForm/index.ts +21 -0
  113. package/web/requestTestForm/{requestTestForm.test.js → requestTestForm.test.ts} +9 -9
  114. package/web/rewindReportsForm/index.ts +18 -0
  115. package/web/rewindReportsForm/{rewindReportsForm.test.js → rewindReportsForm.test.ts} +11 -11
  116. package/web/tutorialAI/index.html +184 -0
  117. package/web/tutorialAI/index.ts +80 -0
  118. package/web/tutorialAI/tutorialAI.test.ts +192 -0
  119. package/web/{tutorial → tutorialWeb}/index.html +9 -8
  120. package/web/tutorialWeb/index.ts +78 -0
  121. package/web/tutorialWeb/tutorialWeb.test.ts +195 -0
  122. package/web/unhideReportForm/{index.js → index.ts} +17 -21
  123. package/web/unhideReportForm/{unhideReportForm.test.js → unhideReportForm.test.ts} +16 -16
  124. package/aceconfig.js +0 -28
  125. package/api/listReports.js +0 -87
  126. package/api/requestFeature.test.js +0 -58
  127. package/api/version.js +0 -8
  128. package/indexFallback.test.js +0 -33
  129. package/indexWorkers.test.js +0 -79
  130. package/jsconfig.json +0 -8
  131. package/pm2.config.js +0 -18
  132. package/smokeTest.js +0 -126
  133. package/util.js +0 -733
  134. package/web/enqueue/index.js +0 -67
  135. package/web/enqueueForm/index.js +0 -49
  136. package/web/manage/index.js +0 -22
  137. package/web/pruneReportsForm/index.js +0 -123
  138. package/web/reannotate/index.js +0 -58
  139. package/web/renewWCAGForm/index.js +0 -22
  140. package/web/requestRetest/index.js +0 -31
  141. package/web/requestRetest/requestRetest.test.js +0 -119
  142. package/web/requestTestForm/index.js +0 -21
  143. package/web/rewindReportsForm/index.js +0 -121
  144. package/web/tutorial/index.js +0 -63
  145. package/web/tutorial/tutorial.test.js +0 -97
  146. /package/test/fixtures/db/jobs/{recs.json → testRequests.json} +0 -0
  147. /package/web/{tutorial → tutorialWeb}/images/newsletter-form.png +0 -0
@@ -3,7 +3,19 @@
3
3
  "allow": [
4
4
  "WebSearch",
5
5
  "Bash(gh api *)",
6
- "WebFetch(domain:raw.githubusercontent.com)"
6
+ "WebFetch(domain:raw.githubusercontent.com)",
7
+ "Bash(ls -la /Users/pool/Documents/Topics/repos/a11yTesting/kilotest/*.json)",
8
+ "Bash(grep -n \"^import\\\\|^export\" /Users/pool/Documents/Topics/repos/a11yTesting/qai/src/*.ts)",
9
+ "Bash(find /Users/pool/Documents/Topics/repos/a11yTesting/kilotest -type f \\\\\\( -name \"*.test.js\" -o -name \"*.test.cjs\" -o -name \"test.js\" \\\\\\) ! -path \"*/node_modules/*\" -exec wc -l {} +)",
10
+ "Bash(find /Users/pool/Documents/Topics/repos/a11yTesting/kilotest -type f \\\\\\( -name \"*.test.js\" -o -name \"*.test.cjs\" -o -name \"test.js\" \\\\\\) ! -path \"*/node_modules/*\" -exec grep -h \"^test\\(\" {} +)",
11
+ "Bash(npm install:*)",
12
+ "Bash(npm run:*)",
13
+ "Bash(npm test:*)",
14
+ "Bash(git add:*)",
15
+ "Bash(git rm:*)",
16
+ "Bash(git commit -m ':*)",
17
+ "Bash(git push:*)",
18
+ "Bash(gh auth:*)"
7
19
  ]
8
20
  }
9
21
  }
@@ -29,7 +29,14 @@
29
29
  "Exec($DOCKER exec)",
30
30
  "Exec($DOCKER stop)",
31
31
  "Exec(sed)",
32
- "Exec(awk)"
32
+ "Exec(awk)",
33
+ "Exec(nvm ls)",
34
+ "Exec(.)",
35
+ "Exec(nvm alias)",
36
+ "Exec(git mv)",
37
+ "Exec(perl)",
38
+ "Exec(ssh)",
39
+ "Exec(scp)"
33
40
  ]
34
41
  }
35
42
  }
@@ -27,6 +27,3 @@ jobs:
27
27
 
28
28
  - name: Run tests with coverage
29
29
  run: npm test
30
-
31
- - name: Run smoke test against deployed server
32
- run: node smokeTest.js
@@ -0,0 +1,30 @@
1
+ name: Periodic Smoke Tests
2
+
3
+ on:
4
+ schedule:
5
+ # Run daily at 14:14 UTC
6
+ - cron: '14 14 * * *'
7
+ workflow_dispatch: # Allow manual triggering
8
+
9
+ jobs:
10
+ smoke-tests:
11
+ runs-on: ubuntu-latest
12
+ permissions:
13
+ contents: read
14
+
15
+ steps:
16
+ - name: Checkout code
17
+ uses: actions/checkout@v4
18
+
19
+ - name: Set Node.js version
20
+ uses: actions/setup-node@v4
21
+ with:
22
+ node-version: '>=24.16.0'
23
+
24
+ - name: Install dependencies
25
+ run: npm ci
26
+
27
+ - name: Run smoke tests against deployed service
28
+ run: npm exec -- node smokeTest.ts
29
+ env:
30
+ SMOKE_HOST: kilotest.com
@@ -5,7 +5,7 @@ on:
5
5
  branches: [main]
6
6
  paths:
7
7
  - 'server.json'
8
- - 'api/version.js'
8
+ - 'api/version.ts'
9
9
 
10
10
  jobs:
11
11
  publish:
@@ -23,12 +23,12 @@ jobs:
23
23
  with:
24
24
  node-version: '>=24.16.0'
25
25
 
26
- - name: Verify server.json version matches api/version.js
26
+ - name: Verify server.json version matches api/version.ts
27
27
  run: |
28
- FILE_VERSION=$(node -e "console.log(require('./api/version').version)")
28
+ FILE_VERSION=$(node -e "console.log(require('./api/version.ts').version)")
29
29
  JSON_VERSION=$(node -e "console.log(require('./server.json').version)")
30
30
  if [ "$FILE_VERSION" != "$JSON_VERSION" ]; then
31
- echo "::error::server.json version ($JSON_VERSION) does not match api/version.js version ($FILE_VERSION)"
31
+ echo "::error::server.json version ($JSON_VERSION) does not match api/version.ts version ($FILE_VERSION)"
32
32
  exit 1
33
33
  fi
34
34
  echo "Versions match: $FILE_VERSION"
@@ -1,19 +1,20 @@
1
1
  /*
2
- alerts.test.js
3
- Tests for alerts.js, covering the success, failure, error, timeout, and unconfigured paths.
2
+ alerts.test.ts
3
+ Tests for alerts.ts, covering the success, failure, error, timeout, and unconfigured paths.
4
4
  */
5
5
 
6
6
  // IMPORTS
7
7
 
8
- const {test, before, after} = require('node:test');
9
- const assert = require('node:assert/strict');
10
- const https = require('node:https');
11
- const {EventEmitter} = require('node:events');
8
+ import {test, before, after} from 'node:test';
9
+ import assert from 'node:assert/strict';
10
+ import https from 'node:https';
11
+ import {EventEmitter} from 'node:events';
12
+ import {sendAlert} from './alerts.ts';
12
13
 
13
14
  // SETUP AND TEARDOWN
14
15
 
15
16
  const originalRequest = https.request;
16
- const savedEnv = {};
17
+ const savedEnv: Record<string, any> = {};
17
18
 
18
19
  before(() => {
19
20
  for (const key of ['MANAGER_EMAIL', 'ALERT_API_HOST', 'ALERT_API_PATH', 'ALERT_API_KEY', 'ALERT_FROM']) {
@@ -31,29 +32,27 @@ after(() => {
31
32
  delete process.env[key];
32
33
  }
33
34
  }
34
- delete require.cache[require.resolve('./alerts')];
35
- require('./alerts');
36
35
  });
37
36
 
38
37
  // HELPER
39
38
 
40
39
  // Replaces https.request with a function that returns a fake request and invokes the callback with a fake response. The mockHandler is called on next tick so that event listeners are attached first.
41
- const mockRequest = mockHandler => {
42
- https.request = (options, callback) => {
43
- const req = new EventEmitter();
40
+ const mockRequest = (mockHandler: any) => {
41
+ https.request = ((options: any, callback: any) => {
42
+ const req: any = new EventEmitter();
44
43
  req.write = () => {};
45
44
  req.end = () => {};
46
45
  req.destroy = () => {
47
46
  req.emit('error', new Error('Socket destroyed'));
48
47
  };
49
- req.setTimeout = (ms, fn) => {
48
+ req.setTimeout = (ms: any, fn: any) => {
50
49
  process.nextTick(fn);
51
50
  };
52
- const res = new EventEmitter();
51
+ const res: any = new EventEmitter();
53
52
  res.statusCode = 200;
54
53
  process.nextTick(() => mockHandler(req, res, callback));
55
54
  return req;
56
- };
55
+ }) as any;
57
56
  };
58
57
 
59
58
  // Sets the alert configuration env vars so that sendAlert enters the configured path.
@@ -72,29 +71,21 @@ const clearAlertConfig = () => {
72
71
  }
73
72
  };
74
73
 
75
- // Loads alerts.js fresh so it picks up the current env vars and https.request.
76
- const loadAlerts = () => {
77
- delete require.cache[require.resolve('./alerts')];
78
- return require('./alerts');
79
- };
80
-
81
74
  // TESTS
82
75
 
83
76
  test('sendAlert resolves without sending when configuration is incomplete', async () => {
84
77
  clearAlertConfig();
85
- const {sendAlert} = loadAlerts();
86
78
  await sendAlert('Test', 'Body');
87
79
  assert.ok(true);
88
80
  });
89
81
 
90
82
  test('sendAlert logs success when the API responds with a 2xx status', async () => {
91
83
  setAlertConfig();
92
- mockRequest((req, res, callback) => {
84
+ mockRequest((req: any, res: any, callback: any) => {
93
85
  res.statusCode = 200;
94
86
  callback(res);
95
87
  res.emit('end');
96
88
  });
97
- const {sendAlert} = loadAlerts();
98
89
  await sendAlert('Test Success', 'Body');
99
90
  https.request = originalRequest;
100
91
  assert.ok(true);
@@ -102,13 +93,12 @@ test('sendAlert logs success when the API responds with a 2xx status', async ()
102
93
 
103
94
  test('sendAlert logs an error when the API responds with a non-2xx status', async () => {
104
95
  setAlertConfig();
105
- mockRequest((req, res, callback) => {
96
+ mockRequest((req: any, res: any, callback: any) => {
106
97
  res.statusCode = 500;
107
98
  callback(res);
108
99
  res.emit('data', 'Server error');
109
100
  res.emit('end');
110
101
  });
111
- const {sendAlert} = loadAlerts();
112
102
  await sendAlert('Test Failure', 'Body');
113
103
  https.request = originalRequest;
114
104
  assert.ok(true);
@@ -116,10 +106,9 @@ test('sendAlert logs an error when the API responds with a non-2xx status', asyn
116
106
 
117
107
  test('sendAlert logs an error when the request errors', async () => {
118
108
  setAlertConfig();
119
- mockRequest((req) => {
109
+ mockRequest((req: any) => {
120
110
  req.emit('error', new Error('Connection refused'));
121
111
  });
122
- const {sendAlert} = loadAlerts();
123
112
  await sendAlert('Test Error', 'Body');
124
113
  https.request = originalRequest;
125
114
  assert.ok(true);
@@ -130,7 +119,6 @@ test('sendAlert logs an error when the request times out', async () => {
130
119
  mockRequest(() => {
131
120
  // Do nothing; the setTimeout mock will fire and call req.destroy().
132
121
  });
133
- const {sendAlert} = loadAlerts();
134
122
  await sendAlert('Test Timeout', 'Body');
135
123
  https.request = originalRequest;
136
124
  assert.ok(true);
@@ -1,25 +1,22 @@
1
1
  /*
2
- alerts.js
2
+ alerts.ts
3
3
  Sends alert emails to a Kilotest manager when required.
4
4
  */
5
5
 
6
6
  // IMPORTS
7
7
 
8
- const https = require('https');
9
-
10
- // CONSTANTS
11
-
12
- // Alert configuration.
13
- const MANAGER_EMAIL = process.env.MANAGER_EMAIL;
14
- const ALERT_API_HOST = process.env.ALERT_API_HOST;
15
- const ALERT_API_PATH = process.env.ALERT_API_PATH;
16
- const ALERT_API_KEY = process.env.ALERT_API_KEY;
17
- const ALERT_FROM = process.env.ALERT_FROM;
8
+ import https from 'node:https';
18
9
 
19
10
  // FUNCTIONS
20
11
 
21
12
  // Sends an email alert to a manager.
22
- exports.sendAlert = (subject, body) => new Promise(resolve => {
13
+ export const sendAlert = (subject: string, body: string): Promise<void> => new Promise(resolve => {
14
+ // Alert configuration, read at call time so it is not frozen when this module is loaded.
15
+ const MANAGER_EMAIL = process.env['MANAGER_EMAIL'];
16
+ const ALERT_API_HOST = process.env['ALERT_API_HOST'];
17
+ const ALERT_API_PATH = process.env['ALERT_API_PATH'];
18
+ const ALERT_API_KEY = process.env['ALERT_API_KEY'];
19
+ const ALERT_FROM = process.env['ALERT_FROM'];
23
20
  // If the alert configuration is complete:
24
21
  if (MANAGER_EMAIL && ALERT_API_HOST && ALERT_API_PATH && ALERT_API_KEY && ALERT_FROM) {
25
22
  const payload = JSON.stringify({
@@ -45,7 +42,7 @@ exports.sendAlert = (subject, body) => new Promise(resolve => {
45
42
  });
46
43
  res.on('end', () => {
47
44
  // If this succeeded:
48
- if (res.statusCode >= 200 && res.statusCode < 300) {
45
+ if (res.statusCode !== undefined && res.statusCode >= 200 && res.statusCode < 300) {
49
46
  // Report this.
50
47
  console.log(`Alert sent (${subject})`);
51
48
  }
@@ -1,21 +1,20 @@
1
1
  /*
2
- getReport.test.js
3
- Tests for api/getReport.js using the fixture corpus.
2
+ getReport.test.ts
3
+ Tests for api/getReport.ts using the fixture corpus.
4
4
  */
5
5
 
6
6
  // IMPORTS
7
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');
8
+ import {test, before, after} from 'node:test';
9
+ import assert from 'node:assert/strict';
10
+ import {response} from './getReport.ts';
12
11
 
13
12
  // SETUP AND TEARDOWN
14
13
 
15
14
  const savedDBDir = process.env.DB_DIR;
16
15
 
17
- before(() => {
18
- process.env.DB_DIR = path.join(__dirname, '..', 'test', 'fixtures', 'db');
16
+ before(async () => {
17
+ process.env.DB_DIR = (await import('../test/dbFixture.ts')).fixtureDBDir;
19
18
  });
20
19
 
21
20
  after(() => {
@@ -33,8 +32,8 @@ test('getReport returns the full report for a valid report', async () => {
33
32
  const body = await response(['260101T0000', 'mix']);
34
33
  const content = body['response content'];
35
34
  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'];
35
+ assert.ok((content['size of the report in bytes'] as number) > 0);
36
+ const report = content['full report'] as any;
38
37
  assert.equal(report.id, '260101T0000-mix');
39
38
  assert.equal(report.target.what, 'Mixed Outcomes Page');
40
39
  assert.equal(report.target.url, 'https://example.com/mixed');
@@ -1,44 +1,43 @@
1
1
  /*
2
- getReport.js
2
+ getReport.ts
3
3
  Returns one report.
4
4
  */
5
5
 
6
6
  // IMPORTS
7
7
 
8
- const {
8
+ import {z} from 'zod';
9
+ import {
9
10
  getResponseMetadata,
10
11
  getToolsFacts,
11
- thisHost
12
- } = require('./util');
13
- const {getReport, getReportStats} = require('../util');
12
+ getThisHost
13
+ } from './util.ts';
14
+ import {getReport, getReportStats} from '../util.ts';
15
+ import {getReportResponseSchema} from './schemas.ts';
16
+
17
+ // TYPES
18
+
19
+ // The response content defined by the response schema.
20
+ type ResponseContent = z.infer<typeof getReportResponseSchema>['response content'];
14
21
 
15
22
  // FUNCTIONS
16
23
 
17
24
  // Returns the response body.
18
- exports.response = async args => {
25
+ export const response = async (args: string[]) => {
19
26
  const [timeStamp = '', jobID = ''] = args;
20
- // Initialize the response content.
21
- const responseContent = {
22
- 'size of the report in bytes': null,
23
- 'full report': null
24
- };
27
+ const thisHost = getThisHost();
25
28
  // Get the report size.
26
29
  const reportStats = await getReportStats(timeStamp, jobID);
27
- // If this failed:
28
- if (!reportStats) {
29
- // Add this to the response content.
30
- responseContent['size of the report in bytes'] = 'Error: The report could not be accessed for an unknown reason.';
31
- }
32
- // Otherwise, i.e. if it succeeded:
33
- else {
34
- const {reportSize} = reportStats;
35
- // Add the size to the response content.
36
- responseContent['size of the report in bytes'] = reportSize;
30
+ // Create the response content.
31
+ const responseContent: ResponseContent = reportStats
32
+ ? {
33
+ 'size of the report in bytes': reportStats.reportSize,
37
34
  // Get the report (which may be only an error message).
38
- const report = await getReport(timeStamp, jobID);
39
- // Add it to the response content.
40
- responseContent['full report'] = report;
35
+ 'full report': await getReport(timeStamp, jobID)
41
36
  }
37
+ : {
38
+ 'size of the report in bytes': 'Error: The report could not be accessed for an unknown reason.',
39
+ 'full report': null
40
+ };
42
41
  // Create a response body.
43
42
  const body = {
44
43
  'tool collection': getToolsFacts(),
@@ -1,21 +1,20 @@
1
1
  /*
2
- listDiagnoses.test.js
3
- Tests for api/listDiagnoses.js using the fixture corpus, with emphasis on outcome handling.
2
+ listDiagnoses.test.ts
3
+ Tests for api/listDiagnoses.ts using the fixture corpus, with emphasis on outcome handling.
4
4
  */
5
5
 
6
6
  // IMPORTS
7
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');
8
+ import {test, before, after} from 'node:test';
9
+ import assert from 'node:assert/strict';
10
+ import {response} from './listDiagnoses.ts';
12
11
 
13
12
  // SETUP AND TEARDOWN
14
13
 
15
14
  const savedDBDir = process.env.DB_DIR;
16
15
 
17
- before(() => {
18
- process.env.DB_DIR = path.join(__dirname, '..', 'test', 'fixtures', 'db');
16
+ before(async () => {
17
+ process.env.DB_DIR = (await import('../test/dbFixture.ts')).fixtureDBDir;
19
18
  });
20
19
 
21
20
  after(() => {
@@ -31,23 +30,23 @@ after(() => {
31
30
 
32
31
  test('listDiagnoses returns 2 diagnoses for catalogIndex 0, linkNoText, in the mixed report (axe and alfa, excluding cantTell)', async () => {
33
32
  const body = await response(['0', 'linkNoText', '260101T0000', 'mix']);
34
- const diagnoses = body['response content']['diagnoses of how the element exhibited the issue'];
33
+ const diagnoses = body['response content']['diagnoses of how the element exhibited the issue'] as any;
35
34
  assert.equal(diagnoses.length, 2);
36
- const rules = diagnoses.map(d => d['identifier of the violated rule']).sort();
35
+ const rules = diagnoses.map((d: any) => d['identifier of the violated rule']).sort();
37
36
  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'));
37
+ const descriptions = diagnoses.map((d: any) => d['description of the violation']);
38
+ assert.ok(descriptions.every((d: any) => d === 'The link does not have an accessible name'));
40
39
  });
41
40
 
42
41
  test('listDiagnoses returns 0 diagnoses when all instances are cantTell', async () => {
43
42
  const body = await response(['0', 'focusIndicationBad', '260101T0001', 'ct']);
44
- const diagnoses = body['response content']['diagnoses of how the element exhibited the issue'];
43
+ const diagnoses = body['response content']['diagnoses of how the element exhibited the issue'] as any;
45
44
  assert.equal(diagnoses.length, 0);
46
45
  });
47
46
 
48
47
  test('listDiagnoses treats missing outcome as a violation', async () => {
49
48
  const body = await response(['0', 'linkNoText', '260101T0002', 'no']);
50
- const diagnoses = body['response content']['diagnoses of how the element exhibited the issue'];
49
+ const diagnoses = body['response content']['diagnoses of how the element exhibited the issue'] as any;
51
50
  assert.equal(diagnoses.length, 1);
52
51
  assert.equal(diagnoses[0]['description of the violation'], 'Button has no accessible name');
53
52
  assert.equal(diagnoses[0]['severity of the violation on a 0-to-3 scale'], 3);
@@ -55,7 +54,7 @@ test('listDiagnoses treats missing outcome as a violation', async () => {
55
54
 
56
55
  test('listDiagnoses returns element basics from the catalog', async () => {
57
56
  const body = await response(['0', 'linkNoText', '260101T0000', 'mix']);
58
- const elementBasics = body['response content']['basics about the element'];
57
+ const elementBasics = body['response content']['basics about the element'] as any;
59
58
  assert.equal(elementBasics.identifier, '0');
60
59
  assert.equal(elementBasics['tag name'], 'A');
61
60
  assert.equal(elementBasics['inner text'], 'About Us');
@@ -63,7 +62,7 @@ test('listDiagnoses returns element basics from the catalog', async () => {
63
62
 
64
63
  test('listDiagnoses returns element details from the catalog', async () => {
65
64
  const body = await response(['0', 'linkNoText', '260101T0000', 'mix']);
66
- const elementDetails = body['response content']['details about the element'];
65
+ const elementDetails = body['response content']['details about the element'] as any;
67
66
  assert.equal(elementDetails['start tag'], '<a>');
68
67
  assert.equal(elementDetails['XPath'], '/html/body/a[1]');
69
68
  assert.equal(elementDetails['x, y, width, and height of bounding box'], '10:20:80:30');
@@ -71,55 +70,55 @@ test('listDiagnoses returns element details from the catalog', async () => {
71
70
 
72
71
  test('listDiagnoses returns an error for an unknown issue ID', async () => {
73
72
  const body = await response(['0', 'nonexistentIssue', '260101T0000', 'mix']);
74
- const basics = body['response content']['basics about the issue'];
73
+ const basics = body['response content']['basics about the issue'] as any;
75
74
  assert.ok(basics.error);
76
75
  });
77
76
 
78
77
  test('listDiagnoses returns an error for a nonexistent catalog index', async () => {
79
78
  const body = await response(['999', 'linkNoText', '260101T0000', 'mix']);
80
- const basics = body['response content']['basics about the element'];
79
+ const basics = body['response content']['basics about the element'] as any;
81
80
  assert.ok(basics.error);
82
81
  });
83
82
 
84
83
  test('listDiagnoses returns an error for a nonexistent report', async () => {
85
84
  const body = await response(['0', 'linkNoText', '999999T9999', 'xyz']);
86
- const basics = body['response content']['basics about the report'];
85
+ const basics = body['response content']['basics about the report'] as any;
87
86
  assert.ok(basics.error);
88
87
  });
89
88
 
90
89
  test('listDiagnoses returns guideline layer for an issue with a short WCAG code', async () => {
91
90
  const body = await response(['0', 'duplicateID', '260101T0009', 'brd']);
92
- const issueBasics = body['response content']['basics about the issue'];
91
+ const issueBasics = body['response content']['basics about the issue'] as any;
93
92
  assert.equal(issueBasics['related WCAG standard'].layer, 'guideline');
94
93
  assert.equal(issueBasics['related WCAG standard'].identifier, '4.1');
95
94
  });
96
95
 
97
96
  test('listDiagnoses returns null tag name and inner text for a catalog item missing them', async () => {
98
97
  const body = await response(['1', 'duplicateID', '260101T0009', 'brd']);
99
- const elementBasics = body['response content']['basics about the element'];
98
+ const elementBasics = body['response content']['basics about the element'] as any;
100
99
  assert.equal(elementBasics['tag name'], null);
101
100
  assert.equal(elementBasics['inner text'], null);
102
101
  });
103
102
 
104
103
  test('listDiagnoses returns null rule ID when ruleID equals what', async () => {
105
104
  const body = await response(['0', 'duplicateID', '260101T0009', 'brd']);
106
- const diagnoses = body['response content']['diagnoses of how the element exhibited the issue'];
107
- const axeDiagnosis = diagnoses.find(d => d['description of the violation'] === 'r99');
105
+ const diagnoses = body['response content']['diagnoses of how the element exhibited the issue'] as any;
106
+ const axeDiagnosis = diagnoses.find((d: any) => d['description of the violation'] === 'r99');
108
107
  assert.ok(axeDiagnosis);
109
108
  assert.equal(axeDiagnosis['identifier of the violated rule'], null);
110
109
  });
111
110
 
112
111
  test('listDiagnoses defaults count to 1 when count is missing', async () => {
113
112
  const body = await response(['1', 'duplicateID', '260101T0009', 'brd']);
114
- const diagnoses = body['response content']['diagnoses of how the element exhibited the issue'];
115
- const ibmDiagnosis = diagnoses.find(d => d['description of the violation'] === 'Element has no role');
113
+ const diagnoses = body['response content']['diagnoses of how the element exhibited the issue'] as any;
114
+ const ibmDiagnosis = diagnoses.find((d: any) => d['description of the violation'] === 'Element has no role');
116
115
  assert.ok(ibmDiagnosis);
117
116
  assert.equal(ibmDiagnosis['count of violations of the rule by the element'], 1);
118
117
  });
119
118
 
120
119
  test('listDiagnoses returns null for missing start tag, XPath, and bounding box', async () => {
121
120
  const body = await response(['2', 'duplicateID', '260101T0009', 'brd']);
122
- const elementDetails = body['response content']['details about the element'];
121
+ const elementDetails = body['response content']['details about the element'] as any;
123
122
  assert.equal(elementDetails['start tag'], null);
124
123
  assert.equal(elementDetails['XPath'], null);
125
124
  assert.equal(elementDetails['x, y, width, and height of bounding box'], null);
@@ -127,6 +126,6 @@ test('listDiagnoses returns null for missing start tag, XPath, and bounding box'
127
126
 
128
127
  test('listDiagnoses returns issue error for a falsy issue ID', async () => {
129
128
  const body = await response(['0', '', '260101T0009', 'brd']);
130
- const issueBasics = body['response content']['basics about the issue'];
129
+ const issueBasics = body['response content']['basics about the issue'] as any;
131
130
  assert.ok(issueBasics.error);
132
131
  });