Cursor plugin for Playwright (@playwright/test 1.x current stable). Teaches semantic locators (getByRole over CSS), web-first assertions (await expect(loc).toBeVisible() over expect(await loc.isVisible()).toBeTruthy()), the test.extend fixture model (over beforeEach), the setup-project + storageState auth pattern (over UI-login per test), page.route mocking with explicit continue/fallback, the POM-via-fixture pattern, and the sharded-CI-with-merge-reports workflow. Catches 34 LLM regressions from pre-Playwright-1.40 training data and Selenium muscle memory.
LLMs trained on early Playwright tutorials (and a lot of Selenium) emit Playwright code that is brittle, slow, or silently broken. They write:
page.click("text=Login")instead ofpage.getByRole("button", { name: "Login" }).click()page.$(".btn")/page.$$(".btn")ElementHandles (snapshot, racy, officially discouraged)await page.waitForTimeout(2000)instead of a web-first assertionexpect(await loc.isVisible()).toBeTruthy()which loses auto-retryexpect(loc).toBeVisible()withoutawait(the Promise is dropped, the test passes immediately)await page.waitForSelector(s); await page.click(s)when locator actions auto-waitif (await loc.isVisible()) ...polling loops with hand-rolled retryexpect(await loc.textContent()).toBe("...")instead ofawait expect(loc).toHaveText("...")assert.equal(...)fromnode:assertinstead of Playwright'sexpect- Hardcoded credentials in
fill("alice@acme.com", "hunter2") - Login via UI in
beforeEachinstead of setup-project +storageState page.route(...)AFTERpage.goto(...)so the initial fetch already fired and the mock never applies- Route handler with no
route.continue/fallbackbranch so non-matching requests hang beforeEachconstructing a POM instead of atest.extend<{...}>({...})fixture- Untyped
test.extend({...})so fixtures areany Page/Locatorimported fromplaywrightinstead of@playwright/test(no test-runner types)headless: falsecommitted inplaywright.config.ts- Missing
forbidOnlyso a straytest.onlysilently skips the rest of the suite - Missing
webServerso tests race the dev server on cold start trace: "on"in CI producing huge artifacts every runretries: 5hiding flake instead of fixing itPromise.all([page.waitForNavigation(), page.click(...)])when click auto-waitsmicrosoft/playwright-github-actionin workflow (deprecated)viewport: { width, height }+ manualuserAgentinstead of...devices['iPhone 14']- Caching
~/.cache/ms-playwrightin CI (official guidance is to skip it)
A short Playwright rule already exists on cursor.directory. It is shallow: ~10 prompt-style bullets, no BAD/CORRECT pairs, no fixtures, no version awareness, no auth/storageState, no sharding, no page.route patterns, no POM-via-fixture, no visual-regression OS-baseline trap. This plugin is the depth play:
- 30+ documented anti-patterns with BAD/CORRECT TS code (the existing rule has none)
- Five focused rules - core, anti-patterns, config, auth, network
- Setup-project + storageState auth, including per-worker isolation, multi-role, and JWT bypass
- Sharded CI shape with the
blobreporter and a separatemerge-reportsjob - Visual-regression skill with the macOS-vs-Linux baseline trap (the #1 visual-regression gotcha) called out
- Reviewer agent with severity grouping (CRITICAL / ERROR / WARN / SUGGESTION)
- Compilable test fixtures (anti-pattern + correct sample) showing 15+ violations and the modern equivalents
- Version-current: rules are pinned to 1.x current stable surface, with deltas for 1.48-1.60 features called out
Copy the rules, skills, and agents into your project's Cursor configuration. Back up your existing files first - the plain cp -r will overwrite same-named rules.
git clone https://github.com/RoninForge/roninforge-playwright.git
# Use -n to avoid clobbering an existing customised rule of the same name.
cp -rn roninforge-playwright/rules/* your-project/.cursor/rules/
cp -rn roninforge-playwright/skills/* your-project/.cursor/skills/
cp -rn roninforge-playwright/agents/* your-project/.cursor/agents/Or vendor the whole repo as a git submodule under your-project/.cursor/plugins/. Refer to the Cursor plugin docs for the current global-install path on your Cursor version.
| Rule | Scope | What it does |
|---|---|---|
playwright-core |
Always active | Semantic locators, web-first assertions, test.extend fixtures, POM-via-fixture, page.route basics, auth via storageState, trace config, version notes |
playwright-anti-patterns |
Always active | 34 LLM regressions: text/CSS engine selectors, page.$/$$, waitForTimeout, isVisible booleanised, missing await on assertion, hardcoded credentials, route-after-goto, untyped fixtures, deprecated GitHub action, etc |
playwright-config |
playwright.config.ts |
defineConfig shape, projects + dependencies, webServer.wait regex (1.57+), trace/screenshot/video, sharded CI matrix, browser binary cache guidance |
playwright-auth |
**/*.setup.ts, **/auth/**, **/fixtures/**, config |
Setup-project + storageState, per-worker auth, multi-role projects, JWT bypass tokens, OAuth/SSO mocks, secrets hygiene |
playwright-network |
Agent-requested | page.route + route.fulfill/continue/fallback, route.fetch for response-modify, routeFromHAR, routeWebSocket (1.48+), the request fixture, mock-vs-real-backend |
| Skill | Command | What it does |
|---|---|---|
| New test | /playwright-new-test |
Scaffold a *.spec.ts with role locators, web-first assertions, fixtures (or POM-via-fixture for repeated screens), env-driven secrets |
| Setup auth | /playwright-setup-auth |
Pick the right auth pattern (single shared / per-worker / multi-role / JWT bypass), wire setup project + dependencies, gitignore playwright/.auth/ |
| Validate | /playwright-validate |
Grep audit for the tracked anti-patterns: hardcoded credentials, missing await on assertion, waitForTimeout, ElementHandle, route-after-goto, deprecated action, etc |
| Visual regression | /playwright-visual-regression |
toHaveScreenshot setup with mask:, animations: "disabled", the macOS-vs-Linux baseline trap, dedicated visual project, CI workflow that never auto-updates baselines |
| Agent | What it does |
|---|---|
playwright-reviewer |
Reviews Playwright TS by severity: critical (security, no-op tests), error (won't compile or wrong runtime), warn (regressions), suggestion (style + future-proofing) |
tests/fixtures/anti-pattern-sample/ is a Playwright suite that bundles 15+ violations: text= engine selectors, page.$ ElementHandles, waitForTimeout, expect(await isVisible()).toBeTruthy(), missing await on assertion, hardcoded credentials, login via UI in beforeEach, route after goto, route handler with no fallback, Page from playwright (wrong package), assert from node:assert, waitForNavigation in Promise.all, .first() for disambiguation, data-testid where role exists, test.only left in. The playwright.config.ts pairs it with headless: false, trace: "on", retries: 5, hardcoded viewport instead of devices, and missing forbidOnly / webServer.
tests/fixtures/correct-sample/ is the same shape rewritten the modern way: defineConfig with forbidOnly: !!process.env.CI, trace: "on-first-retry", three browser projects + setup with dependencies, webServer block, role-based locators, await expect(loc).toBeVisible(), test.extend<Fixtures>({...}), POM as locator getters (no expect inside), env-driven secrets, route registered before goto with explicit fallback.
Rules target Playwright 1.x current stable. Deltas for newer features (1.48 page.routeWebSocket, 1.49 toMatchAriaSnapshot, 1.54 page.ariaSnapshot, 1.57 Chrome-for-Testing + webServer.wait regex, 1.60 toContainClass) are called out inline so an LLM does not regress to 2023 patterns when newer-and-better APIs exist. Where the rule cites a feature version, verify against the changelog for the version you have installed before adopting.
MIT - see LICENSE