فهرست منبع

Merge pull request #6 from RooVetGit/feature/addTerminateTaskButton

John Stearns 1 سال پیش
والد
کامیت
48d7ef958c

BIN
bin/roo-cline-1.0.3.vsix → bin/roo-cline-1.0.4.vsix


+ 1 - 1
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",

+ 3 - 2
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) // special case where we reset the cancel button state							
 							break
 						case "resume_completed_task":
 							setTextAreaDisabled(false)
@@ -316,6 +316,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
 		switch (clineAsk) {
 			case "api_req_failed":
 			case "mistake_limit_reached":
+			case "resume_task":
 				startNewTask()
 				break
 			case "command":

+ 171 - 0
webview-ui/src/components/chat/__tests__/ChatView.test.tsx

@@ -0,0 +1,171 @@
+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: () => <div data-testid="mock-code-block" />
+}))
+
+jest.mock('../../common/MarkdownBlock', () => ({
+    __esModule: true,
+    default: () => <div data-testid="mock-markdown-block" />
+}))
+
+jest.mock('../BrowserSessionRow', () => ({
+    __esModule: true,
+    default: () => <div data-testid="mock-browser-session-row" />
+}))
+
+// Update ChatRow mock to capture props
+let chatRowProps = null
+jest.mock('../ChatRow', () => ({
+    __esModule: true,
+    default: (props: any) => {
+        chatRowProps = props
+        return <div data-testid="mock-chat-row" />
+    }
+}))
+
+
+// Mock Virtuoso component
+jest.mock('react-virtuoso', () => ({
+    Virtuoso: ({ children }: any) => (
+        <div data-testid="mock-virtuoso">{children}</div>
+    )
+}))
+
+// Mock VS Code components
+jest.mock('@vscode/webview-ui-toolkit/react', () => ({
+    VSCodeButton: ({ children, onClick }: any) => (
+        <button onClick={onClick}>{children}</button>
+    ),
+    VSCodeProgressRing: () => <div data-testid="progress-ring" />
+}))
+
+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(
+            <ChatView 
+                isHidden={false}
+                showAnnouncement={false}
+                hideAnnouncement={mockHideAnnouncement}
+                showHistoryView={mockShowHistoryView}
+            />
+        )
+    }
+
+    describe('Streaming State', () => {
+        it('should show cancel button while streaming and trigger cancel on click', async () => {
+            mockState.clineMessages = [
+                { 
+                    type: 'say',
+                    say: 'task',
+                    ts: Date.now(),
+                },
+                { 
+                    type: 'say',
+                    say: 'text',
+                    partial: true,
+                    ts: Date.now(),
+                }
+            ]
+            renderChatView()
+            
+            const cancelButton = screen.getByText('Cancel')
+            await userEvent.click(cancelButton)
+            
+            expect(vscode.postMessage).toHaveBeenCalledWith({
+                type: 'cancelTask'
+            })
+        })
+
+        it('should show terminate button when task is paused and trigger terminate on click', async () => {
+            mockState.clineMessages = [
+                { 
+                    type: 'ask',
+                    ask: 'resume_task',
+                    ts: Date.now(),
+                }
+            ]
+            renderChatView()
+            
+            const terminateButton = screen.getByText('Terminate')
+            await userEvent.click(terminateButton)
+            
+            expect(vscode.postMessage).toHaveBeenCalledWith({
+                type: 'clearTask'
+            })
+        })
+
+        it('should show retry button when API error occurs and trigger retry on click', async () => {
+            mockState.clineMessages = [
+                { 
+                    type: 'ask',
+                    ask: 'api_req_failed',
+                    ts: Date.now(),
+                }
+            ]
+            renderChatView()
+            
+            const retryButton = screen.getByText('Retry')
+            await userEvent.click(retryButton)
+            
+            expect(vscode.postMessage).toHaveBeenCalledWith({
+                type: 'askResponse',
+                askResponse: 'yesButtonClicked'
+            })
+        })
+    })
+})