让玩家与人设等占位在发送前可预览并正确展开。 Co-authored-by: Cursor <cursoragent@cursor.com>
99 lines
3.0 KiB
TypeScript
99 lines
3.0 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
||
import {
|
||
expandAtDirectives,
|
||
expandIdentityDirectives,
|
||
listDirectiveCatalog,
|
||
} from "../src/directives/expand.js";
|
||
|
||
describe("expandAtDirectives", () => {
|
||
it("replaces @玩家 and @user with persona name", () => {
|
||
const r = expandAtDirectives("你好,@玩家;再见 @user。", {
|
||
persona: { name: "林晚" },
|
||
});
|
||
expect(r.text).toBe("你好,林晚;再见 林晚。");
|
||
expect(r.hits.map((h) => h.kind)).toEqual([
|
||
"persona_name",
|
||
"persona_name",
|
||
]);
|
||
});
|
||
|
||
it("falls back to 玩家 when no persona", () => {
|
||
const r = expandAtDirectives("我是@玩家", {});
|
||
expect(r.text).toBe("我是玩家");
|
||
});
|
||
|
||
it("replaces @人设 with description", () => {
|
||
const r = expandAtDirectives("人设:@人设", {
|
||
persona: { name: "林晚", description: "冷静的调查员" },
|
||
});
|
||
expect(r.text).toBe("人设:冷静的调查员");
|
||
});
|
||
|
||
it("compat {{user}}", () => {
|
||
const r = expandIdentityDirectives("对{{user}}说", {
|
||
persona: { name: "阿泽" },
|
||
});
|
||
expect(r.text).toBe("对阿泽说");
|
||
});
|
||
|
||
it("leaves unknown @token alone", () => {
|
||
const r = expandAtDirectives("写给@某人 的信", {
|
||
persona: { name: "林晚" },
|
||
});
|
||
expect(r.text).toBe("写给@某人 的信");
|
||
expect(r.changed).toBe(false);
|
||
});
|
||
|
||
it("does not treat @玩家X as @玩家", () => {
|
||
const r = expandAtDirectives("@玩家X来了", {
|
||
persona: { name: "林晚" },
|
||
});
|
||
expect(r.text).toBe("@玩家X来了");
|
||
});
|
||
|
||
it("expands @玩家 when followed by Chinese", () => {
|
||
const r = expandAtDirectives("且对@玩家没有因为", {
|
||
persona: { name: "林晚" },
|
||
});
|
||
expect(r.text).toBe("且对林晚没有因为");
|
||
});
|
||
|
||
it("rolls @rd100 with target", () => {
|
||
vi.spyOn(Math, "random").mockReturnValue(0.18); // → 19 on 1..100
|
||
const r = expandAtDirectives("检定 @rd100 30", {
|
||
persona: { name: "林晚" },
|
||
});
|
||
expect(r.text).toBe("检定 投掷结果为 19,目标 30(成功)");
|
||
vi.restoreAllMocks();
|
||
});
|
||
|
||
it("rolls @r3d10 without target", () => {
|
||
const seq = [0.3, 0.6, 0.1];
|
||
let i = 0;
|
||
vi.spyOn(Math, "random").mockImplementation(() => seq[i++] ?? 0);
|
||
const r = expandAtDirectives("伤害 @r3d10", {});
|
||
expect(r.text).toMatch(/^伤害 投掷结果为 \d\+\d\+\d = \d+(3d10)$/);
|
||
vi.restoreAllMocks();
|
||
});
|
||
|
||
it("identity expand skips dice", () => {
|
||
const r = expandIdentityDirectives("先 @rd100 50 再说", {
|
||
persona: { name: "林晚" },
|
||
});
|
||
expect(r.text).toBe("先 @rd100 50 再说");
|
||
});
|
||
|
||
it("lists catalog", () => {
|
||
expect(listDirectiveCatalog().some((x) => x.insert === "@玩家")).toBe(
|
||
true,
|
||
);
|
||
});
|
||
|
||
it("keeps @玩家 when the persona name is @玩家", () => {
|
||
const r = expandIdentityDirectives("清晨,@玩家推开门。对{{user}}点头。", {
|
||
persona: { name: "@玩家", description: "占位" },
|
||
});
|
||
expect(r.text).toBe("清晨,@玩家推开门。对@玩家点头。");
|
||
});
|
||
});
|