diff --git a/apps/web/components/document-modal/index.test.tsx b/apps/web/components/document-modal/index.test.tsx new file mode 100644 index 00000000..80548e64 --- /dev/null +++ b/apps/web/components/document-modal/index.test.tsx @@ -0,0 +1,197 @@ +import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test" +import { Window } from "happy-dom" +import { type ComponentProps, type ReactNode, act } from "react" +import { createRoot, type Root } from "react-dom/client" + +const browser = new Window({ url: "http://localhost/" }) + +Object.assign(globalThis, { + window: browser, + document: browser.document, + navigator: browser.navigator, + location: browser.location, + history: browser.history, + localStorage: browser.localStorage, + sessionStorage: browser.sessionStorage, + HTMLElement: browser.HTMLElement, + Element: browser.Element, + Node: browser.Node, + MutationObserver: browser.MutationObserver, + getComputedStyle: browser.getComputedStyle.bind(browser), + requestAnimationFrame: browser.requestAnimationFrame.bind(browser), + cancelAnimationFrame: browser.cancelAnimationFrame.bind(browser), +}) +Object.assign(browser, { SyntaxError }) +;( + globalThis as typeof globalThis & { + IS_REACT_ACT_ENVIRONMENT: boolean + } +).IS_REACT_ACT_ENVIRONMENT = true + +function PassThrough({ children }: { children?: ReactNode }) { + return <>{children}> +} + +type MotionProps = { + initial?: unknown + animate?: unknown + exit?: unknown + transition?: unknown +} + +function MotionButton({ + initial: _initial, + animate: _animate, + exit: _exit, + transition: _transition, + ...props +}: ComponentProps<"button"> & MotionProps) { + return +} + +function MotionDiv({ + initial: _initial, + animate: _animate, + exit: _exit, + transition: _transition, + ...props +}: ComponentProps<"div"> & MotionProps) { + return
+} + +// Keep DocumentModal and its private DeleteButton real; only remove unrelated UI. +mock.module(import.meta.resolve("./title"), () => ({ Title: PassThrough })) +mock.module(import.meta.resolve("./summary"), () => ({ Summary: PassThrough })) +mock.module(import.meta.resolve("./graph-list-memories"), () => ({ + GraphListMemories: PassThrough, +})) +mock.module(import.meta.resolve("./content"), () => ({ + DocumentContent: PassThrough, +})) + +mock.module("@repo/ui/components/dialog", () => ({ + Dialog: PassThrough, + DialogContent: PassThrough, + DialogTitle: PassThrough, +})) +mock.module("@repo/ui/components/drawer", () => ({ + Drawer: PassThrough, + DrawerContent: PassThrough, + DrawerTitle: PassThrough, +})) +mock.module("@ui/components/tabs", () => ({ + Tabs: PassThrough, + TabsContent: PassThrough, + TabsList: PassThrough, + TabsTrigger: PassThrough, +})) +mock.module("@radix-ui/react-dialog", () => ({ Close: PassThrough })) +mock.module("motion/react", () => ({ + AnimatePresence: PassThrough, + motion: { button: MotionButton, div: MotionDiv }, +})) +mock.module("@hooks/use-mobile", () => ({ useIsMobile: () => false })) +mock.module("@/lib/fonts", () => ({ dmSansClassName: () => "" })) +mock.module("@/lib/plugin-document", () => ({ + parsePluginDocument: () => null, +})) +mock.module("@/hooks/use-full-document", () => ({ + useFullDocumentContent: () => null, +})) +mock.module("sonner", () => ({ + toast: { error: mock(() => {}), success: mock(() => {}) }, +})) + +const deleteMutate = mock((_variables: { documentId: string }) => {}) +const deleteMutation = { isPending: false, mutate: deleteMutate } +const updateMutation = { isPending: false, mutate: mock(() => {}) } + +mock.module("@/hooks/use-document-mutations", () => ({ + useDocumentMutations: () => ({ deleteMutation, updateMutation }), +})) + +const { DocumentModal } = await import("./index") + +type ModalDocument = NonNullable< + ComponentProps