import React from "react"; import { describe, expect, it, vi, beforeEach, afterEach } from "vitest"; import { render, act } from "@testing-library/react"; import { MemoryRouter } from "react-router-dom"; // Hoisted mock icons factory const hoistedIcons = vi.hoisted(() => { const iconNames = [ "AudioOutlined", "ThunderboltOutlined", "CheckCircleFilled", "CheckCircleOutlined", "CloseCircleOutlined", "LoadingOutlined", "PlayCircleOutlined", "PauseCircleOutlined", "DownloadOutlined", "ShareAltOutlined", "SaveOutlined", "PlusOutlined", "MinusOutlined", "CloseOutlined", "SearchOutlined", "EditOutlined", "DeleteOutlined", "UploadOutlined", "FolderOutlined", "FolderAddOutlined", "MoreOutlined", "ExperimentOutlined", "ExclamationCircleOutlined", "InboxOutlined", "VideoCameraOutlined", "PictureOutlined", "SoundOutlined", "UserOutlined", "ManOutlined", "WomanOutlined", "TagsOutlined", "MutedOutlined", "RobotOutlined", "UnorderedListOutlined", "AppstoreOutlined", "UndoOutlined", "RedoOutlined", "SettingOutlined", "HistoryOutlined", "FilterOutlined", "FontColorsOutlined", "BgColorsOutlined", "MusicOutlined", "ScissorOutlined", "BulbOutlined", "FundOutlined", "LayoutOutlined", "ColumnHeightOutlined", "SwapOutlined", "LeftOutlined", "RightOutlined", "UpOutlined", "DownOutlined", "CopyOutlined", ]; const icons: Record = {}; iconNames.forEach((name) => { icons[name] = () => React.createElement("span", null, name.charAt(0)); }); return icons; }); // === React Query mock === vi.mock("@tanstack/react-query", () => ({ useQuery: vi.fn(() => ({ data: undefined, isLoading: false, isError: false, refetch: vi.fn(), })), useMutation: vi.fn(() => ({ mutate: vi.fn(), mutateAsync: vi.fn(), isLoading: false, })), useQueryClient: vi.fn(() => ({ invalidateQueries: vi.fn(), setQueryData: vi.fn(), getQueryData: vi.fn(), })), useInfiniteQuery: vi.fn(() => ({ data: { pages: [] }, isLoading: false, fetchNextPage: vi.fn(), hasNextPage: false, })), })); // === Ant Design Icons mock === vi.mock("@ant-design/icons", () => hoistedIcons); // === Ant Design mock === vi.mock("antd", () => { const Typography = { Text: ({ children }: any) => React.createElement("span", null, children), Title: ({ children }: any) => React.createElement("h1", null, children), Paragraph: ({ children }: any) => React.createElement("p", null, children), }; return { Typography, message: { success: vi.fn(), error: vi.fn(), warning: vi.fn(), info: vi.fn(), }, Modal: ({ open, children, title }: any) => open ? React.createElement( "div", { role: "dialog", "data-title": title }, children, ) : null, Progress: () => React.createElement("div"), Popover: ({ children }: any) => React.createElement("span", null, children), Popconfirm: ({ children }: any) => React.createElement("span", null, children), Tooltip: ({ children }: any) => React.createElement("span", null, children), Tabs: () => React.createElement("div"), Drawer: ({ open, children }: any) => open ? React.createElement("div", { role: "dialog" }, children) : null, Upload: ({ children }: any) => React.createElement("div", null, children), Slider: () => React.createElement("div"), Switch: () => React.createElement("input", { type: "checkbox" }), Segmented: () => React.createElement("div"), Spin: () => React.createElement("div", null, "Loading"), Empty: () => React.createElement("div", null, "Empty"), Divider: () => React.createElement("hr"), Space: ({ children }: any) => React.createElement("div", null, children), Dropdown: ({ children }: any) => React.createElement("span", null, children), Menu: () => React.createElement("div"), Badge: ({ children }: any) => React.createElement("span", null, children), Radio: ({ children }: any) => React.createElement("span", null, children), RadioGroup: ({ children }: any) => React.createElement("div", null, children), Checkbox: ({ children }: any) => React.createElement("span", null, children), InputNumber: () => React.createElement("input", { type: "number" }), Form: ({ children }: any) => React.createElement("form", null, children), FormItem: ({ children }: any) => React.createElement("div", null, children), Result: ({ status, title }: any) => React.createElement("div", { "data-status": status }, title), List: () => React.createElement("div"), Table: () => React.createElement("div"), Pagination: () => React.createElement("div"), Card: ({ children }: any) => React.createElement("div", null, children), Avatar: ({ children }: any) => React.createElement("span", null, children), Collapse: ({ children }: any) => React.createElement("div", null, children), CollapsePanel: ({ children }: any) => React.createElement("div", null, children), Tag: ({ children }: any) => React.createElement("span", null, children), Button: ({ children, onClick }: any) => React.createElement("button", { onClick }, children), Input: ({ placeholder }: any) => React.createElement("input", { placeholder }), Select: ({ children }: any) => React.createElement("select", null, children), Option: ({ children }: any) => React.createElement("option", null, children), ConfigProvider: ({ children }: any) => React.createElement(React.Fragment, null, children), TextArea: ({ placeholder }: any) => React.createElement("textarea", { placeholder }), Steps: () => React.createElement("div"), Step: () => React.createElement("div"), }; }); // === UI Components mock === vi.mock("@/components/ui", () => ({ Button: ({ children, onClick }: any) => React.createElement("button", { onClick }, children), Input: ({ placeholder }: any) => React.createElement("input", { placeholder }), Modal: ({ open, children }: any) => open ? React.createElement("div", { role: "dialog" }, children) : null, Drawer: ({ open, children }: any) => open ? React.createElement("div", { role: "dialog" }, children) : null, Empty: () => React.createElement("div", null, "Empty"), Card: ({ children }: any) => React.createElement("div", null, children), Tag: ({ children }: any) => React.createElement("span", null, children), Tooltip: ({ children }: any) => React.createElement("span", null, children), Select: ({ children }: any) => React.createElement("select", null, children), Progress: () => React.createElement("div"), Upload: ({ children }: any) => React.createElement("div", null, children), Slider: () => React.createElement("div"), Switch: () => React.createElement("input", { type: "checkbox" }), })); // === API mocks === vi.mock("@/api/assets", () => ({ getAssetLibraries: vi.fn().mockResolvedValue([]), createAssetLibrary: vi.fn().mockResolvedValue({}), getAssets: vi.fn().mockResolvedValue({ items: [], total: 0 }), getAssetsByKind: vi.fn().mockResolvedValue({ items: [], total: 0 }), smartMatchAssets: vi.fn().mockResolvedValue({ items: [] }), updateAsset: vi.fn().mockResolvedValue({}), deleteAsset: vi.fn().mockResolvedValue({}), uploadAssetDirect: vi.fn().mockResolvedValue({}), batchDeleteAssets: vi.fn().mockResolvedValue({}), isAssetUsable: vi.fn().mockReturnValue(true), AssetType: { VIDEO: "video", IMAGE: "image", AUDIO: "audio", VOICE: "voice" }, })); vi.mock("@/api/tags", () => ({ getTags: vi.fn().mockResolvedValue({ items: [] }), createTag: vi.fn().mockResolvedValue({}), tagAsset: vi.fn().mockResolvedValue({}), untagAsset: vi.fn().mockResolvedValue({}), })); vi.mock("@/api/tts", () => ({ synthesizeSpeech: vi.fn().mockResolvedValue({ job_id: "test-job" }), getTTSJobStatus: vi .fn() .mockResolvedValue({ status: "completed", audio_url: "" }), saveTtsToLibrary: vi.fn().mockResolvedValue({}), })); vi.mock("@/api/voices", () => ({ fetchPresetVoices: vi.fn().mockResolvedValue({ items: [] }), })); vi.mock("@/api/editing-planner", () => ({ getEditingTemplates: vi.fn().mockResolvedValue({ items: [], total: 0 }), MODE_LABELS: { pip: "画中画" }, })); vi.mock("@/api/titles", () => ({ getTitles: vi.fn().mockResolvedValue({ items: [] }), })); vi.mock("@/api/template-editor", () => ({ updateEditPlan: vi .fn() .mockResolvedValue({ plan_id: "test-plan", template_id: "test-template" }), getEditPlan: vi.fn().mockResolvedValue({ plan_id: "test-plan", template_id: "test-template", name: "", config: {}, status: "draft", }), getGenerationTaskResults: vi.fn().mockResolvedValue({ items: [] }), })); vi.mock("@/api/voice-clone", () => ({ formatDuration: vi.fn((s: number) => `${s}s`), })); vi.mock("@/api/client", () => ({ default: { interceptors: { request: { handlers: [] }, response: { handlers: [] } }, get: vi.fn().mockResolvedValue({ data: {} }), post: vi.fn().mockResolvedValue({ data: {} }), put: vi.fn().mockResolvedValue({ data: {} }), delete: vi.fn().mockResolvedValue({ data: {} }), }, })); // === Hooks mock === vi.mock("@/hooks/useCloneProgress", () => ({ useCloneProgress: vi.fn(() => ({ progress: 0, status: "idle", start: vi.fn(), reset: vi.fn(), })), })); // === Components mock === vi.mock("@/components/voice/CloneModal", () => ({ default: () => React.createElement("div", { "data-testid": "CloneModal" }), })); // === PageHead mock === vi.mock("@/components/layout/PageHead", () => ({ default: ({ title }: { title: string }) => React.createElement("div", { "data-testid": "page-head" }, title), })); // CSS mock vi.mock("@/pages/generate/generate.css", () => ({})); import GeneratePage from "@/pages/generate/GeneratePage"; describe("GeneratePage", () => { beforeEach(() => { vi.useFakeTimers(); }); afterEach(() => { vi.useRealTimers(); }); it("renders without crashing", () => { const { container } = render( React.createElement( MemoryRouter, null, React.createElement(GeneratePage), ), ); expect(container).toBeTruthy(); }); it("advances timers without errors", () => { render( React.createElement( MemoryRouter, null, React.createElement(GeneratePage), ), ); act(() => { vi.advanceTimersByTime(30000); }); expect(true).toBe(true); }); });