Skip to content

Commit ac2031d

Browse files
committed
fix: resolve test failures in image-only message tests
- Fixed JavaScript hoisting errors by reordering function definitions - Resolved infinite render loops in useDeepCompareEffect mock - Added missing mocks for useSize and StandardTooltip - Updated test scenarios to properly simulate AI busy/available states - Fixed all ESLint warnings in test files - All 6 image-only message tests now pass
1 parent 7786171 commit ac2031d

File tree

3 files changed

+630
-721
lines changed

3 files changed

+630
-721
lines changed

webview-ui/src/components/chat/ChatView.tsx

Lines changed: 173 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -250,179 +250,6 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
250250
vscode.postMessage({ type: "playTts", text })
251251
}
252252

253-
useDeepCompareEffect(() => {
254-
// if last message is an ask, show user ask UI
255-
// if user finished a task, then start a new task with a new conversation history since in this moment that the extension is waiting for user response, the user could close the extension and the conversation history would be lost.
256-
// basically as long as a task is active, the conversation history will be persisted
257-
if (lastMessage) {
258-
switch (lastMessage.type) {
259-
case "ask":
260-
// Reset user response flag when a new ask arrives to allow auto-approval
261-
userRespondedRef.current = false
262-
const isPartial = lastMessage.partial === true
263-
switch (lastMessage.ask) {
264-
case "api_req_failed":
265-
playSound("progress_loop")
266-
setSendingDisabled(true)
267-
setClineAsk("api_req_failed")
268-
setEnableButtons(true)
269-
setPrimaryButtonText(t("chat:retry.title"))
270-
setSecondaryButtonText(t("chat:startNewTask.title"))
271-
break
272-
case "mistake_limit_reached":
273-
playSound("progress_loop")
274-
setSendingDisabled(false)
275-
setClineAsk("mistake_limit_reached")
276-
setEnableButtons(true)
277-
setPrimaryButtonText(t("chat:proceedAnyways.title"))
278-
setSecondaryButtonText(t("chat:startNewTask.title"))
279-
break
280-
case "followup":
281-
if (!isPartial) {
282-
playSound("notification")
283-
}
284-
setSendingDisabled(isPartial)
285-
setClineAsk("followup")
286-
// setting enable buttons to `false` would trigger a focus grab when
287-
// the text area is enabled which is undesirable.
288-
// We have no buttons for this tool, so no problem having them "enabled"
289-
// to workaround this issue. See #1358.
290-
setEnableButtons(true)
291-
setPrimaryButtonText(undefined)
292-
setSecondaryButtonText(undefined)
293-
break
294-
case "tool":
295-
if (!isAutoApproved(lastMessage) && !isPartial) {
296-
playSound("notification")
297-
}
298-
setSendingDisabled(isPartial)
299-
setClineAsk("tool")
300-
setEnableButtons(!isPartial)
301-
const tool = JSON.parse(lastMessage.text || "{}") as ClineSayTool
302-
switch (tool.tool) {
303-
case "editedExistingFile":
304-
case "appliedDiff":
305-
case "newFileCreated":
306-
case "insertContent":
307-
setPrimaryButtonText(t("chat:save.title"))
308-
setSecondaryButtonText(t("chat:reject.title"))
309-
break
310-
case "finishTask":
311-
setPrimaryButtonText(t("chat:completeSubtaskAndReturn"))
312-
setSecondaryButtonText(undefined)
313-
break
314-
case "readFile":
315-
if (tool.batchFiles && Array.isArray(tool.batchFiles)) {
316-
setPrimaryButtonText(t("chat:read-batch.approve.title"))
317-
setSecondaryButtonText(t("chat:read-batch.deny.title"))
318-
} else {
319-
setPrimaryButtonText(t("chat:approve.title"))
320-
setSecondaryButtonText(t("chat:reject.title"))
321-
}
322-
break
323-
default:
324-
setPrimaryButtonText(t("chat:approve.title"))
325-
setSecondaryButtonText(t("chat:reject.title"))
326-
break
327-
}
328-
break
329-
case "browser_action_launch":
330-
if (!isAutoApproved(lastMessage) && !isPartial) {
331-
playSound("notification")
332-
}
333-
setSendingDisabled(isPartial)
334-
setClineAsk("browser_action_launch")
335-
setEnableButtons(!isPartial)
336-
setPrimaryButtonText(t("chat:approve.title"))
337-
setSecondaryButtonText(t("chat:reject.title"))
338-
break
339-
case "command":
340-
if (!isAutoApproved(lastMessage) && !isPartial) {
341-
playSound("notification")
342-
}
343-
setSendingDisabled(isPartial)
344-
setClineAsk("command")
345-
setEnableButtons(!isPartial)
346-
setPrimaryButtonText(t("chat:runCommand.title"))
347-
setSecondaryButtonText(t("chat:reject.title"))
348-
break
349-
case "command_output":
350-
setSendingDisabled(false)
351-
setClineAsk("command_output")
352-
setEnableButtons(true)
353-
setPrimaryButtonText(t("chat:proceedWhileRunning.title"))
354-
setSecondaryButtonText(t("chat:killCommand.title"))
355-
break
356-
case "use_mcp_server":
357-
if (!isAutoApproved(lastMessage) && !isPartial) {
358-
playSound("notification")
359-
}
360-
setSendingDisabled(isPartial)
361-
setClineAsk("use_mcp_server")
362-
setEnableButtons(!isPartial)
363-
setPrimaryButtonText(t("chat:approve.title"))
364-
setSecondaryButtonText(t("chat:reject.title"))
365-
break
366-
case "completion_result":
367-
// extension waiting for feedback. but we can just present a new task button
368-
if (!isPartial) {
369-
playSound("celebration")
370-
}
371-
setSendingDisabled(isPartial)
372-
setClineAsk("completion_result")
373-
setEnableButtons(!isPartial)
374-
setPrimaryButtonText(t("chat:startNewTask.title"))
375-
setSecondaryButtonText(undefined)
376-
break
377-
case "resume_task":
378-
setSendingDisabled(false)
379-
setClineAsk("resume_task")
380-
setEnableButtons(true)
381-
setPrimaryButtonText(t("chat:resumeTask.title"))
382-
setSecondaryButtonText(t("chat:terminate.title"))
383-
setDidClickCancel(false) // special case where we reset the cancel button state
384-
break
385-
case "resume_completed_task":
386-
setSendingDisabled(false)
387-
setClineAsk("resume_completed_task")
388-
setEnableButtons(true)
389-
setPrimaryButtonText(t("chat:startNewTask.title"))
390-
setSecondaryButtonText(undefined)
391-
setDidClickCancel(false)
392-
break
393-
}
394-
break
395-
case "say":
396-
// Don't want to reset since there could be a "say" after
397-
// an "ask" while ask is waiting for response.
398-
switch (lastMessage.say) {
399-
case "api_req_retry_delayed":
400-
setSendingDisabled(true)
401-
break
402-
case "api_req_started":
403-
if (secondLastMessage?.ask === "command_output") {
404-
setSendingDisabled(true)
405-
setSelectedImages([])
406-
setClineAsk(undefined)
407-
setEnableButtons(false)
408-
}
409-
break
410-
case "api_req_finished":
411-
case "error":
412-
case "text":
413-
case "browser_action":
414-
case "browser_action_result":
415-
case "command_output":
416-
case "mcp_server_request_started":
417-
case "mcp_server_response":
418-
case "completion_result":
419-
break
420-
}
421-
break
422-
}
423-
}
424-
}, [lastMessage, secondLastMessage])
425-
426253
useEffect(() => {
427254
if (messages.length === 0) {
428255
setSendingDisabled(false)
@@ -1181,6 +1008,179 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
11811008
],
11821009
)
11831010

1011+
useDeepCompareEffect(() => {
1012+
// if last message is an ask, show user ask UI
1013+
// if user finished a task, then start a new task with a new conversation history since in this moment that the extension is waiting for user response, the user could close the extension and the conversation history would be lost.
1014+
// basically as long as a task is active, the conversation history will be persisted
1015+
if (lastMessage) {
1016+
switch (lastMessage.type) {
1017+
case "ask":
1018+
// Reset user response flag when a new ask arrives to allow auto-approval
1019+
userRespondedRef.current = false
1020+
const isPartial = lastMessage.partial === true
1021+
switch (lastMessage.ask) {
1022+
case "api_req_failed":
1023+
playSound("progress_loop")
1024+
setSendingDisabled(true)
1025+
setClineAsk("api_req_failed")
1026+
setEnableButtons(true)
1027+
setPrimaryButtonText(t("chat:retry.title"))
1028+
setSecondaryButtonText(t("chat:startNewTask.title"))
1029+
break
1030+
case "mistake_limit_reached":
1031+
playSound("progress_loop")
1032+
setSendingDisabled(false)
1033+
setClineAsk("mistake_limit_reached")
1034+
setEnableButtons(true)
1035+
setPrimaryButtonText(t("chat:proceedAnyways.title"))
1036+
setSecondaryButtonText(t("chat:startNewTask.title"))
1037+
break
1038+
case "followup":
1039+
if (!isPartial) {
1040+
playSound("notification")
1041+
}
1042+
setSendingDisabled(isPartial)
1043+
setClineAsk("followup")
1044+
// setting enable buttons to `false` would trigger a focus grab when
1045+
// the text area is enabled which is undesirable.
1046+
// We have no buttons for this tool, so no problem having them "enabled"
1047+
// to workaround this issue. See #1358.
1048+
setEnableButtons(true)
1049+
setPrimaryButtonText(undefined)
1050+
setSecondaryButtonText(undefined)
1051+
break
1052+
case "tool":
1053+
if (!isAutoApproved(lastMessage) && !isPartial) {
1054+
playSound("notification")
1055+
}
1056+
setSendingDisabled(isPartial)
1057+
setClineAsk("tool")
1058+
setEnableButtons(!isPartial)
1059+
const tool = JSON.parse(lastMessage.text || "{}") as ClineSayTool
1060+
switch (tool.tool) {
1061+
case "editedExistingFile":
1062+
case "appliedDiff":
1063+
case "newFileCreated":
1064+
case "insertContent":
1065+
setPrimaryButtonText(t("chat:save.title"))
1066+
setSecondaryButtonText(t("chat:reject.title"))
1067+
break
1068+
case "finishTask":
1069+
setPrimaryButtonText(t("chat:completeSubtaskAndReturn"))
1070+
setSecondaryButtonText(undefined)
1071+
break
1072+
case "readFile":
1073+
if (tool.batchFiles && Array.isArray(tool.batchFiles)) {
1074+
setPrimaryButtonText(t("chat:read-batch.approve.title"))
1075+
setSecondaryButtonText(t("chat:read-batch.deny.title"))
1076+
} else {
1077+
setPrimaryButtonText(t("chat:approve.title"))
1078+
setSecondaryButtonText(t("chat:reject.title"))
1079+
}
1080+
break
1081+
default:
1082+
setPrimaryButtonText(t("chat:approve.title"))
1083+
setSecondaryButtonText(t("chat:reject.title"))
1084+
break
1085+
}
1086+
break
1087+
case "browser_action_launch":
1088+
if (!isAutoApproved(lastMessage) && !isPartial) {
1089+
playSound("notification")
1090+
}
1091+
setSendingDisabled(isPartial)
1092+
setClineAsk("browser_action_launch")
1093+
setEnableButtons(!isPartial)
1094+
setPrimaryButtonText(t("chat:approve.title"))
1095+
setSecondaryButtonText(t("chat:reject.title"))
1096+
break
1097+
case "command":
1098+
if (!isAutoApproved(lastMessage) && !isPartial) {
1099+
playSound("notification")
1100+
}
1101+
setSendingDisabled(isPartial)
1102+
setClineAsk("command")
1103+
setEnableButtons(!isPartial)
1104+
setPrimaryButtonText(t("chat:runCommand.title"))
1105+
setSecondaryButtonText(t("chat:reject.title"))
1106+
break
1107+
case "command_output":
1108+
setSendingDisabled(false)
1109+
setClineAsk("command_output")
1110+
setEnableButtons(true)
1111+
setPrimaryButtonText(t("chat:proceedWhileRunning.title"))
1112+
setSecondaryButtonText(t("chat:killCommand.title"))
1113+
break
1114+
case "use_mcp_server":
1115+
if (!isAutoApproved(lastMessage) && !isPartial) {
1116+
playSound("notification")
1117+
}
1118+
setSendingDisabled(isPartial)
1119+
setClineAsk("use_mcp_server")
1120+
setEnableButtons(!isPartial)
1121+
setPrimaryButtonText(t("chat:approve.title"))
1122+
setSecondaryButtonText(t("chat:reject.title"))
1123+
break
1124+
case "completion_result":
1125+
// extension waiting for feedback. but we can just present a new task button
1126+
if (!isPartial) {
1127+
playSound("celebration")
1128+
}
1129+
setSendingDisabled(isPartial)
1130+
setClineAsk("completion_result")
1131+
setEnableButtons(!isPartial)
1132+
setPrimaryButtonText(t("chat:startNewTask.title"))
1133+
setSecondaryButtonText(undefined)
1134+
break
1135+
case "resume_task":
1136+
setSendingDisabled(false)
1137+
setClineAsk("resume_task")
1138+
setEnableButtons(true)
1139+
setPrimaryButtonText(t("chat:resumeTask.title"))
1140+
setSecondaryButtonText(t("chat:terminate.title"))
1141+
setDidClickCancel(false) // special case where we reset the cancel button state
1142+
break
1143+
case "resume_completed_task":
1144+
setSendingDisabled(false)
1145+
setClineAsk("resume_completed_task")
1146+
setEnableButtons(true)
1147+
setPrimaryButtonText(t("chat:startNewTask.title"))
1148+
setSecondaryButtonText(undefined)
1149+
setDidClickCancel(false)
1150+
break
1151+
}
1152+
break
1153+
case "say":
1154+
// Don't want to reset since there could be a "say" after
1155+
// an "ask" while ask is waiting for response.
1156+
switch (lastMessage.say) {
1157+
case "api_req_retry_delayed":
1158+
setSendingDisabled(true)
1159+
break
1160+
case "api_req_started":
1161+
if (secondLastMessage?.ask === "command_output") {
1162+
setSendingDisabled(true)
1163+
setSelectedImages([])
1164+
setClineAsk(undefined)
1165+
setEnableButtons(false)
1166+
}
1167+
break
1168+
case "api_req_finished":
1169+
case "error":
1170+
case "text":
1171+
case "browser_action":
1172+
case "browser_action_result":
1173+
case "command_output":
1174+
case "mcp_server_request_started":
1175+
case "mcp_server_response":
1176+
case "completion_result":
1177+
break
1178+
}
1179+
break
1180+
}
1181+
}
1182+
}, [lastMessage, secondLastMessage])
1183+
11841184
useEffect(() => {
11851185
// This ensures the first message is not read, future user messages are
11861186
// labeled as `user_feedback`.

0 commit comments

Comments
 (0)