Skip to content
Back to skills

E2e Debug

ASecurity

Debug Playwright E2E test failures — port conflicts, server startup, test isolation, and post-mortem analysis

  • 21 stars
  • 0 votes
  • 0 copies
  • 4 views
  • Added September 3, 2026
ai-agentsgoshellbashdebuggingbackend

Works with

  • cli
  • mcp

Security analysis

A96/100
  • mediumUses curl or wget to download content

Pro shows the line behind each finding and how to fix it

Scanned September 3, 2026

npx -y skills add bloknayrb/tandem --skill e2e-debug --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of E2e Debug?

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

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

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-debug
description: Debug Playwright E2E test failures — port conflicts, server startup, test isolation, and post-mortem analysis
disable-model-invocation: true
---

# E2E Test Debugging

Post-mortem guide for Playwright E2E failures in Tandem. Complements the `/e2e` skill (which covers the happy path).

## Ports (#1492)

E2E runs on **reserved ports** (`scripts/test-ports.ts`): Vite 4573, backend 4728 (ws) / 4729 (MCP). A running Tandem or `dev:server` on 3478/3479 is fine and is not touched. Three distinct early-abort signatures:

- **Playwright's "http://127.0.0.1:4729/health is already used"** — something (usually a stale E2E server) is answering the reserved MCP port; the backend entry is `reuseExistingServer: false`. Recovery: `fuser -k 4728/tcp 4729/tcp 4573/tcp` and rerun.
- **"Refusing to run this Playwright suite against a server it did not start"** — the `scripts/e2e-guard.ts` identity probe (#1483, now defense-in-depth): something answered :4729 that it could not prove is an E2E server.
- **"…serving a client that does NOT target the harness backend"** — the guard's served-client check: the Vite on :4573 was launched without `VITE_TANDEM_*` (e.g. hand-started), so its client is baked to the product ports and would drive the suite into a real Tandem through the UI. Stop that Vite and let Playwright start its own.

Anything holding the reserved pair that *fails* the health check — wedged, non-HTTP, or sitting on the never-probed :4728 — is silently SIGKILLed by the E2E server's own boot (`freePort()`); that self-healing is why the reserved numbers must never collide with ports any doc tells users to occupy.

## Pre-Flight Checklist

Before running E2E tests, verify:

```bash
# 1. Server bundle exists (E2E uses pre-built server, not tsx)
ls dist/server/index.js

# 2. Reserved ports are free (a holder is either refused or killed — see above)
curl -sf http://127.0.0.1:4729/health && echo "⚠ Server on :4729 (stale E2E?)" || echo "✓ Port free"
curl -sf http://127.0.0.1:4728 && echo "⚠ Something on :4728" || echo "✓ Port free"

# 3. Client build exists (for webServer)
ls dist/client/index.html
```

If `dist/server/index.js` is missing or stale:
```bash
npm run build:server
```

## Failure Categories

### Timeout waiting for health endpoint
**Symptom**: `Timed out waiting for http://127.0.0.1:4729/health`
**Cause**: `dist/server/` is stale or missing
**Fix**: `npm run build:server` then retry

### net::ERR_CONNECTION_REFUSED on :4573
**Symptom**: Client page fails to load
**Cause**: Vite webServer not started (check `playwright.config.ts` webServer section)
**Fix**: Verify `npm run dev -- --port 4573 --strictPort` works standalone; check for port conflicts on :4573.
A hand-started Vite serves a client baked to the PRODUCT ports and the guard will refuse it — if you export
`VITE_TANDEM_WS_PORT`/`VITE_TANDEM_MCP_PORT` to work around that, **unset them again**. They are read from the
ambient shell at build time, so a stale export bakes a harness port into any `npm run build` you run afterwards.
(`scripts/build-client.mjs` strips them for exactly this reason, and CI greps the bundle — but a bare `vite build`
does not.)

### Stale openDocuments / phantom tabs
**Symptom**: Tests find unexpected documents open or wrong tab state
**Cause**: Prior test crash left session state on disk
**Fix**: Delete session directory:
- Windows: `%LOCALAPPDATA%\tandem\Data\sessions\`
- macOS: `~/Library/Application Support/tandem/sessions/`
- Linux: `~/.local/share/tandem/sessions/`

### data-testid not found
**Symptom**: `locator.click: Error: strict mode violation` or element not found
**Cause**: testid was renamed or component restructured
**Fix**: Check CLAUDE.md Critical Rule #7 for the current testid list. Use `[data-testid="..."]` selectors, not CSS classes.

### WebServer cold-start race
**Symptom**: First test in suite fails, rest pass
**Cause**: Known Playwright webServer cold-start issue (#230)
**Fix**: The project uses a retry-on-first-failure workaround. If this regresses, check `playwright.config.ts` for the `retries` and `webServer.timeout` settings.

## One-Off Debug Run

For a single spec with browser visible:
```bash
npx playwright test tests/e2e/specific.spec.ts --headed --workers=1
```

For trace collection on failure:
```bash
npx playwright test tests/e2e/specific.spec.ts --trace on
```

View the trace:
```bash
npx playwright show-trace test-results/specific-spec-ts/trace.zip
```

## Common Patterns

- **Display-toggled panels**: ChatPanel and SidePanel are always mounted (CSS `display` toggle). Use `toBeVisible()` not `toBeAttached()`.
- **ESM __dirname**: E2E test files must use `import.meta.url` + `fileURLToPath`, not `__dirname`.
- **Uploaded files are read-only**: `upload://` paths from test fixtures don't support `tandem_save`.

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.