464 lines
15 KiB
TypeScript
464 lines
15 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import {
|
|
defaultVideoCanvasFunctionConfig,
|
|
hasEnabledFixedVideoCanvasPointCosts,
|
|
normalizeVideoCanvasFunctionConfig,
|
|
} from "../../lib/video-canvas/config";
|
|
import { getVideoCanvasTutorialSupportQr } from "../../lib/video-canvas/tutorial-support";
|
|
|
|
test.describe("video canvas director configuration", () => {
|
|
test("provides competitor-matched AI image import defaults", () => {
|
|
expect(defaultVideoCanvasFunctionConfig.directorDesk.defaultAspectRatio).toBe(
|
|
"9:16",
|
|
);
|
|
expect(defaultVideoCanvasFunctionConfig.directorDesk.aiImport).toEqual({
|
|
cost: 48,
|
|
enabled: true,
|
|
functionKey: "director-ai-import",
|
|
historyLimit: 24,
|
|
localSamEndpoint: "",
|
|
uploadAccept: "image/*",
|
|
});
|
|
expect(defaultVideoCanvasFunctionConfig.functions["director-ai-import"]).toMatchObject({
|
|
enabled: true,
|
|
kind: "multimodal",
|
|
route: "director-ai-import",
|
|
});
|
|
});
|
|
|
|
test("normalizes the configurable initial director aspect ratio", () => {
|
|
expect(
|
|
normalizeVideoCanvasFunctionConfig({
|
|
directorDesk: { defaultAspectRatio: "16:9" },
|
|
}).directorDesk.defaultAspectRatio,
|
|
).toBe("16:9");
|
|
expect(
|
|
normalizeVideoCanvasFunctionConfig({
|
|
directorDesk: { defaultAspectRatio: "2:1" },
|
|
}).directorDesk.defaultAspectRatio,
|
|
).toBe("9:16");
|
|
});
|
|
|
|
test("normalizes administrator AI image import settings", () => {
|
|
const config = normalizeVideoCanvasFunctionConfig({
|
|
directorDesk: {
|
|
aiImport: {
|
|
cost: 10000,
|
|
enabled: false,
|
|
functionKey: " custom-ai-import ",
|
|
historyLimit: 0,
|
|
localSamEndpoint: "javascript:alert(1)",
|
|
uploadAccept: " image/png,image/jpeg ",
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(config.directorDesk.aiImport).toEqual({
|
|
cost: 9999,
|
|
enabled: false,
|
|
functionKey: "custom-ai-import",
|
|
historyLimit: 1,
|
|
localSamEndpoint: "",
|
|
uploadAccept: "image/png,image/jpeg",
|
|
});
|
|
});
|
|
|
|
test("provides competitor-matched crowd defaults", () => {
|
|
expect(defaultVideoCanvasFunctionConfig.directorDesk.crowd).toEqual({
|
|
defaultColumns: 3,
|
|
defaultRows: 3,
|
|
defaultSpacing: 1.2,
|
|
});
|
|
expect(normalizeVideoCanvasFunctionConfig({}).directorDesk.crowd).toEqual({
|
|
defaultColumns: 3,
|
|
defaultRows: 3,
|
|
defaultSpacing: 1.2,
|
|
});
|
|
});
|
|
|
|
test("normalizes administrator crowd defaults before host delivery", () => {
|
|
const config = normalizeVideoCanvasFunctionConfig({
|
|
directorDesk: {
|
|
crowd: {
|
|
defaultColumns: 0,
|
|
defaultRows: 110,
|
|
defaultSpacing: 1.234,
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(config.directorDesk.crowd).toEqual({
|
|
defaultColumns: 1,
|
|
defaultRows: 100,
|
|
defaultSpacing: 1.23,
|
|
});
|
|
});
|
|
|
|
test("provides a configurable two-point video analysis cost", () => {
|
|
expect(defaultVideoCanvasFunctionConfig.functions["video-analysis"].cost).toBe(2);
|
|
expect(hasEnabledFixedVideoCanvasPointCosts(defaultVideoCanvasFunctionConfig)).toBe(true);
|
|
expect(
|
|
normalizeVideoCanvasFunctionConfig({
|
|
functions: { "video-analysis": { cost: 10_000 } },
|
|
}).functions["video-analysis"].cost,
|
|
).toBe(9999);
|
|
expect(
|
|
hasEnabledFixedVideoCanvasPointCosts(
|
|
normalizeVideoCanvasFunctionConfig({
|
|
functions: { "video-analysis": { cost: 0 } },
|
|
}),
|
|
),
|
|
).toBe(false);
|
|
});
|
|
|
|
test("keeps the element-mark generation prompt administrator-configurable", () => {
|
|
expect(
|
|
defaultVideoCanvasFunctionConfig.functions["element-mark"].generationPrompt,
|
|
).toBe("重点遵循已标记元素:{{labels}}");
|
|
expect(
|
|
normalizeVideoCanvasFunctionConfig({
|
|
functions: {
|
|
"element-mark": {
|
|
generationPrompt: "锁定这些元素:{{plainLabels}}",
|
|
},
|
|
},
|
|
}).functions["element-mark"].generationPrompt,
|
|
).toBe("锁定这些元素:{{plainLabels}}");
|
|
});
|
|
|
|
test("provides configurable smart subtitle erase defaults", () => {
|
|
expect(defaultVideoCanvasFunctionConfig.functions["video-subtitle-erase"]).toMatchObject({
|
|
cost: 0,
|
|
enabled: true,
|
|
model: "volcano-subtitle-eraser",
|
|
route: "local",
|
|
});
|
|
expect(normalizeVideoCanvasFunctionConfig({
|
|
videoSubtitleErase: { blurRadius: 18, smartSearchStartRatio: 0.62 },
|
|
}).videoSubtitleErase).toMatchObject({
|
|
blurRadius: 18,
|
|
smartSearchStartRatio: 0.62,
|
|
});
|
|
});
|
|
|
|
test("provides configurable video audio separation defaults", () => {
|
|
expect(defaultVideoCanvasFunctionConfig.functions["video-audio-separation"]).toMatchObject({
|
|
cost: 0,
|
|
enabled: true,
|
|
model: "center-channel-v1",
|
|
route: "local",
|
|
});
|
|
expect(normalizeVideoCanvasFunctionConfig({
|
|
videoAudioSeparation: { backgroundGain: 1.6, frameRate: 24 },
|
|
}).videoAudioSeparation).toMatchObject({ backgroundGain: 1.6, frameRate: 24 });
|
|
});
|
|
|
|
test("provides configurable video download watermark rules", () => {
|
|
const config = normalizeVideoCanvasFunctionConfig({
|
|
videoDownload: {
|
|
rulesTitle: "站点水印规则",
|
|
welcomeText: "欢迎使用{{siteName}}",
|
|
},
|
|
});
|
|
expect(config.videoDownload).toMatchObject({
|
|
rulesTitle: "站点水印规则",
|
|
welcomeText: "欢迎使用{{siteName}}",
|
|
});
|
|
});
|
|
|
|
test("provides normalized database-backed history asset behavior", () => {
|
|
expect(defaultVideoCanvasFunctionConfig.historyAssets).toMatchObject({
|
|
defaultZoomPercent: 100,
|
|
enabled: true,
|
|
maxItems: 300,
|
|
picker: {
|
|
maxSelection: 10,
|
|
mediaShortSide: 350,
|
|
pageSize: 15,
|
|
placementGap: 450,
|
|
},
|
|
previewAutoplay: true,
|
|
showWatermarkBadge: true,
|
|
watermarkBadgeText: "AI",
|
|
});
|
|
expect(
|
|
normalizeVideoCanvasFunctionConfig({
|
|
historyAssets: {
|
|
defaultZoomPercent: 110,
|
|
enabled: false,
|
|
maxItems: 9999,
|
|
previewAutoplay: false,
|
|
showWatermarkBadge: false,
|
|
watermarkBadgeText: " LOCAL SITE MARK ",
|
|
},
|
|
}).historyAssets,
|
|
).toMatchObject({
|
|
defaultZoomPercent: 100,
|
|
enabled: false,
|
|
maxItems: 500,
|
|
picker: {
|
|
maxSelection: 10,
|
|
pageSize: 15,
|
|
},
|
|
previewAutoplay: false,
|
|
showWatermarkBadge: false,
|
|
watermarkBadgeText: "LOCAL SITE M",
|
|
});
|
|
});
|
|
|
|
test("provides configurable tutorial and contact surfaces", () => {
|
|
expect(defaultVideoCanvasFunctionConfig.tutorialSupport).toEqual({
|
|
customerServiceUrl: "",
|
|
officialAccountQrImageUrl: "",
|
|
salesQrImageUrl: "",
|
|
tutorialUrl:
|
|
"https://resonate.feishu.cn/wiki/Loxfw6XHziYRk0kKzdjcFfp9nhb",
|
|
});
|
|
|
|
const config = normalizeVideoCanvasFunctionConfig({
|
|
tutorialSupport: {
|
|
customerServiceUrl: "/support/video-canvas",
|
|
officialAccountQrImageUrl: "javascript:alert(1)",
|
|
salesQrImageUrl: "https://assets.example.com/sales.png",
|
|
tutorialUrl: "https://docs.example.com/video-canvas",
|
|
},
|
|
}).tutorialSupport;
|
|
|
|
expect(config).toEqual({
|
|
customerServiceUrl: "/support/video-canvas",
|
|
officialAccountQrImageUrl: "",
|
|
salesQrImageUrl: "https://assets.example.com/sales.png",
|
|
tutorialUrl: "https://docs.example.com/video-canvas",
|
|
});
|
|
expect(getVideoCanvasTutorialSupportQr(config, "sales")).toEqual({
|
|
alt: "联系销售",
|
|
url: "https://assets.example.com/sales.png",
|
|
});
|
|
expect(getVideoCanvasTutorialSupportQr(config, "official")).toEqual({
|
|
alt: "关注公众号",
|
|
url: "",
|
|
});
|
|
});
|
|
|
|
test("normalizes administrator video reference rules", () => {
|
|
const config = normalizeVideoCanvasFunctionConfig({
|
|
videoReferenceSelection: {
|
|
defaultRule: { maxImages: 99, mode: "unknown" },
|
|
modelRules: [
|
|
{
|
|
autoExit: false,
|
|
hideActionAtLimit: false,
|
|
maxImages: 0,
|
|
mode: "first-frame",
|
|
modelPattern: " Custom Video Model ",
|
|
},
|
|
{ modelPattern: "" },
|
|
],
|
|
},
|
|
});
|
|
|
|
expect(config.videoReferenceSelection.defaultRule).toEqual({
|
|
autoExit: false,
|
|
hideActionAtLimit: false,
|
|
maxImages: 20,
|
|
mode: "reference-images",
|
|
});
|
|
expect(config.videoReferenceSelection.modelRules).toEqual([
|
|
{
|
|
autoExit: false,
|
|
hideActionAtLimit: false,
|
|
maxImages: 1,
|
|
mode: "first-frame",
|
|
modelPattern: "Custom Video Model",
|
|
},
|
|
]);
|
|
});
|
|
|
|
test("normalizes administrator video advanced-setting defaults", () => {
|
|
expect(defaultVideoCanvasFunctionConfig.videoAdvancedSettings).toEqual({
|
|
defaultAutoValidateMaterial: true,
|
|
defaultSearchEnabled: true,
|
|
});
|
|
expect(
|
|
normalizeVideoCanvasFunctionConfig({
|
|
videoAdvancedSettings: {
|
|
defaultAutoValidateMaterial: false,
|
|
defaultSearchEnabled: false,
|
|
},
|
|
}).videoAdvancedSettings,
|
|
).toEqual({
|
|
defaultAutoValidateMaterial: false,
|
|
defaultSearchEnabled: false,
|
|
});
|
|
});
|
|
|
|
test("delivers normalized video generation parameter configuration", () => {
|
|
expect(defaultVideoCanvasFunctionConfig.videoGenerationParameters).toMatchObject({
|
|
countOptions: [1, 2, 4],
|
|
defaultResolution: "720P",
|
|
maxDurationSeconds: 15,
|
|
minDurationSeconds: 4,
|
|
});
|
|
expect(
|
|
normalizeVideoCanvasFunctionConfig({
|
|
videoGenerationParameters: {
|
|
defaultGenerateAudio: false,
|
|
defaultResolution: "4k",
|
|
pointsPerSecondByResolution: { "4k": 155 },
|
|
},
|
|
}).videoGenerationParameters,
|
|
).toMatchObject({
|
|
defaultGenerateAudio: false,
|
|
defaultResolution: "4K",
|
|
pointsPerSecondByResolution: { "4K": 155 },
|
|
});
|
|
});
|
|
|
|
test("delivers backend-configurable new-node defaults", () => {
|
|
expect(defaultVideoCanvasFunctionConfig.nodeCreation).toMatchObject({
|
|
audio: { advancedSettingsOpen: true },
|
|
image: { height: 350, ratio: "16:9", width: 622 },
|
|
video: {
|
|
height: 350,
|
|
modelCatalogId: "seedance-1-5-pro",
|
|
resolution: "1080P",
|
|
width: 622,
|
|
},
|
|
});
|
|
expect(
|
|
normalizeVideoCanvasFunctionConfig({
|
|
nodeCreation: {
|
|
audio: { advancedSettingsOpen: false },
|
|
image: { ratio: "9:16", width: 700 },
|
|
video: { modelCatalogId: "custom-video", resolution: "4k" },
|
|
},
|
|
}).nodeCreation,
|
|
).toMatchObject({
|
|
audio: { advancedSettingsOpen: false },
|
|
image: { ratio: "9:16", width: 700 },
|
|
video: { modelCatalogId: "custom-video", resolution: "4K" },
|
|
});
|
|
});
|
|
|
|
test("provides the competitor public video role catalog in exact page order", () => {
|
|
const roles = defaultVideoCanvasFunctionConfig.rolePresets;
|
|
expect(roles).toHaveLength(23);
|
|
expect(roles.map((role) => role.id)).toEqual([
|
|
"role-003", "role-001", "role-002", "role-004", "role-005",
|
|
"role-006", "role-007", "role-010", "role-011", "role-012",
|
|
"role-013", "role-014", "role-015", "role-016", "role-018",
|
|
"role-022", "role-021", "role-008", "role-009", "role-019",
|
|
"role-020", "role-017", "role-023",
|
|
]);
|
|
expect(roles[0]).toMatchObject({
|
|
name: "甜妹/清新少女",
|
|
tags: "女 · 青年 · 现代 · 女主",
|
|
images: {
|
|
expression: expect.stringContaining("003_expression_grid_1.png"),
|
|
face: expect.stringContaining("003_face_closeup_1.png"),
|
|
full: expect.stringContaining("003_full_body_1.png"),
|
|
threeView: expect.stringContaining("003_character_sheet_1.png"),
|
|
},
|
|
});
|
|
expect(roles[1].images).toMatchObject({
|
|
expression: expect.stringContaining("001_expression_grid.png"),
|
|
face: expect.stringContaining("001_face_closeup.png"),
|
|
full: expect.stringContaining("001_full_body.png"),
|
|
threeView: expect.stringContaining("001_character_sheet.png"),
|
|
});
|
|
expect(roles.slice(10, 13).map((role) => role.name)).toEqual([
|
|
"反派长辈/势利亲戚",
|
|
"生活方式普通人",
|
|
"生活方式普通人",
|
|
]);
|
|
});
|
|
|
|
test("provides competitor-matched subject creation defaults", () => {
|
|
expect(defaultVideoCanvasFunctionConfig.subjectLibrary).toMatchObject({
|
|
descriptionMaxLength: 100,
|
|
imageAccept: "image/jpeg,image/png,image/webp",
|
|
imageMaxCount: 4,
|
|
imageMinCount: 2,
|
|
historyLibraryEnabled: true,
|
|
labels: ["无标签", "人物", "场景", "道具", "特效", "其他"],
|
|
nameMaxLength: 20,
|
|
requireClearFrontFace: true,
|
|
smartCompletion: {
|
|
candidateCount: 9,
|
|
cost: 6,
|
|
maxSelected: 3,
|
|
},
|
|
smartDescription: { cost: 1 },
|
|
unselectedCategory: "其他",
|
|
videoAccept: "video/mp4,video/quicktime",
|
|
videoMaxDurationSeconds: 8,
|
|
videoMinDurationSeconds: 3,
|
|
videoUsage: {
|
|
enabled: true,
|
|
maxSubjects: 4,
|
|
modelPatterns: ["Kling O3"],
|
|
},
|
|
voiceHelpText: "仅主体为人物时支持音色绑定和使用",
|
|
});
|
|
expect(
|
|
defaultVideoCanvasFunctionConfig.subjectLibrary.voices.map(
|
|
(voice) => voice.name,
|
|
),
|
|
).toEqual([
|
|
"无配音",
|
|
"钓系女友",
|
|
"温柔女声",
|
|
"播报男声",
|
|
"盐系少年",
|
|
"撒娇女友",
|
|
"视频原声",
|
|
]);
|
|
});
|
|
|
|
test("normalizes administrator subject creation settings", () => {
|
|
const subjectLibrary = normalizeVideoCanvasFunctionConfig({
|
|
subjectLibrary: {
|
|
descriptionMaxLength: 5000,
|
|
imageMaxCount: 1,
|
|
imageMinCount: 3,
|
|
labels: ["人物", "人物", "场景", ""],
|
|
nameMaxLength: 0,
|
|
smartDescription: {
|
|
maxTokens: 1,
|
|
prompt: " custom subject prompt ",
|
|
temperature: 2,
|
|
},
|
|
videoMaxDurationSeconds: 2,
|
|
videoMinDurationSeconds: 5,
|
|
voices: [{ id: "custom", name: "自定义预设", badge: "站点", previewUrl: "" }],
|
|
videoUsage: {
|
|
maxSubjects: 99,
|
|
modelPatterns: [" Kling O3 ", "Kling O3", "", "Custom Kling"],
|
|
promptTemplate: " 主体{{index}}={{name}} ",
|
|
},
|
|
},
|
|
}).subjectLibrary;
|
|
|
|
expect(subjectLibrary).toMatchObject({
|
|
descriptionMaxLength: 1000,
|
|
imageMaxCount: 3,
|
|
imageMinCount: 3,
|
|
labels: ["人物", "场景"],
|
|
nameMaxLength: 1,
|
|
videoMaxDurationSeconds: 5,
|
|
videoMinDurationSeconds: 5,
|
|
smartDescription: {
|
|
maxTokens: 32,
|
|
prompt: "custom subject prompt",
|
|
temperature: 1,
|
|
},
|
|
voices: [{ id: "custom", name: "自定义预设", badge: "站点", previewUrl: "" }],
|
|
videoUsage: {
|
|
maxSubjects: 12,
|
|
modelPatterns: ["Kling O3", "Custom Kling"],
|
|
promptTemplate: "主体{{index}}={{name}}",
|
|
},
|
|
});
|
|
});
|
|
});
|