Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions docs/design/0256-restore-a11y.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,20 @@ count at exactly 12.

## Testing

Verified end-to-end via CLI: `codelens audit tests --check a11y` → 2 real
findings (missing_label high + semantic_html low) from `tests/fixtures/sample.html`.
`--severity high` passthrough confirmed. `tests/test_command_registry.py`
2 passed.
`tests/test_a11y_command.py` (6 tests): wrapper delegation,
severity/category passthrough, audit umbrella dispatch of `a11y`, category
reaching the engine through the synthetic namespace, and a regression guard
that `css` (#251) and `a11y` coexist independently (neither shadowing the
other in `_CHECKS`).

Verified end-to-end on the real Coretax `smart-tax-assistance` workspace
(not just a fixture): `audit . --check a11y` → **165 findings** across
semantic_html (94), link_text (35), missing_label (24), keyboard_nav (5),
missing_alt (4), color_contrast (3). `--category missing_alt` correctly
narrows to 4, confirming passthrough reaches the engine.

The engine was confirmed working *before* wiring (per the issue's
constraint), so this PR is pure entry-point restoration.

## Alternatives Considered

Expand Down
86 changes: 86 additions & 0 deletions tests/test_a11y_command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# @WHO: tests/test_a11y_command.py
# @WHAT: Tests for restored a11y as audit --check a11y (issue #256)
# @PART: tests
"""Tests for the a11y sub-check of the audit umbrella (issue #256).

a11y_engine was orphaned in the #195 consolidation (its command entry
point was deleted, the engine kept) — the same situation as css-deep
(#251) and export-snapshot (#218). This restores access as
`audit --check a11y` — a sub-check, NOT a new top-level command.
"""

import argparse
import os
import sys
from unittest import mock

import pytest

SCRIPT_DIR = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "scripts"
)
if SCRIPT_DIR not in sys.path:
sys.path.insert(0, SCRIPT_DIR)

from commands import a11y # noqa: E402
from commands import audit # noqa: E402


class TestA11yCommand:
def test_execute_delegates_to_engine(self):
args = argparse.Namespace(workspace=".", severity=None, category=None)
with mock.patch(
"commands.a11y.audit_accessibility",
return_value={"status": "ok", "stats": {"total_issues": 3}},
) as mock_engine:
result = a11y.execute(args, ".")
mock_engine.assert_called_once_with(".", category=None, severity=None)
assert result["status"] == "ok"

def test_severity_and_category_passthrough(self):
args = argparse.Namespace(workspace=".", severity="high", category="missing_alt")
with mock.patch(
"commands.a11y.audit_accessibility",
return_value={"status": "ok"},
) as mock_engine:
a11y.execute(args, ".")
mock_engine.assert_called_once_with(".", category="missing_alt", severity="high")


class TestAuditDispatchesA11y:
def test_a11y_is_registered_check(self):
assert "a11y" in audit.ALL_CHECKS

def test_audit_check_a11y_routes_to_engine(self):
base = argparse.Namespace(
workspace=".", check="a11y", severity=None, category=None,
)
with mock.patch(
"commands.a11y.audit_accessibility",
return_value={"status": "ok", "stats": {"total_issues": 9}},
):
result = audit.execute(base, ".")
assert result["s"] == "ok"
assert result["st"]["checks_run"] == 1
assert result["r"][0]["_check"] == "a11y"
assert result["r"][0]["stats"]["total_issues"] == 9

def test_audit_check_a11y_category_reaches_engine(self):
base = argparse.Namespace(
workspace=".", check="a11y", severity=None, category="missing_label",
)
with mock.patch(
"commands.a11y.audit_accessibility",
return_value={"status": "ok"},
) as mock_engine:
audit.execute(base, ".")
_, kwargs = mock_engine.call_args
assert kwargs.get("category") == "missing_label"

def test_css_and_a11y_both_registered_independently(self):
"""Regression guard: a11y was added alongside the existing css
sub-check (#251) — both must coexist, neither shadowing the other."""
assert "css" in audit.ALL_CHECKS
assert "a11y" in audit.ALL_CHECKS
assert audit._CHECKS["a11y"]["module"] == "commands.a11y"
assert audit._CHECKS["css"]["module"] == "commands.css_deep"
Loading