diff --git a/bin/roo-cline-1.0.3.vsix b/bin/roo-cline-1.0.4.vsix similarity index 90% rename from bin/roo-cline-1.0.3.vsix rename to bin/roo-cline-1.0.4.vsix index aa243b4..fb2aaa3 100644 Binary files a/bin/roo-cline-1.0.3.vsix and b/bin/roo-cline-1.0.4.vsix differ diff --git a/package.json b/package.json index edec539..cf4569d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "roo-cline", "displayName": "Roo Cline", "description": "Autonomous coding agent right in your IDE, capable of creating/editing files, running commands, using the browser, and more with your permission every step of the way.", - "version": "1.0.3", + "version": "1.0.4", "icon": "assets/icons/icon.png", "galleryBanner": { "color": "#617A91", diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index 56fdede..e6f6323 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -146,8 +146,8 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie setClineAsk("resume_task") setEnableButtons(true) setPrimaryButtonText("Resume Task") - setSecondaryButtonText(undefined) - setDidClickCancel(false) // special case where we reset the cancel button state + setSecondaryButtonText("Terminate") + setDidClickCancel(false) break case "resume_completed_task": setTextAreaDisabled(false) @@ -316,20 +316,18 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie switch (clineAsk) { case "api_req_failed": case "mistake_limit_reached": + case "resume_task": startNewTask() break case "command": case "tool": case "browser_action_launch": - // responds to the API with a "This operation failed" and lets it try again vscode.postMessage({ type: "askResponse", askResponse: "noButtonClicked" }) break } setTextAreaDisabled(true) setClineAsk(undefined) setEnableButtons(false) - // setPrimaryButtonText(undefined) - // setSecondaryButtonText(undefined) disableAutoScrollRef.current = false }, [clineAsk, startNewTask, isStreaming]) diff --git a/webview-ui/src/components/chat/__tests__/ChatView.test.tsx b/webview-ui/src/components/chat/__tests__/ChatView.test.tsx new file mode 100644 index 0000000..88d6fd1 --- /dev/null +++ b/webview-ui/src/components/chat/__tests__/ChatView.test.tsx @@ -0,0 +1,152 @@ +import { render, screen, act } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { ExtensionStateContextType } from '../../../context/ExtensionStateContext' +import ChatView from '../ChatView' +import { vscode } from '../../../utils/vscode' +import * as ExtensionStateContext from '../../../context/ExtensionStateContext' + +// Mock vscode +jest.mock('../../../utils/vscode', () => ({ + vscode: { + postMessage: jest.fn() + } +})) + +// Mock all components that use problematic dependencies +jest.mock('../../common/CodeBlock', () => ({ + __esModule: true, + default: () =>
+})) + +jest.mock('../../common/MarkdownBlock', () => ({ + __esModule: true, + default: () =>
+})) + +jest.mock('../BrowserSessionRow', () => ({ + __esModule: true, + default: () =>
+})) + +// Update ChatRow mock to capture props +let chatRowProps = null +jest.mock('../ChatRow', () => ({ + __esModule: true, + default: (props: any) => { + chatRowProps = props + return
+ } +})) + + +// Mock Virtuoso component +jest.mock('react-virtuoso', () => ({ + Virtuoso: ({ children }: any) => ( +
{children}
+ ) +})) + +// Mock VS Code components +jest.mock('@vscode/webview-ui-toolkit/react', () => ({ + VSCodeButton: ({ children, onClick }: any) => ( + + ), + VSCodeProgressRing: () =>
+})) + +describe('ChatView', () => { + const mockShowHistoryView = jest.fn() + const mockHideAnnouncement = jest.fn() + + let mockState: ExtensionStateContextType + + beforeEach(() => { + jest.clearAllMocks() + + mockState = { + clineMessages: [], + apiConfiguration: { + apiProvider: 'anthropic', + apiModelId: 'claude-3-sonnet' + }, + version: '1.0.0', + customInstructions: '', + alwaysAllowReadOnly: true, + alwaysAllowWrite: true, + alwaysAllowExecute: true, + openRouterModels: {}, + didHydrateState: true, + showWelcome: false, + theme: 'dark', + filePaths: [], + taskHistory: [], + shouldShowAnnouncement: false, + uriScheme: 'vscode', + + setAlwaysAllowReadOnly: jest.fn(), + setAlwaysAllowWrite: jest.fn(), + setCustomInstructions: jest.fn(), + setAlwaysAllowExecute: jest.fn(), + setApiConfiguration: jest.fn(), + setShowAnnouncement: jest.fn() + } + + // Mock the useExtensionState hook + jest.spyOn(ExtensionStateContext, 'useExtensionState').mockReturnValue(mockState) + }) + + const renderChatView = () => { + return render( + + ) + } + + describe('Streaming State', () => { + it('should show cancel button while streaming', () => { + mockState.clineMessages = [ + { + type: 'say', + partial: true, + ts: Date.now(), + } + ] + renderChatView() + + const buttons = screen.queryAllByRole('button') + expect(buttons.length).toBeGreaterThan(0) + }) + + it('should show terminate button when task is paused', () => { + mockState.clineMessages = [ + { + type: 'ask', + ask: 'resume_task', + ts: Date.now(), + } + ] + renderChatView() + + const buttons = screen.queryAllByRole('button') + expect(buttons.length).toBeGreaterThan(0) + }) + + it('should show retry button when API error occurs', () => { + mockState.clineMessages = [ + { + type: 'say', + say: 'error', + ts: Date.now(), + } + ] + renderChatView() + + const buttons = screen.queryAllByRole('button') + expect(buttons.length).toBeGreaterThan(0) + }) + }) +}) \ No newline at end of file