Files
UHD_Media_Tool/apps/web/app/components/rules/expr.test.ts
T
rootandClaude Opus 5.5 b908b4a0c9 feat(web): rules page with layer cards, rule-string sync, preview and preferences as tags (FE-3)
- /console/rules tabs: rule groups, custom conditions, defaults, acquisition policies, suppressions
- editor: rule string parsed live via /rules/parse (position + suggestions) and synced both ways with layer cards; hard excludes; expectedRevision conflicts keep local edits
- preview against recent releases or pasted titles with pri / level / reasons / trace
- single-rule groups shown as preference tags; disable/delete writes suppression records
- /console/preferences redirects to /console/rules (origin passed through)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 16:06:16 +08:00

44 lines
1.8 KiB
TypeScript

import type { RuleExpr, RuleLayer } from "@umt/contracts";
import { describe, expect, it } from "vitest";
import { cardsOf, cardToLayer, exprToString, layersToString } from "./expr.ts";
const a = (id: string): RuleExpr => ({ op: "atom", id });
const not = (arg: RuleExpr): RuleExpr => ({ op: "not", arg });
const and = (...args: RuleExpr[]): RuleExpr => ({ op: "and", args });
const or = (...args: RuleExpr[]): RuleExpr => ({ op: "or", args });
describe("规则串序列化", () => {
it("只加必要括号,原子大写", () => {
expect(exprToString(and(a("specsub"), a("CNSUB")))).toBe("SPECSUB & CNSUB");
expect(exprToString(or(and(a("4K"), a("HDR")), a("1080P")))).toBe("4K & HDR | 1080P");
expect(exprToString(and(or(a("4K"), a("1080P")), not(a("BLU"))))).toBe("(4K | 1080P) & !BLU");
expect(exprToString(not(or(a("REMUX"), a("BLU"))))).toBe("!(REMUX | BLU)");
});
it("多层用 > 连接", () => {
const layers: RuleLayer[] = [{ expr: and(a("SPECSUB"), a("CNSUB")) }, { expr: a("BLU") }];
expect(layersToString(layers)).toBe("SPECSUB & CNSUB > BLU");
});
});
describe("层卡片往返", () => {
it("两层以内的表达式可转卡片再转回", () => {
const layers: RuleLayer[] = [
{ expr: and(or(a("4K"), a("1080P")), not(a("BLU"))) },
{ expr: not(a("REMUX")) },
{ expr: or(a("CNSUB"), not(and(a("HDR"), a("DOLBY")))) },
];
const cards = cardsOf(layers);
expect(cards).not.toBeNull();
expect(cards?.map((c) => cardToLayer(c))).toEqual(layers);
});
it("嵌套太深时返回 null,只能在规则串里改", () => {
expect(cardsOf([{ expr: and(a("A"), or(a("B"), and(a("C"), a("D")))) }])).toBeNull();
});
it("空层转不出表达式", () => {
expect(cardToLayer({ op: "and", items: [] })).toBeNull();
});
});