mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-24 08:21:39 +00:00
Merged via squash.
Prepared head SHA: 76eb184dbe
Co-authored-by: huntharo <5617868+huntharo@users.noreply.github.com>
Co-authored-by: huntharo <5617868+huntharo@users.noreply.github.com>
Reviewed-by: @huntharo
132 lines
4.1 KiB
TypeScript
132 lines
4.1 KiB
TypeScript
import { MessageFlags } from "discord-api-types/v10";
|
|
import { beforeEach, describe, expect, it } from "vitest";
|
|
import {
|
|
clearDiscordComponentEntries,
|
|
registerDiscordComponentEntries,
|
|
resolveDiscordComponentEntry,
|
|
resolveDiscordModalEntry,
|
|
} from "./components-registry.js";
|
|
import {
|
|
buildDiscordComponentMessage,
|
|
buildDiscordComponentMessageFlags,
|
|
readDiscordComponentSpec,
|
|
} from "./components.js";
|
|
|
|
describe("discord components", () => {
|
|
it("builds v2 containers with modal trigger", () => {
|
|
const spec = readDiscordComponentSpec({
|
|
text: "Choose a path",
|
|
blocks: [
|
|
{
|
|
type: "actions",
|
|
buttons: [{ label: "Approve", style: "success", callbackData: "codex:approve" }],
|
|
},
|
|
],
|
|
modal: {
|
|
title: "Details",
|
|
callbackData: "codex:modal",
|
|
allowedUsers: ["discord:user-1"],
|
|
fields: [{ type: "text", label: "Requester" }],
|
|
},
|
|
});
|
|
if (!spec) {
|
|
throw new Error("Expected component spec to be parsed");
|
|
}
|
|
|
|
const result = buildDiscordComponentMessage({ spec });
|
|
expect(result.components).toHaveLength(1);
|
|
expect(result.components[0]?.isV2).toBe(true);
|
|
expect(buildDiscordComponentMessageFlags(result.components)).toBe(MessageFlags.IsComponentsV2);
|
|
expect(result.modals).toHaveLength(1);
|
|
|
|
const trigger = result.entries.find((entry) => entry.kind === "modal-trigger");
|
|
expect(trigger?.modalId).toBe(result.modals[0]?.id);
|
|
expect(result.entries.find((entry) => entry.kind === "button")?.callbackData).toBe(
|
|
"codex:approve",
|
|
);
|
|
expect(result.modals[0]?.callbackData).toBe("codex:modal");
|
|
expect(result.modals[0]?.allowedUsers).toEqual(["discord:user-1"]);
|
|
});
|
|
|
|
it("requires options for modal select fields", () => {
|
|
expect(() =>
|
|
readDiscordComponentSpec({
|
|
modal: {
|
|
title: "Details",
|
|
fields: [{ type: "select", label: "Priority" }],
|
|
},
|
|
}),
|
|
).toThrow("options");
|
|
});
|
|
|
|
it("requires attachment references for file blocks", () => {
|
|
expect(() =>
|
|
readDiscordComponentSpec({
|
|
blocks: [{ type: "file", file: "https://example.com/report.pdf" }],
|
|
}),
|
|
).toThrow("attachment://");
|
|
expect(() =>
|
|
readDiscordComponentSpec({
|
|
blocks: [{ type: "file", file: "attachment://" }],
|
|
}),
|
|
).toThrow("filename");
|
|
});
|
|
});
|
|
|
|
describe("discord component registry", () => {
|
|
beforeEach(() => {
|
|
clearDiscordComponentEntries();
|
|
});
|
|
|
|
const componentsRegistryModuleUrl = new URL("./components-registry.ts", import.meta.url).href;
|
|
|
|
it("registers and consumes component entries", () => {
|
|
registerDiscordComponentEntries({
|
|
entries: [{ id: "btn_1", kind: "button", label: "Confirm" }],
|
|
modals: [
|
|
{
|
|
id: "mdl_1",
|
|
title: "Details",
|
|
fields: [{ id: "fld_1", name: "name", label: "Name", type: "text" }],
|
|
},
|
|
],
|
|
messageId: "msg_1",
|
|
ttlMs: 1000,
|
|
});
|
|
|
|
const entry = resolveDiscordComponentEntry({ id: "btn_1", consume: false });
|
|
expect(entry?.messageId).toBe("msg_1");
|
|
|
|
const modal = resolveDiscordModalEntry({ id: "mdl_1", consume: false });
|
|
expect(modal?.messageId).toBe("msg_1");
|
|
|
|
const consumed = resolveDiscordComponentEntry({ id: "btn_1" });
|
|
expect(consumed?.id).toBe("btn_1");
|
|
expect(resolveDiscordComponentEntry({ id: "btn_1" })).toBeNull();
|
|
});
|
|
|
|
it("shares registry state across duplicate module instances", async () => {
|
|
const first = (await import(
|
|
`${componentsRegistryModuleUrl}?t=first-${Date.now()}`
|
|
)) as typeof import("./components-registry.js");
|
|
const second = (await import(
|
|
`${componentsRegistryModuleUrl}?t=second-${Date.now()}`
|
|
)) as typeof import("./components-registry.js");
|
|
|
|
first.clearDiscordComponentEntries();
|
|
first.registerDiscordComponentEntries({
|
|
entries: [{ id: "btn_shared", kind: "button", label: "Shared" }],
|
|
modals: [],
|
|
});
|
|
|
|
expect(second.resolveDiscordComponentEntry({ id: "btn_shared", consume: false })).toMatchObject(
|
|
{
|
|
id: "btn_shared",
|
|
label: "Shared",
|
|
},
|
|
);
|
|
|
|
second.clearDiscordComponentEntries();
|
|
});
|
|
});
|