mcpField
P
playwright-skill

playwright-skill

github.com/fixmyberlinTesting & quality059 installsUpdated 2d ago
Repository

About playwright-skill

A skill for using Playwright in FixMyBerlin/FMC projects, covering project E2E tests, agent exploration boundaries, and one-off automation. It provides TanStack Start configuration and testing patterns, including map and authentication workflows.

Key features

  1. 01Project E2E test setup
  2. 02TanStack Start server configuration
  3. 03Smoke, auth, and map test patterns
  4. 04Console and server error helpers
  5. 05Ad-hoc automation through a skill runner

Use cases

  1. 01Adding regression tests to a project
  2. 02Testing routes and authentication flows
  3. 03Checking map rendering and tile requests
  4. 04Running one-off browser automation

MCP support

Describes agent-browser MCP as the interactive exploration option for debugging and quick UI checks, distinct from Playwright project E2E and ad-hoc scripts.

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/fixmyberlin/fixmyskills --skill playwright-skill

After installing, type/playwright-skillin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

package.json
playwright.config.ts.template
run.js
lib/
helpers.js
references/
tilda-patterns.md

Details

From the project README

Read the project’s tests/README.md first — env vars, Docker, and scripts live there.

Related FMC skills: tanstack-router-conventions (routes/search), tanstack-start-auth (sessions), tanstack-start-conventions.


Official docs (fetch; do not duplicate)

Playwright has no official llms.txt (request closed).

TopicURL
Intro & writing testshttps://playwright.dev/docs/intro
Locators (getByRole, etc.)https://playwright.dev/docs/locators
Test configurationhttps://playwright.dev/docs/test-configuration
Best practiceshttps://playwright.dev/docs/best-practices
CIhttps://playwright.dev/docs/ci
Agents (CLI)https://playwright.dev/docs/getting-started-cli
TanStack doc indexhttps://tanstack.com/llms.txt
TanStack Start e2e exampleshttps://github.com/TanStack/router/tree/main/e2e/react-start
Map interactions (optional)https://mapgrab.github.io/docs/getting-started/stage-two/playwright

Three layers (pick the right tool)

LayerWhenWhere
Project E2ESuites, CI, regression@playwright/test in tests/*.spec.ts, repo playwright.config.ts
Agent explorationInteractive debugging, hydration, React tree, quick UI checksagent-browser MCP — skill tech-stack, skill tech-stack → references/agent-browser-mcp.md
Ad-hoc (this skill)One-off scripts, external sites, no MCP/tmp/playwright-test-*.js via run.js
DB schema / queriesInspecting tablesPostgres MCP — skill tech-stack → references/postgres-mcp.md

For in-repo work, prefer project E2E for anything that should regress.

Boundaries (Playwright vs agent-browser)
Concern@playwright/test (this skill)agent-browser (tech-stack)
Regression, CI, stubbed auth, map test hooks✓—
Hydration, React tree, quick UI debugging—✓
Maps (WebGL)waitForMapLoad, tile/network helpers, MapGrabAnnotated screenshots, eval

tilda-geo smoke / stubbed-auth / map specs: read references/tilda-patterns.md when writing them (env gating, smoke suite, stubbed-session flow, map steps, route fixtures).

Exploration findings that should stick → add tests/*.spec.ts and run bun run e2e.


TanStack Start — playwright.config.ts

Reference implementation: tilda-geo app/playwright.config.ts.

Environment loading

Load env before defineConfig so webServer gets DATABASE_* etc., layered: repo .env, app .env (optional), .env.test (test-only).

Server tiers
TierUsewebServer.commandNotes
Local / smokeFast feedback (TILDA default)bun run devVite dev, e.g. http://127.0.0.1:5173
CI / releaseProduction-likebun run build && bun run startSet PORT / VITE_SERVER_PORT like TanStack e2e
PreviewBuilt assets + SSR previewbun run build && bun run preview --port …TanStack vite preview mode

Docker / DB: If the app needs Postgres or tiles, start compose outside Playwright (docker compose up db tiles -d). Do not cram compose into webServer.command unless CI already proves it works.

Config template

Copy playwright.config.ts.template from this skill directory (baseURL http://127.0.0.1:5173, webServer: bun run dev, chromium project).


FMC / TILDA patterns

Canonical paths in tilda-geo (app/):

ConcernLocation
Configplaywright.config.ts
Setup docstests/README.md
Smoke (public routes)tests/smoke/public-routes.spec.ts
Smoke (trailing slash)tests/smoke/trailing-slash-redirect.spec.ts
Smoke (region beforeLoad)tests/smoke/region-before-load.spec.ts
Stubbed admin / role authtests/pages/admin.stubbed-auth.spec.ts
Regions + map (real OAuth)tests/pages/regions.spec.ts (RUN_OAUTH_E2E=1)
Docs region downloadstests/pages/docs-region-downloads.spec.ts
Contact profile modaltests/pages/contact-profile-modal.spec.ts
Auth fixturestests/fixtures/auth.ts
Route liststests/fixtures/routes.ts
Console / server errorstests/utils/console.ts, tests/utils/server.ts
Map wait / render helperstests/utils/maps.ts
Map network verificationtests/utils/network.ts
Dynamic URL regex escapetests/utils/regex.ts
App test hookssrc/components/shared/utils/playwright.ts
VITE_PLAYWRIGHT_ENABLED

In repo .env (see .env.example):

VITE_PLAYWRIGHT_ENABLED=true

Enables test IDs and map signals only in E2E — not in production HTML.

App hooks (playwright.ts)
import { createIsomorphicFn } from '@tanstack/react-start'

export function playwrightTestId(testId: string) {
  return import.meta.env.VITE_PLAYWRIGHT_ENABLED === 'true' ? testId : undefined
}

export const exposeMainMapForDebugging = createIsomorphicFn()
  .server((_map?: MaplibreMap) => {})
  .client((map?: MaplibreMap) => {
    const playwrightEnabled =
      import.meta.env.VITE_PLAYWRIGHT_ENABLED === 'true' || window.__PLAYWRIGHT_ENABLED === 'true'
    if (import.meta.env.DEV || playwrightEnabled) {
      window.__mainMap = map
    }
  })

export const firePlaywrightMapLoadedEvent = createIsomorphicFn()
  .server(() => {})
  .client(() => {
    const playwrightEnabled =
      import.meta.env.VITE_PLAYWRIGHT_ENABLED === 'true' || window.__PLAYWRIGHT_ENABLED === 'true'
    if (!playwrightEnabled) return
    window.dispatchEvent(new CustomEvent('mapLoaded'))
    window.__mapLoaded = true
  })

Call both helpers from the map onLoad handler: exposeMainMapForDebugging(event.target) and firePlaywrightMapLoadedEvent(). window.__mainMap is available in dev and Playwright mode; the mapLoaded event remains Playwright-gated. Why expose the map and onLoad wiring: skill react-map-gl → references/map-debug-exposure.md. Tests use tests/utils/maps.ts (waitForMapLoad, verifyMapRendered, checkMapTilesLoaded, getMapLayerIds). For tile/API coverage on region pages, use verifyMapNetworkRequests from tests/utils/network.ts (after waitForMapLoad).

For click/drag on map canvas, consider MapGrab (used in legacy Trassenscout surveys; adopt when migrating those flows to Start).

Smoke tests (post-migration)

After Next → TanStack Start, add or run smoke specs:

  1. page.goto(route) with baseURL
  2. Assert pathname unchanged (no crash redirect)
  3. expect(page.locator('main').first()).toBeVisible()
  4. expectNoConsoleErrors(page) (see utils)

Route lists live in tests/fixtures/routes.ts (PUBLIC_SMOKE_ROUTES, ADMIN_ROUTES, ADMIN_REDIRECT_SMOKE_ROUTE, etc.). Run: bun run e2e -- tests/smoke.

Auth
StrategyWhenTILDA
Stubbed adminAdmin pages, search-param UIcreateStubbedAdminSession(page, baseURL, { identityKey }) — Better Auth cookies + Prisma user
Stubbed userNon-admin / modal flowscreateStubbedUserSession(page, baseURL, { identityKey })
Real OAuthMap + regions regressionauth-setup.spec.ts + regions.spec.ts when RUN_OAUTH_E2E=1 + TEST_OSM_* in .env.test

Parallel safety: Stubbed admin suites share DB rows, so use mode: 'serial' and nested test.describe per route so afterEach cleanup does not delete another worker’s session (cleanupStubbedSessionData('ADMIN' | 'USER', identityKey)).

Dynamic URLs: Use escapeRegExp(route) from tests/utils/regex.ts when building toHaveURL(new RegExp(...)) from fixture paths that may contain ? or special characters.

Quality helpers

Before goto, attach collectors; after navigation, assert:

  • Console: collectConsoleErrors → filter KNOWN_ACCEPTABLE_ERRORS → fail on error
  • Server: collectServerErrors(page, baseURL) — same-origin 5xx and requestfailed

Extend allowlists in the util files when a known benign error appears.

Locators

Use playwrightTestId('…') only when role/label/text locators are insufficient.


Project E2E workflow

package.json script (TILDA convention):

"e2e": "playwright test --project=chromium"
  • bun run e2e → chromium project (default local/CI run)
  • bun run e2e -- --ui / bun run e2e -- --debug → pass Playwright flags after --
  • bun run e2e -- tests/smoke → subset of specs
bun add -d @playwright/test dotenv
bunx playwright install chromium
# Start DB/tiles if required (see tests/README.md)

Ad-hoc automation (skill runner)

Setup (once):

cd ${CLAUDE_SKILL_DIR} && bun run setup

Workflow:

  1. For localhost: detect dev servers first:

    bun -e "require('${CLAUDE_SKILL_DIR}/lib/helpers').detectDevServers().then(s => console.log(JSON.stringify(s)))"
    
  2. Write script to /tmp/playwright-test-*.js (never commit ad-hoc scripts to the skill dir).

  3. Parameterize TARGET_URL at the top.

  4. Run from the project directory (not the skill folder): bun ${CLAUDE_SKILL_DIR}/run.js /tmp/playwright-test-*.js. run.js resolves relative script paths and takeScreenshot output against the directory you ran it from; for raw page.screenshot({ path }) use an absolute path.

Headers (identify automated traffic):

PW_HEADER_NAME=X-Automated-By PW_HEADER_VALUE=playwright-skill \
  bun ${CLAUDE_SKILL_DIR}/run.js /tmp/my-script.js

Multiple headers: PW_EXTRA_HEADERS='{"X-Automated-By":"playwright-skill"}'.

Defaults: Run headless by default (agents cannot watch a window); use headless: false only when a human wants to watch.

Helpers (lib/helpers.js): detectDevServers, createContext / getExtraHeadersFromEnv, takeScreenshot.


Comments

Markdown supported