Skip to content

Playwright E2E

How to add browser-based E2E tests with Playwright.


Prerequisites

uv add playwright --group test
uv run playwright install chromium

Auth fixtures

Playwright fixtures are app-specific because auth storage varies (localStorage, cookies, etc.). Add these to your e2e/conftest.py after the bluefox_e2e_setup() call:

e2e/conftest.py
import pytest
from pathlib import Path
from bluefox_test.e2e import bluefox_e2e_setup

globals().update(bluefox_e2e_setup(
    login_endpoint="/auth/login",
    login_payload={"email": "e2e@test.com", "password": "testpassword123"},
))

PLAYWRIGHT_STATE = Path("e2e/.auth/playwright_state.json")


@pytest.fixture(scope="session")
def browser_auth_state(server, auth_token):
    """Save auth state so Playwright tests start logged in."""
    from playwright.sync_api import sync_playwright

    PLAYWRIGHT_STATE.parent.mkdir(exist_ok=True)

    with sync_playwright() as p:
        browser = p.chromium.launch()
        context = browser.new_context()
        page = context.new_page()
        page.goto(f"{server}/login")

        # Adjust to match your app's auth storage:
        page.evaluate(f"""() => {{
            localStorage.setItem('access_token', '{auth_token}');
        }}""")

        context.storage_state(path=str(PLAYWRIGHT_STATE))
        browser.close()

    return str(PLAYWRIGHT_STATE)


@pytest.fixture
def page(server, browser_auth_state):
    """Authenticated Playwright page."""
    from playwright.sync_api import sync_playwright

    with sync_playwright() as p:
        browser = p.chromium.launch()
        context = browser.new_context(storage_state=browser_auth_state)
        pg = context.new_page()
        pg.goto(server)
        yield pg
        browser.close()

Writing browser tests

e2e/test_dashboard.py
import pytest

pytestmark = pytest.mark.e2e


def test_dashboard__shows_username(page):
    page.goto("/dashboard")
    assert page.locator("[data-testid='username']").text_content() == "E2E User"


def test_create_todo__via_ui(page):
    page.goto("/todos")
    page.fill("[data-testid='todo-title']", "Buy groceries")
    page.click("[data-testid='submit-todo']")
    assert page.locator("[data-testid='todo-item']").first.text_content() == "Buy groceries"

CI setup

Add Playwright to your CI workflow:

.github/workflows/test.yml
- run: |
    uv sync --group test
    uv run playwright install chromium
- run: make e2e

Tip

Add e2e/.auth/ to your .gitignore to avoid committing cached auth state.