testaro 78.0.8 → 78.6.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/.gitattributes +94 -0
- package/.github/workflows/ci.yml +58 -0
- package/.github/workflows/publish.yml +50 -0
- package/.github/workflows/typescript.yml +35 -0
- package/AGENTS.md +2 -2
- package/CLAUDE.md +24 -18
- package/CONTAINERS.md +9 -6
- package/CONTRIBUTING.md +29 -3
- package/Dockerfile +1 -1
- package/README.md +143 -19
- package/UPGRADES.md +4 -0
- package/actSpecs-doc.md +13 -1
- package/actSpecs.js +29 -5
- package/call.js +6 -5
- package/dist/nameComputation.js +1 -0
- package/docker-compose.yml +8 -2
- package/docs/checkpoint-scanning.md +199 -0
- package/docs/standard-result-outcome.md +165 -0
- package/env.example +49 -10
- package/eslint.config.mjs +101 -0
- package/netWatch.js +372 -222
- package/package.json +46 -31
- package/pour/README.md +47 -0
- package/pour/pour.min.js +8 -0
- package/procs/actDo.js +808 -0
- package/procs/catalog.d.ts +17 -0
- package/procs/catalog.js +312 -221
- package/procs/catalog.ts +398 -0
- package/procs/checkpoint.js +110 -0
- package/procs/config.d.ts +41 -0
- package/procs/config.js +98 -0
- package/procs/dateTime.js +2 -1
- package/procs/doActs.js +442 -849
- package/procs/doTestAct.js +27 -121
- package/procs/flow.js +221 -0
- package/procs/generateRuleRegistry.js +94 -0
- package/procs/getSource.d.ts +26 -0
- package/procs/job.js +115 -1
- package/procs/launch.d.ts +40 -0
- package/procs/launch.js +243 -108
- package/procs/nu.d.ts +48 -0
- package/procs/scope.js +196 -0
- package/procs/shoot.d.ts +33 -0
- package/procs/standard.d.ts +18 -0
- package/procs/standard.js +83 -0
- package/procs/standard.ts +126 -0
- package/procs/testAct.js +145 -0
- package/procs/testaro.d.ts +20 -0
- package/procs/testaro.js +236 -189
- package/procs/testaro.ts +321 -0
- package/procs/userPath.js +126 -0
- package/procs/xPath.d.ts +4 -0
- package/procs/xPath.js +81 -69
- package/procs/xPath.ts +106 -0
- package/procs/xPathScript.d.ts +6 -0
- package/procs/xPathScript.js +70 -0
- package/run.js +3 -2
- package/surea11y/README.md +44 -0
- package/surea11y/surea11y.browser.js +14 -0
- package/testaro/adbID.d.ts +3 -0
- package/testaro/adbID.js +38 -41
- package/testaro/adbID.ts +66 -0
- package/testaro/allCapStyle.d.ts +3 -0
- package/testaro/allCapStyle.js +31 -34
- package/testaro/allCapStyle.ts +55 -0
- package/testaro/allCaps.d.ts +16 -0
- package/testaro/allCaps.js +179 -151
- package/testaro/allCaps.ts +228 -0
- package/testaro/allHidden.d.ts +13 -0
- package/testaro/allHidden.js +29 -30
- package/testaro/allHidden.ts +50 -0
- package/testaro/allSlanted.d.ts +3 -0
- package/testaro/allSlanted.js +30 -33
- package/testaro/allSlanted.ts +54 -0
- package/testaro/altScheme.d.ts +3 -0
- package/testaro/altScheme.js +27 -30
- package/testaro/altScheme.ts +50 -0
- package/testaro/attVal.d.ts +3 -0
- package/testaro/attVal.js +20 -21
- package/testaro/attVal.ts +52 -0
- package/testaro/autocomplete.d.ts +3 -0
- package/testaro/autocomplete.js +59 -75
- package/testaro/autocomplete.ts +101 -0
- package/testaro/bulk.d.ts +13 -0
- package/testaro/bulk.js +32 -33
- package/testaro/bulk.ts +55 -0
- package/testaro/buttonMenu.d.ts +9 -0
- package/testaro/buttonMenu.js +317 -319
- package/testaro/buttonMenu.ts +391 -0
- package/testaro/captionLoc.d.ts +3 -0
- package/testaro/captionLoc.js +17 -20
- package/testaro/captionLoc.ts +40 -0
- package/testaro/datalistRef.d.ts +3 -0
- package/testaro/datalistRef.js +33 -36
- package/testaro/datalistRef.ts +55 -0
- package/testaro/distortion.d.ts +3 -0
- package/testaro/distortion.js +57 -26
- package/testaro/distortion.ts +81 -0
- package/testaro/docType.d.ts +15 -0
- package/testaro/docType.js +25 -25
- package/testaro/docType.ts +45 -0
- package/testaro/dupAtt.d.ts +16 -0
- package/testaro/dupAtt.js +113 -104
- package/testaro/dupAtt.ts +144 -0
- package/testaro/elements.d.ts +6 -0
- package/testaro/elements.js +153 -153
- package/testaro/elements.ts +215 -0
- package/testaro/embAc.d.ts +3 -0
- package/testaro/embAc.js +19 -20
- package/testaro/embAc.ts +40 -0
- package/testaro/focAll.d.ts +13 -0
- package/testaro/focAll.js +185 -191
- package/testaro/focAll.ts +217 -0
- package/testaro/focAndOp.d.ts +3 -0
- package/testaro/focAndOp.js +100 -104
- package/testaro/focAndOp.ts +128 -0
- package/testaro/focInd.d.ts +3 -0
- package/testaro/focInd.js +64 -66
- package/testaro/focInd.ts +96 -0
- package/testaro/focVis.d.ts +3 -0
- package/testaro/focVis.js +28 -30
- package/testaro/focVis.ts +52 -0
- package/testaro/headEl.d.ts +10 -0
- package/testaro/headEl.js +61 -62
- package/testaro/headEl.ts +82 -0
- package/testaro/headingAmb.d.ts +3 -0
- package/testaro/headingAmb.js +54 -63
- package/testaro/headingAmb.ts +72 -0
- package/testaro/hovInd.d.ts +12 -0
- package/testaro/hovInd.js +163 -130
- package/testaro/hovInd.ts +199 -0
- package/testaro/hover.d.ts +3 -0
- package/testaro/hover.js +155 -126
- package/testaro/hover.ts +154 -0
- package/testaro/hr.d.ts +3 -0
- package/testaro/hr.js +14 -17
- package/testaro/hr.ts +36 -0
- package/testaro/imageLink.d.ts +3 -0
- package/testaro/imageLink.js +17 -20
- package/testaro/imageLink.ts +42 -0
- package/testaro/labClash.d.ts +3 -0
- package/testaro/labClash.js +31 -33
- package/testaro/labClash.ts +54 -0
- package/testaro/legendLoc.d.ts +3 -0
- package/testaro/legendLoc.js +17 -20
- package/testaro/legendLoc.ts +42 -0
- package/testaro/lineHeight.d.ts +3 -0
- package/testaro/lineHeight.js +43 -48
- package/testaro/lineHeight.ts +70 -0
- package/testaro/linkAmb.d.ts +7 -0
- package/testaro/linkAmb.js +79 -80
- package/testaro/linkAmb.ts +105 -0
- package/testaro/linkExt.d.ts +3 -0
- package/testaro/linkExt.js +13 -16
- package/testaro/linkExt.ts +35 -0
- package/testaro/linkOldAtt.d.ts +3 -0
- package/testaro/linkOldAtt.js +25 -28
- package/testaro/linkOldAtt.ts +48 -0
- package/testaro/linkTo.d.ts +3 -0
- package/testaro/linkTo.js +22 -22
- package/testaro/linkTo.ts +43 -0
- package/testaro/linkUl.d.ts +3 -0
- package/testaro/linkUl.js +30 -34
- package/testaro/linkUl.ts +54 -0
- package/testaro/miniText.d.ts +3 -0
- package/testaro/miniText.js +41 -44
- package/testaro/miniText.ts +68 -0
- package/testaro/motion.d.ts +10 -0
- package/testaro/motion.js +92 -96
- package/testaro/motion.ts +125 -0
- package/testaro/nonTable.d.ts +3 -0
- package/testaro/nonTable.js +39 -45
- package/testaro/nonTable.ts +66 -0
- package/testaro/optRoleSel.d.ts +3 -0
- package/testaro/optRoleSel.js +16 -19
- package/testaro/optRoleSel.ts +41 -0
- package/testaro/phOnly.d.ts +3 -0
- package/testaro/phOnly.js +18 -21
- package/testaro/phOnly.ts +43 -0
- package/testaro/pseudoP.d.ts +3 -0
- package/testaro/pseudoP.js +36 -38
- package/testaro/pseudoP.ts +59 -0
- package/testaro/radioSet.d.ts +3 -0
- package/testaro/radioSet.js +57 -59
- package/testaro/radioSet.ts +79 -0
- package/testaro/registry.d.ts +63 -0
- package/testaro/registry.js +67 -0
- package/testaro/registry.ts +141 -0
- package/testaro/role.d.ts +3 -0
- package/testaro/role.js +27 -29
- package/testaro/role.ts +53 -0
- package/testaro/secHeading.d.ts +3 -0
- package/testaro/secHeading.js +30 -33
- package/testaro/secHeading.ts +53 -0
- package/testaro/styleDiff.d.ts +25 -0
- package/testaro/styleDiff.js +248 -252
- package/testaro/styleDiff.ts +303 -0
- package/testaro/tabNav.d.ts +33 -0
- package/testaro/tabNav.js +272 -342
- package/testaro/tabNav.ts +454 -0
- package/testaro/targetsNear.d.ts +9 -0
- package/testaro/targetsNear.js +130 -132
- package/testaro/targetsNear.ts +160 -0
- package/testaro/textNodes.d.ts +6 -0
- package/testaro/textNodes.js +139 -135
- package/testaro/textNodes.ts +185 -0
- package/testaro/textSem.d.ts +3 -0
- package/testaro/textSem.js +25 -28
- package/testaro/textSem.ts +47 -0
- package/testaro/title.d.ts +9 -0
- package/testaro/title.js +16 -13
- package/testaro/title.ts +31 -0
- package/testaro/titledEl.d.ts +3 -0
- package/testaro/titledEl.js +15 -18
- package/testaro/titledEl.ts +38 -0
- package/testaro/zIndex.d.ts +3 -0
- package/testaro/zIndex.js +19 -22
- package/testaro/zIndex.ts +42 -0
- package/tests/alfa.d.ts +45 -0
- package/tests/alfa.js +138 -141
- package/tests/alfa.ts +214 -0
- package/tests/aslint.d.ts +33 -0
- package/tests/aslint.js +272 -249
- package/tests/aslint.ts +301 -0
- package/tests/axe.d.ts +27 -0
- package/tests/axe.js +199 -200
- package/tests/axe.ts +277 -0
- package/tests/ed11y.d.ts +28 -0
- package/tests/ed11y.js +141 -99
- package/tests/ed11y.ts +178 -0
- package/tests/htmlcs.d.ts +21 -0
- package/tests/htmlcs.js +174 -140
- package/tests/htmlcs.ts +181 -0
- package/tests/ibm.d.ts +57 -0
- package/tests/ibm.js +182 -175
- package/tests/ibm.ts +287 -0
- package/tests/nuVal.d.ts +13 -0
- package/tests/nuVal.js +107 -112
- package/tests/nuVal.ts +145 -0
- package/tests/nuVnu.d.ts +15 -0
- package/tests/nuVnu.js +141 -111
- package/tests/nuVnu.ts +144 -0
- package/tests/pour.d.ts +31 -0
- package/tests/pour.js +242 -0
- package/tests/pour.ts +273 -0
- package/tests/qualWeb.d.ts +39 -0
- package/tests/qualWeb.js +262 -273
- package/tests/qualWeb.ts +344 -0
- package/tests/surea11y.d.ts +33 -0
- package/tests/surea11y.js +288 -0
- package/tests/surea11y.ts +334 -0
- package/tests/testaro.d.ts +25 -0
- package/tests/testaro.js +745 -652
- package/tests/testaro.ts +862 -0
- package/tests/wave.d.ts +46 -0
- package/tests/wave.js +166 -177
- package/tests/wave.ts +252 -0
- package/tsconfig.json +17 -0
- package/types.d.ts +243 -0
- package/types.js +10 -0
- package/types.ts +376 -0
- package/validation/act/README.md +46 -0
- package/validation/act/capture.js +877 -0
- package/validation/act/chromium-issue-draft.md +66 -0
- package/validation/act/fp-triage-2026-08-22.md +86 -0
- package/validation/act/isolation-notes.md +159 -0
- package/validation/act/playwright-issue-draft.md +73 -0
- package/validation/act/propose-mappings.js +0 -0
- package/validation/act/repro-cdp-raw.js +105 -0
- package/validation/act/repro-metarefresh.js +65 -0
- package/validation/act/score.js +184 -0
- package/validation/act/stage3a-stress-report.md +69 -0
- package/validation/act/stage3b-mapping-proposals.md +94 -0
- package/validation/act/stage3b-triage-draft.md +138 -0
- package/validation/act/surea11y-track-a-2026-09-01.md +50 -0
- package/validation/executors/netWatch.js +180 -90
- package/validation/executors/test.js +17 -2
- package/validation/executors/tests.js +118 -10
- package/validation/jobs/reports/raw/260901T1000-surea11y-validation.json +964 -0
- package/validation/jobs/todo/240101T1200-simple-example.json +14 -6
- package/validation/jobs/todo/240101T1300-shoot-example.json +2 -1
- package/validation/jobs/todo/260821T1900-pour-validation.json +45 -0
- package/validation/jobs/todo/260901T1000-surea11y-validation.json +45 -0
- package/validation/knownFailures.json +1 -0
- package/validation/tests/jobProperties/adbID.json +27 -2
- package/validation/tests/jobProperties/{focOp.json → allCapStyle.json} +56 -53
- package/validation/tests/jobProperties/allCaps.json +25 -0
- package/validation/tests/jobProperties/allHidden.json +133 -13
- package/validation/tests/jobProperties/allSlanted.json +3 -3
- package/validation/tests/jobProperties/altScheme.json +23 -8
- package/validation/tests/jobProperties/attVal.json +57 -57
- package/validation/tests/jobProperties/autocomplete.json +11 -1
- package/validation/tests/jobProperties/bulk.json +6 -1
- package/validation/tests/jobProperties/buttonMenu.json +63 -42
- package/validation/tests/jobProperties/captionLoc.json +1 -6
- package/validation/tests/jobProperties/checkpoint-browser.json +401 -0
- package/validation/tests/jobProperties/checkpoint-page.json +397 -0
- package/validation/tests/jobProperties/checkpoint.json +395 -0
- package/validation/tests/jobProperties/datalistRef.json +20 -5
- package/validation/tests/jobProperties/distortion.json +28 -3
- package/validation/tests/jobProperties/docType.json +2 -2
- package/validation/tests/jobProperties/dupAtt.json +48 -33
- package/validation/tests/jobProperties/elements.json +28 -28
- package/validation/tests/jobProperties/embAc.json +36 -31
- package/validation/tests/jobProperties/focAndOp.json +284 -0
- package/validation/tests/jobProperties/focInd.json +34 -34
- package/validation/tests/jobProperties/focVis.json +1 -1
- package/validation/tests/jobProperties/hover.json +39 -37
- package/validation/tests/jobProperties/hr.json +13 -3
- package/validation/tests/jobProperties/imageLink.json +0 -5
- package/validation/tests/jobProperties/labClash.json +68 -33
- package/validation/tests/jobProperties/legendLoc.json +1 -6
- package/validation/tests/jobProperties/lineHeight.json +2 -2
- package/validation/tests/jobProperties/linkAmb.json +13 -18
- package/validation/tests/jobProperties/linkExt.json +1 -1
- package/validation/tests/jobProperties/linkOldAtt.json +12 -2
- package/validation/tests/jobProperties/linkTo.json +1 -1
- package/validation/tests/jobProperties/linkUl.json +65 -65
- package/validation/tests/jobProperties/miniText.json +7 -2
- package/validation/tests/jobProperties/motion.json +4 -50
- package/validation/tests/jobProperties/nonTable.json +48 -3
- package/validation/tests/jobProperties/optRoleSel.json +12 -2
- package/validation/tests/jobProperties/phOnly.json +6 -16
- package/validation/tests/jobProperties/pseudoP.json +17 -2
- package/validation/tests/jobProperties/radioSet.json +30 -30
- package/validation/tests/jobProperties/role.json +25 -5
- package/validation/tests/jobProperties/secHeading.json +26 -21
- package/validation/tests/jobProperties/styleDiff.json +35 -5
- package/validation/tests/jobProperties/tabNav.json +3 -1
- package/validation/tests/jobProperties/targetsNear.json +237 -0
- package/validation/tests/jobProperties/textNodes.json +29 -29
- package/validation/tests/jobProperties/textSem.json +26 -1
- package/validation/tests/jobProperties/title.json +12 -2
- package/validation/tests/jobProperties/titledEl.json +39 -9
- package/validation/tests/jobProperties/userPath.json +317 -0
- package/validation/tests/jobProperties/zIndex.json +40 -35
- package/validation/tests/targets/allCapStyle/index.html +26 -0
- package/validation/tests/targets/checkpoint/index.html +38 -0
- package/validation/tests/targets/datalistRef/index.html +1 -1
- package/validation/tests/targets/focAndOp/bad.html +29 -0
- package/validation/tests/targets/{focOp → focAndOp}/good.html +3 -1
- package/validation/tests/targets/focInd/bad.html +2 -1
- package/validation/tests/targets/headEl/index.html +10 -1
- package/validation/tests/targets/{targetSmall → targetsNear}/index.html +15 -1
- package/validation/tests/targets/userPath/index.html +42 -0
- package/validation/validateTest.js +61 -9
- package/.claude/settings.local.json +0 -11
- package/.eslintrc.json +0 -41
- package/htmlcs/.eslintrc.json +0 -67
- package/validation/tests/jobProperties/linkTitle.json +0 -127
- package/validation/tests/jobProperties/opFoc.json +0 -164
- package/validation/tests/jobProperties/targetSmall.json +0 -152
- package/validation/tests/jobProperties/targetTiny.json +0 -142
- package/validation/tests/targets/focOp/bad.html +0 -25
- package/validation/tests/targets/linkTitle/index.html +0 -24
- package/validation/tests/targets/opFoc/bad.html +0 -26
- package/validation/tests/targets/opFoc/good.html +0 -23
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2022–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2026 Jonathan Robert Pool.
|
|
4
|
+
© 2026 Jeff Witt.
|
|
3
5
|
|
|
4
6
|
Licensed under the MIT License. See LICENSE file at the project root or
|
|
5
7
|
https://opensource.org/license/mit/ for details.
|
|
@@ -8,119 +10,207 @@
|
|
|
8
10
|
*/
|
|
9
11
|
|
|
10
12
|
/*
|
|
11
|
-
|
|
12
|
-
Validator for network watching.
|
|
13
|
+
netWatch.js
|
|
14
|
+
Validator for network watching, covering all three auth types.
|
|
13
15
|
*/
|
|
14
16
|
|
|
15
17
|
// IMPORTS
|
|
16
18
|
|
|
17
19
|
const fs = require('fs/promises');
|
|
20
|
+
const client = require('http');
|
|
18
21
|
|
|
19
22
|
// CONSTANTS
|
|
20
23
|
|
|
21
|
-
// Override netWatch environment variables with validation-specific ones.
|
|
22
24
|
const jobDir = `${__dirname}/../jobs/todo`;
|
|
23
|
-
process.env.JOB_URLS = 'http://localhost:3007/api/job';
|
|
24
|
-
process.env.AGENT = 'testarauth';
|
|
25
|
-
const {netWatch} = require('../../netWatch');
|
|
26
|
-
const client = require('http');
|
|
27
25
|
const jobID = '240101T1200-simple-example';
|
|
26
|
+
const workerID = 'testaro1';
|
|
27
|
+
const workerSecret = 'testarosecret';
|
|
28
|
+
// Override netWatch environment variables with validation-specific ones.
|
|
29
|
+
process.env.NETWATCH_URL_JOB = 'http://localhost:3007/api/job';
|
|
30
|
+
process.env.NETWATCH_URL_REPORT = 'http://localhost:3007/api/report';
|
|
31
|
+
process.env.NETWATCH_WORKER_ID = workerID;
|
|
32
|
+
process.env.NETWATCH_WORKER_SECRET = workerSecret;
|
|
33
|
+
const {netWatch} = require('../../netWatch');
|
|
34
|
+
/*
|
|
35
|
+
The authorization-header value netWatch is expected to send in header mode: per RFC 7617, Basic
|
|
36
|
+
followed by base64 of testaro1:testarosecret. Intentionally a literal, not derived from the
|
|
37
|
+
netWatch code, so an encoding regression there fails here.
|
|
38
|
+
*/
|
|
39
|
+
const workerAuth = 'Basic dGVzdGFybzE6dGVzdGFyb3NlY3JldA==';
|
|
40
|
+
// Auth types to be validated.
|
|
41
|
+
const authTypes = ['header', 'pathBody', 'none'];
|
|
28
42
|
|
|
29
|
-
//
|
|
43
|
+
// FUNCTIONS
|
|
30
44
|
|
|
31
|
-
//
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
45
|
+
// Returns an error description if a request carries wrong credentials for an auth type, else ''.
|
|
46
|
+
const credentialError = (authType, request, bodyObj) => {
|
|
47
|
+
const authHeader = request.headers.authorization;
|
|
48
|
+
if (authType === 'header') {
|
|
49
|
+
if (authHeader !== workerAuth) {
|
|
50
|
+
return 'authorization header missing or wrong';
|
|
51
|
+
}
|
|
52
|
+
if (bodyObj.agentPW !== undefined) {
|
|
53
|
+
return 'agentPW unexpectedly in the request body';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else if (authType === 'pathBody') {
|
|
57
|
+
if (authHeader) {
|
|
58
|
+
return 'authorization header unexpectedly present';
|
|
59
|
+
}
|
|
60
|
+
if (bodyObj.agentPW !== workerSecret) {
|
|
61
|
+
return 'agentPW missing from or wrong in the request body';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
if (authHeader) {
|
|
66
|
+
return 'authorization header unexpectedly present';
|
|
67
|
+
}
|
|
68
|
+
if (bodyObj.agentPW !== undefined) {
|
|
69
|
+
return 'agentPW unexpectedly in the request body';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return '';
|
|
73
|
+
};
|
|
74
|
+
// Validates netWatch with one auth type; returns whether the scenario succeeded.
|
|
75
|
+
const runScenario = authType => {
|
|
76
|
+
return new Promise(resolveScenario => {
|
|
77
|
+
process.env.NETWATCH_AUTH_TYPE = authType;
|
|
78
|
+
let jobGiven = false;
|
|
79
|
+
let noJobSent = false;
|
|
80
|
+
let reportOK = false;
|
|
81
|
+
let server;
|
|
82
|
+
// Responds to a request with a JSON object and a status code.
|
|
83
|
+
const respondWithObject = (object, response, statusCode = 200) => {
|
|
84
|
+
response.statusCode = statusCode;
|
|
85
|
+
response.setHeader('content-type', 'application/json; charset=utf-8');
|
|
86
|
+
response.end(JSON.stringify(object));
|
|
87
|
+
};
|
|
88
|
+
// Handles Testaro requests to the server.
|
|
89
|
+
const requestHandler = (request, response) => {
|
|
90
|
+
const {method} = request;
|
|
91
|
+
// Ignore any trailing slash on the request URL.
|
|
92
|
+
const url = request.url.replace(/\/$/, '');
|
|
93
|
+
const bodyParts = [];
|
|
94
|
+
request.on('error', err => {
|
|
95
|
+
console.error(err);
|
|
96
|
+
})
|
|
97
|
+
.on('data', chunk => {
|
|
98
|
+
bodyParts.push(chunk);
|
|
99
|
+
})
|
|
100
|
+
.on('end', async () => {
|
|
101
|
+
let bodyObj = {};
|
|
102
|
+
try {
|
|
103
|
+
bodyObj = JSON.parse(bodyParts.join('') || '{}');
|
|
65
104
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
// Send an empty-object response.
|
|
69
|
-
await response.end('{}');
|
|
105
|
+
catch(error) {
|
|
106
|
+
console.log(`Scenario ${authType} failure: non-JSON request body (${error.message})`);
|
|
70
107
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
108
|
+
// If the request carries wrong credentials for this auth type:
|
|
109
|
+
const credError = credentialError(authType, request, bodyObj);
|
|
110
|
+
if (credError) {
|
|
111
|
+
// Report this and reject the request; netWatch is expected to abort on this.
|
|
112
|
+
console.log(`Scenario ${authType} failure: ${credError}`);
|
|
113
|
+
respondWithObject({error: 'ERROR: Authorization invalid'}, response, 401);
|
|
114
|
+
server.close();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
// Otherwise, if the request is a job request:
|
|
118
|
+
if (method === 'POST' && url === '/api/job') {
|
|
119
|
+
console.log(`Server got a job request from Testaro (${authType})`);
|
|
120
|
+
// If this is the first job request of the scenario:
|
|
121
|
+
if (! noJobSent) {
|
|
122
|
+
// Send a no-job response, to validate the no-job branch.
|
|
123
|
+
noJobSent = true;
|
|
124
|
+
respondWithObject({}, response);
|
|
125
|
+
}
|
|
126
|
+
// Otherwise, i.e. if the no-job branch was already validated:
|
|
127
|
+
else {
|
|
128
|
+
// Respond with a job.
|
|
129
|
+
const jobJSON = await fs.readFile(`${jobDir}/${jobID}.json`);
|
|
130
|
+
response.setHeader('content-type', 'application/json; charset=utf-8');
|
|
131
|
+
response.end(jobJSON);
|
|
132
|
+
console.log(`Server sent job to Testaro (${authType})`);
|
|
133
|
+
jobGiven = true;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Otherwise, if the request is a report submission:
|
|
137
|
+
else if (method === 'POST' && url === '/api/report') {
|
|
138
|
+
console.log(`Server got report from Testaro (${authType})`);
|
|
139
|
+
const ack = {};
|
|
140
|
+
// If a job was earlier given to Testaro:
|
|
141
|
+
if (jobGiven) {
|
|
142
|
+
// Check the report, including the worker identification.
|
|
143
|
+
const {report} = bodyObj;
|
|
92
144
|
if (
|
|
93
|
-
|
|
94
|
-
&&
|
|
95
|
-
&&
|
|
96
|
-
&&
|
|
145
|
+
report
|
|
146
|
+
&& report.acts
|
|
147
|
+
&& report.jobData
|
|
148
|
+
&& report.sources
|
|
149
|
+
&& report.sources.agent === workerID
|
|
97
150
|
) {
|
|
98
151
|
ack.message = 'Success: Valid report submitted';
|
|
152
|
+
reportOK = true;
|
|
99
153
|
}
|
|
100
154
|
else {
|
|
101
|
-
ack.message = 'Failure: Report invalid';
|
|
155
|
+
ack.message = 'Failure: Report invalid or worker not identified';
|
|
102
156
|
}
|
|
103
157
|
}
|
|
104
|
-
|
|
105
|
-
ack.message =
|
|
158
|
+
else {
|
|
159
|
+
ack.message = 'ERROR: Report submitted before a job was given';
|
|
106
160
|
}
|
|
161
|
+
respondWithObject(ack, response);
|
|
162
|
+
console.log(`Server responded: ${ack.message}`);
|
|
163
|
+
// This ends the scenario, so stop the server.
|
|
164
|
+
server.close();
|
|
165
|
+
console.log('Server closed');
|
|
107
166
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
167
|
+
// Otherwise, i.e. if the request is neither:
|
|
168
|
+
else {
|
|
169
|
+
// Report this and reject the request.
|
|
170
|
+
console.log(`Scenario ${authType} failure: unexpected request ${method} ${request.url}`);
|
|
171
|
+
respondWithObject({error: 'ERROR: Request invalid'}, response, 404);
|
|
172
|
+
server.close();
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
};
|
|
176
|
+
// Create a server.
|
|
177
|
+
server = client.createServer({}, requestHandler);
|
|
178
|
+
// Start the server listening for Testaro requests, then start a one-job watch.
|
|
179
|
+
server.listen(3007, async () => {
|
|
180
|
+
console.log(`Job and report server listening on port 3007 (auth type ${authType})`);
|
|
181
|
+
const watchOK = await netWatch(false, 5, false);
|
|
116
182
|
server.close();
|
|
117
|
-
|
|
118
|
-
}
|
|
183
|
+
resolveScenario(reportOK && watchOK);
|
|
184
|
+
});
|
|
119
185
|
});
|
|
120
186
|
};
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
187
|
+
|
|
188
|
+
// OPERATION
|
|
189
|
+
|
|
190
|
+
// Impose a deadline, so a hang becomes a failure instead of a silent stall.
|
|
191
|
+
const watchdog = setTimeout(() => {
|
|
192
|
+
console.log('Failure: netWatch validation timed out');
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}, 600000);
|
|
195
|
+
// Run the scenarios in sequence and exit with a code reporting the outcome.
|
|
196
|
+
(async () => {
|
|
197
|
+
const failures = [];
|
|
198
|
+
for (const authType of authTypes) {
|
|
199
|
+
const isOK = await runScenario(authType);
|
|
200
|
+
// Drop any kept-alive connections to the closed server before the next scenario.
|
|
201
|
+
client.globalAgent.destroy();
|
|
202
|
+
console.log(`Scenario ${authType}: ${isOK ? 'valid' : 'INVALID'}\n`);
|
|
203
|
+
if (! isOK) {
|
|
204
|
+
failures.push(authType);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
clearTimeout(watchdog);
|
|
208
|
+
if (failures.length) {
|
|
209
|
+
console.log(`Failure: Invalid scenarios: ${failures.join(', ')}`);
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
console.log('Success: Valid reports submitted for all auth types');
|
|
214
|
+
process.exit(0);
|
|
215
|
+
}
|
|
216
|
+
})();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2022–2023 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2026 Jeff Witt.
|
|
3
4
|
|
|
4
5
|
Licensed under the MIT License. See LICENSE file at the project root or
|
|
5
6
|
https://opensource.org/license/mit/ for details.
|
|
@@ -13,6 +14,20 @@
|
|
|
13
14
|
Execution example: npm test focOp
|
|
14
15
|
*/
|
|
15
16
|
|
|
16
|
-
const testID = process.argv[2];
|
|
17
17
|
const {validateTest} = require('../validateTest');
|
|
18
|
-
|
|
18
|
+
|
|
19
|
+
// Flushes the standard output and then exits, because lingering browser
|
|
20
|
+
// processes otherwise keep the event loop alive indefinitely.
|
|
21
|
+
const exitWith = code => {
|
|
22
|
+
process.stdout.write('', () => process.exit(code));
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const testID = process.argv[2];
|
|
26
|
+
validateTest(testID)
|
|
27
|
+
.then(result => {
|
|
28
|
+
exitWith(result.success ? 0 : 1);
|
|
29
|
+
})
|
|
30
|
+
.catch(error => {
|
|
31
|
+
console.log(`ERROR validating test ${testID} (${error.message})`);
|
|
32
|
+
exitWith(1);
|
|
33
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2022–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2026 Jeff Witt.
|
|
3
4
|
|
|
4
5
|
Licensed under the MIT License. See LICENSE file at the project root or
|
|
5
6
|
https://opensource.org/license/mit/ for details.
|
|
@@ -14,21 +15,128 @@
|
|
|
14
15
|
|
|
15
16
|
// IMPORTS
|
|
16
17
|
|
|
18
|
+
require('dotenv').config({quiet: true});
|
|
17
19
|
const fs = require('fs').promises;
|
|
18
|
-
const {
|
|
20
|
+
const {spawn} = require('child_process');
|
|
21
|
+
|
|
22
|
+
// CONSTANTS
|
|
23
|
+
|
|
24
|
+
const ruleDir = `${__dirname}/../../testaro`;
|
|
25
|
+
const validatorDir = `${__dirname}/../tests/jobProperties`;
|
|
26
|
+
// Rules that ask an AI model to classify candidate instances.
|
|
27
|
+
const aiRuleIDs = ['allCaps'];
|
|
28
|
+
// Validators of features other than rules, run after the rule validators.
|
|
29
|
+
const featureValidatorIDs = ['checkpoint', 'checkpoint-browser', 'checkpoint-page', 'userPath'];
|
|
30
|
+
// Maximum number of seconds allowed for the validation of one rule.
|
|
31
|
+
const ruleTimeLimit = 180;
|
|
32
|
+
/*
|
|
33
|
+
Rules whose validations currently fail, with the reasons. They do not make
|
|
34
|
+
the validation as a whole fail, so a failure of a healthy rule is blocking
|
|
35
|
+
while these known failures are being repaired. When a repair makes a rule
|
|
36
|
+
pass, it is to be removed from knownFailures.json.
|
|
37
|
+
*/
|
|
38
|
+
const knownFailures = require('../knownFailures.json');
|
|
39
|
+
|
|
40
|
+
// FUNCTIONS
|
|
41
|
+
|
|
42
|
+
/*
|
|
43
|
+
Validates one rule in a child process, so a defective rule cannot crash or
|
|
44
|
+
hang the whole validation and so each job's browser processes are reaped.
|
|
45
|
+
Returns whether the validation succeeded.
|
|
46
|
+
*/
|
|
47
|
+
const validateInChild = testID => new Promise(resolve => {
|
|
48
|
+
const child = spawn(
|
|
49
|
+
process.execPath, [`${__dirname}/test.js`, testID], {stdio: 'inherit'}
|
|
50
|
+
);
|
|
51
|
+
const timer = setTimeout(() => {
|
|
52
|
+
console.log(`######## Failure: Validation of ${testID} timed out (${ruleTimeLimit}s)\n`);
|
|
53
|
+
child.kill('SIGKILL');
|
|
54
|
+
}, 1000 * ruleTimeLimit);
|
|
55
|
+
child.on('close', code => {
|
|
56
|
+
clearTimeout(timer);
|
|
57
|
+
resolve(code === 0);
|
|
58
|
+
});
|
|
59
|
+
child.on('error', error => {
|
|
60
|
+
clearTimeout(timer);
|
|
61
|
+
console.log(`######## Failure: Validation of ${testID} could not start (${error.message})\n`);
|
|
62
|
+
resolve(false);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
19
65
|
|
|
20
66
|
// OPERATION
|
|
21
67
|
|
|
22
|
-
// Get the names of the Testaro
|
|
23
|
-
fs.readdir(
|
|
68
|
+
// Get the names of the Testaro rule files and the validator files.
|
|
69
|
+
Promise.all([fs.readdir(ruleDir), fs.readdir(validatorDir)])
|
|
24
70
|
// When they arrive:
|
|
25
|
-
.then(async
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
71
|
+
.then(async ([ruleFileNames, validatorFileNames]) => {
|
|
72
|
+
// Get the rule IDs and the IDs of the rules with validators.
|
|
73
|
+
// Rule modules are the JavaScript files in the rule directory, except the generated registry.
|
|
74
|
+
const ruleIDs = ruleFileNames
|
|
75
|
+
.filter(name => name.endsWith('.js') && name !== 'registry.js')
|
|
76
|
+
.map(name => name.slice(0, -3));
|
|
77
|
+
const validatorIDs = validatorFileNames
|
|
78
|
+
.filter(name => name.endsWith('.json'))
|
|
79
|
+
.map(name => name.slice(0, -5));
|
|
80
|
+
// Identify the rules without validators and the validators without rules.
|
|
81
|
+
const unvalidatedIDs = ruleIDs.filter(id => ! validatorIDs.includes(id));
|
|
82
|
+
const orphanIDs = validatorIDs.filter(
|
|
83
|
+
id => ! ruleIDs.includes(id) && ! featureValidatorIDs.includes(id)
|
|
84
|
+
);
|
|
85
|
+
// Identify the AI-dependent rules to be skipped for lack of an API key.
|
|
86
|
+
const skippedIDs = process.env.ANTHROPIC_API_KEY
|
|
87
|
+
? []
|
|
88
|
+
: ruleIDs.filter(id => aiRuleIDs.includes(id));
|
|
89
|
+
// Initialize the results.
|
|
90
|
+
const failedIDs = [];
|
|
91
|
+
const knownFailedIDs = [];
|
|
92
|
+
const recoveredIDs = [];
|
|
93
|
+
let validatedCount = 0;
|
|
94
|
+
// For each rule with a validator, and then each feature validator:
|
|
95
|
+
const featureIDs = featureValidatorIDs.filter(id => validatorIDs.includes(id));
|
|
96
|
+
for (
|
|
97
|
+
const testID of ruleIDs
|
|
98
|
+
.filter(id => validatorIDs.includes(id) && ! skippedIDs.includes(id))
|
|
99
|
+
.concat(featureIDs)
|
|
100
|
+
) {
|
|
101
|
+
// Validate the rule.
|
|
31
102
|
console.log(`### Validating Testaro test ${testID}`);
|
|
32
|
-
await
|
|
103
|
+
const succeeded = await validateInChild(testID);
|
|
104
|
+
validatedCount++;
|
|
105
|
+
if (succeeded && knownFailures[testID]) {
|
|
106
|
+
recoveredIDs.push(testID);
|
|
107
|
+
}
|
|
108
|
+
else if (! succeeded) {
|
|
109
|
+
(knownFailures[testID] ? knownFailedIDs : failedIDs).push(testID);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Report a summary.
|
|
113
|
+
console.log('\n#### Validation summary');
|
|
114
|
+
console.log(`Rules validated: ${validatedCount}`);
|
|
115
|
+
console.log(`Rules failing validation: ${failedIDs.length + knownFailedIDs.length}`);
|
|
116
|
+
if (failedIDs.length) {
|
|
117
|
+
console.log(` Failing rules: ${failedIDs.join(', ')}`);
|
|
118
|
+
}
|
|
119
|
+
if (knownFailedIDs.length) {
|
|
120
|
+
console.log(` Known-failing rules, not blocking: ${knownFailedIDs.join(', ')}`);
|
|
121
|
+
}
|
|
122
|
+
if (recoveredIDs.length) {
|
|
123
|
+
console.log(
|
|
124
|
+
`Warning - rules now passing; remove them from knownFailures.json: ${recoveredIDs.join(', ')}`
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
if (skippedIDs.length) {
|
|
128
|
+
console.log(
|
|
129
|
+
`Warning - AI-dependent rules skipped because ANTHROPIC_API_KEY is not set: ${
|
|
130
|
+
skippedIDs.join(', ')
|
|
131
|
+
}`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
if (unvalidatedIDs.length) {
|
|
135
|
+
console.log(`Warning - rules without validators: ${unvalidatedIDs.join(', ')}`);
|
|
136
|
+
}
|
|
137
|
+
if (orphanIDs.length) {
|
|
138
|
+
console.log(`Warning - validators without rules: ${orphanIDs.join(', ')}`);
|
|
33
139
|
}
|
|
140
|
+
// Make the validation failable by CI and other callers.
|
|
141
|
+
process.exitCode = failedIDs.length ? 1 : 0;
|
|
34
142
|
});
|