Webapp Testing

bobmatnyc/claude-mpm-skills/universal/testing/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 模式的参考指南,用于编写 Web 应用测试,涵盖选择器、等待、断言与调试。

功能
该技能提供一份用于 Web 应用测试的 Playwright 自动化模式参考文档。内容涵盖选择器策略、等待策略、元素交互、断言、基于 pytest 的测试组织、网络拦截、截图与视频、调试以及并行执行。它产出的是指导说明和代码示例,而不是可直接运行的测试文件。
适用场景
在编写或审查 Web 应用的浏览器测试,并需要具体的 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 从公开仓库中收录这些内容。

举报或申请下架