"Run Playwright E2E (tests/e2e/) against the docker/ all-in-one harness so it reflects LOCAL code changes, not a stale cached image."
Install
npx skillscat add blackbelttechnology/pi-agent-dashboard/run-dashboard-e2e-local-changes Install via the SkillsCat registry.
SKILL.md
When to Use
Use when validating local dashboard changes via the Docker browser-E2E harness (npm run test:e2e), or when a new/edited E2E spec mysteriously fails to see your code. The harness's test-up.sh runs docker compose up WITHOUT --build, so it reuses a cached pi-dashboard:local image and your committed changes are NOT in the running container until you rebuild.
Procedure
- Rebuild the image from current source FIRST:
docker compose -f docker/compose.yml build(Dockerfile COPYs packages/ + runs npm install && npm run build → bakes local source). Slow (~4-6 min: apt + npm install + build). - Run the suite (or one spec):
npm run test:e2eornpx playwright test <spec-name>. Playwright globalSetup runs docker/test-up.sh (compose up, reuses the just-built image), waits for /api/health 200, runs specs at http://localhost:18000, globalTeardown runs test-down.sh (removes container+volumes+network). - Fast iteration after a build:
PW_E2E_USE_RUNNING=1 npm run test:e2eattaches to an already-running harness and skips teardown. - Spec conventions: select existing app
data-testids (helpers/index.ts TESTIDS map for static keys; page.getByTestId(...-${id}) for dynamic). Navigate Settings→Packages via page.goto('/settings/packages') then wait for testidpackage-browser; RecommendedExtensions card renders inside it (non-collapsible Section).
Pitfalls
- Under heavy host load (multiple worktree test containers + many pi sessions), the managed
npm run test:e2eglobalSetup can blow its 180s health cap even with a pre-built image. Robust fallback = manual up + attach mode for a SINGLE spec: (1) pre-build the per-worktree tag; (2) bring the container up yourself WITH the seed envPI_E2E_SEED=1 PI_TEST_PEERS=both ./docker/test-up.sh -d(writes.pi-test-harness.jsonwith the derived port); (3) pollcurl :$PORT/api/healthuntil 200; (4) runPW_E2E_USE_RUNNING=1 PW_E2E_PORT=$PORT PW_CHANNEL=chrome npx playwright test <spec>(skips globalSetup build + teardown); (5)./docker/test-down.shafter. - CO-RESIDENT HARNESS GUARD (#451):
test-up.shcompares(n+1) × MEM_LIMITagainst the daemon'sMemTotal(n = other runningpi-dash-test-*projects; defaultMEM_LIMIT=4g). It REFUSES to start when(n+1) × MEM_LIMIT >= MemTotal— the refusal runs before the image build and before.pi-test-harness.jsonis written, and names the other project + the arithmetic. Free the peer (./docker/test-down.shfrom ITS worktree) or override withPI_HARNESS_ALLOW_OVERSUBSCRIBE=1. When the limits fit but a peer is up it proceeds with ONE warning: a red result is then not attributable to your change while the peer lives. An unreadableMEM_LIMIT/docker infowarns and proceeds (never refuses on missing data). Seedocker/TESTING.md. - NO COMMITTED WALL-CLOCK BUDGET (#450):
playwright.config.tssets noglobalTimeout, so a full local run is unbounded by design — the old 15-min budget aborted long runs and reported a timeout instead of a verdict. Termination is bounded by the per-testtimeout(60s),expect.timeout(10s) and the harness-down short-circuit (3 probe failures → remaining specs skipped). Want a cap? Pass--global-timeout=<ms>on the CLI; never re-add it to the config (the CI shard job ownstimeout-minutes). CI adds ablobreporter (sharded workflow.github/workflows/ci-e2e-browser.yml); local runs keeplist+html. - MANUAL test-up MUST mirror the managed globalSetup env:
PI_E2E_SEED=1 PI_TEST_PEERS=both PI_E2E_OAUTH=1(and passPW_GATEWAY_PORT=<gatewayPort>from.pi-test-harness.jsonwhen attaching). WithoutPI_E2E_OAUTH=1the MCP token specs (mcp-token-settings,mcp-tiered-token) narrowtrustedNetworksto[], cannot restore it (their JWT cookie is not honoured), and every later spec fails withOffBoxError: ticket mint denied-- a whole-suite cascade that looks like a regression but is harness config. - MANDATORY seed env on manual
test-up.sh: withoutPI_E2E_SEED=1the onboarding "Add folder" CTA renders DISABLED (title="Set up credentials first", gated onprovidersReady), sospawnFreshGitSession/pinDirectorytime out at 60s. The managed globalSetup setsPI_E2E_SEED=1+PI_TEST_PEERS=both; a hand-rolledtest-up.sh -ddoes NOT — pass them explicitly. - Specs needing global roles/models data (e.g.
roles-custom.spec.tsassertingbuiltinRoleNames-driven UI) MUST spawn a live session first: the client only sendsrequest_roles/request_modelsthrough the first non-ended session (App.tsx), so with zero sessions the roles panel never receivesbuiltinRoleNamesand renders its flat back-compat layout.
Verification
docker compose -f docker/compose.yml buildends withImage pi-dashboard:local Built.npm run test:e2eprintsN passedand the teardown removes the pi-dash-test-* container/volumes/network.- A spec asserting your new UI (e.g. recommended-requires) passes only after the rebuild, not before.