| # Testing Guide β Color-UX-Access |
|
|
| > HF Space containers cannot run a browser. Playwright is used locally for |
| > screenshots but tests are source-inspection only β no live browser in CI. |
|
|
| For test setup (venv, uv install), see `docs/DEVELOPMENT.md`. |
|
|
| --- |
|
|
| ## Testing Philosophy |
|
|
| Tests use a layered approach, from fast to slow: |
|
|
| | Category | Marker | What it tests | Duration | |
| |----------|--------|---------------|----------| |
| | Smoke | `smoke` | Import, module load, CVD config, Gradio apps loadable | <10s | |
| | Pipeline | `pipeline` | Full imageβCVDβWCAG report with mocked VLM | <30s | |
| | Slow | `slow` | Real VLM calls, needs HF_TOKEN | 60-90s | |
| |
| **Source inspection tests** verify structural properties that mocks miss β e.g., `isinstance(file_obj, bytes)` check for GradioFile binary type, Modal app GPU config, Gradio 6 theme placement in `launch()` not `Blocks()`. |
|
|
| --- |
|
|
| ## Running Tests |
|
|
| ```bash |
| uv run pytest -v # all tests (smoke + pipeline) β no network, no GPU |
| uv run pytest -m smoke # fast: imports + smoke only (<10s) |
| uv run pytest -m pipeline # full pipeline with mocked VLM (<30s) |
| uv run pytest tests/test_app_space.py -v # single file |
| uv run pytest tests/test_app_space.py::test_cvd_gallery_generates_10_types -v # single test |
| ``` |
|
|
| --- |
|
|
| ## Test Structure |
|
|
| ``` |
| tests/ |
| βββ conftest.py Shared fixtures (img_factory, cvd_img_factory, mock_vlm_factory) |
| βββ fixtures/ Synthetic UI screenshots β generated by generate_fixtures.py |
| β βββ button_panel_accessible.png |
| β βββ button_panel_color_only.png |
| β βββ status_badges_accessible.png |
| β βββ status_badges_color_only.png |
| β βββ form_validation_accessible.png |
| β βββ form_validation_color_only.png |
| β βββ *.png Plus deuteranopia/protanopia/tritanopia variants per original |
| βββ test_smoke.py All core imports work, CVD variants correct, Gradio apps loadable |
| βββ test_capture.py capture module contract: take_screenshot exists + correct signature |
| βββ test_app_space.py Integration: CVD gallery, WCAG report, Gradio 6 theme launch |
| βββ test_gradio_binary.py Source inspection: Gradio File binary type handling |
| βββ test_modal_app.py Source inspection: Modal app structure (GPU, secrets, base URL) |
| ``` |
|
|
| --- |
|
|
| ## Shared Fixtures (from `conftest.py`) |
|
|
| All fixtures are auto-discovered by pytest β import them directly in tests. |
|
|
| ### `img_factory` |
| |
| ```python |
| def test_something(img_factory): |
| img = img_factory(640, 480, seed=42) # deterministic random PIL Image |
| ``` |
| |
| ### `cvd_img_factory` |
| |
| ```python |
| def test_cvd_simulation(cvd_img_factory): |
| img, simulate = cvd_img_factory(200, 100, seed=42) |
| deut = simulate(img, 'deuteranopia') |
| assert deut.size == img.size |
| ``` |
| |
| ### `mock_vlm_factory` |
|
|
| ```python |
| def test_report_passes(mock_vlm_factory): |
| result = mock_vlm_factory(passes=True) |
| assert result['passes'] is True |
| assert result['findings'] == [] |
| ``` |
|
|
| ### `sample_button_panel` |
|
|
| ```python |
| def test_cvd_confuses_buttons(sample_button_panel, cvd_img_factory): |
| img = sample_button_panel |
| _, simulate = cvd_img_factory() |
| deut = simulate(img, 'deuteranopia') |
| # Green (Confirm) and red (Cancel) buttons β visually similar for CVD |
| ``` |
|
|
| ### `ten_type_gallery` |
|
|
| ```python |
| def test_gallery_types(ten_type_gallery): |
| assert len(ten_type_gallery) == 7 |
| ``` |
|
|
| --- |
|
|
| ## TDD Pattern: RED β GREEN β REFACTOR |
|
|
| ```bash |
| # 1. Write a failing test |
| uv run pytest tests/test_capture.py::test_take_screenshot_signature -v |
| # β RED if not yet implemented |
| |
| # 2. Implement the feature |
| # create color_ux_access/capture.py with take_screenshot(page, url, timeout=60000) |
| |
| # 3. Run again β verify GREEN |
| uv run pytest tests/test_capture.py -v |
| |
| # 4. Refactor if needed |
| ``` |
|
|
| For new features: |
| ```python |
| # tests/test_new_feature.py |
| def test_feature_does_x(): |
| from color_ux_access.module import function |
| result = function(input) |
| assert expected_output in result |
| ``` |
|
|
| --- |
|
|
| ## Source Inspection Tests |
|
|
| Tests that verify source code structure rather than runtime behavior: |
|
|
| ```python |
| # tests/test_gradio_binary.py β verify GradioFile bytes handling |
| def test_analyze_screenshot_handles_bytes(): |
| source = pathlib.Path("color_ux_access/modal_app.py").read_text() |
| assert "isinstance(file_obj, bytes)" in source |
| |
| # tests/test_modal_app.py β verify Modal app structure |
| def test_base_url_correct(): |
| source = pathlib.Path("color_ux_access/modal_app.py").read_text() |
| assert "router.huggingface.co/v1" in source |
| ``` |
|
|
| --- |
|
|
| ## CI Integration |
|
|
| No CI configured yet. For now, run `uv run pytest` before every commit locally. |
|
|
| ```yaml |
| # .github/workflows/test.yml (future) |
| name: Test |
| on: [push, pull_request] |
| jobs: |
| test: |
| runs-on: ubuntu-latest |
| steps: |
| - uses: actions/checkout@v4 |
| - uses: astral-sh/setup-uv@v4 |
| with: |
| python-version: "3.12" |
| - run: uv sync --extra dev |
| - run: uv run pytest -m smoke |
| ``` |
|
|
| --- |
|
|
| ## Fixture Generation |
|
|
| Generate synthetic UI screenshots for local testing: |
|
|
| ```bash |
| PYTHONPATH=. python tests/generate_fixtures.py |
| ``` |
|
|
| Each original image gets CVD-simulated variants (deuteranopia, protanopia, tritanopia). Fixture pairs: |
|
|
| - `button_panel_color_only.png` vs `button_panel_accessible.png` β CVD-confusable buttons |
| - `status_badges_color_only.png` vs `status_badges_accessible.png` β CVD-invisible distinctions |
| - `form_validation_color_only.png` vs `form_validation_accessible.png` β CVD-invisible error states |