@mcptoolshop/accessibility-suite 0.1.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.
Files changed (241) hide show
  1. package/.github/workflows/ci.yml +63 -0
  2. package/LICENSE +21 -0
  3. package/README.md +37 -0
  4. package/docs/prov-spec/.github/workflows/ci.yml +68 -0
  5. package/docs/prov-spec/CHANGELOG.md +69 -0
  6. package/docs/prov-spec/CODE_OF_CONDUCT.md +129 -0
  7. package/docs/prov-spec/CONFORMANCE_LEVELS.md +223 -0
  8. package/docs/prov-spec/CONTRIBUTING.md +145 -0
  9. package/docs/prov-spec/IMPLEMENTER_CHECKLIST.md +137 -0
  10. package/docs/prov-spec/LICENSE +21 -0
  11. package/docs/prov-spec/PRESS_RELEASE.md +74 -0
  12. package/docs/prov-spec/README.md +182 -0
  13. package/docs/prov-spec/SETUP.md +135 -0
  14. package/docs/prov-spec/WHY.md +86 -0
  15. package/docs/prov-spec/examples/artifact.example.json +14 -0
  16. package/docs/prov-spec/examples/artifact.ref.example.json +9 -0
  17. package/docs/prov-spec/examples/evidence.example.json +6 -0
  18. package/docs/prov-spec/examples/mcp.envelope.example.json +97 -0
  19. package/docs/prov-spec/examples/mcp.request.example.json +28 -0
  20. package/docs/prov-spec/examples/prov.record.example.json +35 -0
  21. package/docs/prov-spec/interop/PROOF_NODE_ENGINE.md +114 -0
  22. package/docs/prov-spec/spec/MCP_COMPATIBILITY.md +241 -0
  23. package/docs/prov-spec/spec/PROV_METHODS_CATALOG.md +142 -0
  24. package/docs/prov-spec/spec/PROV_METHODS_SPEC.md +397 -0
  25. package/docs/prov-spec/spec/methods.json +213 -0
  26. package/docs/prov-spec/spec/schemas/artifact.ref.schema.v0.1.json +58 -0
  27. package/docs/prov-spec/spec/schemas/artifact.schema.v0.1.json +61 -0
  28. package/docs/prov-spec/spec/schemas/assist.request.schema.v0.1.json +52 -0
  29. package/docs/prov-spec/spec/schemas/assist.response.schema.v0.1.json +70 -0
  30. package/docs/prov-spec/spec/schemas/cli.error.schema.v0.1.json +78 -0
  31. package/docs/prov-spec/spec/schemas/evidence.schema.v0.1.json +37 -0
  32. package/docs/prov-spec/spec/schemas/mcp.envelope.schema.v0.1.json +141 -0
  33. package/docs/prov-spec/spec/schemas/mcp.request.schema.v0.1.json +79 -0
  34. package/docs/prov-spec/spec/schemas/methods.schema.json +93 -0
  35. package/docs/prov-spec/spec/schemas/prov-capabilities.schema.json +122 -0
  36. package/docs/prov-spec/spec/schemas/prov.record.schema.v0.1.json +133 -0
  37. package/docs/prov-spec/spec/vectors/adapter.wrap.envelope_v0_1/expected.json +4 -0
  38. package/docs/prov-spec/spec/vectors/adapter.wrap.envelope_v0_1/input.json +1 -0
  39. package/docs/prov-spec/spec/vectors/adapter.wrap.envelope_v0_1/negative/double_wrapped.json +14 -0
  40. package/docs/prov-spec/spec/vectors/adapter.wrap.envelope_v0_1/negative/wrong_schema_version.json +11 -0
  41. package/docs/prov-spec/spec/vectors/engine.extract.evidence.json_pointer/expected.json +24 -0
  42. package/docs/prov-spec/spec/vectors/engine.extract.evidence.json_pointer/input.json +8 -0
  43. package/docs/prov-spec/spec/vectors/integrity.digest.sha256/expected.json +7 -0
  44. package/docs/prov-spec/spec/vectors/integrity.digest.sha256/input.json +1 -0
  45. package/docs/prov-spec/spec/vectors/integrity.digest.sha256/negative/non_hex_chars.json +16 -0
  46. package/docs/prov-spec/spec/vectors/integrity.digest.sha256/negative/uppercase_hex.json +16 -0
  47. package/docs/prov-spec/spec/vectors/integrity.digest.sha256/negative/wrong_length.json +16 -0
  48. package/docs/prov-spec/spec/vectors/method_id_syntax/negative/hyphen_separator.json +8 -0
  49. package/docs/prov-spec/spec/vectors/method_id_syntax/negative/reserved_namespace.json +8 -0
  50. package/docs/prov-spec/spec/vectors/method_id_syntax/negative/starts_with_digit.json +8 -0
  51. package/docs/prov-spec/spec/vectors/method_id_syntax/negative/uppercase.json +8 -0
  52. package/docs/prov-spec/spec/vectors/method_id_syntax/positive/valid_ids.json +18 -0
  53. package/docs/prov-spec/tools/python/prov_validator.py +428 -0
  54. package/examples/a11y-demo-site/.github/workflows/a11y-artifacts.yml +81 -0
  55. package/examples/a11y-demo-site/.github/workflows/a11y.yml +34 -0
  56. package/examples/a11y-demo-site/CODE_OF_CONDUCT.md +129 -0
  57. package/examples/a11y-demo-site/CONTRIBUTING.md +83 -0
  58. package/examples/a11y-demo-site/LICENSE +21 -0
  59. package/examples/a11y-demo-site/README.md +155 -0
  60. package/examples/a11y-demo-site/html/contact.html +15 -0
  61. package/examples/a11y-demo-site/html/index.html +20 -0
  62. package/examples/a11y-demo-site/scripts/a11y.sh +20 -0
  63. package/package.json +26 -0
  64. package/src/a11y-assist/.github/workflows/publish.yml +52 -0
  65. package/src/a11y-assist/.github/workflows/test.yml +30 -0
  66. package/src/a11y-assist/A11Y_ASSIST_TEST_COVERAGE_REQUIREMENTS.md +104 -0
  67. package/src/a11y-assist/CODE_OF_CONDUCT.md +129 -0
  68. package/src/a11y-assist/CONTRIBUTING.md +98 -0
  69. package/src/a11y-assist/ENGINE.md +363 -0
  70. package/src/a11y-assist/LICENSE +21 -0
  71. package/src/a11y-assist/PRESS_RELEASE.md +71 -0
  72. package/src/a11y-assist/QUICKSTART.md +101 -0
  73. package/src/a11y-assist/README.md +192 -0
  74. package/src/a11y-assist/RELEASE_NOTES.md +319 -0
  75. package/src/a11y-assist/a11y_assist/__init__.py +3 -0
  76. package/src/a11y-assist/a11y_assist/cli.py +599 -0
  77. package/src/a11y-assist/a11y_assist/from_cli_error.py +149 -0
  78. package/src/a11y-assist/a11y_assist/guard.py +444 -0
  79. package/src/a11y-assist/a11y_assist/ingest.py +407 -0
  80. package/src/a11y-assist/a11y_assist/methods.py +137 -0
  81. package/src/a11y-assist/a11y_assist/parse_raw.py +71 -0
  82. package/src/a11y-assist/a11y_assist/profiles/__init__.py +29 -0
  83. package/src/a11y-assist/a11y_assist/profiles/cognitive_load.py +245 -0
  84. package/src/a11y-assist/a11y_assist/profiles/cognitive_load_render.py +86 -0
  85. package/src/a11y-assist/a11y_assist/profiles/dyslexia.py +144 -0
  86. package/src/a11y-assist/a11y_assist/profiles/dyslexia_render.py +77 -0
  87. package/src/a11y-assist/a11y_assist/profiles/plain_language.py +119 -0
  88. package/src/a11y-assist/a11y_assist/profiles/plain_language_render.py +66 -0
  89. package/src/a11y-assist/a11y_assist/profiles/screen_reader.py +348 -0
  90. package/src/a11y-assist/a11y_assist/profiles/screen_reader_render.py +89 -0
  91. package/src/a11y-assist/a11y_assist/render.py +95 -0
  92. package/src/a11y-assist/a11y_assist/schemas/assist.request.schema.v0.1.json +52 -0
  93. package/src/a11y-assist/a11y_assist/schemas/assist.response.schema.v0.1.json +70 -0
  94. package/src/a11y-assist/a11y_assist/schemas/cli.error.schema.v0.1.json +78 -0
  95. package/src/a11y-assist/a11y_assist/storage.py +31 -0
  96. package/src/a11y-assist/pyproject.toml +60 -0
  97. package/src/a11y-assist/tests/__init__.py +1 -0
  98. package/src/a11y-assist/tests/fixtures/base_inputs/cli_error_high.json +18 -0
  99. package/src/a11y-assist/tests/fixtures/base_inputs/cli_error_medium.json +16 -0
  100. package/src/a11y-assist/tests/fixtures/base_inputs/raw_text_low.txt +3 -0
  101. package/src/a11y-assist/tests/fixtures/cli_error_good.json +9 -0
  102. package/src/a11y-assist/tests/fixtures/cli_error_missing_id.json +7 -0
  103. package/src/a11y-assist/tests/fixtures/cli_error_string_format.json +7 -0
  104. package/src/a11y-assist/tests/fixtures/expected/cognitive_load_high.txt +20 -0
  105. package/src/a11y-assist/tests/fixtures/expected/dyslexia_high.txt +20 -0
  106. package/src/a11y-assist/tests/fixtures/expected/lowvision_high.txt +18 -0
  107. package/src/a11y-assist/tests/fixtures/expected/plain_language_high.txt +14 -0
  108. package/src/a11y-assist/tests/fixtures/expected/screen_reader_high.txt +19 -0
  109. package/src/a11y-assist/tests/fixtures/golden_screen_reader_cli_error.txt +16 -0
  110. package/src/a11y-assist/tests/fixtures/golden_screen_reader_raw_no_id.txt +14 -0
  111. package/src/a11y-assist/tests/fixtures/golden_screen_reader_raw_with_id.txt +14 -0
  112. package/src/a11y-assist/tests/fixtures/raw_good.txt +11 -0
  113. package/src/a11y-assist/tests/fixtures/raw_no_id.txt +2 -0
  114. package/src/a11y-assist/tests/test_cognitive_load.py +469 -0
  115. package/src/a11y-assist/tests/test_dyslexia.py +337 -0
  116. package/src/a11y-assist/tests/test_explain.py +74 -0
  117. package/src/a11y-assist/tests/test_golden.py +127 -0
  118. package/src/a11y-assist/tests/test_guard.py +819 -0
  119. package/src/a11y-assist/tests/test_guard_integration.py +457 -0
  120. package/src/a11y-assist/tests/test_ingest.py +311 -0
  121. package/src/a11y-assist/tests/test_methods_metadata.py +236 -0
  122. package/src/a11y-assist/tests/test_plain_language.py +348 -0
  123. package/src/a11y-assist/tests/test_render.py +117 -0
  124. package/src/a11y-assist/tests/test_screen_reader.py +703 -0
  125. package/src/a11y-assist/tests/test_storage_last.py +61 -0
  126. package/src/a11y-assist/tests/test_triage.py +86 -0
  127. package/src/a11y-ci/.github/workflows/ci.yml +43 -0
  128. package/src/a11y-ci/.github/workflows/test.yml +30 -0
  129. package/src/a11y-ci/A11Y_CI_TEST_COVERAGE_REQUIREMENTS.md +94 -0
  130. package/src/a11y-ci/CODE_OF_CONDUCT.md +129 -0
  131. package/src/a11y-ci/CONTRIBUTING.md +142 -0
  132. package/src/a11y-ci/LICENSE +21 -0
  133. package/src/a11y-ci/README.md +105 -0
  134. package/src/a11y-ci/a11y_ci/__init__.py +3 -0
  135. package/src/a11y-ci/a11y_ci/allowlist.py +83 -0
  136. package/src/a11y-ci/a11y_ci/cli.py +145 -0
  137. package/src/a11y-ci/a11y_ci/gate.py +131 -0
  138. package/src/a11y-ci/a11y_ci/render.py +48 -0
  139. package/src/a11y-ci/a11y_ci/schemas/allowlist.schema.json +24 -0
  140. package/src/a11y-ci/a11y_ci/scorecard.py +99 -0
  141. package/src/a11y-ci/npm/package.json +35 -0
  142. package/src/a11y-ci/pyproject.toml +64 -0
  143. package/src/a11y-ci/tests/__init__.py +1 -0
  144. package/src/a11y-ci/tests/fixtures/allowlist_expired.json +10 -0
  145. package/src/a11y-ci/tests/fixtures/allowlist_ok.json +10 -0
  146. package/src/a11y-ci/tests/fixtures/baseline_ok.json +7 -0
  147. package/src/a11y-ci/tests/fixtures/current_fail.json +6 -0
  148. package/src/a11y-ci/tests/fixtures/current_ok.json +6 -0
  149. package/src/a11y-ci/tests/fixtures/current_regresses.json +7 -0
  150. package/src/a11y-ci/tests/test_gate.py +134 -0
  151. package/src/a11y-evidence-engine/.github/workflows/ci.yml +53 -0
  152. package/src/a11y-evidence-engine/CODE_OF_CONDUCT.md +129 -0
  153. package/src/a11y-evidence-engine/CONTRIBUTING.md +128 -0
  154. package/src/a11y-evidence-engine/LICENSE +21 -0
  155. package/src/a11y-evidence-engine/README.md +71 -0
  156. package/src/a11y-evidence-engine/bin/a11y-engine.js +11 -0
  157. package/src/a11y-evidence-engine/fixtures/bad/button-no-name.html +30 -0
  158. package/src/a11y-evidence-engine/fixtures/bad/img-missing-alt.html +19 -0
  159. package/src/a11y-evidence-engine/fixtures/bad/input-missing-label.html +26 -0
  160. package/src/a11y-evidence-engine/fixtures/bad/missing-lang.html +11 -0
  161. package/src/a11y-evidence-engine/fixtures/good/index.html +29 -0
  162. package/src/a11y-evidence-engine/package-lock.json +109 -0
  163. package/src/a11y-evidence-engine/package.json +45 -0
  164. package/src/a11y-evidence-engine/src/cli.js +74 -0
  165. package/src/a11y-evidence-engine/src/evidence/canonicalize.js +52 -0
  166. package/src/a11y-evidence-engine/src/evidence/json_pointer.js +34 -0
  167. package/src/a11y-evidence-engine/src/evidence/prov_emit.js +153 -0
  168. package/src/a11y-evidence-engine/src/fswalk.js +56 -0
  169. package/src/a11y-evidence-engine/src/html_parse.js +117 -0
  170. package/src/a11y-evidence-engine/src/ids.js +53 -0
  171. package/src/a11y-evidence-engine/src/rules/document_missing_lang.js +50 -0
  172. package/src/a11y-evidence-engine/src/rules/form_control_missing_label.js +105 -0
  173. package/src/a11y-evidence-engine/src/rules/img_missing_alt.js +77 -0
  174. package/src/a11y-evidence-engine/src/rules/index.js +37 -0
  175. package/src/a11y-evidence-engine/src/rules/interactive_missing_name.js +129 -0
  176. package/src/a11y-evidence-engine/src/scan.js +128 -0
  177. package/src/a11y-evidence-engine/test/scan.test.js +149 -0
  178. package/src/a11y-evidence-engine/test/vectors.test.js +200 -0
  179. package/src/a11y-lint/.github/workflows/ci.yml +46 -0
  180. package/src/a11y-lint/.github/workflows/test.yml +34 -0
  181. package/src/a11y-lint/CODE_OF_CONDUCT.md +129 -0
  182. package/src/a11y-lint/CONTRIBUTING.md +70 -0
  183. package/src/a11y-lint/GOVERNANCE.md +57 -0
  184. package/src/a11y-lint/LICENSE +21 -0
  185. package/src/a11y-lint/PRESS_RELEASE.md +50 -0
  186. package/src/a11y-lint/README.md +276 -0
  187. package/src/a11y-lint/RELEASE_NOTES.md +57 -0
  188. package/src/a11y-lint/RELEASING.md +57 -0
  189. package/src/a11y-lint/a11y_lint/__init__.py +64 -0
  190. package/src/a11y-lint/a11y_lint/cli.py +319 -0
  191. package/src/a11y-lint/a11y_lint/errors.py +252 -0
  192. package/src/a11y-lint/a11y_lint/render.py +293 -0
  193. package/src/a11y-lint/a11y_lint/report_md.py +289 -0
  194. package/src/a11y-lint/a11y_lint/scan_cli_text.py +434 -0
  195. package/src/a11y-lint/a11y_lint/schemas/cli.error.schema.v0.1.json +83 -0
  196. package/src/a11y-lint/a11y_lint/scorecard.py +244 -0
  197. package/src/a11y-lint/a11y_lint/validate.py +225 -0
  198. package/src/a11y-lint/pyproject.toml +75 -0
  199. package/src/a11y-lint/tests/__init__.py +1 -0
  200. package/src/a11y-lint/tests/test_cli.py +200 -0
  201. package/src/a11y-lint/tests/test_errors.py +188 -0
  202. package/src/a11y-lint/tests/test_render.py +202 -0
  203. package/src/a11y-lint/tests/test_report_md.py +188 -0
  204. package/src/a11y-lint/tests/test_scan_cli_text.py +290 -0
  205. package/src/a11y-lint/tests/test_scorecard.py +195 -0
  206. package/src/a11y-lint/tests/test_validate.py +257 -0
  207. package/src/a11y-mcp-tools/.github/workflows/ci.yml +53 -0
  208. package/src/a11y-mcp-tools/CODE_OF_CONDUCT.md +129 -0
  209. package/src/a11y-mcp-tools/CONTRIBUTING.md +136 -0
  210. package/src/a11y-mcp-tools/LICENSE +21 -0
  211. package/src/a11y-mcp-tools/PROV_METHODS_CATALOG.md +104 -0
  212. package/src/a11y-mcp-tools/README.md +168 -0
  213. package/src/a11y-mcp-tools/bin/cli.js +452 -0
  214. package/src/a11y-mcp-tools/bin/server.js +244 -0
  215. package/src/a11y-mcp-tools/fixtures/requests/a11y.diagnose.ok.json +27 -0
  216. package/src/a11y-mcp-tools/fixtures/requests/a11y.evidence.ok.json +25 -0
  217. package/src/a11y-mcp-tools/fixtures/responses/a11y.diagnose.ok.json +139 -0
  218. package/src/a11y-mcp-tools/fixtures/responses/a11y.diagnose.provenance_fail.json +13 -0
  219. package/src/a11y-mcp-tools/fixtures/responses/a11y.evidence.ok.json +88 -0
  220. package/src/a11y-mcp-tools/package-lock.json +189 -0
  221. package/src/a11y-mcp-tools/package.json +49 -0
  222. package/src/a11y-mcp-tools/src/envelope.js +197 -0
  223. package/src/a11y-mcp-tools/src/index.js +9 -0
  224. package/src/a11y-mcp-tools/src/schemas/artifact.js +85 -0
  225. package/src/a11y-mcp-tools/src/schemas/diagnosis.schema.v0.1.json +137 -0
  226. package/src/a11y-mcp-tools/src/schemas/envelope.schema.v0.1.json +108 -0
  227. package/src/a11y-mcp-tools/src/schemas/evidence.bundle.schema.v0.1.json +129 -0
  228. package/src/a11y-mcp-tools/src/schemas/evidence.js +97 -0
  229. package/src/a11y-mcp-tools/src/schemas/index.js +11 -0
  230. package/src/a11y-mcp-tools/src/schemas/provenance.js +140 -0
  231. package/src/a11y-mcp-tools/src/schemas/tools/a11y.diagnose.request.schema.v0.1.json +77 -0
  232. package/src/a11y-mcp-tools/src/schemas/tools/a11y.diagnose.response.schema.v0.1.json +50 -0
  233. package/src/a11y-mcp-tools/src/schemas/tools/a11y.evidence.request.schema.v0.1.json +120 -0
  234. package/src/a11y-mcp-tools/src/schemas/tools/a11y.evidence.response.schema.v0.1.json +50 -0
  235. package/src/a11y-mcp-tools/src/tools/diagnose.js +597 -0
  236. package/src/a11y-mcp-tools/src/tools/evidence.js +481 -0
  237. package/src/a11y-mcp-tools/src/tools/index.js +10 -0
  238. package/src/a11y-mcp-tools/test/contract.test.mjs +154 -0
  239. package/src/a11y-mcp-tools/test/diagnose.test.js +485 -0
  240. package/src/a11y-mcp-tools/test/evidence.test.js +183 -0
  241. package/src/a11y-mcp-tools/test/schema.test.js +327 -0
@@ -0,0 +1,327 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Schema validation tests.
5
+ *
6
+ * Validates golden fixtures against tool schemas to ensure
7
+ * the contract can't drift.
8
+ */
9
+
10
+ const { describe, it } = require("node:test");
11
+ const assert = require("node:assert");
12
+ const fs = require("fs");
13
+ const path = require("path");
14
+
15
+ const Ajv2020 = require("ajv/dist/2020");
16
+ const addFormats = require("ajv-formats");
17
+
18
+ // Schema paths
19
+ const SCHEMAS_DIR = path.join(__dirname, "../src/schemas");
20
+ const TOOLS_SCHEMAS_DIR = path.join(SCHEMAS_DIR, "tools");
21
+ const FIXTURES_DIR = path.join(__dirname, "../fixtures");
22
+
23
+ /**
24
+ * Load a JSON file and remove $schema to avoid meta-schema issues.
25
+ */
26
+ function loadSchema(filePath) {
27
+ const schema = JSON.parse(fs.readFileSync(filePath, "utf8"));
28
+ delete schema.$schema;
29
+ return schema;
30
+ }
31
+
32
+ /**
33
+ * Load a JSON file.
34
+ */
35
+ function loadJson(filePath) {
36
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
37
+ }
38
+
39
+ /**
40
+ * Create an AJV instance with all schemas loaded.
41
+ */
42
+ function createValidator() {
43
+ const ajv = new Ajv2020({
44
+ strict: false,
45
+ allErrors: true,
46
+ verbose: true,
47
+ });
48
+ addFormats(ajv);
49
+
50
+ // Load core schemas for $ref resolution
51
+ const bundleSchema = loadSchema(path.join(SCHEMAS_DIR, "evidence.bundle.schema.v0.1.json"));
52
+ const diagnosisSchema = loadSchema(path.join(SCHEMAS_DIR, "diagnosis.schema.v0.1.json"));
53
+
54
+ // Add schemas with their $id URLs for proper $ref resolution
55
+ ajv.addSchema(bundleSchema, "https://mcp-tool-shop.github.io/schemas/evidence.bundle.v0.1.json");
56
+ ajv.addSchema(diagnosisSchema, "https://mcp-tool-shop.github.io/schemas/diagnosis.v0.1.json");
57
+
58
+ return ajv;
59
+ }
60
+
61
+ describe("Schema Validation", () => {
62
+ describe("a11y.evidence request schema", () => {
63
+ it("should validate evidence request fixture", () => {
64
+ const ajv = createValidator();
65
+ const schema = loadSchema(path.join(TOOLS_SCHEMAS_DIR, "a11y.evidence.request.schema.v0.1.json"));
66
+ const fixture = loadJson(path.join(FIXTURES_DIR, "requests/a11y.evidence.ok.json"));
67
+
68
+ const validate = ajv.compile(schema);
69
+ const valid = validate(fixture);
70
+
71
+ if (!valid) {
72
+ console.error("Validation errors:", JSON.stringify(validate.errors, null, 2));
73
+ }
74
+
75
+ assert.strictEqual(valid, true, `Evidence request should be valid: ${JSON.stringify(validate.errors)}`);
76
+ });
77
+
78
+ it("should reject invalid evidence request (missing targets)", () => {
79
+ const ajv = createValidator();
80
+ const schema = loadSchema(path.join(TOOLS_SCHEMAS_DIR, "a11y.evidence.request.schema.v0.1.json"));
81
+
82
+ const invalidRequest = {
83
+ mcp: {
84
+ envelope: "mcp.envelope_v0_1",
85
+ request_id: "req_test",
86
+ tool: "a11y.evidence",
87
+ client: { name: "test", version: "1.0.0" },
88
+ },
89
+ input: {
90
+ capture: { html: { canonicalize: true } },
91
+ },
92
+ };
93
+
94
+ const validate = ajv.compile(schema);
95
+ const valid = validate(invalidRequest);
96
+
97
+ assert.strictEqual(valid, false, "Should reject request without targets");
98
+ });
99
+
100
+ it("should reject evidence request with wrong tool name", () => {
101
+ const ajv = createValidator();
102
+ const schema = loadSchema(path.join(TOOLS_SCHEMAS_DIR, "a11y.evidence.request.schema.v0.1.json"));
103
+
104
+ const invalidRequest = {
105
+ mcp: {
106
+ envelope: "mcp.envelope_v0_1",
107
+ request_id: "req_test",
108
+ tool: "a11y.diagnose",
109
+ client: { name: "test", version: "1.0.0" },
110
+ },
111
+ input: {
112
+ targets: [{ kind: "file", path: "test.html" }],
113
+ },
114
+ };
115
+
116
+ const validate = ajv.compile(schema);
117
+ const valid = validate(invalidRequest);
118
+
119
+ assert.strictEqual(valid, false, "Should reject request with wrong tool name");
120
+ });
121
+
122
+ it("should require path for file targets", () => {
123
+ const ajv = createValidator();
124
+ const schema = loadSchema(path.join(TOOLS_SCHEMAS_DIR, "a11y.evidence.request.schema.v0.1.json"));
125
+
126
+ const invalidRequest = {
127
+ mcp: {
128
+ envelope: "mcp.envelope_v0_1",
129
+ request_id: "req_test",
130
+ tool: "a11y.evidence",
131
+ client: { name: "test", version: "1.0.0" },
132
+ },
133
+ input: {
134
+ targets: [{ kind: "file" }],
135
+ },
136
+ };
137
+
138
+ const validate = ajv.compile(schema);
139
+ const valid = validate(invalidRequest);
140
+
141
+ assert.strictEqual(valid, false, "Should reject file target without path");
142
+ });
143
+
144
+ it("should reject request with mcp.ok field (requests must not have ok)", () => {
145
+ const ajv = createValidator();
146
+ const schema = loadSchema(path.join(TOOLS_SCHEMAS_DIR, "a11y.evidence.request.schema.v0.1.json"));
147
+
148
+ const invalidRequest = {
149
+ mcp: {
150
+ envelope: "mcp.envelope_v0_1",
151
+ request_id: "req_test",
152
+ tool: "a11y.evidence",
153
+ client: { name: "test", version: "1.0.0" },
154
+ ok: true,
155
+ },
156
+ input: {
157
+ targets: [{ kind: "file", path: "test.html" }],
158
+ },
159
+ };
160
+
161
+ const validate = ajv.compile(schema);
162
+ const valid = validate(invalidRequest);
163
+
164
+ assert.strictEqual(valid, false, "Should reject request with ok field");
165
+ });
166
+ });
167
+
168
+ describe("a11y.diagnose request schema", () => {
169
+ it("should validate diagnose request fixture", () => {
170
+ const ajv = createValidator();
171
+ const schema = loadSchema(path.join(TOOLS_SCHEMAS_DIR, "a11y.diagnose.request.schema.v0.1.json"));
172
+ const fixture = loadJson(path.join(FIXTURES_DIR, "requests/a11y.diagnose.ok.json"));
173
+
174
+ const validate = ajv.compile(schema);
175
+ const valid = validate(fixture);
176
+
177
+ if (!valid) {
178
+ console.error("Validation errors:", JSON.stringify(validate.errors, null, 2));
179
+ }
180
+
181
+ assert.strictEqual(valid, true, `Diagnose request should be valid: ${JSON.stringify(validate.errors)}`);
182
+ });
183
+
184
+ it("should reject diagnose request without bundle_id", () => {
185
+ const ajv = createValidator();
186
+ const schema = loadSchema(path.join(TOOLS_SCHEMAS_DIR, "a11y.diagnose.request.schema.v0.1.json"));
187
+
188
+ const invalidRequest = {
189
+ mcp: {
190
+ envelope: "mcp.envelope_v0_1",
191
+ request_id: "req_test",
192
+ tool: "a11y.diagnose",
193
+ client: { name: "test", version: "1.0.0" },
194
+ },
195
+ input: {
196
+ artifacts: ["artifact:dom:index"],
197
+ profile: "wcag-2.2-aa",
198
+ },
199
+ };
200
+
201
+ const validate = ajv.compile(schema);
202
+ const valid = validate(invalidRequest);
203
+
204
+ assert.strictEqual(valid, false, "Should reject request without bundle_id");
205
+ });
206
+
207
+ it("should reject diagnose request without artifacts", () => {
208
+ const ajv = createValidator();
209
+ const schema = loadSchema(path.join(TOOLS_SCHEMAS_DIR, "a11y.diagnose.request.schema.v0.1.json"));
210
+
211
+ const invalidRequest = {
212
+ mcp: {
213
+ envelope: "mcp.envelope_v0_1",
214
+ request_id: "req_test",
215
+ tool: "a11y.diagnose",
216
+ client: { name: "test", version: "1.0.0" },
217
+ },
218
+ input: {
219
+ bundle_id: "bundle:test:12345678",
220
+ profile: "wcag-2.2-aa",
221
+ },
222
+ };
223
+
224
+ const validate = ajv.compile(schema);
225
+ const valid = validate(invalidRequest);
226
+
227
+ assert.strictEqual(valid, false, "Should reject request without artifacts");
228
+ });
229
+
230
+ it("should reject diagnose request without profile", () => {
231
+ const ajv = createValidator();
232
+ const schema = loadSchema(path.join(TOOLS_SCHEMAS_DIR, "a11y.diagnose.request.schema.v0.1.json"));
233
+
234
+ const invalidRequest = {
235
+ mcp: {
236
+ envelope: "mcp.envelope_v0_1",
237
+ request_id: "req_test",
238
+ tool: "a11y.diagnose",
239
+ client: { name: "test", version: "1.0.0" },
240
+ },
241
+ input: {
242
+ bundle_id: "bundle:test:12345678",
243
+ artifacts: ["artifact:dom:index"],
244
+ },
245
+ };
246
+
247
+ const validate = ajv.compile(schema);
248
+ const valid = validate(invalidRequest);
249
+
250
+ assert.strictEqual(valid, false, "Should reject request without profile");
251
+ });
252
+ });
253
+
254
+ describe("Response fixtures", () => {
255
+ it("should validate evidence success response fixture", () => {
256
+ const ajv = createValidator();
257
+ const schema = loadSchema(path.join(TOOLS_SCHEMAS_DIR, "a11y.evidence.response.schema.v0.1.json"));
258
+ const fixture = loadJson(path.join(FIXTURES_DIR, "responses/a11y.evidence.ok.json"));
259
+
260
+ const validate = ajv.compile(schema);
261
+ const valid = validate(fixture);
262
+
263
+ if (!valid) {
264
+ console.error("Validation errors:", JSON.stringify(validate.errors, null, 2));
265
+ }
266
+
267
+ assert.strictEqual(valid, true, `Evidence response should be valid: ${JSON.stringify(validate.errors)}`);
268
+ });
269
+
270
+ it("should validate diagnose success response fixture", () => {
271
+ const ajv = createValidator();
272
+ const schema = loadSchema(path.join(TOOLS_SCHEMAS_DIR, "a11y.diagnose.response.schema.v0.1.json"));
273
+ const fixture = loadJson(path.join(FIXTURES_DIR, "responses/a11y.diagnose.ok.json"));
274
+
275
+ const validate = ajv.compile(schema);
276
+ const valid = validate(fixture);
277
+
278
+ if (!valid) {
279
+ console.error("Validation errors:", JSON.stringify(validate.errors, null, 2));
280
+ }
281
+
282
+ assert.strictEqual(valid, true, `Diagnose response should be valid: ${JSON.stringify(validate.errors)}`);
283
+ });
284
+
285
+ it("should validate diagnose provenance failure response fixture", () => {
286
+ const ajv = createValidator();
287
+ const schema = loadSchema(path.join(TOOLS_SCHEMAS_DIR, "a11y.diagnose.response.schema.v0.1.json"));
288
+ const fixture = loadJson(path.join(FIXTURES_DIR, "responses/a11y.diagnose.provenance_fail.json"));
289
+
290
+ const validate = ajv.compile(schema);
291
+ const valid = validate(fixture);
292
+
293
+ if (!valid) {
294
+ console.error("Validation errors:", JSON.stringify(validate.errors, null, 2));
295
+ }
296
+
297
+ assert.strictEqual(valid, true, `Provenance failure response should be valid: ${JSON.stringify(validate.errors)}`);
298
+ });
299
+ });
300
+
301
+ describe("Envelope structure", () => {
302
+ it("should have consistent envelope structure across all fixtures", () => {
303
+ const evidenceReq = loadJson(path.join(FIXTURES_DIR, "requests/a11y.evidence.ok.json"));
304
+ const evidenceRes = loadJson(path.join(FIXTURES_DIR, "responses/a11y.evidence.ok.json"));
305
+ const diagnoseReq = loadJson(path.join(FIXTURES_DIR, "requests/a11y.diagnose.ok.json"));
306
+ const diagnoseRes = loadJson(path.join(FIXTURES_DIR, "responses/a11y.diagnose.ok.json"));
307
+ const diagnoseErr = loadJson(path.join(FIXTURES_DIR, "responses/a11y.diagnose.provenance_fail.json"));
308
+
309
+ // All should have envelope version
310
+ assert.strictEqual(evidenceReq.mcp.envelope, "mcp.envelope_v0_1");
311
+ assert.strictEqual(evidenceRes.mcp.envelope, "mcp.envelope_v0_1");
312
+ assert.strictEqual(diagnoseReq.mcp.envelope, "mcp.envelope_v0_1");
313
+ assert.strictEqual(diagnoseRes.mcp.envelope, "mcp.envelope_v0_1");
314
+ assert.strictEqual(diagnoseErr.mcp.envelope, "mcp.envelope_v0_1");
315
+
316
+ // Responses should have ok field
317
+ assert.strictEqual(evidenceRes.mcp.ok, true);
318
+ assert.strictEqual(diagnoseRes.mcp.ok, true);
319
+ assert.strictEqual(diagnoseErr.mcp.ok, false);
320
+
321
+ // Success responses should have result, error responses should have error
322
+ assert.ok(evidenceRes.result);
323
+ assert.ok(diagnoseRes.result);
324
+ assert.ok(diagnoseErr.error);
325
+ });
326
+ });
327
+ });