112 lines
4.1 KiB
Markdown
112 lines
4.1 KiB
Markdown
# 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/<test-name>/`:
|
|
|
|
- `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\<test-name>\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.
|