deer-flow/frontend/tests/e2e/branch-thread.spec.ts
Ryker_Feng 186c6ea463
feat: add branching support for assistant turns (#3950)
* feat: add assistant turn branching

* fix(threads): skip workspace clone when branching from historical turn

Workspace files are not checkpointed, so cloning them onto a branch
rooted at an older assistant turn leaked files created in a later
timeline. Restrict the best-effort workspace copy to branches taken from
the latest turn; historical-turn branches now report
workspace_clone_mode="skipped_historical_turn" and keep only the
restored message history.

* style(frontend): fix prettier formatting in e2e mock-api

Collapse the branch-title normalization chain onto a single line to
satisfy the frontend lint (prettier --check) CI gate.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-07-06 07:40:41 +08:00

66 lines
1.7 KiB
TypeScript

import { expect, test } from "@playwright/test";
import {
MOCK_THREAD_ID,
MOCK_THREAD_ID_2,
mockLangGraphAPI,
} from "./utils/mock-api";
test.describe("Branch from turn", () => {
test("creates a new chat branch from a completed assistant turn", async ({
page,
}) => {
mockLangGraphAPI(page, {
threads: [
{
thread_id: MOCK_THREAD_ID,
title: "Original chat",
messages: [
{
type: "human",
id: "human-1",
content: [{ type: "text", text: "First question" }],
},
{
type: "ai",
id: "ai-1",
content: "First answer",
},
{
type: "human",
id: "human-2",
content: [{ type: "text", text: "Second question" }],
},
{
type: "ai",
id: "ai-2",
content: "Second answer",
},
],
},
],
});
await page.goto(`/workspace/chats/${MOCK_THREAD_ID}`);
const targetTurn = page
.locator("[data-assistant-turn]")
.filter({ hasText: "Second answer" });
await expect(targetTurn).toBeVisible();
await targetTurn.hover();
await targetTurn
.getByRole("button", { name: /branch conversation/i })
.click();
await expect(page).toHaveURL(
new RegExp(`/workspace/chats/${MOCK_THREAD_ID_2}$`),
);
await expect(page.getByText("Second answer")).toBeVisible();
const branchThreadLink = page.locator(
`a[href="/workspace/chats/${MOCK_THREAD_ID_2}"]`,
);
await expect(branchThreadLink).toContainText("Original chat");
await expect(branchThreadLink).not.toContainText("Branch:");
});
});