| name | sc:webapp-testing |
| description | Test a web app end-to-end with Playwright: drive real flows, assert on visible state, catch console/network errors, and do visual + RTL/Hebrew checks. Covers writing resilient selectors (roles/text over CSS), waiting on conditions (never sleeps), screenshot diffing, and a quick smoke pass before shipping. Activate for: test the web app, e2e test, playwright, browser test, check the UI, visual regression, smoke test, ืืืืงืช ืืชืจ, ืืืืงืืช ืืืืืืฆืื, ืืืืงืช ืืืฉืง. |
| argument-hint | [flow or page to test] |
| license | MIT |
| metadata | {"author":"squadcoder","version":"1.0.0"} |
Webapp Testing โ resilient end-to-end tests with Playwright
Test what the user sees and does, not implementation details. The bundled Playwright MCP/browser
is available; use it to drive a real browser and assert on real state.
Step 0 โ decide the flow
Name the user journey ("sign up โ onboarding โ first project"). Test that, not isolated widgets.
List the success assertions up front (what must be true at the end).
Step 1 โ resilient selectors (in priority order)
- Role + accessible name โ
getByRole("button", { name: "Save" }). Survives restyles.
- Visible text โ
getByText(...).
- Label / placeholder โ for form fields.
data-testid โ only when the above can't disambiguate.
Avoid brittle CSS/XPath chains and nth-child positional selectors.
Step 2 โ wait on conditions, never sleep
- Use auto-waiting assertions:
await expect(locator).toBeVisible(), toHaveText, toHaveURL.
- Never
waitForTimeout(n) to "let it settle" โ wait for the actual condition (element, response, URL).
- For network:
await page.waitForResponse(/api\/thing/) when an action triggers a fetch.
Step 3 โ drive the flow and assert
await page.goto(BASE_URL)
await page.getByRole("button", { name: "Open project" }).click()
await page.getByPlaceholder("Browse folders").fill("~/demo")
await page.keyboard.press("Enter")
await expect(page.getByRole("heading", { name: "demo" })).toBeVisible()
Step 4 โ catch errors the user wouldn't report
- Console: fail the test on
console.error / uncaught exceptions (page.on("console") / "pageerror").
- Network: flag 4xx/5xx on requests the flow depends on.
- A11y: check focus order and that interactive elements have accessible names.
Step 5 โ visual + RTL/Hebrew parity
- Screenshot key states; compare to a baseline (
toHaveScreenshot) with a small tolerance.
- Always test RTL too: load the app in Hebrew (
dir="rtl"), verify the sidebar/layout mirror and
nothing overlaps. RTL bugs hide in physical-CSS leftovers โ this catches them.
- Test at mobile and desktop widths if the app is responsive.
Step 6 โ a fast smoke pass
Before shipping, run one short script that: loads the app, asserts no console errors, walks the primary
flow, and screenshots the result LTR + RTL. Cheap insurance.
Rules
- Test behavior and visible state, not internals.
- One flow per test; clear arrange/act/assert.
- No sleeps, no brittle selectors, no asserting on text you didn't render.
- Report failures with the screenshot + the failing assertion, not a vague "it broke".