Skip to content

Frontend Playwright e2e suite is stale and can run against the wrong renderer server #357

@areycruzer

Description

@areycruzer

Summary

The frontend Playwright e2e suite is currently unreliable on local runs. The tests are intended to run against dev:web with VITE_NO_ELECTRON=1, so useWorkspaceQuery serves deterministic mockWorkspaces. In practice, local runs can fail before assertions because Playwright targets 127.0.0.1:5173 while Vite can bind only to localhost / IPv6 loopback on macOS. If a different renderer is already listening on 127.0.0.1:5173, Playwright can also reuse that incompatible server because reuseExistingServer is enabled outside CI.

Even when the intended preview server is reachable, several checked-in workbench/history/inspector specs are stale: they still assert old route names and accessible labels that no longer match the current React UI.

Severity

P2 developer workflow / regression coverage bug.

This does not affect production behavior directly, but it makes the browser e2e suite noisy and can hide real UI regressions.

Repro A: fresh local e2e run can fail with connection refused

From frontend:

npm run test:e2e -- --workers=1 e2e/workbench.spec.ts

Observed failure:

Error: page.goto: net::ERR_CONNECTION_REFUSED at http://127.0.0.1:5173/

Manual check showed npm run dev:web -- --port 5173 advertised:

Local: http://localhost:5173/

and the listener was on IPv6 loopback:

node ... TCP [::1]:5173 (LISTEN)

localhost responded, but 127.0.0.1 did not:

curl -I http://localhost:5173/      # 200 OK
curl -I http://127.0.0.1:5173/     # connection refused

Repro B: with a reachable preview server, stale selectors/routes fail

Start preview explicitly:

npm run dev:web -- --host 127.0.0.1 --port 5173 --strictPort
npm run test:e2e -- --workers=1 e2e/workbench.spec.ts

Observed failures:

Locator: getByRole('button', { name: 'Orchestrator', exact: true })
Expected: visible
Error: element(s) not found
Locator: getByText('Changed')
Expected: visible
Error: element(s) not found
waiting for getByRole('button', { name: 'refactor-mux', exact: true })

Expected behavior

npm run test:e2e should start or use the intended deterministic preview renderer and pass against current UI routes/selectors.

Actual behavior

The suite can fail in multiple local modes:

  • Playwright tries 127.0.0.1:5173 while Vite is only reachable through localhost.
  • reuseExistingServer: !process.env.CI can silently attach to an already-running incompatible renderer on port 5173.
  • Workbench/history/inspector specs assert stale labels and routes.

Screenshot evidence

Current preview UI with mock data loaded. Notice the UI renders title-based session rows, not a visible refactor-mux button:

Current preview UI with mock data

Wrong-server scenario: if a daemon-backed renderer is already on 127.0.0.1:5173, Playwright reuses it and tests run against real local daemon state instead of mock fixtures:

Daemon-backed renderer reused by e2e

Code pointers / likely root cause

frontend/playwright.config.ts

use: {
  baseURL: "http://127.0.0.1:5173",
},
webServer: {
  command: "npm run dev:web -- --port 5173",
  port: 5173,
  reuseExistingServer: !process.env.CI,
},

The command does not force --host 127.0.0.1, but the tests use 127.0.0.1.

frontend/package.json

"dev:web": "VITE_NO_ELECTRON=1 vite --config vite.renderer.config.ts"

frontend/src/renderer/hooks/useWorkspaceQuery.ts

const usePreviewData = import.meta.env.VITE_NO_ELECTRON === "1";

The e2e tests rely on this preview mode.

frontend/e2e/workbench.spec.ts

await expect(page.getByRole("button", { name: "Orchestrator", exact: true })).toBeVisible();
await page.goto("/#/workspaces/api-gateway/sessions/refactor-mux");
await page.getByRole("button", { name: "refactor-mux", exact: true }).click();

Current generated routes use /projects/$projectId/sessions/$sessionId, not /workspaces/....

frontend/src/renderer/lib/mock-data.ts

id: "refactor-mux",
title: "Split terminal mux responsibilities",

The current visible session button is title-based, not exact refactor-mux.

frontend/src/renderer/components/Sidebar.tsx

Current accessible names include:

aria-label="Orchestrator board"
aria-label={orchestrator ? `Open ${workspace.name} orchestrator` : `Spawn ${workspace.name} orchestrator`}

There is no exact button[name="Orchestrator"] in the current shell.

Suggested fix shape

  • Update frontend/playwright.config.ts to make the server URL deterministic:
    • add --host 127.0.0.1
    • consider --strictPort
    • consider disabling reuseExistingServer or using a dedicated e2e-only port to avoid attaching to the wrong renderer
  • Update stale selectors/routes in:
    • frontend/e2e/workbench.spec.ts
    • frontend/e2e/history-nav.spec.ts
    • frontend/e2e/inspector-toggle.spec.ts
  • Prefer selectors that match current accessible names:
    • Open Split terminal mux responsibilities
    • /projects/api-gateway/sessions/refactor-mux
    • Orchestrator board or project-specific orchestrator button labels, depending on intended behavior
  • Re-run the full browser suite:
npm --prefix frontend run test:e2e

Duplicate / overlap check

Searched current issues and PRs for:

  • Playwright e2e VITE_NO_ELECTRON reuseExistingServer
  • e2e workbench refactor-mux workspaces
  • test:e2e playwright
  • refactor-mux
  • exact title text

No existing issue matched this exact bug.

Closest related PRs checked:

Contribution value

This is a good focused issue because:

  • It is reproducible locally without credentials.
  • It stays inside frontend test/config boundaries.
  • It improves developer confidence in the e2e suite.
  • It should be a small PR: stabilize Playwright server startup and refresh stale selectors/routes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions