# End-to-end browser tests The E2E suite uses Python Playwright, Chromium, `pytest-django`, and Django's transactional `live_server`. It is intentionally separate from fast unit and view tests. ## Structure ```text tests/e2e/ ├── config.py # environment-driven browser settings ├── conftest.py # browser lifecycle, isolated contexts, failure artifacts ├── pages/ # reusable Page Objects and stable UI actions │ ├── base.py │ ├── auth.py │ └── tickets.py ├── test_chat_coach.py # user-visible workflow examples └── test_pwa_versioning.py ``` Keep business setup in the test/database layer and browser interactions in Page Objects. Tests should describe user outcomes, not repeat selectors or login steps. ## Installation ```powershell .\.venv\Scripts\python.exe -m pip install -r requirements-dev.txt .\.venv\Scripts\python.exe -m playwright install chromium ``` ## Running tests ```powershell # All browser tests .\.venv\Scripts\python.exe -m pytest tests\e2e -m e2e -q # One scenario .\.venv\Scripts\python.exe -m pytest tests\e2e\test_chat_coach.py::test_customer_sees_chat_guidance_after_two_rapid_messages -q # Only service-worker/PWA scenarios .\.venv\Scripts\python.exe -m pytest tests\e2e -m pwa -q # Visible browser with slowed interactions for debugging $env:E2E_HEADLESS="false" $env:E2E_SLOW_MO="250" .\.venv\Scripts\python.exe -m pytest tests\e2e -m e2e -s ``` Supported settings are `E2E_HEADLESS`, `E2E_SLOW_MO`, `E2E_ACTION_TIMEOUT_MS`, `E2E_NAVIGATION_TIMEOUT_MS`, `E2E_VIEWPORT_WIDTH`, `E2E_VIEWPORT_HEIGHT`, and `E2E_ARTIFACT_DIR`. ### QA / staging / live smoke checks The destructive workflow suite always uses Django's isolated `live_server`. A separate, read-only smoke layer can target a deployed environment without modifying real tickets: ```powershell $env:E2E_BASE_URL="https://qa.example.com" $env:E2E_ENVIRONMENT="qa" $env:E2E_CUSTOMER_MOBILE="0912..." $env:E2E_CUSTOMER_PASSWORD="..." $env:E2E_STAFF_USERNAME="qa-staff" $env:E2E_STAFF_PASSWORD="..." .\.venv\Scripts\python.exe -m pytest tests\e2e\test_external_environment_smoke.py -m external_e2e -q ``` `BASE_URL` is accepted as an alias for `E2E_BASE_URL`. Missing URLs or credentials are reported as skipped tests, never as a successful external run. Use only controlled accounts. ## Failure diagnostics Every test receives a fresh Chromium process and BrowserContext. This keeps Django's synchronous database lifecycle isolated from Playwright's event loop and prevents cross-test browser state. On failure, the fixture automatically saves these files under `test-results/e2e//`: - `failure.png`: full-page screenshot at failure time; - `trace.zip`: DOM snapshots, screenshots, network timing and browser actions. Open a trace with: ```powershell .\.venv\Scripts\python.exe -m playwright show-trace test-results\e2e\\trace.zip ``` JavaScript page errors also fail the test and produce the same artifacts. Ordinary UI tests block service workers. A PWA test must use `browser_page(service_workers="allow")` and the `pwa` marker. ## Adding a scenario 1. Mark the module with `e2e` and `django_db(transaction=True)`. 2. Create only the minimum database state required by the scenario. 3. Open an isolated page using `with browser_page() as page:`. 4. Use an existing Page Object; add reusable selectors/actions there if needed. 5. Assert a user-visible outcome with Playwright's auto-waiting `expect` API. 6. Avoid `sleep`; wait on URLs, elements, responses, or application state. Minimal pattern: ```python pytestmark = [pytest.mark.e2e, pytest.mark.django_db(transaction=True)] def test_example(live_server, browser_page, customer): with browser_page() as page: AuthenticationPage(page, live_server.url).login_customer(customer, PASSWORD) tickets = TicketConversationPage(page, live_server.url) tickets.goto("/tickets/") expect(page.get_by_role("heading", name="درخواست‌های من")).to_be_visible() ``` The fixture prefers Playwright's bundled Chromium and falls back to installed Google Chrome.