import { act } from "react"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { createRoot, type Root } from "react-dom/client"; import { PreferredSidebarSnapshotProvider } from "@/components/sidebar/preferred-sidebar-snapshot-context"; import type { SidebarInitialData } from "@/components/sidebar/types"; import { buildSidebarInitialData } from "@/lib/sidebar-data"; import type { DocumentRecord } from "@/lib/documents"; import { usePageHeadTitle } from "./use-page-head-title"; function buildDocument(overrides: Partial = {}): DocumentRecord { return { access_scope: "private", id: "doc-1", workspace_id: "ws-1", title: "标题 A", parent_id: null, sort_order: 0, is_starred: false, is_template: false, created_at: "2026-04-21T00:00:00.000Z", updated_at: "2026-04-21T00:00:00.000Z", ...overrides, }; } function buildSidebarData(documents: DocumentRecord[]): SidebarInitialData { return buildSidebarInitialData({ activeWorkspaceId: "ws-1", workspaces: [], documents, trashedDocuments: [], mindmaps: [], mediaAssets: [], trashedMediaAssets: [], tables: [], }); } function HookProbe(props: { documentId: string; fallbackTitle: string }) { const state = usePageHeadTitle(props); return ( <>
); } describe("usePageHeadTitle", () => { let container: HTMLDivElement; let root: Root; beforeEach(() => { (globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; container = document.createElement("div"); document.body.appendChild(container); root = createRoot(container); }); afterEach(() => { act(() => { root.unmount(); }); container.remove(); }); it("应优先使用 live sidebar snapshot 中的标题作为 committed title", async () => { const snapshot = buildSidebarData([ buildDocument({ id: "doc-1", title: "树标题", }), ]); await act(async () => { root.render( , ); }); const probe = container.querySelector("[data-testid='page-head-title']"); expect(probe?.getAttribute("data-committed-title")).toBe("树标题"); expect(probe?.getAttribute("data-display-title")).toBe("树标题"); expect(probe?.getAttribute("data-has-draft")).toBe("0"); }); it("应只把本地输入保留为短暂 draft,并在 live 标题追平后自动清空", async () => { const initialSnapshot = buildSidebarData([ buildDocument({ id: "doc-1", title: "旧标题", }), ]); await act(async () => { root.render( , ); }); const setDraftButton = container.querySelector("[data-testid='set-draft']"); act(() => { setDraftButton?.click(); }); let probe = container.querySelector("[data-testid='page-head-title']"); expect(probe?.getAttribute("data-display-title")).toBe(" 新标题 "); expect(probe?.getAttribute("data-committed-title")).toBe("旧标题"); expect(probe?.getAttribute("data-has-draft")).toBe("1"); const commitPersistedButton = container.querySelector("[data-testid='commit-persisted']"); act(() => { commitPersistedButton?.click(); }); probe = container.querySelector("[data-testid='page-head-title']"); expect(probe?.getAttribute("data-display-title")).toBe("新标题"); expect(probe?.getAttribute("data-has-draft")).toBe("1"); const syncedSnapshot = buildSidebarData([ buildDocument({ id: "doc-1", title: "新标题", updated_at: "2026-04-21T00:00:02.000Z", }), ]); await act(async () => { root.render( , ); }); probe = container.querySelector("[data-testid='page-head-title']"); expect(probe?.getAttribute("data-display-title")).toBe("新标题"); expect(probe?.getAttribute("data-committed-title")).toBe("新标题"); expect(probe?.getAttribute("data-has-draft")).toBe("0"); }); });