testaro 78.0.8 → 78.4.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 +49 -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 +73 -19
- package/UPGRADES.md +4 -0
- package/actSpecs-doc.md +13 -1
- package/actSpecs.js +29 -5
- package/call.js +6 -5
- package/docker-compose.yml +8 -2
- package/docs/checkpoint-scanning.md +199 -0
- package/docs/standard-result-outcome.md +165 -0
- package/env.example +44 -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 +35 -0
- package/procs/config.js +62 -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 +239 -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 +52 -0
- package/tests/ibm.js +164 -166
- package/tests/ibm.ts +251 -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 +302 -272
- package/tests/qualWeb.ts +415 -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 +424 -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
package/tests/ibm.ts
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/*
|
|
2
|
+
© 2021–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2026 Jeff Witt.
|
|
4
|
+
© 2025–2026 Jonathan Robert Pool.
|
|
5
|
+
|
|
6
|
+
Licensed under the MIT License. See LICENSE file at the project root or
|
|
7
|
+
https://opensource.org/license/mit/ for details.
|
|
8
|
+
|
|
9
|
+
SPDX-License-Identifier: MIT
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// IMPORTS
|
|
13
|
+
|
|
14
|
+
import type {Page} from 'playwright';
|
|
15
|
+
import {getAttributeXPath, getXPathCatalogIndex} from '../procs/xPath';
|
|
16
|
+
import {getStandardResult, pushInstance} from '../procs/standard';
|
|
17
|
+
import type {Act, Report, StandardInstance, StandardResult} from '../types';
|
|
18
|
+
const accessibilityChecker = require('accessibility-checker') as typeof import('accessibility-checker');
|
|
19
|
+
const {getCompliance} = accessibilityChecker;
|
|
20
|
+
|
|
21
|
+
// TYPES
|
|
22
|
+
|
|
23
|
+
// The ibm-act properties this reporter consumes.
|
|
24
|
+
interface IbmAct extends Act {
|
|
25
|
+
withItems?: boolean;
|
|
26
|
+
rules?: string[];
|
|
27
|
+
}
|
|
28
|
+
// One item (rule violation) of an IBM act report.
|
|
29
|
+
interface IbmItem {
|
|
30
|
+
ruleId: string;
|
|
31
|
+
level: string;
|
|
32
|
+
message: string;
|
|
33
|
+
snippet: string;
|
|
34
|
+
apiArgs?: unknown;
|
|
35
|
+
category?: unknown;
|
|
36
|
+
ignored?: unknown;
|
|
37
|
+
messageArgs?: unknown;
|
|
38
|
+
reasonId?: unknown;
|
|
39
|
+
ruleTime?: unknown;
|
|
40
|
+
value?: unknown;
|
|
41
|
+
}
|
|
42
|
+
// The violation totals of an IBM act report.
|
|
43
|
+
interface IbmCounts {
|
|
44
|
+
violation: number;
|
|
45
|
+
recommendation: number;
|
|
46
|
+
[key: string]: number;
|
|
47
|
+
}
|
|
48
|
+
// An IBM act report before trimming.
|
|
49
|
+
interface IbmActReport {
|
|
50
|
+
summary?: {counts?: IbmCounts};
|
|
51
|
+
results: IbmItem[];
|
|
52
|
+
items?: IbmItem[];
|
|
53
|
+
}
|
|
54
|
+
// An IBM act report after trimming: results, or an error.
|
|
55
|
+
type IbmTrimmedReport =
|
|
56
|
+
| {totals: IbmCounts; items: IbmItem[] | undefined; error?: undefined}
|
|
57
|
+
| {totals: null; items: IbmItem[]; error: string};
|
|
58
|
+
|
|
59
|
+
/*
|
|
60
|
+
ibm
|
|
61
|
+
Implements the IBM Equal Access ruleset for accessibility.
|
|
62
|
+
|
|
63
|
+
This rule engine depends on aceconfig.js.
|
|
64
|
+
|
|
65
|
+
This rule engine is compatible with Windows only if the accessibility-checker package
|
|
66
|
+
is revised. See README.md for details.
|
|
67
|
+
Compiled to ibm.js by tsc (issue #73); edit this file, not the emitted one.
|
|
68
|
+
*/
|
|
69
|
+
|
|
70
|
+
// FUNCTIONS
|
|
71
|
+
|
|
72
|
+
// Runs the IBM test and returns the result.
|
|
73
|
+
const run = async (content: Page) => {
|
|
74
|
+
const nowLabel = (new Date()).toISOString().slice(0, 19);
|
|
75
|
+
try {
|
|
76
|
+
const ibmReport = await getCompliance(content, nowLabel);
|
|
77
|
+
if (typeof ibmReport === 'object' && ibmReport.report) {
|
|
78
|
+
return ibmReport as unknown as {report: IbmActReport};
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
return {
|
|
82
|
+
prevented: true,
|
|
83
|
+
error: 'ibm getCompliance produced no report'
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch(error) {
|
|
88
|
+
console.log('ibm getCompliance failed');
|
|
89
|
+
return {
|
|
90
|
+
prevented: true,
|
|
91
|
+
error: (error as Error).message.slice(0, 200)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
// Revises act-report totals for any rule limitation.
|
|
96
|
+
const limitRuleTotals = (actReport: IbmActReport, rules: string[] | undefined) => {
|
|
97
|
+
if (rules && Array.isArray(rules) && rules.length) {
|
|
98
|
+
const totals = actReport.summary!.counts!;
|
|
99
|
+
const items = actReport.results;
|
|
100
|
+
totals.violation = totals.recommendation = 0;
|
|
101
|
+
items.forEach(item => {
|
|
102
|
+
if (rules.includes(item.ruleId)) {
|
|
103
|
+
totals[item.level]++;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
// Trims an IBM report.
|
|
109
|
+
const trimActReport = (
|
|
110
|
+
actReport: IbmActReport | undefined, withItems: boolean | undefined, rules: string[] | undefined
|
|
111
|
+
): IbmTrimmedReport => {
|
|
112
|
+
// If the act report includes a summary:
|
|
113
|
+
if (actReport && actReport.summary) {
|
|
114
|
+
// Remove excluded rules from the act report.
|
|
115
|
+
limitRuleTotals(actReport, rules);
|
|
116
|
+
const totals = actReport.summary.counts;
|
|
117
|
+
// If the act report includes totals:
|
|
118
|
+
if (totals) {
|
|
119
|
+
// If itemization is required:
|
|
120
|
+
if (withItems) {
|
|
121
|
+
// Trim the items.
|
|
122
|
+
if (rules && Array.isArray(rules) && rules.length) {
|
|
123
|
+
actReport.items = actReport.results.filter(item => rules.includes(item.ruleId));
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
actReport.items = actReport.results;
|
|
127
|
+
}
|
|
128
|
+
actReport.items.forEach(item => {
|
|
129
|
+
delete item.apiArgs;
|
|
130
|
+
delete item.category;
|
|
131
|
+
delete item.ignored;
|
|
132
|
+
delete item.messageArgs;
|
|
133
|
+
delete item.reasonId;
|
|
134
|
+
delete item.ruleTime;
|
|
135
|
+
delete item.value;
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
// Return the act report, trimmed.
|
|
139
|
+
return {
|
|
140
|
+
totals,
|
|
141
|
+
items: actReport.items
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
// Otherwise, i.e. if it excludes totals:
|
|
145
|
+
else {
|
|
146
|
+
// Return an act report with this error.
|
|
147
|
+
return {
|
|
148
|
+
totals: null,
|
|
149
|
+
items: [],
|
|
150
|
+
error: 'No totals reported'
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Otherwise, i.e. if it excludes a summary:
|
|
155
|
+
else {
|
|
156
|
+
// Return an act report with this error.
|
|
157
|
+
return {
|
|
158
|
+
totals: null,
|
|
159
|
+
items: [],
|
|
160
|
+
error: 'No summary reported'
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
// Conducts and reports the IBM Equal Access tests.
|
|
165
|
+
export const reporter = async (page: Page, report: Report, actIndex: number) => {
|
|
166
|
+
const act = report.acts[actIndex] as IbmAct;
|
|
167
|
+
const {withItems, rules} = act;
|
|
168
|
+
// Initialize the act report.
|
|
169
|
+
const result: {nativeResult: IbmTrimmedReport | Record<string, never>; standardResult: StandardResult} = {
|
|
170
|
+
nativeResult: {},
|
|
171
|
+
standardResult: {}
|
|
172
|
+
};
|
|
173
|
+
const standard = report.standard !== 'no';
|
|
174
|
+
// If standard results are to be reported:
|
|
175
|
+
if (standard) {
|
|
176
|
+
// Initialize the standard result.
|
|
177
|
+
result.standardResult = getStandardResult();
|
|
178
|
+
}
|
|
179
|
+
try {
|
|
180
|
+
// Conduct the tests.
|
|
181
|
+
const runReport = await run(page);
|
|
182
|
+
const actReport = (runReport as {report?: IbmActReport}).report;
|
|
183
|
+
// If there were results:
|
|
184
|
+
if (actReport) {
|
|
185
|
+
// Trim them.
|
|
186
|
+
result.nativeResult = trimActReport(actReport, withItems, rules);
|
|
187
|
+
const {nativeResult, standardResult} = result as
|
|
188
|
+
{nativeResult: IbmTrimmedReport; standardResult: StandardResult};
|
|
189
|
+
const {error, totals} = nativeResult;
|
|
190
|
+
// If they were not trimmable:
|
|
191
|
+
if (error) {
|
|
192
|
+
// Return an act report with this error.
|
|
193
|
+
return {
|
|
194
|
+
data: {
|
|
195
|
+
prevented: true,
|
|
196
|
+
error
|
|
197
|
+
},
|
|
198
|
+
result: {}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Otherwise, i.e. if they were trimmable, and if standard results are to be reported:
|
|
202
|
+
if (standard) {
|
|
203
|
+
// Populate the totals of the standard result.
|
|
204
|
+
standardResult.totals = [totals!.recommendation, 0, totals!.violation, 0];
|
|
205
|
+
// For each item of the native result (without itemization, items is
|
|
206
|
+
// undefined and this throws, caught below; verbatim from the original):
|
|
207
|
+
nativeResult.items!.forEach(item => {
|
|
208
|
+
// Populate a standard instance. Potential violations, potential recommendations, and
|
|
209
|
+
// manual checks are engine-flagged uncertainty.
|
|
210
|
+
const standardItem: StandardInstance = {
|
|
211
|
+
ruleID: item.ruleId,
|
|
212
|
+
what: item.message,
|
|
213
|
+
ordinalSeverity: item.level === 'recommendation' ? 0 : 2,
|
|
214
|
+
outcome: ['violation', 'recommendation'].includes(item.level) ? 'failed' : 'cantTell'
|
|
215
|
+
};
|
|
216
|
+
// Get the XPath from the added attribute, because path.dom is wrong.
|
|
217
|
+
const xPath = getAttributeXPath(item.snippet);
|
|
218
|
+
// If the XPath was obtained:
|
|
219
|
+
if (xPath) {
|
|
220
|
+
// Add the catalog index to the standard instance.
|
|
221
|
+
standardItem.catalogIndex = getXPathCatalogIndex(report, xPath);
|
|
222
|
+
}
|
|
223
|
+
// Add the standard instance to the standard result.
|
|
224
|
+
pushInstance(standardResult, standardItem);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
data: {},
|
|
229
|
+
result
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
// Otherwise, i.e. if there was only an error, return it in an act report.
|
|
233
|
+
return {
|
|
234
|
+
data: runReport,
|
|
235
|
+
result: {}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
// If an error occurred:
|
|
239
|
+
catch(error) {
|
|
240
|
+
const message = `Act failed (${(error as Error).message.slice(0, 200)})`;
|
|
241
|
+
console.log(message);
|
|
242
|
+
// Return it in an act report.
|
|
243
|
+
return {
|
|
244
|
+
data: {
|
|
245
|
+
prevented: true,
|
|
246
|
+
error: message
|
|
247
|
+
},
|
|
248
|
+
result: {}
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
};
|
package/tests/nuVal.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Page } from 'playwright';
|
|
2
|
+
import type { NuResult } from '../procs/nu';
|
|
3
|
+
import type { Report, StandardResult } from '../types';
|
|
4
|
+
export declare const reporter: (page: Page, report: Report, actIndex: number) => Promise<{
|
|
5
|
+
data: {
|
|
6
|
+
prevented?: boolean;
|
|
7
|
+
error?: string;
|
|
8
|
+
};
|
|
9
|
+
result: {
|
|
10
|
+
nativeResult: NuResult;
|
|
11
|
+
standardResult: StandardResult;
|
|
12
|
+
};
|
|
13
|
+
}>;
|
package/tests/nuVal.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/*
|
|
2
3
|
© 2022–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
4
|
© 2026 Jeff Witt.
|
|
@@ -8,131 +9,125 @@
|
|
|
8
9
|
|
|
9
10
|
SPDX-License-Identifier: MIT
|
|
10
11
|
*/
|
|
11
|
-
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.reporter = void 0;
|
|
14
|
+
const nu_1 = require("../procs/nu");
|
|
15
|
+
const xPath_1 = require("../procs/xPath");
|
|
16
|
+
const standard_1 = require("../procs/standard");
|
|
12
17
|
/*
|
|
13
18
|
nuVal
|
|
14
19
|
Subjects a page and its source to the Nu Html Checker, thereby testing scripted content found only in the loaded page and erroneous content before the browser corrects it. The API erratically replaces left and right double quotation marks with invalid UTF-8, which appears as 2 or 3 successive instances of the replacement character (U+fffd). Therefore, this test removes all such quotation marks and the replacement character. That causes 'Bad value “” for' to become 'Bad value for'. Since the corruption of quotation marks is erratic, no better solution is known.
|
|
15
20
|
This rule engine is the API version of the Nu Html Checker. It is an alternative to the nuVnu rule engine, which uses the same validator as an installed dependency. Each rule engine has advantages and disadvantages. The main advantage of nuVal is that it does not require the Testaro host to provide a Java virtual machine. The main advantage of the nuVnu tool is that it can evaluate pages reachable from the host that Testaro runs on even if not reachable from the public Internet.
|
|
16
21
|
This rule engine calls the W3C validation service unless a TESTARO_NU_URL environment variable is defined with the URL of another instance of the API.
|
|
22
|
+
Compiled to nuVal.js by tsc (issue #73); edit this file, not the emitted one.
|
|
17
23
|
*/
|
|
18
|
-
|
|
19
|
-
// IMPORTS
|
|
20
|
-
|
|
21
|
-
const {curate, getContent, getExtractExcerpt} = require('../procs/nu');
|
|
22
|
-
const {getAttributeXPath, getXPathCatalogIndex} = require('../procs/xPath');
|
|
23
|
-
|
|
24
24
|
// FUNCTIONS
|
|
25
|
-
|
|
26
25
|
// Conducts and reports the Nu Html Checker API tests.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
};
|
|
36
|
-
const standard = report.standard !== 'no';
|
|
37
|
-
// If standard results are to be reported:
|
|
38
|
-
if (standard) {
|
|
39
|
-
// Initialize the standard result.
|
|
40
|
-
result.standardResult = {
|
|
41
|
-
prevented: false,
|
|
42
|
-
totals: [0, 0, 0, 0],
|
|
43
|
-
instances: []
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
const {standardResult} = result;
|
|
47
|
-
// Get the content.
|
|
48
|
-
const content = await getContent(page, withSource);
|
|
49
|
-
const {testTarget} = content;
|
|
50
|
-
// If it was obtained and contains a test target:
|
|
51
|
-
if (testTarget) {
|
|
52
|
-
const fetchOptions = {
|
|
53
|
-
method: 'post',
|
|
54
|
-
headers: {
|
|
55
|
-
'user-agent': 'Mozilla/5.0',
|
|
56
|
-
'content-type': 'text/html; charset=utf-8'
|
|
57
|
-
},
|
|
58
|
-
body: testTarget
|
|
59
|
-
};
|
|
60
|
-
// Override target for self-hosted Nu Html Checker instances (Docker
|
|
61
|
-
// image ghcr.io/validator/validator or equivalent). Defaults to the
|
|
62
|
-
// public W3C service, which 502s on bodies >~80 kB.
|
|
63
|
-
const nuURL = process.env.TESTARO_NU_URL
|
|
64
|
-
|| 'https://validator.w3.org/nu/?parser=html&out=json';
|
|
65
|
-
let nuData = {};
|
|
66
|
-
let nuResponse = new Response();
|
|
67
|
-
try {
|
|
68
|
-
// Get a Nu Html Checker report from the W3C validator service.
|
|
69
|
-
nuResponse = await fetch(nuURL, fetchOptions);
|
|
70
|
-
const {ok, status, statusText} = nuResponse;
|
|
71
|
-
// If the acquisition succeeded:
|
|
72
|
-
if (ok) {
|
|
73
|
-
// Get the response body as an object.
|
|
74
|
-
nuData = await nuResponse.json();
|
|
75
|
-
}
|
|
76
|
-
// Otherwise, i.e. if the request failed:
|
|
77
|
-
else {
|
|
78
|
-
// Get the response body as text.
|
|
79
|
-
const nuResponseText = await nuResponse.text();
|
|
80
|
-
// Add a failure report to the data.
|
|
81
|
-
data.prevented = true;
|
|
82
|
-
data.error = `HTTP ${status}: ${statusText} (${nuResponseText?.slice(0, 200)})`;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
// If an error occurred:
|
|
86
|
-
catch (error) {
|
|
87
|
-
// Report it.
|
|
88
|
-
const message = `ERROR getting results (${error.message}; status ${nuResponse.status || 'none'} (${JSON.stringify(nuData?.body || 'no body', null, 2)})`;
|
|
89
|
-
console.log(message);
|
|
90
|
-
data.prevented = true;
|
|
91
|
-
data.error = message;
|
|
26
|
+
const reporter = async (page, report, actIndex) => {
|
|
27
|
+
const act = report.acts[actIndex];
|
|
28
|
+
const { rules, withSource } = act;
|
|
29
|
+
// Initialize the act report.
|
|
30
|
+
const data = {};
|
|
31
|
+
const result = {
|
|
32
|
+
nativeResult: {},
|
|
33
|
+
standardResult: {}
|
|
92
34
|
};
|
|
93
|
-
|
|
94
|
-
result.nativeResult = await curate(data, nuData, rules);
|
|
35
|
+
const standard = report.standard !== 'no';
|
|
95
36
|
// If standard results are to be reported:
|
|
96
37
|
if (standard) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
38
|
+
// Initialize the standard result.
|
|
39
|
+
result.standardResult = (0, standard_1.getStandardResult)();
|
|
40
|
+
}
|
|
41
|
+
const { standardResult } = result;
|
|
42
|
+
// Get the content.
|
|
43
|
+
const content = await (0, nu_1.getContent)(page, withSource);
|
|
44
|
+
const { testTarget } = content;
|
|
45
|
+
// If it was obtained and contains a test target:
|
|
46
|
+
if (testTarget) {
|
|
47
|
+
const fetchOptions = {
|
|
48
|
+
method: 'post',
|
|
49
|
+
headers: {
|
|
50
|
+
'user-agent': 'Mozilla/5.0',
|
|
51
|
+
'content-type': 'text/html; charset=utf-8'
|
|
52
|
+
},
|
|
53
|
+
body: testTarget
|
|
108
54
|
};
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
55
|
+
// Override target for self-hosted Nu Html Checker instances (Docker
|
|
56
|
+
// image ghcr.io/validator/validator or equivalent). Defaults to the
|
|
57
|
+
// public W3C service, which 502s on bodies >~80 kB.
|
|
58
|
+
const nuURL = process.env.TESTARO_NU_URL
|
|
59
|
+
|| 'https://validator.w3.org/nu/?parser=html&out=json';
|
|
60
|
+
let nuData = {};
|
|
61
|
+
let nuResponse = new Response();
|
|
62
|
+
try {
|
|
63
|
+
// Get a Nu Html Checker report from the W3C validator service.
|
|
64
|
+
nuResponse = await fetch(nuURL, fetchOptions);
|
|
65
|
+
const { ok, status, statusText } = nuResponse;
|
|
66
|
+
// If the acquisition succeeded:
|
|
67
|
+
if (ok) {
|
|
68
|
+
// Get the response body as an object.
|
|
69
|
+
nuData = await nuResponse.json();
|
|
70
|
+
}
|
|
71
|
+
// Otherwise, i.e. if the request failed:
|
|
72
|
+
else {
|
|
73
|
+
// Get the response body as text.
|
|
74
|
+
const nuResponseText = await nuResponse.text();
|
|
75
|
+
// Add a failure report to the data.
|
|
76
|
+
data.prevented = true;
|
|
77
|
+
data.error = `HTTP ${status}: ${statusText} (${nuResponseText?.slice(0, 200)})`;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// If an error occurred:
|
|
81
|
+
catch (error) {
|
|
82
|
+
// Report it.
|
|
83
|
+
const message = `ERROR getting results (${error.message}; status ${nuResponse.status || 'none'} (${JSON.stringify(nuData?.body || 'no body', null, 2)})`;
|
|
84
|
+
console.log(message);
|
|
85
|
+
data.prevented = true;
|
|
86
|
+
data.error = message;
|
|
115
87
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
88
|
+
;
|
|
89
|
+
// Postprocess the response data and add the postprocessed data to the native result.
|
|
90
|
+
result.nativeResult = await (0, nu_1.curate)(data, nuData, rules);
|
|
91
|
+
// If standard results are to be reported:
|
|
92
|
+
if (standard) {
|
|
93
|
+
// For each message in the native result:
|
|
94
|
+
result.nativeResult.messages.forEach(message => {
|
|
95
|
+
const isInfo = message.type === 'info';
|
|
96
|
+
// Initialize a standard instance. Informational messages are uncertainty.
|
|
97
|
+
const standardInstance = {
|
|
98
|
+
ruleID: message.message,
|
|
99
|
+
what: message.message,
|
|
100
|
+
ordinalSeverity: isInfo ? 0 : 3,
|
|
101
|
+
outcome: isInfo ? 'cantTell' : 'failed'
|
|
102
|
+
};
|
|
103
|
+
// Get the XPath of the element from its extract.
|
|
104
|
+
const xPath = (0, xPath_1.getAttributeXPath)(message.extract);
|
|
105
|
+
// If the acquisition succeeded:
|
|
106
|
+
if (xPath) {
|
|
107
|
+
// Add the catalog index to the standard instance.
|
|
108
|
+
standardInstance.catalogIndex = (0, xPath_1.getXPathCatalogIndex)(report, xPath);
|
|
109
|
+
}
|
|
110
|
+
// Get an excerpt of the extract, if the extract identifies no element.
|
|
111
|
+
const extractExcerpt = (0, nu_1.getExtractExcerpt)(message.extract);
|
|
112
|
+
// If one was obtained (e.g. the erroneous CSS of a CSS: Parse Error message):
|
|
113
|
+
if (extractExcerpt) {
|
|
114
|
+
// Add it to the description of the violation, so the extract is not lost.
|
|
115
|
+
standardInstance.what = `${message.message} Extract: ${extractExcerpt}`;
|
|
116
|
+
}
|
|
117
|
+
// Add the standard instance to the standard result.
|
|
118
|
+
(0, standard_1.addInstance)(standardResult, standardInstance);
|
|
119
|
+
});
|
|
122
120
|
}
|
|
123
|
-
// Add the standard instance to the standard result.
|
|
124
|
-
standardResult.instances.push(standardInstance);
|
|
125
|
-
})
|
|
126
121
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
};
|
|
122
|
+
// Otherwise, i.e. if the page content was not obtained:
|
|
123
|
+
else {
|
|
124
|
+
// Report this.
|
|
125
|
+
data.prevented = true;
|
|
126
|
+
data.error = 'Content not obtained';
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
data,
|
|
130
|
+
result
|
|
131
|
+
};
|
|
138
132
|
};
|
|
133
|
+
exports.reporter = reporter;
|
package/tests/nuVal.ts
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/*
|
|
2
|
+
© 2022–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2026 Jeff Witt.
|
|
4
|
+
© 2026 Jonathan Robert Pool.
|
|
5
|
+
|
|
6
|
+
Licensed under the MIT License. See LICENSE file at the project root or
|
|
7
|
+
https://opensource.org/license/mit/ for details.
|
|
8
|
+
|
|
9
|
+
SPDX-License-Identifier: MIT
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// IMPORTS
|
|
13
|
+
|
|
14
|
+
import type {Page} from 'playwright';
|
|
15
|
+
import {curate, getContent, getExtractExcerpt} from '../procs/nu';
|
|
16
|
+
import type {NuResult} from '../procs/nu';
|
|
17
|
+
import {getAttributeXPath, getXPathCatalogIndex} from '../procs/xPath';
|
|
18
|
+
import {getStandardResult, addInstance} from '../procs/standard';
|
|
19
|
+
import type {Act, Report, StandardInstance, StandardResult} from '../types';
|
|
20
|
+
|
|
21
|
+
// TYPES
|
|
22
|
+
|
|
23
|
+
// The nuVal-act properties this reporter consumes.
|
|
24
|
+
interface NuValAct extends Act {
|
|
25
|
+
rules?: string[];
|
|
26
|
+
withSource?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/*
|
|
30
|
+
nuVal
|
|
31
|
+
Subjects a page and its source to the Nu Html Checker, thereby testing scripted content found only in the loaded page and erroneous content before the browser corrects it. The API erratically replaces left and right double quotation marks with invalid UTF-8, which appears as 2 or 3 successive instances of the replacement character (U+fffd). Therefore, this test removes all such quotation marks and the replacement character. That causes 'Bad value “” for' to become 'Bad value for'. Since the corruption of quotation marks is erratic, no better solution is known.
|
|
32
|
+
This rule engine is the API version of the Nu Html Checker. It is an alternative to the nuVnu rule engine, which uses the same validator as an installed dependency. Each rule engine has advantages and disadvantages. The main advantage of nuVal is that it does not require the Testaro host to provide a Java virtual machine. The main advantage of the nuVnu tool is that it can evaluate pages reachable from the host that Testaro runs on even if not reachable from the public Internet.
|
|
33
|
+
This rule engine calls the W3C validation service unless a TESTARO_NU_URL environment variable is defined with the URL of another instance of the API.
|
|
34
|
+
Compiled to nuVal.js by tsc (issue #73); edit this file, not the emitted one.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
// FUNCTIONS
|
|
38
|
+
|
|
39
|
+
// Conducts and reports the Nu Html Checker API tests.
|
|
40
|
+
export const reporter = async (page: Page, report: Report, actIndex: number) => {
|
|
41
|
+
const act = report.acts[actIndex] as NuValAct;
|
|
42
|
+
const {rules, withSource} = act;
|
|
43
|
+
// Initialize the act report.
|
|
44
|
+
const data: {prevented?: boolean; error?: string} = {};
|
|
45
|
+
const result: {nativeResult: NuResult; standardResult: StandardResult} = {
|
|
46
|
+
nativeResult: {} as NuResult,
|
|
47
|
+
standardResult: {}
|
|
48
|
+
};
|
|
49
|
+
const standard = report.standard !== 'no';
|
|
50
|
+
// If standard results are to be reported:
|
|
51
|
+
if (standard) {
|
|
52
|
+
// Initialize the standard result.
|
|
53
|
+
result.standardResult = getStandardResult();
|
|
54
|
+
}
|
|
55
|
+
const {standardResult} = result;
|
|
56
|
+
// Get the content.
|
|
57
|
+
const content = await getContent(page, withSource);
|
|
58
|
+
const {testTarget} = content;
|
|
59
|
+
// If it was obtained and contains a test target:
|
|
60
|
+
if (testTarget) {
|
|
61
|
+
const fetchOptions = {
|
|
62
|
+
method: 'post',
|
|
63
|
+
headers: {
|
|
64
|
+
'user-agent': 'Mozilla/5.0',
|
|
65
|
+
'content-type': 'text/html; charset=utf-8'
|
|
66
|
+
},
|
|
67
|
+
body: testTarget
|
|
68
|
+
};
|
|
69
|
+
// Override target for self-hosted Nu Html Checker instances (Docker
|
|
70
|
+
// image ghcr.io/validator/validator or equivalent). Defaults to the
|
|
71
|
+
// public W3C service, which 502s on bodies >~80 kB.
|
|
72
|
+
const nuURL = process.env.TESTARO_NU_URL
|
|
73
|
+
|| 'https://validator.w3.org/nu/?parser=html&out=json';
|
|
74
|
+
let nuData: unknown = {};
|
|
75
|
+
let nuResponse = new Response();
|
|
76
|
+
try {
|
|
77
|
+
// Get a Nu Html Checker report from the W3C validator service.
|
|
78
|
+
nuResponse = await fetch(nuURL, fetchOptions);
|
|
79
|
+
const {ok, status, statusText} = nuResponse;
|
|
80
|
+
// If the acquisition succeeded:
|
|
81
|
+
if (ok) {
|
|
82
|
+
// Get the response body as an object.
|
|
83
|
+
nuData = await nuResponse.json();
|
|
84
|
+
}
|
|
85
|
+
// Otherwise, i.e. if the request failed:
|
|
86
|
+
else {
|
|
87
|
+
// Get the response body as text.
|
|
88
|
+
const nuResponseText = await nuResponse.text();
|
|
89
|
+
// Add a failure report to the data.
|
|
90
|
+
data.prevented = true;
|
|
91
|
+
data.error = `HTTP ${status}: ${statusText} (${nuResponseText?.slice(0, 200)})`;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// If an error occurred:
|
|
95
|
+
catch (error) {
|
|
96
|
+
// Report it.
|
|
97
|
+
const message = `ERROR getting results (${(error as Error).message}; status ${nuResponse.status || 'none'} (${JSON.stringify((nuData as {body?: unknown})?.body || 'no body', null, 2)})`;
|
|
98
|
+
console.log(message);
|
|
99
|
+
data.prevented = true;
|
|
100
|
+
data.error = message;
|
|
101
|
+
};
|
|
102
|
+
// Postprocess the response data and add the postprocessed data to the native result.
|
|
103
|
+
result.nativeResult = await curate(data, nuData, rules) as NuResult;
|
|
104
|
+
// If standard results are to be reported:
|
|
105
|
+
if (standard) {
|
|
106
|
+
// For each message in the native result:
|
|
107
|
+
result.nativeResult.messages.forEach(message => {
|
|
108
|
+
const isInfo = message.type === 'info';
|
|
109
|
+
// Initialize a standard instance. Informational messages are uncertainty.
|
|
110
|
+
const standardInstance: StandardInstance = {
|
|
111
|
+
ruleID: message.message,
|
|
112
|
+
what: message.message,
|
|
113
|
+
ordinalSeverity: isInfo ? 0 : 3,
|
|
114
|
+
outcome: isInfo ? 'cantTell' : 'failed'
|
|
115
|
+
};
|
|
116
|
+
// Get the XPath of the element from its extract.
|
|
117
|
+
const xPath = getAttributeXPath(message.extract);
|
|
118
|
+
// If the acquisition succeeded:
|
|
119
|
+
if (xPath) {
|
|
120
|
+
// Add the catalog index to the standard instance.
|
|
121
|
+
standardInstance.catalogIndex = getXPathCatalogIndex(report, xPath);
|
|
122
|
+
}
|
|
123
|
+
// Get an excerpt of the extract, if the extract identifies no element.
|
|
124
|
+
const extractExcerpt = getExtractExcerpt(message.extract);
|
|
125
|
+
// If one was obtained (e.g. the erroneous CSS of a CSS: Parse Error message):
|
|
126
|
+
if (extractExcerpt) {
|
|
127
|
+
// Add it to the description of the violation, so the extract is not lost.
|
|
128
|
+
standardInstance.what = `${message.message} Extract: ${extractExcerpt}`;
|
|
129
|
+
}
|
|
130
|
+
// Add the standard instance to the standard result.
|
|
131
|
+
addInstance(standardResult, standardInstance);
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Otherwise, i.e. if the page content was not obtained:
|
|
136
|
+
else {
|
|
137
|
+
// Report this.
|
|
138
|
+
data.prevented = true;
|
|
139
|
+
data.error = 'Content not obtained';
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
data,
|
|
143
|
+
result
|
|
144
|
+
};
|
|
145
|
+
};
|
package/tests/nuVnu.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Page } from 'playwright';
|
|
2
|
+
import type { NuResult } from '../procs/nu';
|
|
3
|
+
import type { Report, StandardResult } from '../types';
|
|
4
|
+
export declare const reporter: (page: Page, report: Report, actIndex: number) => Promise<{
|
|
5
|
+
data: {
|
|
6
|
+
prevented?: boolean;
|
|
7
|
+
error?: string;
|
|
8
|
+
skipped?: boolean;
|
|
9
|
+
reason?: string;
|
|
10
|
+
};
|
|
11
|
+
result: {
|
|
12
|
+
nativeResult: NuResult;
|
|
13
|
+
standardResult: StandardResult;
|
|
14
|
+
};
|
|
15
|
+
}>;
|