Files
UHD_Media_Tool/apps/web/app/components/slots/slot-mounts.test.ts
T
rootandClaude Opus 5.5 62119a9b30 feat(web): 前端插槽设施,作品页与整理记录行挂「补字幕」
- lib/slots.ts 按位置注册(侧栏入口、作品页操作区 / 面板区、整理记录行操作、设置分区),默认直接显示;
  components/slots 挂载点:面板类直接渲染,动作类渲染成按钮 / 菜单项、对话框在菜单外渲染
- 作品页操作区与面板区、整理记录行菜单接入插槽;补字幕改由 features/subtitles.tsx 注册,
  作品页按钮行为不变,整理记录行菜单新增「补字幕」(成功 / 部分成功的入库类整理)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
2026-10-06 19:27:14 +08:00

96 lines
4.3 KiB
TypeScript

import type { PublicOrganizationRecord } from "@umt/contracts";
import { createElement } from "react";
import { renderToStaticMarkup } from "react-dom/server";
import { describe, expect, it } from "vitest";
import { createSlotRegistry, type SlotEntity, slots, visibleActions } from "~/lib/slots.ts";
import { SlotPanels, useSlotActions } from "./slot-mounts.tsx";
// 前端插槽(ADR-006):按位置注册、按 order 与注册顺序排列、默认直接显示(没有开关)、挂载点渲染。
const entity: SlotEntity = { id: "e1", title: "示例剧", kind: "series", status: "active" };
const record = (over: Partial<PublicOrganizationRecord> = {}) =>
({
id: "r1",
status: "succeeded",
action: "copy",
media: { entityId: "e1", title: "示例剧" },
...over,
}) as unknown as PublicOrganizationRecord;
describe("插槽注册表", () => {
it("按 order 排序,同序保持注册顺序;同一位置 id 重复报错", () => {
const reg = createSlotRegistry();
const Panel = (name: string) => () => createElement("p", null, name);
reg.register("entity.panels", { id: "b", feature: "x", Component: Panel("B") });
reg.register("entity.panels", { id: "a", feature: "x", order: 10, Component: Panel("A") });
reg.register("entity.panels", { id: "c", feature: "x", Component: Panel("C") });
expect(reg.entries("entity.panels").map((e) => e.id)).toEqual(["a", "b", "c"]);
expect(reg.entries("settings.sections")).toEqual([]);
expect(() => reg.register("entity.panels", { id: "a", feature: "x", Component: Panel("A2") })).toThrow(/已有 a/);
});
it("面板类挂载点按顺序渲染全部组件,拿到页面给的 props;没有注册时什么都不渲染", () => {
const reg = createSlotRegistry();
reg.register("entity.panels", {
id: "p",
feature: "x",
Component: ({ entity: e }) => createElement("section", null, `面板:${e.title}`),
});
const html = renderToStaticMarkup(
createElement(SlotPanels, { position: "entity.panels", props: { entity }, registry: reg }),
);
expect(html).toBe("<section>面板:示例剧</section>");
expect(
renderToStaticMarkup(
createElement(SlotPanels, { position: "entity.panels", props: { entity }, registry: createSlotRegistry() }),
),
).toBe("");
});
it("动作类:visible 过滤,缺省显示;挂载点给出可见项", () => {
const reg = createSlotRegistry();
const Dialog = () => null;
reg.register("entity.actions", { id: "always", feature: "x", label: "总在", Dialog });
reg.register("entity.actions", {
id: "active-only",
feature: "x",
label: "未合并才有",
visible: ({ entity: e }) => e.status !== "merged",
Dialog,
});
expect(visibleActions(reg, "entity.actions", { entity }).map((a) => a.id)).toEqual(["always", "active-only"]);
expect(visibleActions(reg, "entity.actions", { entity: { ...entity, status: "merged" } }).map((a) => a.id)).toEqual(
["always"],
);
const Probe = () => {
const s = useSlotActions("entity.actions", reg);
return createElement(
"ul",
null,
s.items({ entity }).map((a) => createElement("li", { key: a.id }, a.label)),
s.dialog,
);
};
expect(renderToStaticMarkup(createElement(Probe))).toBe("<ul><li>总在</li><li>未合并才有</li></ul>");
});
});
describe("补字幕样板挂在两个真实位置", () => {
it("作品页操作区「补字幕」:合并后的作品不显示", () => {
expect(visibleActions(slots, "entity.actions", { entity }).map((a) => [a.feature, a.label])).toEqual([
["subtitles", "补字幕"],
]);
expect(visibleActions(slots, "entity.actions", { entity: { ...entity, status: "merged" } })).toEqual([]);
});
it("整理记录行菜单「补字幕」:只给成功 / 部分成功、认到作品的入库类整理", () => {
const labels = (r: PublicOrganizationRecord) =>
visibleActions(slots, "organize.record.actions", { record: r }).map((a) => a.label);
expect(labels(record())).toEqual(["补字幕"]);
expect(labels(record({ status: "partial", action: "move" }))).toEqual(["补字幕"]);
expect(labels(record({ status: "failed" }))).toEqual([]);
expect(labels(record({ action: "trash" }))).toEqual([]);
expect(labels(record({ media: null }))).toEqual([]);
});
});