From 2ff309db4de807dfa740613dc8e2c968d4470dec Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 15 Mar 2026 18:52:24 -0700 Subject: [PATCH] Reply: update shared interactive flow tests --- src/auto-reply/reply/reply-flow.test.ts | 295 +++++++----------------- 1 file changed, 81 insertions(+), 214 deletions(-) diff --git a/src/auto-reply/reply/reply-flow.test.ts b/src/auto-reply/reply/reply-flow.test.ts index d7efa640b1c..d9e985c8b31 100644 --- a/src/auto-reply/reply/reply-flow.test.ts +++ b/src/auto-reply/reply/reply-flow.test.ts @@ -197,8 +197,8 @@ describe("inbound context contract (providers + extensions)", () => { const getLineData = (result: ReturnType) => (result.channelData?.line as Record | undefined) ?? {}; -const getSlackData = (result: ReturnType) => - (result.channelData?.slack as Record | undefined) ?? {}; +const getSlackInteractive = (result: ReturnType) => + result.interactive?.blocks ?? []; describe("hasLineDirectives", () => { it("matches expected detection across directive patterns", () => { @@ -601,93 +601,52 @@ describe("parseLineDirectives", () => { }); describe("parseSlackDirectives", () => { - it("builds section and button blocks from slack_buttons directives", () => { + it("builds shared text and button blocks from slack_buttons directives", () => { const result = parseSlackDirectives({ text: "Choose an action [[slack_buttons: Approve:approve, Reject:reject]]", }); expect(result.text).toBe("Choose an action"); - expect(getSlackData(result).blocks).toEqual([ + expect(getSlackInteractive(result)).toEqual([ { - type: "section", - text: { - type: "mrkdwn", - text: "Choose an action", - }, + type: "text", + text: "Choose an action", }, { - type: "actions", - block_id: "openclaw_reply_buttons_1", - elements: [ + type: "buttons", + buttons: [ { - type: "button", - action_id: "openclaw:reply_button", - text: { - type: "plain_text", - text: "Approve", - emoji: true, - }, - value: "reply_1_approve", + label: "Approve", + value: "approve", }, { - type: "button", - action_id: "openclaw:reply_button", - text: { - type: "plain_text", - text: "Reject", - emoji: true, - }, - value: "reply_2_reject", + label: "Reject", + value: "reject", }, ], }, ]); }); - it("builds static select blocks from slack_select directives", () => { + it("builds shared select blocks from slack_select directives", () => { const result = parseSlackDirectives({ text: "[[slack_select: Choose a project | Alpha:alpha, Beta:beta]]", }); expect(result.text).toBeUndefined(); - expect(getSlackData(result).blocks).toEqual([ + expect(getSlackInteractive(result)).toEqual([ { - type: "actions", - block_id: "openclaw_reply_select_1", - elements: [ - { - type: "static_select", - action_id: "openclaw:reply_select", - placeholder: { - type: "plain_text", - text: "Choose a project", - emoji: true, - }, - options: [ - { - text: { - type: "plain_text", - text: "Alpha", - emoji: true, - }, - value: "reply_1_alpha", - }, - { - text: { - type: "plain_text", - text: "Beta", - emoji: true, - }, - value: "reply_2_beta", - }, - ], - }, + type: "select", + placeholder: "Choose a project", + options: [ + { label: "Alpha", value: "alpha" }, + { label: "Beta", value: "beta" }, ], }, ]); }); - it("appends Slack interactive blocks to existing slack blocks", () => { + it("leaves existing slack blocks in channelData and appends shared interactive blocks", () => { const result = parseSlackDirectives({ text: "Act now [[slack_buttons: Retry:retry]]", channelData: { @@ -698,30 +657,19 @@ describe("parseSlackDirectives", () => { }); expect(result.text).toBe("Act now"); - expect(getSlackData(result).blocks).toEqual([ - { type: "divider" }, + expect(result.channelData).toEqual({ + slack: { + blocks: [{ type: "divider" }], + }, + }); + expect(getSlackInteractive(result)).toEqual([ { - type: "section", - text: { - type: "mrkdwn", - text: "Act now", - }, + type: "text", + text: "Act now", }, { - type: "actions", - block_id: "openclaw_reply_buttons_1", - elements: [ - { - type: "button", - action_id: "openclaw:reply_button", - text: { - type: "plain_text", - text: "Retry", - emoji: true, - }, - value: "reply_1_retry", - }, - ], + type: "buttons", + buttons: [{ label: "Retry", value: "retry" }], }, ]); }); @@ -731,146 +679,70 @@ describe("parseSlackDirectives", () => { text: "[[slack_select: Pick one | Alpha:alpha]] then [[slack_buttons: Retry:retry]]", }); - expect(getSlackData(result).blocks).toEqual([ + expect(getSlackInteractive(result)).toEqual([ { - type: "actions", - block_id: "openclaw_reply_select_1", - elements: [ - { - type: "static_select", - action_id: "openclaw:reply_select", - placeholder: { - type: "plain_text", - text: "Pick one", - emoji: true, - }, - options: [ - { - text: { - type: "plain_text", - text: "Alpha", - emoji: true, - }, - value: "reply_1_alpha", - }, - ], - }, - ], + type: "select", + placeholder: "Pick one", + options: [{ label: "Alpha", value: "alpha" }], }, { - type: "section", - text: { - type: "mrkdwn", - text: "then", - }, + type: "text", + text: "then", }, { - type: "actions", - block_id: "openclaw_reply_buttons_1", - elements: [ - { - type: "button", - action_id: "openclaw:reply_button", - text: { - type: "plain_text", - text: "Retry", - emoji: true, - }, - value: "reply_1_retry", - }, - ], + type: "buttons", + buttons: [{ label: "Retry", value: "retry" }], }, ]); }); - it("truncates Slack interactive reply strings to safe Block Kit limits", () => { + it("preserves long Slack directive values in the shared interactive model", () => { const long = "x".repeat(120); const result = parseSlackDirectives({ text: `${"y".repeat(3100)} [[slack_select: ${long} | ${long}:${long}]] [[slack_buttons: ${long}:${long}]]`, }); - const blocks = getSlackData(result).blocks as Array>; - expect(blocks).toHaveLength(3); - expect(((blocks[0]?.text as { text?: string })?.text ?? "").length).toBeLessThanOrEqual(3000); - expect( - ( - ( - (blocks[1]?.elements as Array>)?.[0]?.placeholder as { - text?: string; - } - )?.text ?? "" - ).length, - ).toBeLessThanOrEqual(75); - expect( - ( - ( - ( - (blocks[1]?.elements as Array>)?.[0]?.options as Array< - Record - > - )?.[0]?.text as { text?: string } - )?.text ?? "" - ).length, - ).toBeLessThanOrEqual(75); - expect( - ( - (( - (blocks[1]?.elements as Array>)?.[0]?.options as Array< - Record - > - )?.[0]?.value as string | undefined) ?? "" - ).length, - ).toBeLessThanOrEqual(75); - expect( - ( - ( - (blocks[2]?.elements as Array>)?.[0]?.text as { - text?: string; - } - )?.text ?? "" - ).length, - ).toBeLessThanOrEqual(75); - expect( - ( - ((blocks[2]?.elements as Array>)?.[0]?.value as - | string - | undefined) ?? "" - ).length, - ).toBeLessThanOrEqual(75); + expect(getSlackInteractive(result)).toEqual([ + { + type: "text", + text: "y".repeat(3100), + }, + { + type: "select", + placeholder: long, + options: [{ label: long, value: long }], + }, + { + type: "buttons", + buttons: [{ label: long, value: long }], + }, + ]); }); - it("falls back to the original payload when generated blocks would exceed Slack limits", () => { + it("keeps existing interactive blocks when compiling additional Slack directives", () => { const result = parseSlackDirectives({ text: "Choose [[slack_buttons: Retry:retry]]", - channelData: { - slack: { - blocks: Array.from({ length: 49 }, () => ({ type: "divider" })), - }, + interactive: { + blocks: [{ type: "text", text: "Existing" }], }, }); + expect(getSlackInteractive(result)).toEqual([ + { type: "text", text: "Existing" }, + { type: "text", text: "Choose" }, + { type: "buttons", buttons: [{ label: "Retry", value: "retry" }] }, + ]); + }); + + it("ignores malformed directive choices when none remain", () => { + const result = parseSlackDirectives({ + text: "Choose [[slack_buttons: : , : ]]", + }); + expect(result).toEqual({ - text: "Choose [[slack_buttons: Retry:retry]]", - channelData: { - slack: { - blocks: Array.from({ length: 49 }, () => ({ type: "divider" })), - }, - }, + text: "Choose [[slack_buttons: : , : ]]", }); }); - - it("ignores malformed existing Slack blocks during directive compilation", () => { - expect(() => - parseSlackDirectives({ - text: "Choose [[slack_buttons: Retry:retry]]", - channelData: { - slack: { - blocks: "{not json}", - }, - }, - }), - ).not.toThrow(); - }); }); function createDeferred() { @@ -1796,22 +1668,17 @@ describe("createReplyDispatcher", () => { expect(deliver).toHaveBeenCalledTimes(1); expect(deliver.mock.calls[0]?.[0]).toMatchObject({ text: "Choose", - channelData: { - slack: { - blocks: [ - { - type: "section", - text: { - type: "mrkdwn", - text: "Choose", - }, - }, - { - type: "actions", - block_id: "openclaw_reply_buttons_1", - }, - ], - }, + interactive: { + blocks: [ + { + type: "text", + text: "Choose", + }, + { + type: "buttons", + buttons: [{ label: "Retry", value: "retry" }], + }, + ], }, }); });