Skip to content

Commit 1a61ff6

Browse files
committed
fix(registerCommands): defensive error handling on toggleAutoApprove + log context
Addresses two CodeRabbit review items: - toggleAutoApprove now wraps the await in try/catch logging to outputChannel, matching the defensive posture used on the other handlers (settingsButtonClicked, historyButtonClicked, etc.). An unhandled rejection from postMessageToWebview would otherwise bubble to VS Code's command dispatcher rather than being logged consistently with the rest of the file. - All postMessageToWebview failure log messages now carry a [<command-name>] prefix so multi-failure logs are unambiguous.
1 parent d2a9299 commit 1a61ff6

2 files changed

Lines changed: 52 additions & 21 deletions

File tree

src/activate/__tests__/registerCommands.spec.ts

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -292,23 +292,48 @@ describe("registerCommands handlers", () => {
292292
// postMessageToWebview sites in registerCommands.ts (settingsButtonClicked
293293
// posts twice, plus historyButtonClicked, marketplaceButtonClicked, and
294294
// acceptInput). Each handler is synchronous, so the .catch arm runs on a
295-
// microtask; awaiting Promise.resolve() flushes it before we assert.
295+
// microtask; awaiting Promise.resolve() flushes it before we assert. The
296+
// log messages carry a `[<handlerName>]` prefix so multi-failure logs
297+
// remain unambiguous; the prefix is per-handler, not per-call (both of
298+
// settingsButtonClicked's posts share the same prefix).
296299
it.each([
297-
{ command: "zoo-code.settingsButtonClicked", expectedCalls: 2 },
298-
{ command: "zoo-code.historyButtonClicked", expectedCalls: 1 },
299-
{ command: "zoo-code.marketplaceButtonClicked", expectedCalls: 1 },
300-
{ command: "zoo-code.acceptInput", expectedCalls: 1 },
301-
])("$command logs to outputChannel when postMessageToWebview rejects", async ({ command, expectedCalls }) => {
300+
{ command: "zoo-code.settingsButtonClicked", prefix: "settingsButtonClicked", expectedCalls: 2 },
301+
{ command: "zoo-code.historyButtonClicked", prefix: "historyButtonClicked", expectedCalls: 1 },
302+
{ command: "zoo-code.marketplaceButtonClicked", prefix: "marketplaceButtonClicked", expectedCalls: 1 },
303+
{ command: "zoo-code.acceptInput", prefix: "acceptInput", expectedCalls: 1 },
304+
])(
305+
"$command logs to outputChannel when postMessageToWebview rejects",
306+
async ({ command, prefix, expectedCalls }) => {
307+
const boom = new Error("boom")
308+
mockVisibleProvider.postMessageToWebview.mockReset()
309+
mockVisibleProvider.postMessageToWebview.mockRejectedValue(boom)
310+
311+
handlers[command]()
312+
313+
// Flush microtasks so the chained .catch arm runs.
314+
await new Promise((resolve) => setImmediate(resolve))
315+
316+
expect(mockOutputChannel.appendLine).toHaveBeenCalledTimes(expectedCalls)
317+
expect(mockOutputChannel.appendLine).toHaveBeenCalledWith(
318+
`[${prefix}] postMessageToWebview failed: ${boom}`,
319+
)
320+
},
321+
)
322+
323+
it("toggleAutoApprove logs to outputChannel when postMessageToWebview rejects", async () => {
324+
// toggleAutoApprove is `async` and awaits postMessageToWebview inside a
325+
// try/catch (rather than relying on a `.catch` microtask like the
326+
// void-prefixed sites), so awaiting the handler itself is sufficient to
327+
// observe the appendLine call.
302328
const boom = new Error("boom")
303329
mockVisibleProvider.postMessageToWebview.mockReset()
304330
mockVisibleProvider.postMessageToWebview.mockRejectedValue(boom)
305331

306-
handlers[command]()
332+
await handlers["zoo-code.toggleAutoApprove"]()
307333

308-
// Flush microtasks so the chained .catch arm runs.
309-
await new Promise((resolve) => setImmediate(resolve))
310-
311-
expect(mockOutputChannel.appendLine).toHaveBeenCalledTimes(expectedCalls)
312-
expect(mockOutputChannel.appendLine).toHaveBeenCalledWith(`postMessageToWebview failed: ${boom}`)
334+
expect(mockOutputChannel.appendLine).toHaveBeenCalledTimes(1)
335+
expect(mockOutputChannel.appendLine).toHaveBeenCalledWith(
336+
`[toggleAutoApprove] postMessageToWebview failed: ${boom}`,
337+
)
313338
})
314339
})

src/activate/registerCommands.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,11 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt
105105

106106
void visibleProvider
107107
.postMessageToWebview({ type: "action", action: "settingsButtonClicked" })
108-
.catch((error) => outputChannel.appendLine(`postMessageToWebview failed: ${error}`))
108+
.catch((error) => outputChannel.appendLine(`[settingsButtonClicked] postMessageToWebview failed: ${error}`))
109109
// Also explicitly post the visibility message to trigger scroll reliably
110110
void visibleProvider
111111
.postMessageToWebview({ type: "action", action: "didBecomeVisible" })
112-
.catch((error) => outputChannel.appendLine(`postMessageToWebview failed: ${error}`))
112+
.catch((error) => outputChannel.appendLine(`[settingsButtonClicked] postMessageToWebview failed: ${error}`))
113113
},
114114
historyButtonClicked: () => {
115115
const visibleProvider = getVisibleProviderOrLog(outputChannel)
@@ -122,14 +122,16 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt
122122

123123
void visibleProvider
124124
.postMessageToWebview({ type: "action", action: "historyButtonClicked" })
125-
.catch((error) => outputChannel.appendLine(`postMessageToWebview failed: ${error}`))
125+
.catch((error) => outputChannel.appendLine(`[historyButtonClicked] postMessageToWebview failed: ${error}`))
126126
},
127127
marketplaceButtonClicked: () => {
128128
const visibleProvider = getVisibleProviderOrLog(outputChannel)
129129
if (!visibleProvider) return
130130
void visibleProvider
131131
.postMessageToWebview({ type: "action", action: "marketplaceButtonClicked" })
132-
.catch((error) => outputChannel.appendLine(`postMessageToWebview failed: ${error}`))
132+
.catch((error) =>
133+
outputChannel.appendLine(`[marketplaceButtonClicked] postMessageToWebview failed: ${error}`),
134+
)
133135
},
134136
newTask: handleNewTask,
135137
setCustomStoragePath: async () => {
@@ -180,7 +182,7 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt
180182

181183
void visibleProvider
182184
.postMessageToWebview({ type: "acceptInput" })
183-
.catch((error) => outputChannel.appendLine(`postMessageToWebview failed: ${error}`))
185+
.catch((error) => outputChannel.appendLine(`[acceptInput] postMessageToWebview failed: ${error}`))
184186
},
185187
toggleAutoApprove: async () => {
186188
const visibleProvider = getVisibleProviderOrLog(outputChannel)
@@ -189,10 +191,14 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt
189191
return
190192
}
191193

192-
await visibleProvider.postMessageToWebview({
193-
type: "action",
194-
action: "toggleAutoApprove",
195-
})
194+
try {
195+
await visibleProvider.postMessageToWebview({
196+
type: "action",
197+
action: "toggleAutoApprove",
198+
})
199+
} catch (error) {
200+
outputChannel.appendLine(`[toggleAutoApprove] postMessageToWebview failed: ${error}`)
201+
}
196202
},
197203
})
198204

0 commit comments

Comments
 (0)