Skip to content
File

Blob: tests/client/components/editor/document-page.dom.test.tsx

typescript108 lines
1import { useEffect } from "react";
2import { act } from "react";
3import { createRoot, type Root } from "react-dom/client";
4import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
5import type { EditorAffordance } from "@/client/lib/affordance/editor";
6 
7const affordance: EditorAffordance = {
8 documentEditable: true,
9 canInsertPageMentions: true,
10 canInsertImages: true,
11 canUseAiRewrite: true,
12 canUseAiGenerate: true,
13 canSummarizePage: true,
14 canAskPage: true,
15};
16 
17beforeEach(() => {
18 vi.resetModules();
19});
20 
21afterEach(() => {
22 vi.restoreAllMocks();
23 document.body.innerHTML = "";
24});
25 
26describe("DocumentPage", () => {
27 it("does not retain schema errors across keyed page identity changes", async () => {
28 let pageAErrored = false;
29 
30 vi.doMock("@/client/hooks/use-media-query", () => ({
31 useMediaQuery: () => false,
32 }));
33 vi.doMock("@/client/lib/report-client-error", () => ({
34 reportClientError: vi.fn(),
35 }));
36 vi.doMock("@/client/components/toast-store", () => ({
37 toast: {
38 error: vi.fn(),
39 },
40 }));
41 vi.doMock("@/client/components/editor/use-editor-session", () => ({
42 useEditorSession: (opts: { initialTitle: string }) => ({
43 kind: "ready",
44 title: opts.initialTitle,
45 onTitleInput: vi.fn(),
46 fragment: {},
47 provider: {},
48 }),
49 }));
50 vi.doMock("@/client/components/editor/editor-body", () => ({
51 EditorBody: ({ pageId, onSchemaError }: { pageId: string; onSchemaError?: (error: Error) => void }) => {
52 useEffect(() => {
53 if (pageId !== "page-a" || pageAErrored) return;
54 pageAErrored = true;
55 onSchemaError?.(new Error("bad schema"));
56 }, [onSchemaError, pageId]);
57 return <div>{`Editor ${pageId}`}</div>;
58 },
59 }));
60 
61 const { DocumentPage } = await import("@/client/components/editor/document-page");
62 
63 function Harness({ pageId }: { pageId: string }) {
64 return (
65 <DocumentPage
66 key={pageId}
67 pageId={pageId}
68 initialTitle={pageId}
69 workspaceId="workspace-1"
70 affordance={affordance}
71 outlineMode="inline"
72 chrome={<div>Chrome</div>}
73 />
74 );
75 }
76 
77 const host = document.createElement("div");
78 document.body.appendChild(host);
79 let root: Root | null = createRoot(host);
80 
81 try {
82 await act(async () => {
83 root!.render(<Harness pageId="page-a" />);
84 });
85 
86 expect(host.textContent).toContain("This build can't parse this page. Reload to catch up.");
87 
88 await act(async () => {
89 root!.render(<Harness pageId="page-b" />);
90 });
91 
92 expect(host.textContent).toContain("Editor page-b");
93 
94 await act(async () => {
95 root!.render(<Harness pageId="page-a" />);
96 });
97 
98 expect(host.textContent).toContain("Editor page-a");
99 } finally {
100 await act(async () => {
101 root?.unmount();
102 });
103 root = null;
104 host.remove();
105 }
106 });
107});