@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.
- package/.github/workflows/ci.yml +63 -0
- package/LICENSE +21 -0
- package/README.md +37 -0
- package/docs/prov-spec/.github/workflows/ci.yml +68 -0
- package/docs/prov-spec/CHANGELOG.md +69 -0
- package/docs/prov-spec/CODE_OF_CONDUCT.md +129 -0
- package/docs/prov-spec/CONFORMANCE_LEVELS.md +223 -0
- package/docs/prov-spec/CONTRIBUTING.md +145 -0
- package/docs/prov-spec/IMPLEMENTER_CHECKLIST.md +137 -0
- package/docs/prov-spec/LICENSE +21 -0
- package/docs/prov-spec/PRESS_RELEASE.md +74 -0
- package/docs/prov-spec/README.md +182 -0
- package/docs/prov-spec/SETUP.md +135 -0
- package/docs/prov-spec/WHY.md +86 -0
- package/docs/prov-spec/examples/artifact.example.json +14 -0
- package/docs/prov-spec/examples/artifact.ref.example.json +9 -0
- package/docs/prov-spec/examples/evidence.example.json +6 -0
- package/docs/prov-spec/examples/mcp.envelope.example.json +97 -0
- package/docs/prov-spec/examples/mcp.request.example.json +28 -0
- package/docs/prov-spec/examples/prov.record.example.json +35 -0
- package/docs/prov-spec/interop/PROOF_NODE_ENGINE.md +114 -0
- package/docs/prov-spec/spec/MCP_COMPATIBILITY.md +241 -0
- package/docs/prov-spec/spec/PROV_METHODS_CATALOG.md +142 -0
- package/docs/prov-spec/spec/PROV_METHODS_SPEC.md +397 -0
- package/docs/prov-spec/spec/methods.json +213 -0
- package/docs/prov-spec/spec/schemas/artifact.ref.schema.v0.1.json +58 -0
- package/docs/prov-spec/spec/schemas/artifact.schema.v0.1.json +61 -0
- package/docs/prov-spec/spec/schemas/assist.request.schema.v0.1.json +52 -0
- package/docs/prov-spec/spec/schemas/assist.response.schema.v0.1.json +70 -0
- package/docs/prov-spec/spec/schemas/cli.error.schema.v0.1.json +78 -0
- package/docs/prov-spec/spec/schemas/evidence.schema.v0.1.json +37 -0
- package/docs/prov-spec/spec/schemas/mcp.envelope.schema.v0.1.json +141 -0
- package/docs/prov-spec/spec/schemas/mcp.request.schema.v0.1.json +79 -0
- package/docs/prov-spec/spec/schemas/methods.schema.json +93 -0
- package/docs/prov-spec/spec/schemas/prov-capabilities.schema.json +122 -0
- package/docs/prov-spec/spec/schemas/prov.record.schema.v0.1.json +133 -0
- package/docs/prov-spec/spec/vectors/adapter.wrap.envelope_v0_1/expected.json +4 -0
- package/docs/prov-spec/spec/vectors/adapter.wrap.envelope_v0_1/input.json +1 -0
- package/docs/prov-spec/spec/vectors/adapter.wrap.envelope_v0_1/negative/double_wrapped.json +14 -0
- package/docs/prov-spec/spec/vectors/adapter.wrap.envelope_v0_1/negative/wrong_schema_version.json +11 -0
- package/docs/prov-spec/spec/vectors/engine.extract.evidence.json_pointer/expected.json +24 -0
- package/docs/prov-spec/spec/vectors/engine.extract.evidence.json_pointer/input.json +8 -0
- package/docs/prov-spec/spec/vectors/integrity.digest.sha256/expected.json +7 -0
- package/docs/prov-spec/spec/vectors/integrity.digest.sha256/input.json +1 -0
- package/docs/prov-spec/spec/vectors/integrity.digest.sha256/negative/non_hex_chars.json +16 -0
- package/docs/prov-spec/spec/vectors/integrity.digest.sha256/negative/uppercase_hex.json +16 -0
- package/docs/prov-spec/spec/vectors/integrity.digest.sha256/negative/wrong_length.json +16 -0
- package/docs/prov-spec/spec/vectors/method_id_syntax/negative/hyphen_separator.json +8 -0
- package/docs/prov-spec/spec/vectors/method_id_syntax/negative/reserved_namespace.json +8 -0
- package/docs/prov-spec/spec/vectors/method_id_syntax/negative/starts_with_digit.json +8 -0
- package/docs/prov-spec/spec/vectors/method_id_syntax/negative/uppercase.json +8 -0
- package/docs/prov-spec/spec/vectors/method_id_syntax/positive/valid_ids.json +18 -0
- package/docs/prov-spec/tools/python/prov_validator.py +428 -0
- package/examples/a11y-demo-site/.github/workflows/a11y-artifacts.yml +81 -0
- package/examples/a11y-demo-site/.github/workflows/a11y.yml +34 -0
- package/examples/a11y-demo-site/CODE_OF_CONDUCT.md +129 -0
- package/examples/a11y-demo-site/CONTRIBUTING.md +83 -0
- package/examples/a11y-demo-site/LICENSE +21 -0
- package/examples/a11y-demo-site/README.md +155 -0
- package/examples/a11y-demo-site/html/contact.html +15 -0
- package/examples/a11y-demo-site/html/index.html +20 -0
- package/examples/a11y-demo-site/scripts/a11y.sh +20 -0
- package/package.json +26 -0
- package/src/a11y-assist/.github/workflows/publish.yml +52 -0
- package/src/a11y-assist/.github/workflows/test.yml +30 -0
- package/src/a11y-assist/A11Y_ASSIST_TEST_COVERAGE_REQUIREMENTS.md +104 -0
- package/src/a11y-assist/CODE_OF_CONDUCT.md +129 -0
- package/src/a11y-assist/CONTRIBUTING.md +98 -0
- package/src/a11y-assist/ENGINE.md +363 -0
- package/src/a11y-assist/LICENSE +21 -0
- package/src/a11y-assist/PRESS_RELEASE.md +71 -0
- package/src/a11y-assist/QUICKSTART.md +101 -0
- package/src/a11y-assist/README.md +192 -0
- package/src/a11y-assist/RELEASE_NOTES.md +319 -0
- package/src/a11y-assist/a11y_assist/__init__.py +3 -0
- package/src/a11y-assist/a11y_assist/cli.py +599 -0
- package/src/a11y-assist/a11y_assist/from_cli_error.py +149 -0
- package/src/a11y-assist/a11y_assist/guard.py +444 -0
- package/src/a11y-assist/a11y_assist/ingest.py +407 -0
- package/src/a11y-assist/a11y_assist/methods.py +137 -0
- package/src/a11y-assist/a11y_assist/parse_raw.py +71 -0
- package/src/a11y-assist/a11y_assist/profiles/__init__.py +29 -0
- package/src/a11y-assist/a11y_assist/profiles/cognitive_load.py +245 -0
- package/src/a11y-assist/a11y_assist/profiles/cognitive_load_render.py +86 -0
- package/src/a11y-assist/a11y_assist/profiles/dyslexia.py +144 -0
- package/src/a11y-assist/a11y_assist/profiles/dyslexia_render.py +77 -0
- package/src/a11y-assist/a11y_assist/profiles/plain_language.py +119 -0
- package/src/a11y-assist/a11y_assist/profiles/plain_language_render.py +66 -0
- package/src/a11y-assist/a11y_assist/profiles/screen_reader.py +348 -0
- package/src/a11y-assist/a11y_assist/profiles/screen_reader_render.py +89 -0
- package/src/a11y-assist/a11y_assist/render.py +95 -0
- package/src/a11y-assist/a11y_assist/schemas/assist.request.schema.v0.1.json +52 -0
- package/src/a11y-assist/a11y_assist/schemas/assist.response.schema.v0.1.json +70 -0
- package/src/a11y-assist/a11y_assist/schemas/cli.error.schema.v0.1.json +78 -0
- package/src/a11y-assist/a11y_assist/storage.py +31 -0
- package/src/a11y-assist/pyproject.toml +60 -0
- package/src/a11y-assist/tests/__init__.py +1 -0
- package/src/a11y-assist/tests/fixtures/base_inputs/cli_error_high.json +18 -0
- package/src/a11y-assist/tests/fixtures/base_inputs/cli_error_medium.json +16 -0
- package/src/a11y-assist/tests/fixtures/base_inputs/raw_text_low.txt +3 -0
- package/src/a11y-assist/tests/fixtures/cli_error_good.json +9 -0
- package/src/a11y-assist/tests/fixtures/cli_error_missing_id.json +7 -0
- package/src/a11y-assist/tests/fixtures/cli_error_string_format.json +7 -0
- package/src/a11y-assist/tests/fixtures/expected/cognitive_load_high.txt +20 -0
- package/src/a11y-assist/tests/fixtures/expected/dyslexia_high.txt +20 -0
- package/src/a11y-assist/tests/fixtures/expected/lowvision_high.txt +18 -0
- package/src/a11y-assist/tests/fixtures/expected/plain_language_high.txt +14 -0
- package/src/a11y-assist/tests/fixtures/expected/screen_reader_high.txt +19 -0
- package/src/a11y-assist/tests/fixtures/golden_screen_reader_cli_error.txt +16 -0
- package/src/a11y-assist/tests/fixtures/golden_screen_reader_raw_no_id.txt +14 -0
- package/src/a11y-assist/tests/fixtures/golden_screen_reader_raw_with_id.txt +14 -0
- package/src/a11y-assist/tests/fixtures/raw_good.txt +11 -0
- package/src/a11y-assist/tests/fixtures/raw_no_id.txt +2 -0
- package/src/a11y-assist/tests/test_cognitive_load.py +469 -0
- package/src/a11y-assist/tests/test_dyslexia.py +337 -0
- package/src/a11y-assist/tests/test_explain.py +74 -0
- package/src/a11y-assist/tests/test_golden.py +127 -0
- package/src/a11y-assist/tests/test_guard.py +819 -0
- package/src/a11y-assist/tests/test_guard_integration.py +457 -0
- package/src/a11y-assist/tests/test_ingest.py +311 -0
- package/src/a11y-assist/tests/test_methods_metadata.py +236 -0
- package/src/a11y-assist/tests/test_plain_language.py +348 -0
- package/src/a11y-assist/tests/test_render.py +117 -0
- package/src/a11y-assist/tests/test_screen_reader.py +703 -0
- package/src/a11y-assist/tests/test_storage_last.py +61 -0
- package/src/a11y-assist/tests/test_triage.py +86 -0
- package/src/a11y-ci/.github/workflows/ci.yml +43 -0
- package/src/a11y-ci/.github/workflows/test.yml +30 -0
- package/src/a11y-ci/A11Y_CI_TEST_COVERAGE_REQUIREMENTS.md +94 -0
- package/src/a11y-ci/CODE_OF_CONDUCT.md +129 -0
- package/src/a11y-ci/CONTRIBUTING.md +142 -0
- package/src/a11y-ci/LICENSE +21 -0
- package/src/a11y-ci/README.md +105 -0
- package/src/a11y-ci/a11y_ci/__init__.py +3 -0
- package/src/a11y-ci/a11y_ci/allowlist.py +83 -0
- package/src/a11y-ci/a11y_ci/cli.py +145 -0
- package/src/a11y-ci/a11y_ci/gate.py +131 -0
- package/src/a11y-ci/a11y_ci/render.py +48 -0
- package/src/a11y-ci/a11y_ci/schemas/allowlist.schema.json +24 -0
- package/src/a11y-ci/a11y_ci/scorecard.py +99 -0
- package/src/a11y-ci/npm/package.json +35 -0
- package/src/a11y-ci/pyproject.toml +64 -0
- package/src/a11y-ci/tests/__init__.py +1 -0
- package/src/a11y-ci/tests/fixtures/allowlist_expired.json +10 -0
- package/src/a11y-ci/tests/fixtures/allowlist_ok.json +10 -0
- package/src/a11y-ci/tests/fixtures/baseline_ok.json +7 -0
- package/src/a11y-ci/tests/fixtures/current_fail.json +6 -0
- package/src/a11y-ci/tests/fixtures/current_ok.json +6 -0
- package/src/a11y-ci/tests/fixtures/current_regresses.json +7 -0
- package/src/a11y-ci/tests/test_gate.py +134 -0
- package/src/a11y-evidence-engine/.github/workflows/ci.yml +53 -0
- package/src/a11y-evidence-engine/CODE_OF_CONDUCT.md +129 -0
- package/src/a11y-evidence-engine/CONTRIBUTING.md +128 -0
- package/src/a11y-evidence-engine/LICENSE +21 -0
- package/src/a11y-evidence-engine/README.md +71 -0
- package/src/a11y-evidence-engine/bin/a11y-engine.js +11 -0
- package/src/a11y-evidence-engine/fixtures/bad/button-no-name.html +30 -0
- package/src/a11y-evidence-engine/fixtures/bad/img-missing-alt.html +19 -0
- package/src/a11y-evidence-engine/fixtures/bad/input-missing-label.html +26 -0
- package/src/a11y-evidence-engine/fixtures/bad/missing-lang.html +11 -0
- package/src/a11y-evidence-engine/fixtures/good/index.html +29 -0
- package/src/a11y-evidence-engine/package-lock.json +109 -0
- package/src/a11y-evidence-engine/package.json +45 -0
- package/src/a11y-evidence-engine/src/cli.js +74 -0
- package/src/a11y-evidence-engine/src/evidence/canonicalize.js +52 -0
- package/src/a11y-evidence-engine/src/evidence/json_pointer.js +34 -0
- package/src/a11y-evidence-engine/src/evidence/prov_emit.js +153 -0
- package/src/a11y-evidence-engine/src/fswalk.js +56 -0
- package/src/a11y-evidence-engine/src/html_parse.js +117 -0
- package/src/a11y-evidence-engine/src/ids.js +53 -0
- package/src/a11y-evidence-engine/src/rules/document_missing_lang.js +50 -0
- package/src/a11y-evidence-engine/src/rules/form_control_missing_label.js +105 -0
- package/src/a11y-evidence-engine/src/rules/img_missing_alt.js +77 -0
- package/src/a11y-evidence-engine/src/rules/index.js +37 -0
- package/src/a11y-evidence-engine/src/rules/interactive_missing_name.js +129 -0
- package/src/a11y-evidence-engine/src/scan.js +128 -0
- package/src/a11y-evidence-engine/test/scan.test.js +149 -0
- package/src/a11y-evidence-engine/test/vectors.test.js +200 -0
- package/src/a11y-lint/.github/workflows/ci.yml +46 -0
- package/src/a11y-lint/.github/workflows/test.yml +34 -0
- package/src/a11y-lint/CODE_OF_CONDUCT.md +129 -0
- package/src/a11y-lint/CONTRIBUTING.md +70 -0
- package/src/a11y-lint/GOVERNANCE.md +57 -0
- package/src/a11y-lint/LICENSE +21 -0
- package/src/a11y-lint/PRESS_RELEASE.md +50 -0
- package/src/a11y-lint/README.md +276 -0
- package/src/a11y-lint/RELEASE_NOTES.md +57 -0
- package/src/a11y-lint/RELEASING.md +57 -0
- package/src/a11y-lint/a11y_lint/__init__.py +64 -0
- package/src/a11y-lint/a11y_lint/cli.py +319 -0
- package/src/a11y-lint/a11y_lint/errors.py +252 -0
- package/src/a11y-lint/a11y_lint/render.py +293 -0
- package/src/a11y-lint/a11y_lint/report_md.py +289 -0
- package/src/a11y-lint/a11y_lint/scan_cli_text.py +434 -0
- package/src/a11y-lint/a11y_lint/schemas/cli.error.schema.v0.1.json +83 -0
- package/src/a11y-lint/a11y_lint/scorecard.py +244 -0
- package/src/a11y-lint/a11y_lint/validate.py +225 -0
- package/src/a11y-lint/pyproject.toml +75 -0
- package/src/a11y-lint/tests/__init__.py +1 -0
- package/src/a11y-lint/tests/test_cli.py +200 -0
- package/src/a11y-lint/tests/test_errors.py +188 -0
- package/src/a11y-lint/tests/test_render.py +202 -0
- package/src/a11y-lint/tests/test_report_md.py +188 -0
- package/src/a11y-lint/tests/test_scan_cli_text.py +290 -0
- package/src/a11y-lint/tests/test_scorecard.py +195 -0
- package/src/a11y-lint/tests/test_validate.py +257 -0
- package/src/a11y-mcp-tools/.github/workflows/ci.yml +53 -0
- package/src/a11y-mcp-tools/CODE_OF_CONDUCT.md +129 -0
- package/src/a11y-mcp-tools/CONTRIBUTING.md +136 -0
- package/src/a11y-mcp-tools/LICENSE +21 -0
- package/src/a11y-mcp-tools/PROV_METHODS_CATALOG.md +104 -0
- package/src/a11y-mcp-tools/README.md +168 -0
- package/src/a11y-mcp-tools/bin/cli.js +452 -0
- package/src/a11y-mcp-tools/bin/server.js +244 -0
- package/src/a11y-mcp-tools/fixtures/requests/a11y.diagnose.ok.json +27 -0
- package/src/a11y-mcp-tools/fixtures/requests/a11y.evidence.ok.json +25 -0
- package/src/a11y-mcp-tools/fixtures/responses/a11y.diagnose.ok.json +139 -0
- package/src/a11y-mcp-tools/fixtures/responses/a11y.diagnose.provenance_fail.json +13 -0
- package/src/a11y-mcp-tools/fixtures/responses/a11y.evidence.ok.json +88 -0
- package/src/a11y-mcp-tools/package-lock.json +189 -0
- package/src/a11y-mcp-tools/package.json +49 -0
- package/src/a11y-mcp-tools/src/envelope.js +197 -0
- package/src/a11y-mcp-tools/src/index.js +9 -0
- package/src/a11y-mcp-tools/src/schemas/artifact.js +85 -0
- package/src/a11y-mcp-tools/src/schemas/diagnosis.schema.v0.1.json +137 -0
- package/src/a11y-mcp-tools/src/schemas/envelope.schema.v0.1.json +108 -0
- package/src/a11y-mcp-tools/src/schemas/evidence.bundle.schema.v0.1.json +129 -0
- package/src/a11y-mcp-tools/src/schemas/evidence.js +97 -0
- package/src/a11y-mcp-tools/src/schemas/index.js +11 -0
- package/src/a11y-mcp-tools/src/schemas/provenance.js +140 -0
- package/src/a11y-mcp-tools/src/schemas/tools/a11y.diagnose.request.schema.v0.1.json +77 -0
- package/src/a11y-mcp-tools/src/schemas/tools/a11y.diagnose.response.schema.v0.1.json +50 -0
- package/src/a11y-mcp-tools/src/schemas/tools/a11y.evidence.request.schema.v0.1.json +120 -0
- package/src/a11y-mcp-tools/src/schemas/tools/a11y.evidence.response.schema.v0.1.json +50 -0
- package/src/a11y-mcp-tools/src/tools/diagnose.js +597 -0
- package/src/a11y-mcp-tools/src/tools/evidence.js +481 -0
- package/src/a11y-mcp-tools/src/tools/index.js +10 -0
- package/src/a11y-mcp-tools/test/contract.test.mjs +154 -0
- package/src/a11y-mcp-tools/test/diagnose.test.js +485 -0
- package/src/a11y-mcp-tools/test/evidence.test.js +183 -0
- package/src/a11y-mcp-tools/test/schema.test.js +327 -0
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
"""Integration tests for Profile Guard through CLI pipeline.
|
|
2
|
+
|
|
3
|
+
Tests that guard validation works correctly when invoked through
|
|
4
|
+
the actual CLI commands with all three profiles.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import tempfile
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
import pytest
|
|
12
|
+
from click.testing import CliRunner
|
|
13
|
+
|
|
14
|
+
from a11y_assist.cli import main
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.fixture
|
|
18
|
+
def runner() -> CliRunner:
|
|
19
|
+
"""CLI test runner."""
|
|
20
|
+
return CliRunner()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.fixture
|
|
24
|
+
def valid_cli_error_json() -> dict:
|
|
25
|
+
"""Valid cli.error.v0.1 JSON for testing."""
|
|
26
|
+
return {
|
|
27
|
+
"level": "ERROR",
|
|
28
|
+
"code": "CFG001",
|
|
29
|
+
"id": "TEST.CONFIG.MISSING",
|
|
30
|
+
"title": "Configuration file not found",
|
|
31
|
+
"what": "The config.yaml file is missing from the current directory.",
|
|
32
|
+
"why": "The tool requires a configuration file to run.",
|
|
33
|
+
"fix": [
|
|
34
|
+
"Run: config init --dry-run",
|
|
35
|
+
"Then: config init",
|
|
36
|
+
"Verify the file was created.",
|
|
37
|
+
],
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@pytest.fixture
|
|
42
|
+
def json_file(valid_cli_error_json: dict) -> str:
|
|
43
|
+
"""Create a temporary JSON file with valid cli.error.v0.1 content."""
|
|
44
|
+
with tempfile.NamedTemporaryFile(
|
|
45
|
+
mode="w", suffix=".json", delete=False
|
|
46
|
+
) as f:
|
|
47
|
+
json.dump(valid_cli_error_json, f)
|
|
48
|
+
return f.name
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# Integration: explain command with guard
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_explain_lowvision_passes_guard(runner: CliRunner, json_file: str):
|
|
55
|
+
"""Explain command with lowvision profile should pass guard."""
|
|
56
|
+
result = runner.invoke(main, ["explain", "--json", json_file, "--profile", "lowvision"])
|
|
57
|
+
|
|
58
|
+
# Should succeed
|
|
59
|
+
assert result.exit_code == 0
|
|
60
|
+
assert "ASSIST (Low Vision)" in result.output
|
|
61
|
+
assert "TEST.CONFIG.MISSING" in result.output
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_explain_cognitive_load_passes_guard(runner: CliRunner, json_file: str):
|
|
65
|
+
"""Explain command with cognitive-load profile should pass guard."""
|
|
66
|
+
result = runner.invoke(main, ["explain", "--json", json_file, "--profile", "cognitive-load"])
|
|
67
|
+
|
|
68
|
+
# Should succeed
|
|
69
|
+
assert result.exit_code == 0
|
|
70
|
+
assert "ASSIST (Cognitive Load)" in result.output
|
|
71
|
+
assert "TEST.CONFIG.MISSING" in result.output
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_explain_screen_reader_passes_guard(runner: CliRunner, json_file: str):
|
|
75
|
+
"""Explain command with screen-reader profile should pass guard."""
|
|
76
|
+
result = runner.invoke(main, ["explain", "--json", json_file, "--profile", "screen-reader"])
|
|
77
|
+
|
|
78
|
+
# Should succeed
|
|
79
|
+
assert result.exit_code == 0
|
|
80
|
+
assert "ASSIST. Profile: Screen reader." in result.output
|
|
81
|
+
assert "TEST.CONFIG.MISSING" in result.output
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# Integration: triage command with guard
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_triage_lowvision_passes_guard(runner: CliRunner):
|
|
88
|
+
"""Triage command with lowvision profile should pass guard."""
|
|
89
|
+
input_text = """[ERROR] (ID: TOOL.PARSE.FAIL)
|
|
90
|
+
What: Failed to parse input file.
|
|
91
|
+
Why: Invalid syntax on line 42.
|
|
92
|
+
Fix:
|
|
93
|
+
Check line 42 for typos.
|
|
94
|
+
Run: tool validate --dry-run
|
|
95
|
+
"""
|
|
96
|
+
result = runner.invoke(
|
|
97
|
+
main, ["triage", "--stdin", "--profile", "lowvision"], input=input_text
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Should succeed
|
|
101
|
+
assert result.exit_code == 0
|
|
102
|
+
assert "ASSIST (Low Vision)" in result.output
|
|
103
|
+
assert "TOOL.PARSE.FAIL" in result.output
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_triage_cognitive_load_passes_guard(runner: CliRunner):
|
|
107
|
+
"""Triage command with cognitive-load profile should pass guard."""
|
|
108
|
+
input_text = """[ERROR] (ID: TOOL.PARSE.FAIL)
|
|
109
|
+
What: Failed to parse input file.
|
|
110
|
+
Why: Invalid syntax on line 42.
|
|
111
|
+
Fix:
|
|
112
|
+
Check line 42 for typos.
|
|
113
|
+
Run: tool validate --dry-run
|
|
114
|
+
"""
|
|
115
|
+
result = runner.invoke(
|
|
116
|
+
main, ["triage", "--stdin", "--profile", "cognitive-load"], input=input_text
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Should succeed
|
|
120
|
+
assert result.exit_code == 0
|
|
121
|
+
assert "ASSIST (Cognitive Load)" in result.output
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_triage_screen_reader_passes_guard(runner: CliRunner):
|
|
125
|
+
"""Triage command with screen-reader profile should pass guard."""
|
|
126
|
+
input_text = """[ERROR] (ID: TOOL.PARSE.FAIL)
|
|
127
|
+
What: Failed to parse input file.
|
|
128
|
+
Why: Invalid syntax on line 42.
|
|
129
|
+
Fix:
|
|
130
|
+
Check line 42 for typos.
|
|
131
|
+
Run: tool validate --dry-run
|
|
132
|
+
"""
|
|
133
|
+
result = runner.invoke(
|
|
134
|
+
main, ["triage", "--stdin", "--profile", "screen-reader"], input=input_text
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# Should succeed
|
|
138
|
+
assert result.exit_code == 0
|
|
139
|
+
assert "ASSIST. Profile: Screen reader." in result.output
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# Integration: triage with no ID (Low confidence)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def test_triage_low_confidence_lowvision(runner: CliRunner):
|
|
146
|
+
"""Triage with no ID should produce Low confidence output."""
|
|
147
|
+
input_text = """Error: Something went wrong.
|
|
148
|
+
Please check the configuration and try again.
|
|
149
|
+
"""
|
|
150
|
+
result = runner.invoke(
|
|
151
|
+
main, ["triage", "--stdin", "--profile", "lowvision"], input=input_text
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# Should succeed with Low confidence
|
|
155
|
+
assert result.exit_code == 0
|
|
156
|
+
assert "Confidence: Low" in result.output
|
|
157
|
+
assert "No (ID: ...) found" in result.output
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def test_triage_low_confidence_cognitive_load(runner: CliRunner):
|
|
161
|
+
"""Triage with no ID should produce Low confidence output for cognitive-load."""
|
|
162
|
+
input_text = """Error: Something went wrong.
|
|
163
|
+
Please check the configuration and try again.
|
|
164
|
+
"""
|
|
165
|
+
result = runner.invoke(
|
|
166
|
+
main, ["triage", "--stdin", "--profile", "cognitive-load"], input=input_text
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Should succeed with Low confidence
|
|
170
|
+
assert result.exit_code == 0
|
|
171
|
+
assert "Confidence: Low" in result.output
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def test_triage_low_confidence_screen_reader(runner: CliRunner):
|
|
175
|
+
"""Triage with no ID should produce Low confidence output for screen-reader."""
|
|
176
|
+
input_text = """Error: Something went wrong.
|
|
177
|
+
Please check the configuration and try again.
|
|
178
|
+
"""
|
|
179
|
+
result = runner.invoke(
|
|
180
|
+
main, ["triage", "--stdin", "--profile", "screen-reader"], input=input_text
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# Should succeed with Low confidence
|
|
184
|
+
assert result.exit_code == 0
|
|
185
|
+
assert "Confidence: Low" in result.output
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
# Integration: explain with invalid JSON
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def test_explain_invalid_json_lowvision(runner: CliRunner):
|
|
192
|
+
"""Explain with invalid JSON should produce Low confidence validation error."""
|
|
193
|
+
# Create invalid JSON (missing required fields)
|
|
194
|
+
invalid_json = {"level": "ERROR", "code": "TST001"} # Missing what, why, fix
|
|
195
|
+
|
|
196
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
|
|
197
|
+
json.dump(invalid_json, f)
|
|
198
|
+
json_path = f.name
|
|
199
|
+
|
|
200
|
+
result = runner.invoke(main, ["explain", "--json", json_path, "--profile", "lowvision"])
|
|
201
|
+
|
|
202
|
+
# Should exit with code 2 (validation error)
|
|
203
|
+
assert result.exit_code == 2
|
|
204
|
+
assert "Confidence: Low" in result.output
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def test_explain_invalid_json_cognitive_load(runner: CliRunner):
|
|
208
|
+
"""Explain with invalid JSON should work with cognitive-load profile."""
|
|
209
|
+
invalid_json = {"level": "ERROR", "code": "TST001"} # Missing what, why, fix
|
|
210
|
+
|
|
211
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
|
|
212
|
+
json.dump(invalid_json, f)
|
|
213
|
+
json_path = f.name
|
|
214
|
+
|
|
215
|
+
result = runner.invoke(main, ["explain", "--json", json_path, "--profile", "cognitive-load"])
|
|
216
|
+
|
|
217
|
+
# Should exit with code 2 (validation error)
|
|
218
|
+
assert result.exit_code == 2
|
|
219
|
+
assert "Confidence: Low" in result.output
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def test_explain_invalid_json_screen_reader(runner: CliRunner):
|
|
223
|
+
"""Explain with invalid JSON should work with screen-reader profile."""
|
|
224
|
+
invalid_json = {"level": "ERROR", "code": "TST001"} # Missing what, why, fix
|
|
225
|
+
|
|
226
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
|
|
227
|
+
json.dump(invalid_json, f)
|
|
228
|
+
json_path = f.name
|
|
229
|
+
|
|
230
|
+
result = runner.invoke(main, ["explain", "--json", json_path, "--profile", "screen-reader"])
|
|
231
|
+
|
|
232
|
+
# Should exit with code 2 (validation error)
|
|
233
|
+
assert result.exit_code == 2
|
|
234
|
+
assert "Confidence: Low" in result.output
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
# Integration: Verify guard catches profile bugs (simulated)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def test_guard_error_format_on_violation():
|
|
241
|
+
"""Guard violations should produce structured error output.
|
|
242
|
+
|
|
243
|
+
This test documents the expected error format. In practice,
|
|
244
|
+
guard violations indicate bugs in profile transforms, not
|
|
245
|
+
user errors. This test ensures the error format is correct
|
|
246
|
+
if such a bug were to occur.
|
|
247
|
+
"""
|
|
248
|
+
# The guard error format should include:
|
|
249
|
+
# - [ERROR] A11Y.ASSIST.ENGINE.GUARD.FAIL
|
|
250
|
+
# - What: description
|
|
251
|
+
# - Why: explanation
|
|
252
|
+
# - Fix: instructions
|
|
253
|
+
# - Guard codes: list of violations
|
|
254
|
+
|
|
255
|
+
# Since we can't easily trigger a guard violation without
|
|
256
|
+
# introducing a bug, we just document the expected format
|
|
257
|
+
# in this test. Actual guard violation tests are in test_guard.py.
|
|
258
|
+
pass
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
# Integration: Verify max steps enforced per profile
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def test_cognitive_load_max_3_steps_integration(runner: CliRunner):
|
|
265
|
+
"""Cognitive-load profile should enforce max 3 steps."""
|
|
266
|
+
# Create JSON with many fix steps
|
|
267
|
+
json_data = {
|
|
268
|
+
"level": "ERROR",
|
|
269
|
+
"code": "TST001",
|
|
270
|
+
"id": "TEST.MANY.STEPS",
|
|
271
|
+
"title": "Error with many steps",
|
|
272
|
+
"what": "Something went wrong.",
|
|
273
|
+
"why": "Multiple issues need fixing.",
|
|
274
|
+
"fix": [
|
|
275
|
+
"Step 1: Do first thing.",
|
|
276
|
+
"Step 2: Do second thing.",
|
|
277
|
+
"Step 3: Do third thing.",
|
|
278
|
+
"Step 4: Do fourth thing.",
|
|
279
|
+
"Step 5: Do fifth thing.",
|
|
280
|
+
],
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
|
|
284
|
+
json.dump(json_data, f)
|
|
285
|
+
json_path = f.name
|
|
286
|
+
|
|
287
|
+
result = runner.invoke(main, ["explain", "--json", json_path, "--profile", "cognitive-load"])
|
|
288
|
+
|
|
289
|
+
# Should succeed - profile should truncate to 3 steps
|
|
290
|
+
assert result.exit_code == 0
|
|
291
|
+
# Count "First:", "Next:", "Last:" labels (max 3)
|
|
292
|
+
assert result.output.count("First:") <= 1
|
|
293
|
+
assert result.output.count("Last:") <= 1
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def test_lowvision_within_5_steps_succeeds(runner: CliRunner):
|
|
297
|
+
"""Lowvision profile should succeed when steps are within limit."""
|
|
298
|
+
json_data = {
|
|
299
|
+
"level": "ERROR",
|
|
300
|
+
"code": "TST002",
|
|
301
|
+
"id": "TEST.FEW.STEPS",
|
|
302
|
+
"title": "Error with few steps",
|
|
303
|
+
"what": "Something went wrong.",
|
|
304
|
+
"why": "A few issues need fixing.",
|
|
305
|
+
"fix": [
|
|
306
|
+
"Step 1",
|
|
307
|
+
"Step 2",
|
|
308
|
+
"Step 3",
|
|
309
|
+
"Step 4",
|
|
310
|
+
"Step 5",
|
|
311
|
+
],
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
|
|
315
|
+
json.dump(json_data, f)
|
|
316
|
+
json_path = f.name
|
|
317
|
+
|
|
318
|
+
result = runner.invoke(main, ["explain", "--json", json_path, "--profile", "lowvision"])
|
|
319
|
+
|
|
320
|
+
# Should succeed - 5 steps is within limit
|
|
321
|
+
assert result.exit_code == 0
|
|
322
|
+
assert "ASSIST (Low Vision)" in result.output
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
# Integration: Screen-reader abbreviation expansion
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def test_screen_reader_expands_cli_abbreviation(runner: CliRunner):
|
|
329
|
+
"""Screen-reader profile should expand CLI abbreviation."""
|
|
330
|
+
json_data = {
|
|
331
|
+
"level": "ERROR",
|
|
332
|
+
"code": "CLI001",
|
|
333
|
+
"id": "TEST.CLI.ERROR",
|
|
334
|
+
"title": "CLI tool failed",
|
|
335
|
+
"what": "The CLI command failed to execute.",
|
|
336
|
+
"why": "Invalid CLI arguments provided.",
|
|
337
|
+
"fix": ["Check CLI documentation.", "Run: cli --help"],
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
|
|
341
|
+
json.dump(json_data, f)
|
|
342
|
+
json_path = f.name
|
|
343
|
+
|
|
344
|
+
result = runner.invoke(main, ["explain", "--json", json_path, "--profile", "screen-reader"])
|
|
345
|
+
|
|
346
|
+
assert result.exit_code == 0
|
|
347
|
+
# CLI should be expanded to "command line" in output
|
|
348
|
+
assert "command line" in result.output.lower() or "C L I" in result.output
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def test_screen_reader_expands_json_abbreviation(runner: CliRunner):
|
|
352
|
+
"""Screen-reader profile should expand JSON abbreviation."""
|
|
353
|
+
json_data = {
|
|
354
|
+
"level": "ERROR",
|
|
355
|
+
"code": "JSN001",
|
|
356
|
+
"id": "TEST.JSON.ERROR",
|
|
357
|
+
"title": "JSON parse error",
|
|
358
|
+
"what": "The JSON file is malformed.",
|
|
359
|
+
"why": "Invalid JSON syntax.",
|
|
360
|
+
"fix": ["Validate your JSON file.", "Check for missing commas."],
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
|
|
364
|
+
json.dump(json_data, f)
|
|
365
|
+
json_path = f.name
|
|
366
|
+
|
|
367
|
+
result = runner.invoke(main, ["explain", "--json", json_path, "--profile", "screen-reader"])
|
|
368
|
+
|
|
369
|
+
assert result.exit_code == 0
|
|
370
|
+
# JSON should be expanded to "J S O N" in output
|
|
371
|
+
assert "J S O N" in result.output
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
# Integration: Verify SAFE commands pass through correctly
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def test_safe_commands_preserved_lowvision(runner: CliRunner):
|
|
378
|
+
"""SAFE commands should be preserved in lowvision output."""
|
|
379
|
+
json_data = {
|
|
380
|
+
"level": "ERROR",
|
|
381
|
+
"code": "TST003",
|
|
382
|
+
"id": "TEST.SAFE.CMD",
|
|
383
|
+
"title": "Error with safe command",
|
|
384
|
+
"what": "Operation failed.",
|
|
385
|
+
"why": "Need to retry.",
|
|
386
|
+
"fix": ["Run: tool fix --dry-run"],
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
|
|
390
|
+
json.dump(json_data, f)
|
|
391
|
+
json_path = f.name
|
|
392
|
+
|
|
393
|
+
result = runner.invoke(main, ["explain", "--json", json_path, "--profile", "lowvision"])
|
|
394
|
+
|
|
395
|
+
assert result.exit_code == 0
|
|
396
|
+
assert "tool fix --dry-run" in result.output
|
|
397
|
+
assert "Next (SAFE):" in result.output
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def test_safe_commands_preserved_cognitive_load(runner: CliRunner):
|
|
401
|
+
"""SAFE commands should be preserved in cognitive-load output (max 1)."""
|
|
402
|
+
json_data = {
|
|
403
|
+
"level": "ERROR",
|
|
404
|
+
"code": "TST004",
|
|
405
|
+
"id": "TEST.SAFE.CMD",
|
|
406
|
+
"title": "Error with safe command",
|
|
407
|
+
"what": "Operation failed.",
|
|
408
|
+
"why": "Need to retry.",
|
|
409
|
+
"fix": [
|
|
410
|
+
"Run: tool fix --dry-run",
|
|
411
|
+
"Run: tool check --dry-run",
|
|
412
|
+
],
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
|
|
416
|
+
json.dump(json_data, f)
|
|
417
|
+
json_path = f.name
|
|
418
|
+
|
|
419
|
+
result = runner.invoke(main, ["explain", "--json", json_path, "--profile", "cognitive-load"])
|
|
420
|
+
|
|
421
|
+
assert result.exit_code == 0
|
|
422
|
+
# Should have at most one SAFE command
|
|
423
|
+
assert "Next (SAFE):" in result.output
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def test_safe_commands_preserved_screen_reader(runner: CliRunner):
|
|
427
|
+
"""SAFE commands should be preserved in screen-reader output."""
|
|
428
|
+
json_data = {
|
|
429
|
+
"level": "ERROR",
|
|
430
|
+
"code": "TST005",
|
|
431
|
+
"id": "TEST.SAFE.CMD",
|
|
432
|
+
"title": "Error with safe command",
|
|
433
|
+
"what": "Operation failed.",
|
|
434
|
+
"why": "Need to retry.",
|
|
435
|
+
"fix": ["Run: tool fix --dry-run"],
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
|
|
439
|
+
json.dump(json_data, f)
|
|
440
|
+
json_path = f.name
|
|
441
|
+
|
|
442
|
+
result = runner.invoke(main, ["explain", "--json", json_path, "--profile", "screen-reader"])
|
|
443
|
+
|
|
444
|
+
assert result.exit_code == 0
|
|
445
|
+
assert "tool fix --dry-run" in result.output
|
|
446
|
+
assert "Next safe command" in result.output
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
# Integration: Default profile is lowvision
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
def test_default_profile_is_lowvision(runner: CliRunner, json_file: str):
|
|
453
|
+
"""Default profile should be lowvision when --profile not specified."""
|
|
454
|
+
result = runner.invoke(main, ["explain", "--json", json_file])
|
|
455
|
+
|
|
456
|
+
assert result.exit_code == 0
|
|
457
|
+
assert "ASSIST (Low Vision)" in result.output
|