@@ -55,6 +55,7 @@ describe("NewThreadButton", () => {
5555 messages : [ { id : "msg-1" } ] ,
5656 clearMessages : vi . fn ( ) ,
5757 metadata : { assistant_id : "assistant-1" } ,
58+ abortQuery : vi . fn ( ) ,
5859 resetToDefault : vi . fn ( ) ,
5960 loadPersistentContextFiles : vi . fn ( ) ,
6061 clearThreadScopedFiles : vi . fn ( ) ,
@@ -68,12 +69,46 @@ describe("NewThreadButton", () => {
6869 fireEvent . click ( screen . getByTitle ( "New Chat" ) ) ;
6970
7071 const context = mockUseChatContext . mock . results [ 0 ] . value ;
72+ expect ( context . abortQuery ) . toHaveBeenCalledTimes ( 1 ) ;
7173 expect ( context . clearMessages ) . toHaveBeenCalledTimes ( 1 ) ;
7274 expect ( context . clearThreadScopedFiles ) . toHaveBeenCalledTimes ( 1 ) ;
7375 expect ( context . clearBackendSyncFiles ) . toHaveBeenCalledTimes ( 1 ) ;
7476 expect ( context . resetToDefault ) . toHaveBeenCalledTimes ( 1 ) ;
7577 expect ( context . loadPersistentContextFiles ) . toHaveBeenCalledTimes ( 1 ) ;
76- expect ( mockNavigate ) . toHaveBeenCalledWith ( "/chat" ) ;
78+ expect ( mockNavigate ) . toHaveBeenCalledWith ( "/chat" , {
79+ state : { staleThreadId : undefined } ,
80+ } ) ;
81+ } ) ;
82+
83+ it ( "aborts the active stream before clearing messages" , ( ) => {
84+ mockUseChatContext . mockReturnValue ( {
85+ messages : [ { id : "msg-1" } ] ,
86+ clearMessages : vi . fn ( ) ,
87+ metadata : { thread_id : "thread-1" , assistant_id : "assistant-1" } ,
88+ abortQuery : vi . fn ( ) ,
89+ resetToDefault : vi . fn ( ) ,
90+ loadPersistentContextFiles : vi . fn ( ) ,
91+ clearThreadScopedFiles : vi . fn ( ) ,
92+ clearBackendSyncFiles : vi . fn ( ) ,
93+ } ) ;
94+
95+ renderAt ( "/thread/thread-1" ) ;
96+
97+ fireEvent . click ( screen . getByTitle ( "New Chat" ) ) ;
98+
99+ const context = mockUseChatContext . mock . results [ 0 ] . value ;
100+ expect ( context . abortQuery ) . toHaveBeenCalledTimes ( 1 ) ;
101+ expect ( context . clearMessages ) . toHaveBeenCalledTimes ( 1 ) ;
102+
103+ // Verify abort was called before clearMessages
104+ const abortOrder = context . abortQuery . mock . invocationCallOrder [ 0 ] ;
105+ const clearOrder = context . clearMessages . mock . invocationCallOrder [ 0 ] ;
106+ expect ( abortOrder ) . toBeLessThan ( clearOrder ) ;
107+
108+ // Verify staleThreadId is passed
109+ expect ( mockNavigate ) . toHaveBeenCalledWith ( "/chat" , {
110+ state : { staleThreadId : "thread-1" } ,
111+ } ) ;
77112 } ) ;
78113
79114 it ( "navigates assistant thread resets back to the assistant route" , ( ) => {
@@ -89,6 +124,7 @@ describe("NewThreadButton", () => {
89124 messages : [ ] ,
90125 clearMessages : vi . fn ( ) ,
91126 metadata : { } ,
127+ abortQuery : vi . fn ( ) ,
92128 resetToDefault : vi . fn ( ) ,
93129 loadPersistentContextFiles : vi . fn ( ) ,
94130 clearThreadScopedFiles : vi . fn ( ) ,
@@ -98,4 +134,25 @@ describe("NewThreadButton", () => {
98134 const { container } = renderAt ( "/chat" ) ;
99135 expect ( container ) . toBeEmptyDOMElement ( ) ;
100136 } ) ;
137+
138+ it ( "passes staleThreadId when falling back from a project route to /chat" , ( ) => {
139+ mockUseChatContext . mockReturnValue ( {
140+ messages : [ { id : "msg-1" } ] ,
141+ clearMessages : vi . fn ( ) ,
142+ metadata : { thread_id : "proj-thread-1" } ,
143+ abortQuery : vi . fn ( ) ,
144+ resetToDefault : vi . fn ( ) ,
145+ loadPersistentContextFiles : vi . fn ( ) ,
146+ clearThreadScopedFiles : vi . fn ( ) ,
147+ clearBackendSyncFiles : vi . fn ( ) ,
148+ } ) ;
149+
150+ renderAt ( "/p/project-1" ) ;
151+
152+ fireEvent . click ( screen . getByTitle ( "New Chat" ) ) ;
153+
154+ expect ( mockNavigate ) . toHaveBeenCalledWith ( "/chat" , {
155+ state : { staleThreadId : "proj-thread-1" } ,
156+ } ) ;
157+ } ) ;
101158} ) ;
0 commit comments