Skip to main content

Chat E2E Testing Notes

Goal

Make chat routing and persistence regressions quick to verify without manually signing in, sending real Claude requests, or creating database rows. The Playwright coverage in e2e/tests/chat-routing-and-persistence.spec.ts mocks the chat APIs at the browser boundary. This keeps the tests fast and deterministic while still exercising the real chat page, React Query cache, route updates, and historical-session rendering.

What Was Manually Verified

  • Opening /chat while signed in renders the chat page and chat history.
  • Submitting a new message adds a history row immediately.
  • When /api/claude sends session_id, the URL changes to /chat/:sessionId.
  • The assistant response renders in the selected chat.
  • Refreshing /chat/:sessionId restores the selected chat instead of showing the new-chat empty state.
  • The shared history row had invalid nested <button> markup; this produced a React hydration warning and was fixed by making the row container a keyboard-accessible role="button" wrapper.
  • Chat completion was refetching /api/chat/sessions multiple times; this was reduced to a single React Query invalidation for the chat sessions key.

Automated Coverage

Run the focused tests:
The spec currently covers:
  • New chat optimistic history row appears before the mocked Claude stream resolves.
  • The mocked session_id event moves the browser from /chat to /chat/:sessionId.
  • The assistant response renders after the SSE stream completes.
  • Chat sessions are invalidated exactly once after completion.
  • Reloading /chat/:sessionId renders the persisted historical prompt and response.
  • Switching from one chat to another fetches only the selected session detail, preventing the previous current-session refetch pattern.

How The Mock Works

  • GET /api/chat/sessions returns an in-memory session list.
  • GET /api/chat/sessions/:sessionId returns in-memory messages.
  • POST /api/claude waits until the test releases it, then returns a custom SSE payload:
Because the Claude response is held open, the test can assert that the optimistic chat history row appears before the server sends the real session_id.
  • Running historical session: GET /api/chat/sessions/:id returns status="running" with partial response, and the chat body shows the generating indicator without falling back to a full skeleton.
  • Failed session: status="failed" renders the prompt plus recoverable error copy.
  • New Chat during active stream: verify the stale stream cannot write into the newly visible session.
  • Mobile viewport: submit on a small viewport and verify tab/background-style stream recovery behavior as far as Playwright can simulate.
  • Rename/delete: verify mixed running, completed, and failed rows still rename/delete all rows for a session.

Manual Smoke Checklist

Use this when validating with a real account and real Claude stream:
  1. Open /chat.
  2. Submit a short prompt.
  3. Confirm the history row appears immediately.
  4. Confirm the URL changes to /chat/:sessionId.
  5. Refresh that exact URL and confirm the selected chat returns.
  6. Click a different history row and confirm the page changes to that chat without flashing back to the previous chat or a skeleton.
  7. Start a chat, click New Chat, send another prompt, and confirm the sessions do not mix.
  8. Watch the terminal for repeated /api/chat/sessions/:id or excessive /api/chat/sessions calls.