54 lines
4.6 KiB
TypeScript
54 lines
4.6 KiB
TypeScript
import { act, render, screen, waitFor } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { ApprovalDialog } from "../src/components/approvals/ApprovalDialog.js";
|
|
import { ImageVideoPanel, ImageVideoTaskCard } from "../src/components/tools/ImageVideoPanel.js";
|
|
|
|
const id = "i2v_11111111-1111-1111-1111-111111111111";
|
|
const task = { id, title: "Window", prompt: "Turn naturally toward the window; ambient rain", durationSeconds: 10, state: "queued", referenceUrl: `/api/image-video/tasks/${id}/reference`, createdAt: "2026-09-15T00:00:00Z", updatedAt: "2026-09-15T00:00:00Z" };
|
|
afterEach(() => { vi.unstubAllGlobals(); vi.useRealTimers(); });
|
|
|
|
describe("image video UI", () => {
|
|
it("restores queued tasks, polls at 30 seconds, and cleans up after unmount", async () => {
|
|
vi.useFakeTimers();
|
|
const fetcher = vi.fn(() => Promise.resolve(new Response(JSON.stringify({ tasks: [task] }))));
|
|
vi.stubGlobal("fetch", fetcher); const view = render(<ImageVideoPanel />);
|
|
await act(() => vi.advanceTimersByTimeAsync(0));
|
|
expect(screen.getByText("Window")).toBeVisible(); expect(screen.getByText(/排队中/)).toBeVisible();
|
|
await act(() => vi.advanceTimersByTimeAsync(29_999)); expect(fetcher).toHaveBeenCalledTimes(1);
|
|
await act(() => vi.advanceTimersByTimeAsync(1)); expect(fetcher).toHaveBeenCalledTimes(2);
|
|
view.unmount(); await act(() => vi.advanceTimersByTimeAsync(60_000)); expect(fetcher).toHaveBeenCalledTimes(2);
|
|
});
|
|
it("creates a draft from a local image without needing model image capability", async () => {
|
|
const onDraft = vi.fn(); const requests: { url: string; body: unknown }[] = [];
|
|
vi.stubGlobal("fetch", (url: string, init?: RequestInit) => { requests.push({ url, body: init?.body }); return Promise.resolve(new Response(JSON.stringify(url.includes("attachments") ? { attachment: { attachmentId: "att_local" } } : init?.method === "POST" ? { task } : { tasks: [] }))); });
|
|
render(<ImageVideoPanel sessionId="ses_1" onDraft={onDraft} />); const user = userEvent.setup();
|
|
await user.upload(screen.getByLabelText("参考图片"), new File(["test"], "test.png", { type: "image/png" }));
|
|
await user.type(screen.getByLabelText("标题"), "Window"); await user.type(screen.getByLabelText("动作与声音"), task.prompt);
|
|
await user.click(screen.getByRole("button", { name: "准备本地任务" }));
|
|
await waitFor(() => expect(onDraft).toHaveBeenCalledWith(expect.objectContaining({ id })));
|
|
expect(requests.some(request => request.url.includes("/sessions/ses_1/attachments"))).toBe(true);
|
|
expect(requests.filter(request => request.url.endsWith("/image-video/tasks") && typeof request.body === "string")[0]?.body).toContain('"attachmentId":"att_local"');
|
|
expect(requests.some(request => /submit|upload_reference/.test(request.url))).toBe(false);
|
|
});
|
|
it("renders a native player/download only for this registered task video", () => {
|
|
const view = render(<ImageVideoTaskCard task={{ ...task, state: "ready", videoUrl: `/api/image-video/tasks/${id}/video` }} />);
|
|
expect(screen.getByLabelText("生成预览播放器")).toHaveAttribute("controls");
|
|
expect(screen.getByRole("link", { name: "下载 MP4" })).toHaveAttribute("href", `/api/image-video/tasks/${id}/video?download=1`);
|
|
expect(screen.getByRole("status")).toHaveTextContent("待视听检查");
|
|
view.rerender(<ImageVideoTaskCard task={{ ...task, state: "ready", videoUrl: "https://evil.example/video" }} />);
|
|
expect(screen.queryByRole("link", { name: "下载 MP4" })).not.toBeInTheDocument();
|
|
});
|
|
it("requires an unchecked confirmation after showing the frozen reference/prompt/duration", async () => {
|
|
vi.stubGlobal("fetch", () => Promise.resolve(new Response(JSON.stringify({ task }))));
|
|
const approve = vi.fn();
|
|
render(<ApprovalDialog request={{ approvalId: "ap_i2v", toolCallId: "tc", toolName: "i2v_submit", arguments: { taskId: id }, riskLevel: "medium", expiresAt: "2099-01-01T00:00:00Z", status: "pending" }} onApprove={approve} onDeny={vi.fn()} />);
|
|
expect(await screen.findByText(task.prompt)).toBeVisible();
|
|
expect(screen.getByRole("img")).toHaveAttribute("src", task.referenceUrl);
|
|
expect(screen.getByText(/10 秒/)).toBeVisible(); expect(screen.getByText(/费用未知/)).toBeVisible();
|
|
const button = screen.getByRole("button", { name: "批准 H3 生成" }); expect(button).toBeDisabled();
|
|
await userEvent.click(screen.getByRole("checkbox")); expect(button).toBeEnabled();
|
|
await userEvent.click(button); expect(approve).toHaveBeenCalledWith("ap_i2v");
|
|
});
|
|
});
|