import { expect, test } from "@playwright/test"; import { requestCreativeAnalysis, type CreativeAnalysisModel } from "@/components/canvas/tvstar/creativeAnalysisClient"; import { buildTVstarRevisionPrompt, tvstarAllowsGeneration, tvstarRequestsRunExisting, tvstarWantsDraftNodes } from "@/components/canvas/tvstar/session-context"; import { resolveTVstarGenerationReferences } from "@/components/canvas/tvstar/generation-references"; import { tvstarTiming } from "@/lib/video-canvas/tvstar-timing"; const models: CreativeAnalysisModel[] = [ { id: "broken", label: "Broken provider", modelId: "agnes-2.0-flash", providerType: "custom", supportsVision: true }, { id: "official", label: "Qwen Max", modelId: "qwen3.8-max", providerType: "alibaba", supportsVision: true }, { id: "backup", label: "Qwen Omni", modelId: "qwen3.8-omni-flash", providerType: "alibaba", supportsVision: true }, ]; test("TVstar tries the verified official model first and retries authentication failures in auto mode", async () => { const originalFetch = globalThis.fetch; const requestedModels: string[] = []; globalThis.fetch = async (_url, init) => { const body = JSON.parse(String(init?.body || "{}")); requestedModels.push(body.preferredModelRef); return Response.json(body.preferredModelRef === "official" ? { success: false, error: "Invalid Authentication" } : { success: true, data: { summary: "创意规划已生成" } }, { status: body.preferredModelRef === "official" ? 500 : 200 }); }; try { const result = await requestCreativeAnalysis({ mode: "agent-plan", prompt: "三镜头广告", nodeTitle: "TVstar", models }); expect(result.summary).toBe("创意规划已生成"); expect(requestedModels).toEqual(["official", "backup"]); } finally { globalThis.fetch = originalFetch; } }); test("TVstar keeps the current storyboard and the latest instruction in one conversation", () => { const prompt = buildTVstarRevisionPrompt({ goal: "做一支 4 秒毛笔广告", plan: { summary: "暖光书法短片" }, shots: [{ shot: "01", duration: "4s", sceneDescription: "毛笔写字", cameraMovement: "缓推" }], messages: [{ id: "a", role: "user", content: "请保持暖光", createdAt: 1 }], }, "改成 6 秒,增加蘸墨特写"); expect(prompt).toContain("当前分镜:"); expect(prompt).toContain("毛笔写字"); expect(prompt).toContain("最新用户指令:改成 6 秒,增加蘸墨特写"); expect(prompt).toContain("请保持暖光"); }); test("TVstar obeys planning-only instructions even in automatic creation mode", () => { expect(tvstarAllowsGeneration("请只分析当前空画布可用的创作步骤,不提交生成任务")).toBe(false); expect(tvstarAllowsGeneration("先规划一支 15 秒广告,暂不生成视频")).toBe(false); expect(tvstarAllowsGeneration("不要扣费,只写方案")).toBe(false); expect(tvstarAllowsGeneration("请在画布创建待运行节点,不要执行任何图片、视频或音频生成")).toBe(false); expect(tvstarAllowsGeneration("先搭建未运行的分镜工作流")).toBe(false); expect(tvstarAllowsGeneration("做一支 15 秒毛笔广告并生成成片")).toBe(true); expect(tvstarAllowsGeneration("规划已经确认,可以开始生成视频")).toBe(true); expect(tvstarWantsDraftNodes("请在画布创建待运行的毛笔广告分镜节点,不提交生成任务")).toBe(true); expect(tvstarWantsDraftNodes("只分析当前空画布的创作步骤,不提交生成任务")).toBe(false); expect(tvstarWantsDraftNodes("不要创建画布节点,只写方案")).toBe(false); expect(tvstarRequestsRunExisting("现在开始制作视频")).toBe(true); expect(tvstarRequestsRunExisting("请运行画布工作流")).toBe(true); expect(tvstarRequestsRunExisting("把第二镜头修改后开始制作视频")).toBe(false); expect(tvstarRequestsRunExisting("先不要执行生成任务")).toBe(false); }); test("TVstar keeps whole-film timing when only a single shot duration changes", () => { const shots = [{ duration: "8s" }, { duration: "7s" }]; expect(tvstarTiming("做一支 15 秒广告\n最新修改:镜头 2 改成 3 秒", shots).targetDuration).toBe(15); expect(tvstarTiming("做一支 15 秒广告\n最新修改:整片改成 20 秒,镜头 2 保持 3 秒", shots).targetDuration).toBe(20); }); test("TVstar sends image and video references into generation, extracting video frames one at a time", async () => { let active = 0; let maximum = 0; const calls: string[] = []; const fetcher = (async (_url: unknown, init?: RequestInit) => { active++; maximum = Math.max(maximum, active); const body = JSON.parse(String(init?.body)); calls.push(body.url); await new Promise(resolve => setTimeout(resolve, 1)); active--; return Response.json({ success: true, data: { url: `${body.url}.png` } }); }) as typeof fetch; const images = await resolveTVstarGenerationReferences([ { type: "image", title: "角色图", url: "https://media.test/role.png" }, { type: "video", title: "参考片 A", url: "https://media.test/a.mp4" }, { type: "video", title: "参考片 B", url: "https://media.test/b.mp4" }, ], fetcher); expect(images).toEqual(["https://media.test/role.png", "https://media.test/a.mp4.png", "https://media.test/b.mp4.png"]); expect(calls).toEqual(["https://media.test/a.mp4", "https://media.test/b.mp4"]); expect(maximum).toBe(1); });