style: format GeneratePage.test.tsx with prettier
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled

This commit is contained in:
2026-09-14 21:20:16 +08:00
parent 81fd9eceb3
commit 75bec86e9f
+99 -62
View File
@@ -1,7 +1,7 @@
import React from "react" import React from "react";
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest" import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
import { render, act } from "@testing-library/react" import { render, act } from "@testing-library/react";
import { MemoryRouter } from "react-router-dom" import { MemoryRouter } from "react-router-dom";
// Hoisted mock icons factory // Hoisted mock icons factory
const hoistedIcons = vi.hoisted(() => { const hoistedIcons = vi.hoisted(() => {
@@ -60,13 +60,13 @@ const hoistedIcons = vi.hoisted(() => {
"UpOutlined", "UpOutlined",
"DownOutlined", "DownOutlined",
"CopyOutlined", "CopyOutlined",
] ];
const icons: Record<string, React.FC> = {} const icons: Record<string, React.FC> = {};
iconNames.forEach((name) => { iconNames.forEach((name) => {
icons[name] = () => React.createElement("span", null, name.charAt(0)) icons[name] = () => React.createElement("span", null, name.charAt(0));
}) });
return icons return icons;
}) });
// === React Query mock === // === React Query mock ===
vi.mock("@tanstack/react-query", () => ({ vi.mock("@tanstack/react-query", () => ({
@@ -92,10 +92,10 @@ vi.mock("@tanstack/react-query", () => ({
fetchNextPage: vi.fn(), fetchNextPage: vi.fn(),
hasNextPage: false, hasNextPage: false,
})), })),
})) }));
// === Ant Design Icons mock === // === Ant Design Icons mock ===
vi.mock("@ant-design/icons", () => hoistedIcons) vi.mock("@ant-design/icons", () => hoistedIcons);
// === Ant Design mock === // === Ant Design mock ===
vi.mock("antd", () => { vi.mock("antd", () => {
@@ -103,15 +103,27 @@ vi.mock("antd", () => {
Text: ({ children }: any) => React.createElement("span", null, children), Text: ({ children }: any) => React.createElement("span", null, children),
Title: ({ children }: any) => React.createElement("h1", null, children), Title: ({ children }: any) => React.createElement("h1", null, children),
Paragraph: ({ children }: any) => React.createElement("p", null, children), Paragraph: ({ children }: any) => React.createElement("p", null, children),
} };
return { return {
Typography, Typography,
message: { success: vi.fn(), error: vi.fn(), warning: vi.fn(), info: vi.fn() }, message: {
success: vi.fn(),
error: vi.fn(),
warning: vi.fn(),
info: vi.fn(),
},
Modal: ({ open, children, title }: any) => Modal: ({ open, children, title }: any) =>
open ? React.createElement("div", { role: "dialog", "data-title": title }, children) : null, open
? React.createElement(
"div",
{ role: "dialog", "data-title": title },
children,
)
: null,
Progress: () => React.createElement("div"), Progress: () => React.createElement("div"),
Popover: ({ children }: any) => React.createElement("span", null, children), Popover: ({ children }: any) => React.createElement("span", null, children),
Popconfirm: ({ children }: any) => React.createElement("span", null, children), Popconfirm: ({ children }: any) =>
React.createElement("span", null, children),
Tooltip: ({ children }: any) => React.createElement("span", null, children), Tooltip: ({ children }: any) => React.createElement("span", null, children),
Tabs: () => React.createElement("div"), Tabs: () => React.createElement("div"),
Drawer: ({ open, children }: any) => Drawer: ({ open, children }: any) =>
@@ -124,12 +136,15 @@ vi.mock("antd", () => {
Empty: () => React.createElement("div", null, "Empty"), Empty: () => React.createElement("div", null, "Empty"),
Divider: () => React.createElement("hr"), Divider: () => React.createElement("hr"),
Space: ({ children }: any) => React.createElement("div", null, children), Space: ({ children }: any) => React.createElement("div", null, children),
Dropdown: ({ children }: any) => React.createElement("span", null, children), Dropdown: ({ children }: any) =>
React.createElement("span", null, children),
Menu: () => React.createElement("div"), Menu: () => React.createElement("div"),
Badge: ({ children }: any) => React.createElement("span", null, children), Badge: ({ children }: any) => React.createElement("span", null, children),
Radio: ({ children }: any) => React.createElement("span", null, children), Radio: ({ children }: any) => React.createElement("span", null, children),
RadioGroup: ({ children }: any) => React.createElement("div", null, children), RadioGroup: ({ children }: any) =>
Checkbox: ({ children }: any) => React.createElement("span", null, children), React.createElement("div", null, children),
Checkbox: ({ children }: any) =>
React.createElement("span", null, children),
InputNumber: () => React.createElement("input", { type: "number" }), InputNumber: () => React.createElement("input", { type: "number" }),
Form: ({ children }: any) => React.createElement("form", null, children), Form: ({ children }: any) => React.createElement("form", null, children),
FormItem: ({ children }: any) => React.createElement("div", null, children), FormItem: ({ children }: any) => React.createElement("div", null, children),
@@ -141,23 +156,32 @@ vi.mock("antd", () => {
Card: ({ children }: any) => React.createElement("div", null, children), Card: ({ children }: any) => React.createElement("div", null, children),
Avatar: ({ children }: any) => React.createElement("span", null, children), Avatar: ({ children }: any) => React.createElement("span", null, children),
Collapse: ({ children }: any) => React.createElement("div", null, children), Collapse: ({ children }: any) => React.createElement("div", null, children),
CollapsePanel: ({ children }: any) => React.createElement("div", null, children), CollapsePanel: ({ children }: any) =>
React.createElement("div", null, children),
Tag: ({ children }: any) => React.createElement("span", null, children), Tag: ({ children }: any) => React.createElement("span", null, children),
Button: ({ children, onClick }: any) => React.createElement("button", { onClick }, children), Button: ({ children, onClick }: any) =>
Input: ({ placeholder }: any) => React.createElement("input", { placeholder }), React.createElement("button", { onClick }, children),
Select: ({ children }: any) => React.createElement("select", null, children), Input: ({ placeholder }: any) =>
Option: ({ children }: any) => React.createElement("option", null, children), React.createElement("input", { placeholder }),
ConfigProvider: ({ children }: any) => React.createElement(React.Fragment, null, children), Select: ({ children }: any) =>
TextArea: ({ placeholder }: any) => React.createElement("textarea", { placeholder }), 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"), Steps: () => React.createElement("div"),
Step: () => React.createElement("div"), Step: () => React.createElement("div"),
} };
}) });
// === UI Components mock === // === UI Components mock ===
vi.mock("@/components/ui", () => ({ vi.mock("@/components/ui", () => ({
Button: ({ children, onClick }: any) => React.createElement("button", { onClick }, children), Button: ({ children, onClick }: any) =>
Input: ({ placeholder }: any) => React.createElement("input", { placeholder }), React.createElement("button", { onClick }, children),
Input: ({ placeholder }: any) =>
React.createElement("input", { placeholder }),
Modal: ({ open, children }: any) => Modal: ({ open, children }: any) =>
open ? React.createElement("div", { role: "dialog" }, children) : null, open ? React.createElement("div", { role: "dialog" }, children) : null,
Drawer: ({ open, children }: any) => Drawer: ({ open, children }: any) =>
@@ -171,7 +195,7 @@ vi.mock("@/components/ui", () => ({
Upload: ({ children }: any) => React.createElement("div", null, children), Upload: ({ children }: any) => React.createElement("div", null, children),
Slider: () => React.createElement("div"), Slider: () => React.createElement("div"),
Switch: () => React.createElement("input", { type: "checkbox" }), Switch: () => React.createElement("input", { type: "checkbox" }),
})) }));
// === API mocks === // === API mocks ===
vi.mock("@/api/assets", () => ({ vi.mock("@/api/assets", () => ({
@@ -186,36 +210,40 @@ vi.mock("@/api/assets", () => ({
batchDeleteAssets: vi.fn().mockResolvedValue({}), batchDeleteAssets: vi.fn().mockResolvedValue({}),
isAssetUsable: vi.fn().mockReturnValue(true), isAssetUsable: vi.fn().mockReturnValue(true),
AssetType: { VIDEO: "video", IMAGE: "image", AUDIO: "audio", VOICE: "voice" }, AssetType: { VIDEO: "video", IMAGE: "image", AUDIO: "audio", VOICE: "voice" },
})) }));
vi.mock("@/api/tags", () => ({ vi.mock("@/api/tags", () => ({
getTags: vi.fn().mockResolvedValue({ items: [] }), getTags: vi.fn().mockResolvedValue({ items: [] }),
createTag: vi.fn().mockResolvedValue({}), createTag: vi.fn().mockResolvedValue({}),
tagAsset: vi.fn().mockResolvedValue({}), tagAsset: vi.fn().mockResolvedValue({}),
untagAsset: vi.fn().mockResolvedValue({}), untagAsset: vi.fn().mockResolvedValue({}),
})) }));
vi.mock("@/api/tts", () => ({ vi.mock("@/api/tts", () => ({
synthesizeSpeech: vi.fn().mockResolvedValue({ job_id: "test-job" }), synthesizeSpeech: vi.fn().mockResolvedValue({ job_id: "test-job" }),
getTTSJobStatus: vi.fn().mockResolvedValue({ status: "completed", audio_url: "" }), getTTSJobStatus: vi
.fn()
.mockResolvedValue({ status: "completed", audio_url: "" }),
saveTtsToLibrary: vi.fn().mockResolvedValue({}), saveTtsToLibrary: vi.fn().mockResolvedValue({}),
})) }));
vi.mock("@/api/voices", () => ({ vi.mock("@/api/voices", () => ({
fetchPresetVoices: vi.fn().mockResolvedValue({ items: [] }), fetchPresetVoices: vi.fn().mockResolvedValue({ items: [] }),
})) }));
vi.mock("@/api/editing-planner", () => ({ vi.mock("@/api/editing-planner", () => ({
getEditingTemplates: vi.fn().mockResolvedValue({ items: [], total: 0 }), getEditingTemplates: vi.fn().mockResolvedValue({ items: [], total: 0 }),
MODE_LABELS: { pip: "画中画" }, MODE_LABELS: { pip: "画中画" },
})) }));
vi.mock("@/api/titles", () => ({ vi.mock("@/api/titles", () => ({
getTitles: vi.fn().mockResolvedValue({ items: [] }), getTitles: vi.fn().mockResolvedValue({ items: [] }),
})) }));
vi.mock("@/api/template-editor", () => ({ vi.mock("@/api/template-editor", () => ({
updateEditPlan: vi.fn().mockResolvedValue({ plan_id: "test-plan", template_id: "test-template" }), updateEditPlan: vi
.fn()
.mockResolvedValue({ plan_id: "test-plan", template_id: "test-template" }),
getEditPlan: vi.fn().mockResolvedValue({ getEditPlan: vi.fn().mockResolvedValue({
plan_id: "test-plan", plan_id: "test-plan",
template_id: "test-template", template_id: "test-template",
@@ -224,11 +252,11 @@ vi.mock("@/api/template-editor", () => ({
status: "draft", status: "draft",
}), }),
getGenerationTaskResults: vi.fn().mockResolvedValue({ items: [] }), getGenerationTaskResults: vi.fn().mockResolvedValue({ items: [] }),
})) }));
vi.mock("@/api/voice-clone", () => ({ vi.mock("@/api/voice-clone", () => ({
formatDuration: vi.fn((s: number) => `${s}s`), formatDuration: vi.fn((s: number) => `${s}s`),
})) }));
vi.mock("@/api/client", () => ({ vi.mock("@/api/client", () => ({
default: { default: {
@@ -238,7 +266,7 @@ vi.mock("@/api/client", () => ({
put: vi.fn().mockResolvedValue({ data: {} }), put: vi.fn().mockResolvedValue({ data: {} }),
delete: vi.fn().mockResolvedValue({ data: {} }), delete: vi.fn().mockResolvedValue({ data: {} }),
}, },
})) }));
// === Hooks mock === // === Hooks mock ===
vi.mock("@/hooks/useCloneProgress", () => ({ vi.mock("@/hooks/useCloneProgress", () => ({
@@ -248,46 +276,55 @@ vi.mock("@/hooks/useCloneProgress", () => ({
start: vi.fn(), start: vi.fn(),
reset: vi.fn(), reset: vi.fn(),
})), })),
})) }));
// === Components mock === // === Components mock ===
vi.mock("@/components/voice/CloneModal", () => ({ vi.mock("@/components/voice/CloneModal", () => ({
default: () => React.createElement("div", { "data-testid": "CloneModal" }), default: () => React.createElement("div", { "data-testid": "CloneModal" }),
})) }));
// === PageHead mock === // === PageHead mock ===
vi.mock("@/components/layout/PageHead", () => ({ vi.mock("@/components/layout/PageHead", () => ({
default: ({ title }: { title: string }) => default: ({ title }: { title: string }) =>
React.createElement("div", { "data-testid": "page-head" }, title), React.createElement("div", { "data-testid": "page-head" }, title),
})) }));
// CSS mock // CSS mock
vi.mock("@/pages/generate/generate.css", () => ({})) vi.mock("@/pages/generate/generate.css", () => ({}));
import GeneratePage from "@/pages/generate/GeneratePage" import GeneratePage from "@/pages/generate/GeneratePage";
describe("GeneratePage", () => { describe("GeneratePage", () => {
beforeEach(() => { beforeEach(() => {
vi.useFakeTimers() vi.useFakeTimers();
}) });
afterEach(() => { afterEach(() => {
vi.useRealTimers() vi.useRealTimers();
}) });
it("renders without crashing", () => { it("renders without crashing", () => {
const { container } = render( const { container } = render(
React.createElement(MemoryRouter, null, React.createElement(GeneratePage)), React.createElement(
) MemoryRouter,
expect(container).toBeTruthy() null,
}) React.createElement(GeneratePage),
),
);
expect(container).toBeTruthy();
});
it("advances timers without errors", () => { it("advances timers without errors", () => {
render(React.createElement(MemoryRouter, null, React.createElement(GeneratePage))) render(
React.createElement(
MemoryRouter,
null,
React.createElement(GeneratePage),
),
);
act(() => { act(() => {
vi.advanceTimersByTime(30000) vi.advanceTimersByTime(30000);
}) });
expect(true).toBe(true) expect(true).toBe(true);
}) });
}) });