ai-chatbot/tests/routes/video-smart-storyboard.test.ts

164 lines
5.2 KiB
TypeScript

import { expect, test } from "@playwright/test";
import {
defaultVideoCanvasFunctionConfig,
normalizeVideoCanvasFunctionConfig,
} from "../../lib/video-canvas/config";
import {
composeVideoCanvasSmartStoryboardPrompt,
getVideoCanvasSmartStoryboard,
normalizeVideoCanvasSmartStoryboard,
setVideoCanvasSmartStoryboard,
supportsVideoCanvasSmartStoryboard,
validateVideoCanvasSmartStoryboard,
} from "../../lib/video-canvas/smart-storyboard";
const config = defaultVideoCanvasFunctionConfig.smartStoryboard;
test.describe("video canvas smart storyboard", () => {
test("matches the observed Kling O3 duration and model rules", () => {
expect(config).toMatchObject({
defaultShotDurationSeconds: 2,
maxTotalDurationSeconds: 15,
minShotDurationSeconds: 1,
minTotalDurationSeconds: 3,
modelPatterns: ["Kling O3"],
});
expect(supportsVideoCanvasSmartStoryboard(config, "Kling O3")).toBe(true);
expect(
supportsVideoCanvasSmartStoryboard(config, "Seedance 2.0 VIP"),
).toBe(false);
});
test("creates one disabled shot from the current node prompt", () => {
expect(
normalizeVideoCanvasSmartStoryboard(undefined, {
config,
fallbackPrompt: "人物走向镜头。",
fallbackTotalDurationSeconds: 5,
}),
).toEqual({
enabled: false,
shots: [
{
description: "人物走向镜头。",
durationSeconds: 5,
id: "shot-1",
},
],
totalDurationSeconds: 5,
});
});
test("validates empty descriptions before exact total duration", () => {
const storyboard = normalizeVideoCanvasSmartStoryboard(
{
enabled: true,
shots: [
{ description: "第一个镜头。", durationSeconds: 5, id: "one" },
{ description: "", durationSeconds: 2, id: "two" },
],
totalDurationSeconds: 5,
},
{ config },
);
expect(validateVideoCanvasSmartStoryboard(storyboard)).toEqual({
emptyShotIndexes: [1],
status: "over",
totalShotDurationSeconds: 7,
valid: false,
});
});
test("requires shot durations to equal the selected total exactly", () => {
const base = {
enabled: true,
shots: [
{ description: "镜头一。", durationSeconds: 5, id: "one" },
{ description: "镜头二。", durationSeconds: 2, id: "two" },
],
totalDurationSeconds: 7,
};
expect(validateVideoCanvasSmartStoryboard(base).valid).toBe(true);
expect(
validateVideoCanvasSmartStoryboard({ ...base, totalDurationSeconds: 8 }),
).toMatchObject({ status: "under", valid: false });
expect(
validateVideoCanvasSmartStoryboard({ ...base, totalDurationSeconds: 6 }),
).toMatchObject({ status: "over", valid: false });
});
test("persists structured shots in both node and provider params", () => {
const storyboard = normalizeVideoCanvasSmartStoryboard(
{
enabled: true,
shots: [{ description: "镜头一。", durationSeconds: 5, id: "one" }],
totalDurationSeconds: 5,
},
{ config },
);
const draftValues = setVideoCanvasSmartStoryboard(
{ params: { model: "kling-o3", prompt: "人物走向镜头。" }, untouched: true },
storyboard,
);
expect(draftValues).toMatchObject({
params: { model: "kling-o3", smartStoryboard: storyboard },
smartStoryboard: storyboard,
untouched: true,
});
expect(
getVideoCanvasSmartStoryboard(draftValues, {
config,
fallbackTotalDurationSeconds: 3,
}),
).toEqual(storyboard);
});
test("serializes exact shot timing through administrator templates", () => {
const storyboard = {
enabled: true,
shots: [
{ description: "人物抬头。", durationSeconds: 3, id: "one" },
{ description: "镜头推近。", durationSeconds: 2, id: "two" },
],
totalDurationSeconds: 5,
};
const prompt = composeVideoCanvasSmartStoryboardPrompt(
"保持角色一致。",
storyboard,
{
...config,
promptTemplate: "总计{{totalDuration}}秒/{{shotCount}}镜头\n{{prompt}}\n{{shots}}",
shotPromptTemplate: "#{{index}} {{duration}}秒 {{description}}",
},
);
expect(prompt).toBe(
"总计5秒/2镜头\n保持角色一致。\n#1 3秒 人物抬头。\n#2 2秒 镜头推近。",
);
});
test("normalizes administrator ranges and hidden templates", () => {
const normalized = normalizeVideoCanvasFunctionConfig({
smartStoryboard: {
defaultShotDurationSeconds: 99,
maxShots: 999,
maxTotalDurationSeconds: 12,
minShotDurationSeconds: 0,
minTotalDurationSeconds: 4,
modelPatterns: [" Kling O3 ", "Kling O3"],
promptTemplate: "{{shots}}",
shotPromptTemplate: "{{description}}",
},
}).smartStoryboard;
expect(normalized).toMatchObject({
defaultShotDurationSeconds: 12,
maxShots: 100,
maxTotalDurationSeconds: 12,
minShotDurationSeconds: 1,
minTotalDurationSeconds: 4,
modelPatterns: ["Kling O3"],
promptTemplate: "{{shots}}",
shotPromptTemplate: "{{description}}",
});
});
});