deer-flow/frontend/tests/e2e/thread-history.spec.ts
yangzheli c6b0423558
feat(frontend): add Playwright E2E tests with CI workflow (#2279)
* feat(frontend): add Playwright E2E tests with CI workflow

Add end-to-end testing infrastructure using Playwright (Chromium only).
14 tests across 5 spec files cover landing page, chat workspace,
thread history, sidebar navigation, and agent chat — all with mocked
LangGraph/Backend APIs via network interception (zero backend dependency).

New files:
- playwright.config.ts — Chromium, 30s timeout, auto-start Next.js
- tests/e2e/utils/mock-api.ts — shared API mocks & SSE stream helpers
- tests/e2e/{landing,chat,thread-history,sidebar,agent-chat}.spec.ts
- .github/workflows/e2e-tests.yml — push main + PR trigger, paths filter

Updated: package.json, Makefile, .gitignore, CONTRIBUTING.md,
frontend/CLAUDE.md, frontend/AGENTS.md, frontend/README.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: apply Copilot suggestions

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-04-18 08:21:08 +08:00

77 lines
2.2 KiB
TypeScript

import { expect, test } from "@playwright/test";
import {
mockLangGraphAPI,
MOCK_THREAD_ID,
MOCK_THREAD_ID_2,
} from "./utils/mock-api";
const THREADS = [
{
thread_id: MOCK_THREAD_ID,
title: "First conversation",
updated_at: "2025-06-01T12:00:00Z",
},
{
thread_id: MOCK_THREAD_ID_2,
title: "Second conversation",
updated_at: "2025-06-02T12:00:00Z",
},
];
test.describe("Thread history", () => {
test("sidebar shows existing threads", async ({ page }) => {
mockLangGraphAPI(page, { threads: THREADS });
await page.goto("/workspace/chats/new");
// Both thread titles should appear in the sidebar
await expect(page.getByText("First conversation")).toBeVisible({
timeout: 15_000,
});
await expect(page.getByText("Second conversation")).toBeVisible();
});
test("clicking a thread in sidebar navigates to it", async ({ page }) => {
mockLangGraphAPI(page, { threads: THREADS });
await page.goto("/workspace/chats/new");
// Wait for sidebar to populate
const firstThread = page.getByText("First conversation");
await expect(firstThread).toBeVisible({ timeout: 15_000 });
// Click on the first thread
await firstThread.click();
// Should navigate to that thread's URL
await page.waitForURL(`**/workspace/chats/${MOCK_THREAD_ID}`);
await expect(page).toHaveURL(new RegExp(MOCK_THREAD_ID));
});
test("existing thread loads historical messages", async ({ page }) => {
mockLangGraphAPI(page, { threads: THREADS });
// Navigate directly to an existing thread
await page.goto(`/workspace/chats/${MOCK_THREAD_ID}`);
// The historical AI response should be displayed
await expect(
page.getByText("Response in thread First conversation"),
).toBeVisible({ timeout: 15_000 });
});
test("chats list page shows all threads", async ({ page }) => {
mockLangGraphAPI(page, { threads: THREADS });
await page.goto("/workspace/chats");
// Both threads should be listed in the main content area
const main = page.locator("main");
await expect(main.getByText("First conversation")).toBeVisible({
timeout: 15_000,
});
await expect(main.getByText("Second conversation")).toBeVisible();
});
});