feat(*): 创作平台 - 升级短剧分镜工作流并完善模型计费与界面状态
This commit is contained in:
55
web/test/channel-model-price-tier-form.test.ts
Normal file
55
web/test/channel-model-price-tier-form.test.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
import { defaultPriceTier, priceTierResolutionFromForm, priceTierToForm, priceTierVideoSecondsFromForm, skuSelectorFromForm } from "../src/pages/admin/components/channel-model-price-tier-form";
|
||||
import type { ChannelModelPriceTier } from "../src/services/api/wallet";
|
||||
|
||||
describe("channel model price tier defaults", () => {
|
||||
test("creates a usable all-spec fallback price by default", () => {
|
||||
const tier = defaultPriceTier();
|
||||
|
||||
expect(tier.matchMode).toBe("default");
|
||||
expect(tier.priceConfigured).toBe(true);
|
||||
expect(tier.enabled).toBe(true);
|
||||
expect(skuSelectorFromForm("image", { ...tier, quality: "2k", size: "1:1" })).toEqual({});
|
||||
});
|
||||
|
||||
test("keeps explicit image specification pricing when advanced mode is selected", () => {
|
||||
const tier = defaultPriceTier("advanced");
|
||||
|
||||
expect(skuSelectorFromForm("image", { ...tier, quality: "2k", size: "1:1" })).toEqual({ quality: "2k", size: "1:1" });
|
||||
});
|
||||
|
||||
test("drops stale video selectors after switching back to the default price", () => {
|
||||
const tier = { ...defaultPriceTier("advanced"), resolution: "1080p", videoSeconds: 10, imageCount: 2 };
|
||||
const defaultTier = { ...tier, matchMode: "default" as const };
|
||||
|
||||
expect(skuSelectorFromForm("video", defaultTier)).toEqual({});
|
||||
expect(priceTierResolutionFromForm("video", defaultTier)).toBe("*");
|
||||
expect(priceTierVideoSecondsFromForm("video", defaultTier)).toBe(0);
|
||||
});
|
||||
|
||||
test("restores existing specific tiers in advanced mode and wildcard tiers as defaults", () => {
|
||||
const base: ChannelModelPriceTier = {
|
||||
id: "tier-1",
|
||||
channelModelId: "model-1",
|
||||
selector: {},
|
||||
selectorKey: "{}",
|
||||
resolution: "*",
|
||||
videoSeconds: 0,
|
||||
providerModelKey: "gpt-image-2",
|
||||
billingMode: "fixed_request" as const,
|
||||
unitPriceMicrocredits: 4_000_000,
|
||||
inputTokenPriceMicrocredits: 0,
|
||||
outputTokenPriceMicrocredits: 0,
|
||||
cachedTokenPriceMicrocredits: 0,
|
||||
priceConfigured: true,
|
||||
enabled: true,
|
||||
priceVersion: 1,
|
||||
createdAt: "2026-08-29T00:00:00Z",
|
||||
updatedAt: "2026-08-29T00:00:00Z",
|
||||
};
|
||||
|
||||
expect(priceTierToForm(base).matchMode).toBe("default");
|
||||
expect(priceTierToForm({ ...base, selector: { quality: "2k" } }).matchMode).toBe("advanced");
|
||||
});
|
||||
});
|
||||
49
web/test/workspace-sidebar-state.test.ts
Normal file
49
web/test/workspace-sidebar-state.test.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
import {
|
||||
readWorkspaceSidebarCollapsed,
|
||||
WORKSPACE_SIDEBAR_STORAGE_KEY,
|
||||
writeWorkspaceSidebarCollapsed,
|
||||
} from "../src/components/layout/workspace-sidebar-state";
|
||||
|
||||
function memoryStorage(initial?: string) {
|
||||
const values = new Map<string, string>();
|
||||
if (initial !== undefined) values.set(WORKSPACE_SIDEBAR_STORAGE_KEY, initial);
|
||||
return {
|
||||
values,
|
||||
getItem: (key: string) => values.get(key) ?? null,
|
||||
setItem: (key: string, value: string) => values.set(key, value),
|
||||
};
|
||||
}
|
||||
|
||||
describe("workspace sidebar state", () => {
|
||||
test("restores the persisted collapsed state after refresh", () => {
|
||||
expect(readWorkspaceSidebarCollapsed(memoryStorage("1"))).toBe(true);
|
||||
expect(readWorkspaceSidebarCollapsed(memoryStorage("0"))).toBe(false);
|
||||
expect(readWorkspaceSidebarCollapsed(memoryStorage())).toBe(false);
|
||||
});
|
||||
|
||||
test("persists both collapsed and expanded states", () => {
|
||||
const storage = memoryStorage();
|
||||
|
||||
writeWorkspaceSidebarCollapsed(true, storage);
|
||||
expect(storage.values.get(WORKSPACE_SIDEBAR_STORAGE_KEY)).toBe("1");
|
||||
|
||||
writeWorkspaceSidebarCollapsed(false, storage);
|
||||
expect(storage.values.get(WORKSPACE_SIDEBAR_STORAGE_KEY)).toBe("0");
|
||||
});
|
||||
|
||||
test("keeps sidebar interaction available when storage is blocked", () => {
|
||||
const blockedStorage = {
|
||||
getItem: () => {
|
||||
throw new Error("blocked");
|
||||
},
|
||||
setItem: () => {
|
||||
throw new Error("blocked");
|
||||
},
|
||||
};
|
||||
|
||||
expect(readWorkspaceSidebarCollapsed(blockedStorage)).toBe(false);
|
||||
expect(() => writeWorkspaceSidebarCollapsed(true, blockedStorage)).not.toThrow();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user