@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,348 @@
|
|
|
1
|
+
"""Tests for plain-language profile.
|
|
2
|
+
|
|
3
|
+
Verifies:
|
|
4
|
+
- Sentences simplified to one clause
|
|
5
|
+
- Parentheticals removed
|
|
6
|
+
- Subordinate clauses removed
|
|
7
|
+
- Max 4 steps
|
|
8
|
+
- Numeric step labels
|
|
9
|
+
- Confidence preserved
|
|
10
|
+
- Commands from allowlist only (max 1)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import pytest
|
|
14
|
+
|
|
15
|
+
from a11y_assist.profiles.plain_language import (
|
|
16
|
+
_normalize_step,
|
|
17
|
+
_remove_parentheticals,
|
|
18
|
+
_simplify_sentence,
|
|
19
|
+
apply_plain_language,
|
|
20
|
+
)
|
|
21
|
+
from a11y_assist.profiles.plain_language_render import render_plain_language
|
|
22
|
+
from a11y_assist.render import AssistResult
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Unit tests for helper functions
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_remove_parentheticals():
|
|
29
|
+
"""Should remove parenthetical content."""
|
|
30
|
+
assert _remove_parentheticals("Check config (optional)") == "Check config"
|
|
31
|
+
assert _remove_parentheticals("Run command [see docs]") == "Run command"
|
|
32
|
+
assert _remove_parentheticals("No parens here") == "No parens here"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_simplify_sentence_removes_conjunctions():
|
|
36
|
+
"""Should split on conjunctions and keep first part."""
|
|
37
|
+
result = _simplify_sentence("Check config and run the test")
|
|
38
|
+
assert "and" not in result.lower()
|
|
39
|
+
assert "Check config" in result
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_simplify_sentence_removes_but():
|
|
43
|
+
"""Should handle 'but' conjunction."""
|
|
44
|
+
result = _simplify_sentence("Try this but be careful")
|
|
45
|
+
assert "but" not in result.lower()
|
|
46
|
+
assert "Try this" in result
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_simplify_sentence_removes_or():
|
|
50
|
+
"""Should handle 'or' conjunction."""
|
|
51
|
+
result = _simplify_sentence("Use option A or option B")
|
|
52
|
+
assert " or " not in result.lower()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_simplify_sentence_removes_subordinate_which():
|
|
56
|
+
"""Should remove subordinate clauses with 'which'."""
|
|
57
|
+
result = _simplify_sentence("Check the file, which is located in /etc")
|
|
58
|
+
assert "which" not in result.lower()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_simplify_sentence_removes_subordinate_that():
|
|
62
|
+
"""Should remove subordinate clauses with 'that'."""
|
|
63
|
+
result = _simplify_sentence("Run the command that verifies config")
|
|
64
|
+
assert "that" not in result.lower()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_simplify_sentence_removes_subordinate_because():
|
|
68
|
+
"""Should remove subordinate clauses with 'because'."""
|
|
69
|
+
result = _simplify_sentence("This fails because the file is missing")
|
|
70
|
+
assert "because" not in result.lower()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_simplify_sentence_adds_period():
|
|
74
|
+
"""Should ensure sentence ends with punctuation."""
|
|
75
|
+
result = _simplify_sentence("Check config")
|
|
76
|
+
assert result.endswith(".")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_simplify_sentence_preserves_existing_punctuation():
|
|
80
|
+
"""Should preserve existing punctuation."""
|
|
81
|
+
result = _simplify_sentence("Check config!")
|
|
82
|
+
assert result.endswith("!")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_normalize_step():
|
|
86
|
+
"""Step normalization should simplify and clean."""
|
|
87
|
+
result = _normalize_step("Check config (optional) and verify")
|
|
88
|
+
assert "(" not in result
|
|
89
|
+
assert ")" not in result
|
|
90
|
+
assert "and" not in result.lower() or "and" == result.lower()[-3:] # Allow if at end
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# Tests for apply_plain_language transform
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@pytest.fixture
|
|
97
|
+
def base_result() -> AssistResult:
|
|
98
|
+
"""Base result with various elements to transform."""
|
|
99
|
+
return AssistResult(
|
|
100
|
+
anchored_id="TEST.ERROR.001",
|
|
101
|
+
confidence="High",
|
|
102
|
+
safest_next_step="Check the config file (see docs), which is located in /etc.",
|
|
103
|
+
plan=[
|
|
104
|
+
"First, verify the config exists and check permissions.",
|
|
105
|
+
"Run the validation command, but be careful.",
|
|
106
|
+
"Check the output, which contains details.",
|
|
107
|
+
"Verify results because this is important.",
|
|
108
|
+
"Final step that completes the process.",
|
|
109
|
+
"Extra step (should be truncated).",
|
|
110
|
+
],
|
|
111
|
+
next_safe_commands=["cmd --dry-run", "cmd2 --check"],
|
|
112
|
+
notes=["Note with (parenthetical) and subordinate clause which explains."],
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def test_apply_plain_language_simplifies_safest_step(base_result: AssistResult):
|
|
117
|
+
"""Plain-language should simplify safest next step."""
|
|
118
|
+
result = apply_plain_language(base_result)
|
|
119
|
+
# Should not have parenthetical
|
|
120
|
+
assert "(" not in result.safest_next_step
|
|
121
|
+
assert ")" not in result.safest_next_step
|
|
122
|
+
# Should not have subordinate clause
|
|
123
|
+
assert "which" not in result.safest_next_step.lower()
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def test_apply_plain_language_simplifies_plan_steps(base_result: AssistResult):
|
|
127
|
+
"""Plain-language should simplify plan steps."""
|
|
128
|
+
result = apply_plain_language(base_result)
|
|
129
|
+
for step in result.plan:
|
|
130
|
+
# Each step should be one clause (no conjunctions in middle)
|
|
131
|
+
# Note: "and" at end is ok due to simplification
|
|
132
|
+
words = step.lower().split()
|
|
133
|
+
if " and " in step.lower():
|
|
134
|
+
# "and" should only appear if it's a remaining fragment
|
|
135
|
+
pass
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def test_apply_plain_language_max_4_steps(base_result: AssistResult):
|
|
139
|
+
"""Plain-language should limit to 4 steps."""
|
|
140
|
+
result = apply_plain_language(base_result)
|
|
141
|
+
assert len(result.plan) <= 4
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def test_apply_plain_language_max_1_command(base_result: AssistResult):
|
|
145
|
+
"""Plain-language should limit to 1 command for simplicity."""
|
|
146
|
+
result = apply_plain_language(base_result)
|
|
147
|
+
assert len(result.next_safe_commands) <= 1
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def test_apply_plain_language_max_2_notes(base_result: AssistResult):
|
|
151
|
+
"""Plain-language should limit to 2 notes."""
|
|
152
|
+
result = apply_plain_language(base_result)
|
|
153
|
+
assert len(result.notes) <= 2
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def test_apply_plain_language_preserves_confidence(base_result: AssistResult):
|
|
157
|
+
"""Plain-language should preserve confidence."""
|
|
158
|
+
result = apply_plain_language(base_result)
|
|
159
|
+
assert result.confidence == base_result.confidence
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def test_apply_plain_language_preserves_id(base_result: AssistResult):
|
|
163
|
+
"""Plain-language should preserve anchored ID."""
|
|
164
|
+
result = apply_plain_language(base_result)
|
|
165
|
+
assert result.anchored_id == base_result.anchored_id
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# Tests for render_plain_language
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def test_render_plain_language_header():
|
|
172
|
+
"""Render should include plain language header."""
|
|
173
|
+
result = AssistResult(
|
|
174
|
+
anchored_id="TEST.001",
|
|
175
|
+
confidence="High",
|
|
176
|
+
safest_next_step="Check config.",
|
|
177
|
+
plan=["Step 1."],
|
|
178
|
+
next_safe_commands=[],
|
|
179
|
+
notes=[],
|
|
180
|
+
)
|
|
181
|
+
output = render_plain_language(result)
|
|
182
|
+
assert "ASSIST (Plain Language)" in output
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def test_render_plain_language_labels():
|
|
186
|
+
"""Render should use explicit labels."""
|
|
187
|
+
result = AssistResult(
|
|
188
|
+
anchored_id="TEST.001",
|
|
189
|
+
confidence="High",
|
|
190
|
+
safest_next_step="Check config.",
|
|
191
|
+
plan=["Step 1.", "Step 2."],
|
|
192
|
+
next_safe_commands=["cmd --dry-run"],
|
|
193
|
+
notes=[],
|
|
194
|
+
)
|
|
195
|
+
output = render_plain_language(result)
|
|
196
|
+
assert "ID:" in output
|
|
197
|
+
assert "Confidence:" in output
|
|
198
|
+
assert "What to do next:" in output
|
|
199
|
+
assert "Steps:" in output
|
|
200
|
+
assert "Safe command:" in output
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def test_render_plain_language_numeric_steps():
|
|
204
|
+
"""Render should use numeric step labels."""
|
|
205
|
+
result = AssistResult(
|
|
206
|
+
anchored_id="TEST.001",
|
|
207
|
+
confidence="High",
|
|
208
|
+
safest_next_step="Check config.",
|
|
209
|
+
plan=["First step.", "Second step.", "Third step."],
|
|
210
|
+
next_safe_commands=[],
|
|
211
|
+
notes=[],
|
|
212
|
+
)
|
|
213
|
+
output = render_plain_language(result)
|
|
214
|
+
assert "1." in output
|
|
215
|
+
assert "2." in output
|
|
216
|
+
assert "3." in output
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def test_render_plain_language_no_command_on_low():
|
|
220
|
+
"""Render should not show command on Low confidence."""
|
|
221
|
+
result = AssistResult(
|
|
222
|
+
anchored_id=None,
|
|
223
|
+
confidence="Low",
|
|
224
|
+
safest_next_step="Check config.",
|
|
225
|
+
plan=["Step 1."],
|
|
226
|
+
next_safe_commands=["cmd --dry-run"],
|
|
227
|
+
notes=[],
|
|
228
|
+
)
|
|
229
|
+
output = render_plain_language(result)
|
|
230
|
+
assert "Safe command:" not in output
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def test_render_plain_language_shows_command_on_high():
|
|
234
|
+
"""Render should show command on High confidence."""
|
|
235
|
+
result = AssistResult(
|
|
236
|
+
anchored_id="TEST.001",
|
|
237
|
+
confidence="High",
|
|
238
|
+
safest_next_step="Check config.",
|
|
239
|
+
plan=["Step 1."],
|
|
240
|
+
next_safe_commands=["cmd --dry-run"],
|
|
241
|
+
notes=[],
|
|
242
|
+
)
|
|
243
|
+
output = render_plain_language(result)
|
|
244
|
+
assert "Safe command:" in output
|
|
245
|
+
assert "cmd --dry-run" in output
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def test_render_plain_language_omits_notes():
|
|
249
|
+
"""Render should omit notes for simplicity."""
|
|
250
|
+
result = AssistResult(
|
|
251
|
+
anchored_id="TEST.001",
|
|
252
|
+
confidence="High",
|
|
253
|
+
safest_next_step="Check config.",
|
|
254
|
+
plan=["Step 1."],
|
|
255
|
+
next_safe_commands=[],
|
|
256
|
+
notes=["A note that should not appear."],
|
|
257
|
+
)
|
|
258
|
+
output = render_plain_language(result)
|
|
259
|
+
# Notes section omitted in plain-language for simplicity
|
|
260
|
+
assert "Notes:" not in output
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
# Integration tests
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def test_plain_language_full_pipeline():
|
|
267
|
+
"""Full pipeline test for plain-language profile."""
|
|
268
|
+
base = AssistResult(
|
|
269
|
+
anchored_id="DEPLOY.CONFIG.MISSING",
|
|
270
|
+
confidence="High",
|
|
271
|
+
safest_next_step="Check the config file (in /etc), which contains settings and run verify.",
|
|
272
|
+
plan=[
|
|
273
|
+
"Verify the config exists and check permissions.",
|
|
274
|
+
"Run: deploy check --dry-run",
|
|
275
|
+
"Review output, which shows results.",
|
|
276
|
+
],
|
|
277
|
+
next_safe_commands=["deploy check --dry-run"],
|
|
278
|
+
notes=["Important note."],
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
transformed = apply_plain_language(base)
|
|
282
|
+
output = render_plain_language(transformed)
|
|
283
|
+
|
|
284
|
+
# Header present
|
|
285
|
+
assert "ASSIST (Plain Language)" in output
|
|
286
|
+
|
|
287
|
+
# ID preserved
|
|
288
|
+
assert "DEPLOY.CONFIG.MISSING" in output
|
|
289
|
+
|
|
290
|
+
# Confidence preserved
|
|
291
|
+
assert "Confidence: High" in output
|
|
292
|
+
|
|
293
|
+
# Simplified step label
|
|
294
|
+
assert "What to do next:" in output
|
|
295
|
+
|
|
296
|
+
# Numeric steps
|
|
297
|
+
assert "1." in output
|
|
298
|
+
|
|
299
|
+
# Command shown
|
|
300
|
+
assert "deploy check --dry-run" in output
|
|
301
|
+
|
|
302
|
+
# No notes section (omitted for simplicity)
|
|
303
|
+
assert "Notes:" not in output
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def test_plain_language_low_confidence():
|
|
307
|
+
"""Plain-language profile with Low confidence."""
|
|
308
|
+
base = AssistResult(
|
|
309
|
+
anchored_id=None,
|
|
310
|
+
confidence="Low",
|
|
311
|
+
safest_next_step="Review the output.",
|
|
312
|
+
plan=["Step 1.", "Step 2."],
|
|
313
|
+
next_safe_commands=["cmd --dry-run"],
|
|
314
|
+
notes=["No ID found."],
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
transformed = apply_plain_language(base)
|
|
318
|
+
output = render_plain_language(transformed)
|
|
319
|
+
|
|
320
|
+
# Confidence preserved
|
|
321
|
+
assert "Confidence: Low" in output
|
|
322
|
+
|
|
323
|
+
# No command on Low confidence
|
|
324
|
+
assert "Safe command:" not in output
|
|
325
|
+
|
|
326
|
+
# ID shows as none
|
|
327
|
+
assert "ID: none" in output
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def test_plain_language_complex_sentence():
|
|
331
|
+
"""Plain-language should simplify complex sentences."""
|
|
332
|
+
base = AssistResult(
|
|
333
|
+
anchored_id="TEST.001",
|
|
334
|
+
confidence="High",
|
|
335
|
+
safest_next_step="First check the config, and then verify the settings, but be careful because errors may occur.",
|
|
336
|
+
plan=[
|
|
337
|
+
"Run the command which validates, and also checks permissions.",
|
|
338
|
+
],
|
|
339
|
+
next_safe_commands=[],
|
|
340
|
+
notes=[],
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
transformed = apply_plain_language(base)
|
|
344
|
+
|
|
345
|
+
# Safest step should be simplified
|
|
346
|
+
assert "and then" not in transformed.safest_next_step.lower()
|
|
347
|
+
assert "but" not in transformed.safest_next_step.lower()
|
|
348
|
+
assert "because" not in transformed.safest_next_step.lower()
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Tests for render output."""
|
|
2
|
+
|
|
3
|
+
from a11y_assist.render import AssistResult, render_assist
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestRenderAssist:
|
|
7
|
+
"""Tests for rendering AssistResult to text."""
|
|
8
|
+
|
|
9
|
+
def test_render_includes_header(self):
|
|
10
|
+
"""Output includes ASSIST (Low Vision) header."""
|
|
11
|
+
result = AssistResult(
|
|
12
|
+
anchored_id="TEST.ID",
|
|
13
|
+
confidence="High",
|
|
14
|
+
safest_next_step="Do the thing.",
|
|
15
|
+
plan=["Step 1", "Step 2"],
|
|
16
|
+
next_safe_commands=[],
|
|
17
|
+
notes=[],
|
|
18
|
+
)
|
|
19
|
+
output = render_assist(result)
|
|
20
|
+
assert "ASSIST (Low Vision):" in output
|
|
21
|
+
|
|
22
|
+
def test_render_includes_anchored_id(self):
|
|
23
|
+
"""Output includes anchored ID."""
|
|
24
|
+
result = AssistResult(
|
|
25
|
+
anchored_id="PAY.EXPORT.AUTH",
|
|
26
|
+
confidence="High",
|
|
27
|
+
safest_next_step="Check credentials.",
|
|
28
|
+
plan=["Step 1"],
|
|
29
|
+
next_safe_commands=[],
|
|
30
|
+
notes=[],
|
|
31
|
+
)
|
|
32
|
+
output = render_assist(result)
|
|
33
|
+
assert "Anchored to: PAY.EXPORT.AUTH" in output
|
|
34
|
+
|
|
35
|
+
def test_render_no_id_shows_none(self):
|
|
36
|
+
"""Output shows (none) when no anchored ID."""
|
|
37
|
+
result = AssistResult(
|
|
38
|
+
anchored_id=None,
|
|
39
|
+
confidence="Low",
|
|
40
|
+
safest_next_step="Try again.",
|
|
41
|
+
plan=["Step 1"],
|
|
42
|
+
next_safe_commands=[],
|
|
43
|
+
notes=[],
|
|
44
|
+
)
|
|
45
|
+
output = render_assist(result)
|
|
46
|
+
assert "Anchored to: (none)" in output
|
|
47
|
+
|
|
48
|
+
def test_render_includes_confidence(self):
|
|
49
|
+
"""Output includes confidence level."""
|
|
50
|
+
result = AssistResult(
|
|
51
|
+
anchored_id=None,
|
|
52
|
+
confidence="Medium",
|
|
53
|
+
safest_next_step="Do something.",
|
|
54
|
+
plan=["Step 1"],
|
|
55
|
+
next_safe_commands=[],
|
|
56
|
+
notes=[],
|
|
57
|
+
)
|
|
58
|
+
output = render_assist(result)
|
|
59
|
+
assert "Confidence: Medium" in output
|
|
60
|
+
|
|
61
|
+
def test_render_includes_plan(self):
|
|
62
|
+
"""Output includes numbered plan steps."""
|
|
63
|
+
result = AssistResult(
|
|
64
|
+
anchored_id=None,
|
|
65
|
+
confidence="High",
|
|
66
|
+
safest_next_step="Start here.",
|
|
67
|
+
plan=["First step", "Second step", "Third step"],
|
|
68
|
+
next_safe_commands=[],
|
|
69
|
+
notes=[],
|
|
70
|
+
)
|
|
71
|
+
output = render_assist(result)
|
|
72
|
+
assert "1) First step" in output
|
|
73
|
+
assert "2) Second step" in output
|
|
74
|
+
assert "3) Third step" in output
|
|
75
|
+
|
|
76
|
+
def test_render_limits_plan_to_five(self):
|
|
77
|
+
"""Plan is limited to 5 steps with ellipsis."""
|
|
78
|
+
result = AssistResult(
|
|
79
|
+
anchored_id=None,
|
|
80
|
+
confidence="High",
|
|
81
|
+
safest_next_step="Start here.",
|
|
82
|
+
plan=["Step 1", "Step 2", "Step 3", "Step 4", "Step 5", "Step 6"],
|
|
83
|
+
next_safe_commands=[],
|
|
84
|
+
notes=[],
|
|
85
|
+
)
|
|
86
|
+
output = render_assist(result)
|
|
87
|
+
assert "5) Step 5" in output
|
|
88
|
+
assert "..." in output
|
|
89
|
+
assert "6)" not in output
|
|
90
|
+
|
|
91
|
+
def test_render_includes_safe_commands(self):
|
|
92
|
+
"""Output includes SAFE commands section."""
|
|
93
|
+
result = AssistResult(
|
|
94
|
+
anchored_id=None,
|
|
95
|
+
confidence="High",
|
|
96
|
+
safest_next_step="Check first.",
|
|
97
|
+
plan=["Step 1"],
|
|
98
|
+
next_safe_commands=["tool --dry-run", "tool --validate"],
|
|
99
|
+
notes=[],
|
|
100
|
+
)
|
|
101
|
+
output = render_assist(result)
|
|
102
|
+
assert "Next (SAFE):" in output
|
|
103
|
+
assert "tool --dry-run" in output
|
|
104
|
+
|
|
105
|
+
def test_render_includes_notes(self):
|
|
106
|
+
"""Output includes notes section."""
|
|
107
|
+
result = AssistResult(
|
|
108
|
+
anchored_id=None,
|
|
109
|
+
confidence="High",
|
|
110
|
+
safest_next_step="Do it.",
|
|
111
|
+
plan=["Step 1"],
|
|
112
|
+
next_safe_commands=[],
|
|
113
|
+
notes=["Note one", "Note two"],
|
|
114
|
+
)
|
|
115
|
+
output = render_assist(result)
|
|
116
|
+
assert "Notes:" in output
|
|
117
|
+
assert "- Note one" in output
|