Skip to content
Back to skills

E2e Testing

ASecurity

Guides Playwright end-to-end test design — Page Object Model, CI configuration, and flaky test diagnosis. Use when writing browser-based end-to-end tests, debugging a test that fails intermittently, setting up Playwright CI configuration, or reviewing E2E test code for race conditions and arbitrary waits.

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 16, 2026
ai-agentstypescriptgotestingdebugginggitapi

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned October 16, 2026

npx -y skills add shennawardana23/skillme --skill e2e-testing --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of E2e Testing?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for E2e Testing
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/shennawardana23-e2e-testing/badge)](https://www.skillsdirectory.com/skills/shennawardana23-e2e-testing)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: e2e-testing
description: Guides Playwright end-to-end test design — Page Object Model, CI configuration, and flaky test diagnosis. Use when writing browser-based end-to-end tests, debugging a test that fails intermittently, setting up Playwright CI configuration, or reviewing E2E test code for race conditions and arbitrary waits.
license: Apache-2.0
metadata:
  version: "0.1.0"
---

# End-to-End Testing (Playwright)

E2E tests sit at the top of the test pyramid — expensive, slow, and the
first place flakiness shows up. Most flakiness traces back to one thing:
the test racing the page instead of waiting for a specific, observable
condition.

## Page Object Model

```typescript
export class ReservationsPage {
  constructor(private page: Page) {}

  async goto() {
    await this.page.goto("/reservations");
  }

  async search(query: string) {
    await this.page.locator('[data-testid="search-input"]').fill(query);
    await this.page.waitForResponse((r) => r.url().includes("/api/search"));
  }

  rows() {
    return this.page.locator('[data-testid="reservation-row"]');
  }
}
```

## The single highest-value fix: stop waiting on time, wait on state

```typescript
// Flaky: races the animation/network, passes locally, fails in CI
await page.waitForTimeout(2000);
await page.click('[data-testid="confirm"]');

// Stable: waits for the actual condition
await page.locator('[data-testid="confirm"]').waitFor({ state: "visible" });
await page.locator('[data-testid="confirm"]').click();
```

Playwright locators already auto-wait for actionability (attached,
visible, stable, enabled) before acting — `page.waitForTimeout` is almost
never the right tool and should be treated as a review finding, not a style
preference.

## Gotchas

- `page.click(selector)` on a raw selector string skips Playwright's
  auto-waiting in some older API patterns; `page.locator(selector).click()`
  is the form that gets the actionability checks. Prefer locators
  throughout, not selector strings passed directly to action methods.
- A test that passes locally but fails in CI is frequently a timing issue
  invisible on a fast local machine and a slow, resource-constrained CI
  runner — reproduce with `--repeat-each=10` before assuming it's an
  environment misconfiguration.
- `test.skip`/`test.fixme` used to silence a flaky test without an
  attached issue reference is a one-way door — the test quietly stops
  providing coverage and nobody notices it never runs again. Always attach
  a tracking reference when quarantining a test.
- CI-only flakiness is commonly a resource contention issue, not a logic
  bug: setting `workers: 1` for CI (serializing tests) and `retries: 2` is
  a legitimate stabilizing measure while investigating root cause, but
  should not be treated as the fix itself — a retried, silently-passing
  flaky test is still hiding a real race condition.

## Configuration baseline

```typescript
export default defineConfig({
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  use: {
    trace: "on-first-retry",
    screenshot: "only-on-failure",
    video: "retain-on-failure",
  },
});
```

## Real-world grounding

Flaky E2E tests are a widely documented cross-industry problem, not a sign
of one team's carelessness — the common root causes converge across
codebases: arbitrary sleeps instead of condition waits, shared mutable
state between parallel test workers, and animation/transition timing. The
fix is never "add a retry and move on" as the permanent state — retries
mask the failure rate without addressing why the test races the
application in the first place; use them as a stabilizing measure while
you diagnose the actual race, not as the diagnosis's conclusion.

## Verification

- [ ] No `waitForTimeout` used as a substitute for waiting on a real condition
- [ ] Every action goes through a `page.locator(...)` call, not a bare selector
- [ ] Flaky/quarantined tests have an attached tracking reference
- [ ] CI config sets deterministic retries and worker count, separate from local dev config
- [ ] Critical user flows (not every flow) get E2E coverage — the rest belongs in the unit/integration layers

Files in this skill

  • SKILL.md4.2 KB
  • evals/evals.json2.5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.