test(channels): dedupe slack arg-menu and discord reply chunk assertions

This commit is contained in:
Peter Steinberger 2026-02-18 13:37:50 +00:00
parent c7bfa818ea
commit 41e68c31db
2 changed files with 48 additions and 48 deletions

View File

@ -22,6 +22,32 @@ vi.mock("../web/media.js", async () => {
});
describe("sendMessageDiscord", () => {
function expectReplyReference(
body: { message_reference?: unknown } | undefined,
messageId: string,
) {
expect(body?.message_reference).toEqual({
message_id: messageId,
fail_if_not_exists: false,
});
}
async function sendChunkedReplyAndCollectBodies(params: { text: string; mediaUrl?: string }) {
const { rest, postMock } = makeDiscordRest();
postMock.mockResolvedValue({ id: "msg1", channel_id: "789" });
await sendMessageDiscord("channel:789", params.text, {
rest,
token: "t",
replyTo: "orig-123",
...(params.mediaUrl ? { mediaUrl: params.mediaUrl } : {}),
});
expect(postMock).toHaveBeenCalledTimes(2);
return {
firstBody: postMock.mock.calls[0]?.[1]?.body as { message_reference?: unknown } | undefined,
secondBody: postMock.mock.calls[1]?.[1]?.body as { message_reference?: unknown } | undefined,
};
}
beforeEach(() => {
vi.clearAllMocks();
});
@ -257,46 +283,20 @@ describe("sendMessageDiscord", () => {
});
it("preserves reply reference across all text chunks", async () => {
const { rest, postMock } = makeDiscordRest();
postMock.mockResolvedValue({ id: "msg1", channel_id: "789" });
await sendMessageDiscord("channel:789", "a".repeat(2001), {
rest,
token: "t",
replyTo: "orig-123",
});
expect(postMock).toHaveBeenCalledTimes(2);
const firstBody = postMock.mock.calls[0]?.[1]?.body;
const secondBody = postMock.mock.calls[1]?.[1]?.body;
expect(firstBody?.message_reference).toEqual({
message_id: "orig-123",
fail_if_not_exists: false,
});
expect(secondBody?.message_reference).toEqual({
message_id: "orig-123",
fail_if_not_exists: false,
const { firstBody, secondBody } = await sendChunkedReplyAndCollectBodies({
text: "a".repeat(2001),
});
expectReplyReference(firstBody, "orig-123");
expectReplyReference(secondBody, "orig-123");
});
it("preserves reply reference for follow-up text chunks after media caption split", async () => {
const { rest, postMock } = makeDiscordRest();
postMock.mockResolvedValue({ id: "msg1", channel_id: "789" });
await sendMessageDiscord("channel:789", "a".repeat(2500), {
rest,
token: "t",
const { firstBody, secondBody } = await sendChunkedReplyAndCollectBodies({
text: "a".repeat(2500),
mediaUrl: "file:///tmp/photo.jpg",
replyTo: "orig-123",
});
expect(postMock).toHaveBeenCalledTimes(2);
const firstBody = postMock.mock.calls[0]?.[1]?.body;
const secondBody = postMock.mock.calls[1]?.[1]?.body;
expect(firstBody?.message_reference).toEqual({
message_id: "orig-123",
fail_if_not_exists: false,
});
expect(secondBody?.message_reference).toEqual({
message_id: "orig-123",
fail_if_not_exists: false,
});
expectReplyReference(firstBody, "orig-123");
expectReplyReference(secondBody, "orig-123");
});
});

View File

@ -300,6 +300,18 @@ async function runCommandHandler(handler: (args: unknown) => Promise<void>) {
return { respond, ack };
}
function expectArgMenuLayout(respond: ReturnType<typeof vi.fn>): {
type: string;
elements?: Array<{ type?: string; action_id?: string; confirm?: unknown }>;
} {
expect(respond).toHaveBeenCalledTimes(1);
const payload = respond.mock.calls[0]?.[0] as { blocks?: Array<{ type: string }> };
expect(payload.blocks?.[0]?.type).toBe("header");
expect(payload.blocks?.[1]?.type).toBe("section");
expect(payload.blocks?.[2]?.type).toBe("context");
return findFirstActionsBlock(payload) ?? { type: "actions", elements: [] };
}
async function runArgMenuAction(
handler: (args: unknown) => Promise<void>,
params: {
@ -360,13 +372,7 @@ describe("Slack native command argument menus", () => {
it("shows a button menu when required args are omitted", async () => {
const { respond } = await runCommandHandler(usageHandler);
expect(respond).toHaveBeenCalledTimes(1);
const payload = respond.mock.calls[0]?.[0] as { blocks?: Array<{ type: string }> };
expect(payload.blocks?.[0]?.type).toBe("header");
expect(payload.blocks?.[1]?.type).toBe("section");
expect(payload.blocks?.[2]?.type).toBe("context");
const actions = findFirstActionsBlock(payload);
const actions = expectArgMenuLayout(respond);
const elementType = actions?.elements?.[0]?.type;
expect(elementType).toBe("button");
expect(actions?.elements?.[0]?.confirm).toBeTruthy();
@ -374,13 +380,7 @@ describe("Slack native command argument menus", () => {
it("shows a static_select menu when choices exceed button row size", async () => {
const { respond } = await runCommandHandler(reportHandler);
expect(respond).toHaveBeenCalledTimes(1);
const payload = respond.mock.calls[0]?.[0] as { blocks?: Array<{ type: string }> };
expect(payload.blocks?.[0]?.type).toBe("header");
expect(payload.blocks?.[1]?.type).toBe("section");
expect(payload.blocks?.[2]?.type).toBe("context");
const actions = findFirstActionsBlock(payload);
const actions = expectArgMenuLayout(respond);
const element = actions?.elements?.[0];
expect(element?.type).toBe("static_select");
expect(element?.action_id).toBe("openclaw_cmdarg");