


























@@ -32,6 +32,16 @@ class TestSlackStreamNotDeliveredError extends Error {
3232let mockedNativeStreaming = false;
3333let mockedBlockStreamingEnabled: boolean | undefined = false;
3434let capturedReplyOptions: { disableBlockStreaming?: boolean } | undefined;
35+let capturedStatusReactionOptions: { enabled?: boolean; initialEmoji?: string } | undefined;
36+const statusReactionControllerMock = {
37+setQueued: vi.fn(async () => {}),
38+setThinking: vi.fn(async () => {}),
39+setTool: vi.fn(async () => {}),
40+setError: vi.fn(async () => {}),
41+setDone: vi.fn(async () => {}),
42+clear: vi.fn(async () => {}),
43+restoreInitial: vi.fn(async () => {}),
44+};
3545let mockedReplyThreadTs: string | undefined = THREAD_TS;
3646let mockedReplyThreadTsSequence: Array<string | undefined> | undefined;
3747let capturedTyping:
@@ -87,6 +97,8 @@ function createPreparedSlackMessage(params?: {
8797status: string;
8898}) => Promise<void>;
8999typingReaction?: string;
100+ackReactionMessageTs?: string;
101+ackReactionPromise?: Promise<boolean> | null;
90102}) {
91103return {
92104ctx: {
@@ -136,7 +148,8 @@ function createPreparedSlackMessage(params?: {
136148historyKey: "history-key",
137149preview: "",
138150ackReactionValue: "eyes",
139-ackReactionPromise: null,
151+ackReactionMessageTs: params?.ackReactionMessageTs,
152+ackReactionPromise: params?.ackReactionPromise ?? null,
140153} as never;
141154}
142155@@ -149,15 +162,10 @@ vi.mock("openclaw/plugin-sdk/channel-feedback", () => ({
149162doneHoldMs: 0,
150163errorHoldMs: 0,
151164},
152-createStatusReactionController: () => ({
153-setQueued: async () => {},
154-setThinking: async () => {},
155-setTool: async () => {},
156-setError: async () => {},
157-setDone: async () => {},
158-clear: async () => {},
159-restoreInitial: async () => {},
160-}),
165+createStatusReactionController: (params: { enabled?: boolean; initialEmoji?: string }) => {
166+capturedStatusReactionOptions = params;
167+return statusReactionControllerMock;
168+},
161169logAckFailure: () => {},
162170logTypingFailure: () => {},
163171removeAckReactionAfterReply: () => {},
@@ -408,9 +416,13 @@ describe("dispatchPreparedSlackMessage preview fallback", () => {
408416stopSlackStreamMock.mockReset();
409417reactSlackMessageMock.mockReset();
410418removeSlackReactionMock.mockReset();
419+for (const value of Object.values(statusReactionControllerMock)) {
420+value.mockClear();
421+}
411422mockedNativeStreaming = false;
412423mockedBlockStreamingEnabled = false;
413424capturedReplyOptions = undefined;
425+capturedStatusReactionOptions = undefined;
414426capturedTyping = undefined;
415427mockedReplyThreadTs = THREAD_TS;
416428mockedReplyThreadTsSequence = undefined;
@@ -520,6 +532,32 @@ describe("dispatchPreparedSlackMessage preview fallback", () => {
520532);
521533});
522534535+it("keeps Slack status reactions when channel replies are message-tool-only", async () => {
536+await dispatchPreparedSlackMessage(
537+createPreparedSlackMessage({
538+cfg: {
539+messages: {
540+groupChat: { visibleReplies: "message_tool" },
541+statusReactions: { enabled: true },
542+},
543+},
544+ctxPayload: { ChatType: "channel" },
545+ackReactionMessageTs: "171234.111",
546+ackReactionPromise: Promise.resolve(true),
547+}),
548+);
549+550+expect(capturedReplyOptions?.disableBlockStreaming).toBe(true);
551+expect(capturedStatusReactionOptions).toEqual(
552+expect.objectContaining({
553+enabled: true,
554+initialEmoji: "eyes",
555+}),
556+);
557+expect(statusReactionControllerMock.setQueued).toHaveBeenCalledTimes(1);
558+expect(statusReactionControllerMock.setDone).toHaveBeenCalledTimes(1);
559+});
560+523561it("escapes Slack mrkdwn in tool progress preview labels", async () => {
524562const draftStream = createDraftStreamStub();
525563createSlackDraftStreamMock.mockReturnValueOnce(draftStream);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。