1+ import { render , screen , act } from '@testing-library/react'
2+ import userEvent from '@testing-library/user-event'
3+ import { ExtensionStateContextType } from '../../../context/ExtensionStateContext'
4+ import ChatView from '../ChatView'
5+ import { vscode } from '../../../utils/vscode'
6+ import * as ExtensionStateContext from '../../../context/ExtensionStateContext'
7+
8+ // Mock vscode
9+ jest . mock ( '../../../utils/vscode' , ( ) => ( {
10+ vscode : {
11+ postMessage : jest . fn ( )
12+ }
13+ } ) )
14+
15+ // Mock all components that use problematic dependencies
16+ jest . mock ( '../../common/CodeBlock' , ( ) => ( {
17+ __esModule : true ,
18+ default : ( ) => < div data-testid = "mock-code-block" />
19+ } ) )
20+
21+ jest . mock ( '../../common/MarkdownBlock' , ( ) => ( {
22+ __esModule : true ,
23+ default : ( ) => < div data-testid = "mock-markdown-block" />
24+ } ) )
25+
26+ jest . mock ( '../BrowserSessionRow' , ( ) => ( {
27+ __esModule : true ,
28+ default : ( ) => < div data-testid = "mock-browser-session-row" />
29+ } ) )
30+
31+ // Update ChatRow mock to capture props
32+ let chatRowProps = null
33+ jest . mock ( '../ChatRow' , ( ) => ( {
34+ __esModule : true ,
35+ default : ( props : any ) => {
36+ chatRowProps = props
37+ return < div data-testid = "mock-chat-row" />
38+ }
39+ } ) )
40+
41+
42+ // Mock Virtuoso component
43+ jest . mock ( 'react-virtuoso' , ( ) => ( {
44+ Virtuoso : ( { children } : any ) => (
45+ < div data-testid = "mock-virtuoso" > { children } </ div >
46+ )
47+ } ) )
48+
49+ // Mock VS Code components
50+ jest . mock ( '@vscode/webview-ui-toolkit/react' , ( ) => ( {
51+ VSCodeButton : ( { children, onClick } : any ) => (
52+ < button onClick = { onClick } > { children } </ button >
53+ ) ,
54+ VSCodeProgressRing : ( ) => < div data-testid = "progress-ring" />
55+ } ) )
56+
57+ describe ( 'ChatView' , ( ) => {
58+ const mockShowHistoryView = jest . fn ( )
59+ const mockHideAnnouncement = jest . fn ( )
60+
61+ let mockState : ExtensionStateContextType
62+
63+ beforeEach ( ( ) => {
64+ jest . clearAllMocks ( )
65+
66+ mockState = {
67+ clineMessages : [ ] ,
68+ apiConfiguration : {
69+ apiProvider : 'anthropic' ,
70+ apiModelId : 'claude-3-sonnet'
71+ } ,
72+ version : '1.0.0' ,
73+ customInstructions : '' ,
74+ alwaysAllowReadOnly : true ,
75+ alwaysAllowWrite : true ,
76+ alwaysAllowExecute : true ,
77+ openRouterModels : { } ,
78+ didHydrateState : true ,
79+ showWelcome : false ,
80+ theme : 'dark' ,
81+ filePaths : [ ] ,
82+ taskHistory : [ ] ,
83+ shouldShowAnnouncement : false ,
84+ uriScheme : 'vscode' ,
85+
86+ setAlwaysAllowReadOnly : jest . fn ( ) ,
87+ setAlwaysAllowWrite : jest . fn ( ) ,
88+ setCustomInstructions : jest . fn ( ) ,
89+ setAlwaysAllowExecute : jest . fn ( ) ,
90+ setApiConfiguration : jest . fn ( ) ,
91+ setShowAnnouncement : jest . fn ( )
92+ }
93+
94+ // Mock the useExtensionState hook
95+ jest . spyOn ( ExtensionStateContext , 'useExtensionState' ) . mockReturnValue ( mockState )
96+ } )
97+
98+ const renderChatView = ( ) => {
99+ return render (
100+ < ChatView
101+ isHidden = { false }
102+ showAnnouncement = { false }
103+ hideAnnouncement = { mockHideAnnouncement }
104+ showHistoryView = { mockShowHistoryView }
105+ />
106+ )
107+ }
108+
109+ describe ( 'Streaming State' , ( ) => {
110+ it ( 'should show cancel button while streaming and trigger cancel on click' , async ( ) => {
111+ mockState . clineMessages = [
112+ {
113+ type : 'say' ,
114+ say : 'task' ,
115+ ts : Date . now ( ) ,
116+ } ,
117+ {
118+ type : 'say' ,
119+ say : 'text' ,
120+ partial : true ,
121+ ts : Date . now ( ) ,
122+ }
123+ ]
124+ renderChatView ( )
125+
126+ const cancelButton = screen . getByText ( 'Cancel' )
127+ await userEvent . click ( cancelButton )
128+
129+ expect ( vscode . postMessage ) . toHaveBeenCalledWith ( {
130+ type : 'cancelTask'
131+ } )
132+ } )
133+
134+ it ( 'should show terminate button when task is paused and trigger terminate on click' , async ( ) => {
135+ mockState . clineMessages = [
136+ {
137+ type : 'ask' ,
138+ ask : 'resume_task' ,
139+ ts : Date . now ( ) ,
140+ }
141+ ]
142+ renderChatView ( )
143+
144+ const terminateButton = screen . getByText ( 'Terminate' )
145+ await userEvent . click ( terminateButton )
146+
147+ expect ( vscode . postMessage ) . toHaveBeenCalledWith ( {
148+ type : 'clearTask'
149+ } )
150+ } )
151+
152+ it ( 'should show retry button when API error occurs and trigger retry on click' , async ( ) => {
153+ mockState . clineMessages = [
154+ {
155+ type : 'ask' ,
156+ ask : 'api_req_failed' ,
157+ ts : Date . now ( ) ,
158+ }
159+ ]
160+ renderChatView ( )
161+
162+ const retryButton = screen . getByText ( 'Retry' )
163+ await userEvent . click ( retryButton )
164+
165+ expect ( vscode . postMessage ) . toHaveBeenCalledWith ( {
166+ type : 'askResponse' ,
167+ askResponse : 'yesButtonClicked'
168+ } )
169+ } )
170+ } )
171+ } )
0 commit comments