首页 / Skills / 开发提效 / 网页应用测试 Web Application Testing
开发提效

网页应用测试 Web Application Testing

Anthropic, PBCApache-2.0Skill 语言: 英文
协议已核对含脚本
演示真实运行记录 · claude-sonnet-5-5 · 2026年10月
I have a local React application already running at http://localhost:5173. Create a Python Playwright smoke-test script that first waits for the dynamic page to become idle, then discovers the visible buttons, clicks the button named "Save profile", captures browser console messages, and saves a full-page screenshot. Explain any assumptions about selectors. Use the webapp-testing skill.
Skill: webapp-testing

I couldn't save the script to disk because the Write tool is disabled in this session. The full script is below, so you can save it as smoke_test.py yourself. I haven't run it.

import sys
from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeout

URL = "http://localhost:5173"
BUTTON_NAME = "Save profile"
SCREENSHOT_PATH = "smoke_test.png"

console_messages = []


def main() -> int:
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        page = browser.new_page()

        # Register listeners before navigation so early messages are captured
        page.on("console", lambda msg: console_messages.append((msg.type, msg.text)))
        page.on("pageerror", lambda err: console_messages.append(("pageerror", str(err))))

        exit_code = 0
        try:
            page.goto(URL)
            # Critical for dynamic apps: wait for JS to render and network to settle
            page.wait_for_load_state("networkidle")

            # Discover visible buttons
            buttons = [b for b in page.get_by_role("button").all() if b.is_visible()]
            print(f"Found {len(buttons)} visible button(s):")
            for i, b in enumerate(buttons):
                label = (b.inner_text() or b.get_attribute("aria-label") or "").strip()
                print(f"  [{i}] {label!r}")

            # Click by accessible name
            target = page.get_by_role("button", name=BUTTON_NAME, exact=True)
            target.wait_for(state="visible", timeout=5000)
            target.click()
            page.wait_for_load_state("networkidle")
            print(f"Clicked button {BUTTON_NAME!r}")
        except PlaywrightTimeout as e:
            print(f"FAIL: button {BUTTON_NAME!r} not found/visible: {e}")
            exit_code = 1
        finally:
            page.screenshot(path=SCREENSHOT_PATH, full_page=True)
            print(f"Screenshot saved to {SCREENSHOT_PATH}")

            print(f"\nConsole messages ({len(console_messages)}):")
            for kind, text in console_messages:
                print(f"  [{kind}] {text}")
            if any(k in ("error", "pageerror") for k, _ in console_messages):
                print("WARNING: console errors detected")
                exit_code = exit_code or 1

            browser.close()
        return exit_code


if __name__ == "__main__":
    sys.exit(main())

Run it with python smoke_test.py. Your server is already up, so you don't need with_server.py. If you haven't installed Playwright yet, run pip install playwright && playwright install chromium first.

What it does, in order

  1. It registers the console and pageerror listeners before goto, so messages logged during the initial render are captured.
  2. It waits for networkidle so the dynamic page has finished rendering before it inspects anything.
  3. It lists every visible button, using get_by_role("button") plus an is_visible() filter.
  4. It clicks the button named "Save profile", waits for the network to settle again, and prints the captured console messages.
  5. It saves a full-page screenshot to smoke_test.png. This happens in finally, so you get a screenshot even if the click fails.

Assumptions about selectors

  • Role and name: I used get_by_role("button", name="Save profile", exact=True). This assumes the control is a real <button>, <input type="submit">, or an element with role="button". Its accessible name must be exactly "Save profile", whether from its text, aria-label, or an associated label. It also keeps working if CSS classes change.
  • A <div onClick> won't match: If "Save profile" is a styled <div> without role="button", the lookup will time out. In that case, switch to page.get_by_text("Save profile") or add a data-testid and use get_by_test_id.
  • Exact match: exact=True is a case-sensitive, full-string match. Remove it if the label might change, for example to "Save profile…" or "Saving…".
  • One match only: Playwright's strict mode raises an error if more than one button matches. If the page has duplicates, such as one per tab, scope the lookup, for example page.locator("form").get_by_role(...).
  • Idle waiting: networkidle can hang if the app polls, uses websockets, or runs HMR traffic. If that happens, wait for a specific element instead of network idle.
  • Failure condition: The script exits non-zero if the button isn't found or if any console error or uncaught page error appears.

Real run recorded with claude-code / claude-sonnet-5-5. Output is shown verbatim, unmodified.

它做什么

指导代理使用原生 Python Playwright 脚本测试本地网页应用。

工作方式

  1. 判断应用是静态页面还是动态应用。
  2. 在检查渲染后的 DOM 前,等待动态页面进入空闲状态。
  3. 使用可见选择器、截图和浏览器日志来自动化并诊断界面行为。

适合场景

冒烟测试、界面回归排查、选择器发现、截图与控制台日志采集。

说明与风险

包含 Python 示例和服务器生命周期辅助脚本。它可以访问本地 URL、启动浏览器自动化、写入截图或日志,并启动作为本地开发服务器提供的 shell 命令。不包含凭据;运行前请审查命令和文件路径。