Reference Files
Before writing tests, read the relevant reference files for accurate API usage:
references/locators.md- Locator strategies (get_by_role, get_by_text, get_by_label, etc.), filtering, chaining, listsreferences/actions-input.md- Click, fill, type, select, check, upload, drag, scroll, keyboardreferences/assertions.md- expect() assertions, locator/page/response assertions, timeoutsreferences/network-mocking.md- Network monitoring, request interception, API mocking, HAR replay, WebSocket mockingreferences/api-testing.md- APIRequestContext, server-side testing, fixtures for API testsreferences/browser-contexts-pages.md- Browser launch, contexts, pages, frames, dialogs, downloads, isolationreferences/auth-emulation.md- Authentication patterns, storage state, device emulation, viewport, geolocation, localereferences/page-object-model.md- POM pattern with sync/async examplesreferences/debugging-tooling.md- PWDEBUG, Inspector, trace viewer, codegen, screenshots, videos, ARIA snapshotsreferences/clock.md- Clock API for time manipulation in testsreferences/ci-docker.md- CI configs (GitHub Actions, GitLab, Jenkins, Azure), Docker setupreferences/api-classes.md- Low-level APIs (Keyboard, Mouse, Touchscreen), Page utility methods, Locator data extraction, BrowserContext cookies/timeouts, Request/Response properties, Route details, ConsoleMessage, FileChooser, BrowserType launch options, Workers
Core Principles
1. Use pytest-playwright
pip install pytest-playwright
playwright install
Tests use the page fixture automatically:
from playwright.sync_api import Page, expect
def test_example(page: Page):
page.goto("https://example.com")
expect(page).to_have_title("Example Domain")
2. Prefer Resilient Locators (Priority Order)
page.get_by_role()- Best: reflects how users perceive the pagepage.get_by_label()- For form controlspage.get_by_placeholder()- For inputs with placeholderspage.get_by_text()- For text contentpage.get_by_alt_text()- For imagespage.get_by_title()- For title attributespage.get_by_test_id()- For data-testid attributespage.locator("css=...")- Last resort
3. Use Web-First Assertions
# GOOD - auto-waits and retries
expect(page.get_by_text("Success")).to_be_visible()
# BAD - no auto-waiting
assert page.get_by_text("Success").is_visible()
4. Sync vs Async
Default to sync API for pytest tests. Use async only when specifically needed:
# Sync (default for pytest)
def test_example(page: Page):
page.goto("https://example.com")
# Async (when needed)
async def test_example(page: Page):
await page.goto("https://example.com")
5. Page Object Model for Large Suites
class LoginPage:
def __init__(self, page: Page):
self.page = page
self.username = page.get_by_label("Username")
self.password = page.get_by_label("Password")
self.submit = page.get_by_role("button", name="Sign in")
def login(self, username: str, password: str):
self.username.fill(username)
self.password.fill(password)
self.submit.click()
6. Auto-Waiting
Playwright auto-waits for elements to be actionable. Do NOT add manual sleeps:
# GOOD - Playwright waits automatically
page.get_by_role("button", name="Submit").click()
# BAD - unnecessary sleep
import time
time.sleep(2)
page.get_by_role("button", name="Submit").click()
7. Test Isolation
Each test gets a fresh browser context. Use fixtures for shared setup:
import pytest
@pytest.fixture
def authenticated_page(page: Page):
page.goto("/login")
page.get_by_label("Username").fill("user")
page.get_by_label("Password").fill("pass")
page.get_by_role("button", name="Sign in").click()
return page
8. Network Mocking
def test_with_mock(page: Page):
page.route("**/api/data", lambda route: route.fulfill(
json={"items": [{"id": 1, "name": "Test"}]}
))
page.goto("https://example.com")
9. API Testing
def test_api(playwright):
context = playwright.request.new_context(base_url="https://api.example.com")
response = context.get("/users")
assert response.ok
assert len(response.json()) > 0
context.dispose()
Common Patterns
Wait for Navigation After Click
page.get_by_text("Login").click()
page.wait_for_url("**/dashboard")
Handle Dialogs
page.on("dialog", lambda dialog: dialog.accept())
page.get_by_role("button", name="Delete").click()
File Downloads
with page.expect_download() as download_info:
page.get_by_text("Download").click()
download = download_info.value
download.save_as("/tmp/file.pdf")
Screenshots and Traces
# Screenshot
page.screenshot(path="screenshot.png", full_page=True)
# Trace
context.tracing.start(screenshots=True, snapshots=True)
# ... test actions ...
context.tracing.stop(path="trace.zip")
Multiple Browser Contexts (Multi-User)
def test_chat(browser):
user_ctx = browser.new_context()
admin_ctx = browser.new_context()
user_page = user_ctx.new_page()
admin_page = admin_ctx.new_page()
Converted and distributed by TomeVault — claim your Tome and manage your conversions.