Webapp Testing

作者 bobmatnyc718070a7d622無授權條款77 個星標收錄於 2026年10月8日更新於 2026年10月8日儲存庫2 個月前更新

Comprehensive web application testing patterns with Playwright selectors, wait strategies, and best practices

AI 產生的概覽

提供 Playwright 模式的參考指南,用於撰寫網頁應用程式測試,涵蓋選擇器、等待、斷言與除錯。

功能
此技能提供一份用於網頁應用程式測試的 Playwright 自動化模式參考文件。內容涵蓋選擇器策略、等待策略、元素互動、斷言、以 pytest 為基礎的測試組織、網路攔截、螢幕截圖與影片、除錯以及平行執行。它產出的是指引說明與程式碼範例,而非可直接執行的測試檔案。
適用情境
在撰寫或審查網頁應用程式的瀏覽器測試,且需要具體的 Playwright 選擇器、等待或斷言模式時使用。它也適合用來組織 pytest fixture、模擬網路回應,或除錯不穩定的 UI 測試。
執行需求
未隨附指令碼,僅為說明性內容。套用其中的範例需要安裝 Playwright 的 Python 環境;使用 pytest 相關模式還需要 pytest,可選用 pytest-xdist;執行測試需要瀏覽器並能存取受測應用程式。

Playwright Patterns Reference

Complete guide to Playwright automation patterns, selectors, and best practices.

Table of Contents

Selectors

Text Selectors

Most readable and maintainable approach when text is unique:

python
page.click('text=Login')page.click('text="Sign Up"')  # Exact matchpage.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 readypage.wait_for_load_state('domcontentloaded')
# Wait for full load including imagespage.wait_for_load_state('load')

Element Waits

Wait for specific elements before interacting:

python
# Wait for element to be visiblepage.wait_for_selector('button.submit', state='visible')
# Wait for element to be hiddenpage.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 DOMpage.wait_for_selector('.error-message', state='detached')

Timeout Waits

Fixed time delays (use sparingly):

python
# Wait for animations to completepage.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 conditionpage.wait_for_function('() => document.querySelector(".data").innerText !== "Loading..."')
# Wait for variable to be setpage.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 elementspage.click('button.submit')  # Auto-waitspage.fill('input.email', '[email protected]')  # Auto-waits

Element Interactions

Clicking

python
# Basic clickpage.click('button.submit')
# Click with optionspage.click('button.submit', button='right')  # Right-clickpage.click('button.submit', click_count=2)  # Double-clickpage.click('button.submit', modifiers=['Control'])  # Ctrl+click
# Force click (bypass actionability checks)page.click('button.submit', force=True)

Filling Forms

python
# Text inputspage.fill('input[name="email"]', '[email protected]')page.type('input[name="search"]', 'query', delay=100)  # Type with delay
# Clear then fillpage.fill('input[name="email"]', '')page.fill('input[name="email"]', '[email protected]')
# Press keyspage.press('input[name="search"]', 'Enter')page.press('input[name="text"]', 'Control+A')

Dropdowns and Selects

python
# Select by labelpage.select_option('select[name="country"]', label='United States')
# Select by valuepage.select_option('select[name="country"]', value='us')
# Select by indexpage.select_option('select[name="country"]', index=2)
# Select multiple optionspage.select_option('select[multiple]', ['option1', 'option2'])

Checkboxes and Radio Buttons

python
# Check a checkboxpage.check('input[type="checkbox"]')
# Uncheck a checkboxpage.uncheck('input[type="checkbox"]')
# Check a radio buttonpage.check('input[value="option1"]')
# Toggle checkboxif page.is_checked('input[type="checkbox"]'):    page.uncheck('input[type="checkbox"]')else:    page.check('input[type="checkbox"]')

File Uploads

python
# Upload single filepage.set_input_files('input[type="file"]', '/path/to/file.pdf')
# Upload multiple filespage.set_input_files('input[type="file"]', ['/path/to/file1.pdf', '/path/to/file2.pdf'])
# Clear file inputpage.set_input_files('input[type="file"]', [])

Hover and Focus

python
# Hover over elementpage.hover('button.tooltip-trigger')
# Focus elementpage.focus('input[name="email"]')
# Blur elementpage.evaluate('document.activeElement.blur()')

Assertions

Element Visibility

python
from playwright.sync_api import expect
# Expect element to be visibleexpect(page.locator('button.submit')).to_be_visible()
# Expect element to be hiddenexpect(page.locator('.error-message')).to_be_hidden()

Text Content

python
# Expect exact textexpect(page.locator('.title')).to_have_text('Welcome')
# Expect partial textexpect(page.locator('.message')).to_contain_text('success')
# Expect text matching patternexpect(page.locator('.code')).to_have_text(re.compile(r'\d{6}'))

Element State

python
# Expect element to be enabled/disabledexpect(page.locator('button.submit')).to_be_enabled()expect(page.locator('button.submit')).to_be_disabled()
# Expect checkbox to be checkedexpect(page.locator('input[type="checkbox"]')).to_be_checked()
# Expect element to be editableexpect(page.locator('input[name="email"]')).to_be_editable()

Attributes and Values

python
# Expect attribute valueexpect(page.locator('img')).to_have_attribute('src', '/logo.png')
# Expect CSS classexpect(page.locator('button')).to_have_class('btn-primary')
# Expect input valueexpect(page.locator('input[name="email"]')).to_have_value('[email protected]')

Count and Collections

python
# Expect specific countexpect(page.locator('li')).to_have_count(5)
# Get all elements and assertitems = 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 pytestfrom 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.fixturedef page(browser):    page = browser.new_page()    yield page    page.close()
def test_login(page):    page.goto('http://localhost:3000')    page.fill('input[name="email"]', '[email protected]')    page.fill('input[name="password"]', 'password123')    page.click('button[type="submit"]')    expect(page.locator('.welcome-message')).to_be_visible()

Test Grouping with Describe Blocks

python
class TestAuthentication:    def test_successful_login(self, page):        # Test successful login        pass
    def test_failed_login(self, page):        # Test failed login        pass
    def test_logout(self, page):        # Test logout        pass

Setup and Teardown

python
@pytest.fixture(autouse=True)def setup_and_teardown(page):    # Setup - runs before each test    page.goto('http://localhost:3000')    page.wait_for_load_state('networkidle')
    yield  # Test runs here
    # Teardown - runs after each test    page.evaluate('localStorage.clear()')

Network Interception

Mock API Responses

python
# Intercept and mock API responsedef handle_route(route):    route.fulfill(        status=200,        body='{"success": true, "data": "mocked"}',        headers={'Content-Type': 'application/json'}    )
page.route('**/api/data', handle_route)page.goto('http://localhost:3000')

Block Resources

python
# Block images and stylesheets for faster testspage.route('**/*.{png,jpg,jpeg,gif,svg,css}', lambda route: route.abort())

Wait for Network Responses

python
# Wait for specific API callwith page.expect_response('**/api/users') as response_info:    page.click('button.load-users')response = response_info.valueassert response.status == 200

Screenshots and Videos

Screenshots

python
# Full page screenshotpage.screenshot(path='/tmp/screenshot.png', full_page=True)
# Element screenshotpage.locator('.modal').screenshot(path='/tmp/modal.png')
# Screenshot with custom dimensionspage.set_viewport_size({'width': 1920, 'height': 1080})page.screenshot(path='/tmp/desktop.png')

Video Recording

python
browser = p.chromium.launch(headless=True)context = browser.new_context(record_video_dir='/tmp/videos/')page = context.new_page()
# Perform actions...
context.close()  # Video saved on close

Debugging

Pause Execution

python
page.pause()  # Opens Playwright Inspector

Console Logs

python
def handle_console(msg):    print(f"[{msg.type}] {msg.text}")
page.on("console", handle_console)

Slow Motion

python
browser = p.chromium.launch(headless=False, slow_mo=1000)  # 1 second delay

Verbose Logging

python
# Set DEBUG environment variable# DEBUG=pw:api python test.py

Parallel Execution

Pytest Parallel

bash
# Install pytest-xdistpip install pytest-xdist
# Run tests in parallelpytest -n auto  # Auto-detect CPU corespytest -n 4     # Run with 4 workers

Browser Context Isolation

python
# Each test gets isolated context (cookies, localStorage, etc.)@pytest.fixturedef context(browser):    context = browser.new_context()    yield context    context.close()
@pytest.fixturedef page(context):    return context.new_page()

來源與署名

來源:bobmatnyc/claude-mpm-skills位於universal/testing/webapp-testing提交718070a

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架