From 322fc2050f06f509836c201b87e4614e95505af7 Mon Sep 17 00:00:00 2001 From: Kawa Date: Mon, 13 Apr 2026 10:57:07 +0200 Subject: [PATCH] test(09-02): add Playwright UX-02 port autofill test - Create tests/e2e/test_port_autofill.py - headless chromium test for PRNT-03 - Verifies IP->port_name auto-derivation via Alpine @input handler at /printers - Update 09-VALIDATION.md: mark UX-02 tasks green, cite test file as evidence --- .../09-ux-tech-debt-closure/09-VALIDATION.md | 10 +++---- tests/e2e/test_port_autofill.py | 29 +++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 tests/e2e/test_port_autofill.py diff --git a/.planning/phases/09-ux-tech-debt-closure/09-VALIDATION.md b/.planning/phases/09-ux-tech-debt-closure/09-VALIDATION.md index 57ddcf1..ffeb8f9 100644 --- a/.planning/phases/09-ux-tech-debt-closure/09-VALIDATION.md +++ b/.planning/phases/09-ux-tech-debt-closure/09-VALIDATION.md @@ -45,8 +45,8 @@ Task IDs follow `{phase}-{plan}-{task}` where task numbers match the `` or | 09-01-01 | 01 (driver-upload-fix-and-inline-oob) | 1 | UX-01 | integration (TDD red) | `pytest tests/test_driver_upload.py::test_upload_500_regression tests/test_driver_upload.py::test_upload_returns_oob_when_called_from_form tests/test_driver_upload.py::test_upload_oob_autoselects_new_driver -x` | ✅ (file) / ❌ (test fns Wave 0) | ⬜ pending | | 09-01-02 | 01 | 1 | UX-01 | integration (TDD green) | `pytest tests/test_driver_upload.py -x -v && pytest tests/ -x -q --ignore=tests/e2e` | ✅ | ⬜ pending | | 09-01-03 | 01 | 1 | UX-01 | integration | `pytest tests/test_printer_form.py -x -v && pytest tests/ -x -q --ignore=tests/e2e` | ✅ | ⬜ pending | -| 09-02-01 | 02 (playwright-port-autofill) | 2 | UX-02 | setup | `python -c "import pytest_playwright, playwright; print('ok')" && pytest --collect-only tests/e2e/` | ❌ W0 (tests/e2e/) | ⬜ pending | -| 09-02-02 | 02 | 2 | UX-02 | e2e (Playwright) | `pytest tests/e2e/test_port_autofill.py -v` | ❌ W0 | ⬜ pending | +| 09-02-01 | 02 (playwright-port-autofill) | 2 | UX-02 | setup | `python -c "import pytest_playwright, playwright; print('ok')" && pytest --collect-only tests/e2e/` | ✅ tests/e2e/__init__.py + tests/e2e/conftest.py | ✅ green | +| 09-02-02 | 02 | 2 | UX-02 | e2e (Playwright) | `pytest tests/e2e/test_port_autofill.py -v` | ✅ tests/e2e/test_port_autofill.py | ✅ green | | 09-03-01 | 03 (script-download-links) | 1 | UX-03 | integration (TDD red) | `pytest tests/test_script_download.py tests/test_packages.py::TestCommandPreview::test_detail_page_shows_script_links -x` | ❌ W0 (test_script_download.py) / ✅ (test_packages.py) | ⬜ pending | | 09-03-02 | 03 | 1 | UX-03 | integration (TDD green) | `pytest tests/test_script_download.py tests/test_packages.py::TestCommandPreview -x -v && pytest tests/ -x -q --ignore=tests/e2e` | ✅ | ⬜ pending | @@ -59,11 +59,11 @@ Task IDs follow `{phase}-{plan}-{task}` where task numbers match the `` or ## Wave 0 Requirements - [ ] `tests/test_driver_upload.py` — add three new test functions: `test_upload_500_regression`, `test_upload_returns_oob_when_called_from_form`, `test_upload_oob_autoselects_new_driver` (+ optional `test_upload_no_oob_from_standalone_drivers_page`) — **Plan 09-01 Task 1** -- [ ] `tests/e2e/__init__.py` + `tests/e2e/conftest.py` — create e2e package with session-scoped `live_server` fixture (uvicorn thread, free port, /health readiness poll, tmp data dir) — **Plan 09-02 Task 1** -- [ ] `tests/e2e/test_port_autofill.py` — UX-02 Playwright test — **Plan 09-02 Task 2** +- [x] `tests/e2e/__init__.py` + `tests/e2e/conftest.py` — create e2e package with session-scoped `live_server` fixture (uvicorn thread, free port, /health readiness poll, tmp data dir) — **Plan 09-02 Task 1** (commit 4e9bd9b) +- [x] `tests/e2e/test_port_autofill.py` — UX-02 Playwright test — **Plan 09-02 Task 2** (evidence: `pytest tests/e2e/test_port_autofill.py -v` → 1 passed) - [ ] `tests/test_script_download.py` — new file with 5 tests covering .ps1 routes — **Plan 09-03 Task 1** - [ ] `tests/test_packages.py::TestCommandPreview::test_detail_page_shows_script_links` — new assertion — **Plan 09-03 Task 1** -- [ ] Dev deps: add `pytest-playwright` and `playwright` to `requirements-dev.txt`; run `pip install -r requirements-dev.txt && playwright install chromium` — **Plan 09-02 Task 1** +- [x] Dev deps: add `pytest-playwright` and `playwright` to `requirements-dev.txt`; run `pip install -r requirements-dev.txt && playwright install chromium` — **Plan 09-02 Task 1** (commit 4e9bd9b) --- diff --git a/tests/e2e/test_port_autofill.py b/tests/e2e/test_port_autofill.py new file mode 100644 index 0000000..e4f74eb --- /dev/null +++ b/tests/e2e/test_port_autofill.py @@ -0,0 +1,29 @@ +"""UX-02: live-browser verification of PRNT-03 Alpine IP->port auto-derivation.""" +from __future__ import annotations + +import pytest + + +def test_port_autofill(page, live_server: str) -> None: + """Typing an IP address into the printer form auto-populates port_name. + + Evidence for UX-02: tests/e2e/test_port_autofill.py + Closes: PRNT-03 Alpine.js port auto-derivation requirement. + """ + # /printers renders a full page (extends base.html) with Alpine.js loaded + # and the printer form embedded via {% include "partials/printer_form.html" %} + page.goto(f"{live_server}/printers", wait_until="domcontentloaded") + + # Wait for Alpine to initialise (x-data hydration on the form wrapper) + page.wait_for_selector("input[name='ip_address']") + + # Fill triggers the 'input' event that Alpine @input listens to + page.fill("input[name='ip_address']", "192.168.1.100") + + # Alpine @input reacts synchronously; wait_for_function keeps the test stable + page.wait_for_function( + "document.querySelector(\"input[name='port_name']\").value === 'IP_192_168_1_100'", + timeout=2000, + ) + + assert page.input_value("input[name='port_name']") == "IP_192_168_1_100"