Files
xiaoxia-saas/apps/web/src/test/pages/EditingPlanner.test.tsx
T
xiaoxia febb1bcfce
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 1m2s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Migration (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 / 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
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
chore: 清理剪辑模板编辑器生成视频死代码 + 重命名为剪辑模板 (#1472)
前端:
- 删除 GenerationProgressModal/GenerationHistoryModal 组件及目录
- 删除 generateEditPlan/getGenerationStatus/getEditPlanGenerations 三个死API函数
- 删除对应类型 GenerateResponse/EditPlanGeneration/GenerationStatusResponse
- 清理测试文件中的 mock 和引用
- 用户可见文案 '剪辑编辑器' 统一改为 '剪辑模板'
- 不改变量名/组件名/路由路径/API路径

后端:
- 删除 templates_editor/generation.py 死接口文件
- 清理 _fallback.py、dependencies.py、schemas.py 中的死代码
- 删除对应无用测试文件
2026-08-23 23:05:15 +08:00

312 lines
13 KiB
TypeScript

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"
// === 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(),
})),
}))
// === Ant Design Icons mock ===
vi.mock("@ant-design/icons", () => ({
VideoCameraOutlined: () => React.createElement("span", null, "V"),
PictureOutlined: () => React.createElement("span", null, "P"),
SoundOutlined: () => React.createElement("span", null, "S"),
PlusOutlined: () => React.createElement("span", null, "+"),
DeleteOutlined: () => React.createElement("span", null, "D"),
EditOutlined: () => React.createElement("span", null, "E"),
CopyOutlined: () => React.createElement("span", null, "C"),
DownloadOutlined: () => React.createElement("span", null, "D"),
PlayCircleOutlined: () => React.createElement("span", null, ">"),
PauseCircleOutlined: () => React.createElement("span", null, "||"),
LeftOutlined: () => React.createElement("span", null, "<"),
RightOutlined: () => React.createElement("span", null, ">"),
UpOutlined: () => React.createElement("span", null, "^"),
DownOutlined: () => React.createElement("span", null, "v"),
SaveOutlined: () => React.createElement("span", null, "S"),
UndoOutlined: () => React.createElement("span", null, "U"),
RedoOutlined: () => React.createElement("span", null, "R"),
CloseOutlined: () => React.createElement("span", null, "X"),
CheckOutlined: () => React.createElement("span", null, "v"),
SettingOutlined: () => React.createElement("span", null, "S"),
AppstoreOutlined: () => React.createElement("span", null, "#"),
UnorderedListOutlined: () => React.createElement("span", null, "="),
HistoryOutlined: () => React.createElement("span", null, "H"),
UploadOutlined: () => React.createElement("span", null, "U"),
SearchOutlined: () => React.createElement("span", null, "S"),
FilterOutlined: () => React.createElement("span", null, "F"),
FontColorsOutlined: () => React.createElement("span", null, "A"),
BgColorsOutlined: () => React.createElement("span", null, "B"),
AudioOutlined: () => React.createElement("span", null, "A"),
MusicOutlined: () => React.createElement("span", null, "M"),
ScissorOutlined: () => React.createElement("span", null, "X"),
ThunderboltOutlined: () => React.createElement("span", null, "T"),
ExperimentOutlined: () => React.createElement("span", null, "E"),
BulbOutlined: () => React.createElement("span", null, "B"),
FundOutlined: () => React.createElement("span", null, "F"),
LayoutOutlined: () => React.createElement("span", null, "L"),
ColumnHeightOutlined: () => React.createElement("span", null, "C"),
SwapOutlined: () => React.createElement("span", null, "S"),
}))
// === Ant Design mock ===
vi.mock("antd", () => ({
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"),
Button: ({ children, onClick }: any) => React.createElement("button", { onClick }, children),
Tabs: ({ items }: any) =>
React.createElement(
"div",
null,
items?.map?.(() => React.createElement("div")),
),
TabPane: () => React.createElement("div"),
Drawer: ({ open, children, title }: any) =>
open ? React.createElement("div", { role: "dialog", "data-title": title }, children) : null,
Select: ({ children }: any) => React.createElement("select", null, children),
Option: ({ children }: any) => React.createElement("option", null, children),
Input: ({ placeholder }: any) => React.createElement("input", { placeholder }),
InputNumber: () => React.createElement("input", { type: "number" }),
Switch: () => React.createElement("input", { type: "checkbox" }),
Slider: () => React.createElement("div"),
ColorPicker: () => React.createElement("div"),
Upload: ({ children }: any) => React.createElement("div", null, children),
Space: ({ children }: any) => React.createElement("div", null, children),
Row: ({ children }: any) => React.createElement("div", null, children),
Col: ({ children }: any) => React.createElement("div", null, children),
Card: ({ children }: any) => React.createElement("div", null, children),
Tag: ({ children }: any) => React.createElement("span", null, children),
Tooltip: ({ children }: any) => React.createElement("span", null, children),
Popover: ({ children }: any) => React.createElement("span", null, children),
Dropdown: ({ children }: any) => React.createElement("span", null, children),
Menu: () => React.createElement("div"),
Divider: () => React.createElement("hr"),
Empty: () => React.createElement("div", null, "Empty"),
Spin: () => React.createElement("div", null, "Loading"),
Badge: ({ children }: any) => React.createElement("span", null, children),
Avatar: ({ children }: any) => React.createElement("span", null, children),
Checkbox: ({ children }: any) => React.createElement("span", null, children),
Radio: ({ children }: any) => React.createElement("span", null, children),
RadioGroup: ({ children }: any) => React.createElement("div", null, children),
Segmented: () => React.createElement("div"),
Collapse: ({ children }: any) => React.createElement("div", null, children),
CollapsePanel: ({ children }: any) => React.createElement("div", null, children),
Form: ({ children }: any) => React.createElement("form", null, children),
FormItem: ({ children }: any) => React.createElement("div", null, children),
List: () => React.createElement("div"),
Table: () => React.createElement("div"),
Pagination: () => React.createElement("div"),
Popconfirm: ({ children }: any) => React.createElement("span", null, children),
Result: ({ status, title }: any) => React.createElement("div", { "data-status": status }, title),
ConfigProvider: ({ children }: any) => React.createElement(React.Fragment, null, children),
}))
// === 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),
}))
// === API mocks ===
vi.mock("@/api/editing-planner", () => ({
getEditingTemplates: vi.fn().mockResolvedValue({ items: [], total: 0 }),
getEditingTemplate: vi.fn().mockResolvedValue({}),
createEditingTemplate: vi.fn().mockResolvedValue({}),
updateEditingTemplate: vi.fn().mockResolvedValue({}),
getTemplateCategories: vi.fn().mockResolvedValue({ items: [] }),
MODE_LABELS: { pip: "画中画", intro_outro: "片头片尾", watermark: "水印" },
}))
vi.mock("@/api/template-editor", () => ({
getMediaAssets: vi.fn().mockResolvedValue({ items: [] }),
getEditPlan: vi.fn().mockResolvedValue({}),
updateEditPlan: vi.fn().mockResolvedValue({}),
getGenerationTaskResults: vi.fn().mockResolvedValue({ items: [] }),
getEditPlanClips: vi.fn().mockResolvedValue({ items: [] }),
createEditPlanClip: vi.fn().mockResolvedValue({}),
batchDeleteEditPlanClips: vi.fn().mockResolvedValue({}),
}))
vi.mock("@/api/assets", () => ({
ensureDefaultLibrary: vi.fn().mockResolvedValue({ id: "default-lib" }),
getAssetsByKind: vi.fn().mockResolvedValue({ items: [] }),
}))
vi.mock("@/api/projects", () => ({
getOrCreateDefaultProject: vi.fn().mockResolvedValue({ id: "default-project" }),
}))
vi.mock("@/api/bgm", () => ({
DEFAULT_BGM_MIX_CONFIG: { volume: 1, fade_in: 0, fade_out: 0 },
}))
vi.mock("@/pages/editing-planner/components/MediaPanel", () => ({
default: () => React.createElement("div", { "data-testid": "MediaPanel" }),
}))
vi.mock("@/pages/editing-planner/components/PreviewPlayer", () => ({
default: () => React.createElement("div", { "data-testid": "PreviewPlayer" }),
}))
vi.mock("@/pages/editing-planner/components/TimelinePanel", () => ({
default: () => React.createElement("div", { "data-testid": "TimelinePanel" }),
}))
vi.mock("@/pages/editing-planner/components/ClipPropertiesPanel", () => ({
default: () => React.createElement("div", { "data-testid": "ClipPropertiesPanel" }),
}))
vi.mock("@/pages/editing-planner/components/EditorClipList", () => ({
default: () => React.createElement("div", { "data-testid": "EditorClipList" }),
}))
vi.mock("@/pages/editing-planner/components/BgmSelector", () => ({
default: () => React.createElement("div", { "data-testid": "BgmSelector" }),
}))
vi.mock("@/pages/editing-planner/components/SubtitleStylePanel", () => ({
default: () => React.createElement("div", { "data-testid": "SubtitleStylePanel" }),
}))
vi.mock("@/pages/editing-planner/components/TransitionSelector", () => ({
default: () => React.createElement("div", { "data-testid": "TransitionSelector" }),
}))
vi.mock("@/pages/editing-planner/components/SpeedPanel", () => ({
default: () => React.createElement("div", { "data-testid": "SpeedPanel" }),
}))
vi.mock("@/pages/editing-planner/components/TtsPanel", () => ({
default: () => React.createElement("div", { "data-testid": "TtsPanel" }),
}))
vi.mock("@/pages/editing-planner/components/WatermarkPanel", () => ({
default: () => React.createElement("div", { "data-testid": "WatermarkPanel" }),
}))
vi.mock("@/pages/editing-planner/components/IntroOutroPanel", () => ({
default: () => React.createElement("div", { "data-testid": "IntroOutroPanel" }),
}))
vi.mock("@/pages/editing-planner/components/PipConfigPanel", () => ({
default: () => React.createElement("div", { "data-testid": "PipConfigPanel" }),
}))
vi.mock("@/pages/editing-planner/components/FilterPanel", () => ({
default: () => React.createElement("div", { "data-testid": "FilterPanel" }),
}))
vi.mock("@/pages/editing-planner/components/GreenScreenPanel", () => ({
default: () => React.createElement("div", { "data-testid": "GreenScreenPanel" }),
}))
vi.mock("@/pages/editing-planner/components/StickerPanel", () => ({
default: () => React.createElement("div", { "data-testid": "StickerPanel" }),
}))
vi.mock("@/pages/editing-planner/components/SaveModal", () => ({
default: () => React.createElement("div", { "data-testid": "SaveModal" }),
}))
// === useUndoRedo hook mock ===
vi.mock("@/pages/editing-planner/hooks/useUndoRedo", () => ({
useUndoRedo: vi.fn((initial: any) => ({
state: initial,
setState: vi.fn(),
undo: vi.fn(),
redo: vi.fn(),
canUndo: false,
canRedo: false,
reset: vi.fn(),
})),
}))
// === Types mock ===
vi.mock("@/pages/editing-planner/types", () => ({
DEFAULT_TRANSITION: { type: "fade", duration: 0.5 },
DEFAULT_SPEED: { rate: 1 },
DEFAULT_TTS_CONFIG: { enabled: false },
DEFAULT_WATERMARK: { enabled: false },
DEFAULT_INTRO_OUTRO: { enabled: false },
DEFAULT_PIP_CONFIG: { enabled: false },
DEFAULT_FILTER_CONFIG: { enabled: false },
DEFAULT_CHROMA_KEY_CONFIG: { enabled: false },
DEFAULT_STICKER_CONFIG: { enabled: false },
}))
vi.mock("@/pages/editing-planner/types/subtitle", () => ({
DEFAULT_SUBTITLE_STYLE: {
font_size: 24,
font_color: "#ffffff",
background_color: "#000000",
},
}))
// === PageHead mock ===
vi.mock("@/components/layout/PageHead", () => ({
default: ({ title }: { title: string }) =>
React.createElement("div", { "data-testid": "page-head" }, title),
}))
import EditingPlanner from "@/pages/editing-planner/EditingPlanner"
describe("EditingPlanner", () => {
beforeEach(() => {
vi.useFakeTimers()
})
afterEach(() => {
vi.useRealTimers()
})
it("renders without crashing", () => {
const { container } = render(
React.createElement(MemoryRouter, null, React.createElement(EditingPlanner)),
)
expect(container).toBeTruthy()
})
it("renders with templateId query param", () => {
const { container } = render(
React.createElement(
MemoryRouter,
{ initialEntries: ["?templateId=tpl-123"] },
React.createElement(EditingPlanner),
),
)
expect(container).toBeTruthy()
})
it("renders with planId query param", () => {
const { container } = render(
React.createElement(
MemoryRouter,
{ initialEntries: ["?planId=plan-456"] },
React.createElement(EditingPlanner),
),
)
expect(container).toBeTruthy()
})
it("advances timers without errors", () => {
render(React.createElement(MemoryRouter, null, React.createElement(EditingPlanner)))
act(() => {
vi.advanceTimersByTime(10000)
})
expect(true).toBe(true)
})
})