webapp-testing-patterns
Comprehensive web application testing patterns with Playwright selectors, wait strategies, and best practices
What this skill does
# Playwright Patterns Reference
Complete guide to Playwright automation patterns, selectors, and best practices.
## Table of Contents
- [Selectors](#selectors)
- [Wait Strategies](#wait-strategies)
- [Element Interactions](#element-interactions)
- [Assertions](#assertions)
- [Test Organization](#test-organization)
- [Network Interception](#network-interception)
- [Screenshots and Videos](#screenshots-and-videos)
- [Debugging](#debugging)
- [Parallel Execution](#parallel-execution)
## Selectors
### Text Selectors
Most readable and maintainable approach when text is unique:
```python
page.click('text=Login')
page.click('text="Sign Up"') # Exact match
page.click('text=/log.*in/i') # Regex, case-insensitive
```
### Role-Based Selectors
Semantic selectors based on ARIA roles:
```python
page.click('role=button[name="Submit"]')
page.fill('role=textbox[name="Email"]', '[email protected]')
page.click('role=link[name="Learn more"]')
page.check('role=checkbox[name="Accept terms"]')
```
### CSS Selectors
Traditional CSS selectors for precise targeting:
```python
page.click('#submit-button')
page.fill('.email-input', '[email protected]')
page.click('button.primary')
page.click('nav > ul > li:first-child')
```
### XPath Selectors
For complex DOM navigation:
```python
page.click('xpath=//button[contains(text(), "Submit")]')
page.click('xpath=//div[@class="modal"]//button[@type="submit"]')
```
### Data Attributes
Best practice for test-specific selectors:
```python
page.click('[data-testid="submit-btn"]')
page.fill('[data-test="email-input"]', '[email protected]')
```
### Chaining Selectors
Combine selectors for precision:
```python
page.locator('div.modal').locator('button.submit').click()
page.locator('role=dialog').locator('text=Confirm').click()
```
### Selector Best Practices
**Priority order (most stable to least stable):**
1. `data-testid` attributes (most stable)
2. `role=` selectors (semantic, accessible)
3. `text=` selectors (readable, but text may change)
4. `id` attributes (stable if not dynamic)
5. CSS classes (less stable, may change with styling)
6. XPath (fragile, avoid if possible)
## Wait Strategies
### Load State Waits
Essential for dynamic applications:
```python
# Wait for network to be idle (most common)
page.goto('http://localhost:3000')
page.wait_for_load_state('networkidle')
# Wait for DOM to be ready
page.wait_for_load_state('domcontentloaded')
# Wait for full load including images
page.wait_for_load_state('load')
```
### Element Waits
Wait for specific elements before interacting:
```python
# Wait for element to be visible
page.wait_for_selector('button.submit', state='visible')
# Wait for element to be hidden
page.wait_for_selector('.loading-spinner', state='hidden')
# Wait for element to exist in DOM (may not be visible)
page.wait_for_selector('.modal', state='attached')
# Wait for element to be removed from DOM
page.wait_for_selector('.error-message', state='detached')
```
### Timeout Waits
Fixed time delays (use sparingly):
```python
# Wait for animations to complete
page.wait_for_timeout(500)
# Wait for delayed content (better to use wait_for_selector)
page.wait_for_timeout(2000)
```
### Custom Wait Conditions
Wait for JavaScript conditions:
```python
# Wait for custom JavaScript condition
page.wait_for_function('() => document.querySelector(".data").innerText !== "Loading..."')
# Wait for variable to be set
page.wait_for_function('() => window.appReady === true')
```
### Auto-Waiting
Playwright automatically waits for elements to be actionable:
```python
# These automatically wait for element to be:
# - Visible
# - Stable (not animating)
# - Enabled (not disabled)
# - Not obscured by other elements
page.click('button.submit') # Auto-waits
page.fill('input.email', '[email protected]') # Auto-waits
```
## Element Interactions
### Clicking
```python
# Basic click
page.click('button.submit')
# Click with options
page.click('button.submit', button='right') # Right-click
page.click('button.submit', click_count=2) # Double-click
page.click('button.submit', modifiers=['Control']) # Ctrl+click
# Force click (bypass actionability checks)
page.click('button.submit', force=True)
```
### Filling Forms
```python
# Text inputs
page.fill('input[name="email"]', '[email protected]')
page.type('input[name="search"]', 'query', delay=100) # Type with delay
# Clear then fill
page.fill('input[name="email"]', '')
page.fill('input[name="email"]', '[email protected]')
# Press keys
page.press('input[name="search"]', 'Enter')
page.press('input[name="text"]', 'Control+A')
```
### Dropdowns and Selects
```python
# Select by label
page.select_option('select[name="country"]', label='United States')
# Select by value
page.select_option('select[name="country"]', value='us')
# Select by index
page.select_option('select[name="country"]', index=2)
# Select multiple options
page.select_option('select[multiple]', ['option1', 'option2'])
```
### Checkboxes and Radio Buttons
```python
# Check a checkbox
page.check('input[type="checkbox"]')
# Uncheck a checkbox
page.uncheck('input[type="checkbox"]')
# Check a radio button
page.check('input[value="option1"]')
# Toggle checkbox
if page.is_checked('input[type="checkbox"]'):
page.uncheck('input[type="checkbox"]')
else:
page.check('input[type="checkbox"]')
```
### File Uploads
```python
# Upload single file
page.set_input_files('input[type="file"]', '/path/to/file.pdf')
# Upload multiple files
page.set_input_files('input[type="file"]', ['/path/to/file1.pdf', '/path/to/file2.pdf'])
# Clear file input
page.set_input_files('input[type="file"]', [])
```
### Hover and Focus
```python
# Hover over element
page.hover('button.tooltip-trigger')
# Focus element
page.focus('input[name="email"]')
# Blur element
page.evaluate('document.activeElement.blur()')
```
## Assertions
### Element Visibility
```python
from playwright.sync_api import expect
# Expect element to be visible
expect(page.locator('button.submit')).to_be_visible()
# Expect element to be hidden
expect(page.locator('.error-message')).to_be_hidden()
```
### Text Content
```python
# Expect exact text
expect(page.locator('.title')).to_have_text('Welcome')
# Expect partial text
expect(page.locator('.message')).to_contain_text('success')
# Expect text matching pattern
expect(page.locator('.code')).to_have_text(re.compile(r'\d{6}'))
```
### Element State
```python
# Expect element to be enabled/disabled
expect(page.locator('button.submit')).to_be_enabled()
expect(page.locator('button.submit')).to_be_disabled()
# Expect checkbox to be checked
expect(page.locator('input[type="checkbox"]')).to_be_checked()
# Expect element to be editable
expect(page.locator('input[name="email"]')).to_be_editable()
```
### Attributes and Values
```python
# Expect attribute value
expect(page.locator('img')).to_have_attribute('src', '/logo.png')
# Expect CSS class
expect(page.locator('button')).to_have_class('btn-primary')
# Expect input value
expect(page.locator('input[name="email"]')).to_have_value('[email protected]')
```
### Count and Collections
```python
# Expect specific count
expect(page.locator('li')).to_have_count(5)
# Get all elements and assert
items = page.locator('li').all()
assert len(items) == 5
```
## Test Organization
### Basic Test Structure
```python
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
# Test logic here
page.goto('http://localhost:3000')
page.wait_for_load_state('networkidle')
browser.close()
```
### Using Pytest (Recommended)
```python
import pytest
from playwright.sync_api import sync_playwright
@pytest.fixture(scope="session")
def browser():
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
yield browser
browser.close()
@pytest.fixture
def page(browser):
page = browser.new_page()
yield page
page.close()
Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.