@@ -24,19 +24,25 @@ function createDeferredPromise<T>() {
2424
2525const {
2626 activeRunStackedActionDeferredRef,
27+ activeDraftThreadRef,
28+ hasServerThreadRef,
2729 invalidateGitQueriesSpy,
2830 refreshGitStatusSpy,
2931 runStackedActionMutateAsyncSpy,
32+ setDraftThreadContextSpy,
3033 setThreadBranchSpy,
3134 toastAddSpy,
3235 toastCloseSpy,
3336 toastPromiseSpy,
3437 toastUpdateSpy,
3538} = vi . hoisted ( ( ) => ( {
3639 activeRunStackedActionDeferredRef : { current : createDeferredPromise < never > ( ) } ,
40+ activeDraftThreadRef : { current : null as unknown } ,
41+ hasServerThreadRef : { current : true } ,
3742 invalidateGitQueriesSpy : vi . fn ( ( ) => Promise . resolve ( ) ) ,
3843 refreshGitStatusSpy : vi . fn ( ( ) => Promise . resolve ( null ) ) ,
3944 runStackedActionMutateAsyncSpy : vi . fn ( ( ) => activeRunStackedActionDeferredRef . current . promise ) ,
45+ setDraftThreadContextSpy : vi . fn ( ) ,
4046 setThreadBranchSpy : vi . fn ( ) ,
4147 toastAddSpy : vi . fn ( ( ) => "toast-1" ) ,
4248 toastCloseSpy : vi . fn ( ) ,
@@ -123,6 +129,36 @@ vi.mock("~/localApi", () => ({
123129 readLocalApi : vi . fn ( ( ) => null ) ,
124130} ) ) ;
125131
132+ vi . mock ( "~/composerDraftStore" , async ( ) => {
133+ const draftStoreState = {
134+ getDraftThreadByRef : ( ) => activeDraftThreadRef . current ,
135+ getDraftSession : ( ) => activeDraftThreadRef . current ,
136+ getDraftThread : ( ) => activeDraftThreadRef . current ,
137+ getDraftSessionByLogicalProjectKey : ( ) => null ,
138+ setDraftThreadContext : setDraftThreadContextSpy ,
139+ setLogicalProjectDraftThreadId : vi . fn ( ) ,
140+ setProjectDraftThreadId : vi . fn ( ) ,
141+ hasDraftThreadsInEnvironment : ( ) => false ,
142+ clearDraftThread : vi . fn ( ) ,
143+ } ;
144+
145+ return {
146+ DraftId : {
147+ makeUnsafe : ( value : string ) => value ,
148+ } ,
149+ useComposerDraftStore : Object . assign (
150+ ( selector : ( state : unknown ) => unknown ) => selector ( draftStoreState ) ,
151+ { getState : ( ) => draftStoreState } ,
152+ ) ,
153+ markPromotedDraftThread : vi . fn ( ) ,
154+ markPromotedDraftThreadByRef : vi . fn ( ) ,
155+ markPromotedDraftThreads : vi . fn ( ) ,
156+ markPromotedDraftThreadsByRef : vi . fn ( ) ,
157+ finalizePromotedDraftThreadByRef : vi . fn ( ) ,
158+ finalizePromotedDraftThreadsByRef : vi . fn ( ) ,
159+ } ;
160+ } ) ;
161+
126162vi . mock ( "~/store" , ( ) => ( {
127163 selectEnvironmentState : (
128164 state : { environmentStateById : Record < string , unknown > } ,
@@ -155,13 +191,15 @@ vi.mock("~/store", () => ({
155191 setThreadBranch : setThreadBranchSpy ,
156192 environmentStateById : {
157193 [ ENVIRONMENT_A ] : {
158- threadShellById : {
159- [ SHARED_THREAD_ID ] : {
160- id : SHARED_THREAD_ID ,
161- branch : BRANCH_NAME ,
162- worktreePath : null ,
163- } ,
164- } ,
194+ threadShellById : hasServerThreadRef . current
195+ ? {
196+ [ SHARED_THREAD_ID ] : {
197+ id : SHARED_THREAD_ID ,
198+ branch : BRANCH_NAME ,
199+ worktreePath : null ,
200+ } ,
201+ }
202+ : { } ,
165203 threadSessionById : { } ,
166204 threadTurnStateById : { } ,
167205 messageIdsByThreadId : { } ,
@@ -174,13 +212,15 @@ vi.mock("~/store", () => ({
174212 turnDiffSummaryByThreadId : { } ,
175213 } ,
176214 [ ENVIRONMENT_B ] : {
177- threadShellById : {
178- [ SHARED_THREAD_ID ] : {
179- id : SHARED_THREAD_ID ,
180- branch : BRANCH_NAME ,
181- worktreePath : null ,
182- } ,
183- } ,
215+ threadShellById : hasServerThreadRef . current
216+ ? {
217+ [ SHARED_THREAD_ID ] : {
218+ id : SHARED_THREAD_ID ,
219+ branch : BRANCH_NAME ,
220+ worktreePath : null ,
221+ } ,
222+ }
223+ : { } ,
184224 threadSessionById : { } ,
185225 threadTurnStateById : { } ,
186226 messageIdsByThreadId : { } ,
@@ -231,6 +271,8 @@ describe("GitActionsControl thread-scoped progress toast", () => {
231271 vi . useRealTimers ( ) ;
232272 vi . clearAllMocks ( ) ;
233273 activeRunStackedActionDeferredRef . current = createDeferredPromise < never > ( ) ;
274+ activeDraftThreadRef . current = null ;
275+ hasServerThreadRef . current = true ;
234276 document . body . innerHTML = "" ;
235277 } ) ;
236278
@@ -343,4 +385,42 @@ describe("GitActionsControl thread-scoped progress toast", () => {
343385 host . remove ( ) ;
344386 }
345387 } ) ;
388+
389+ it ( "syncs the live branch into the active draft thread when no server thread exists" , async ( ) => {
390+ hasServerThreadRef . current = false ;
391+ activeDraftThreadRef . current = {
392+ threadId : SHARED_THREAD_ID ,
393+ environmentId : ENVIRONMENT_A ,
394+ branch : null ,
395+ worktreePath : null ,
396+ } ;
397+
398+ const host = document . createElement ( "div" ) ;
399+ document . body . append ( host ) ;
400+ const screen = await render (
401+ < GitActionsControl
402+ gitCwd = { GIT_CWD }
403+ activeThreadRef = { scopeThreadRef ( ENVIRONMENT_A , SHARED_THREAD_ID ) }
404+ /> ,
405+ {
406+ container : host ,
407+ } ,
408+ ) ;
409+
410+ try {
411+ await Promise . resolve ( ) ;
412+
413+ expect ( setDraftThreadContextSpy ) . toHaveBeenCalledWith (
414+ scopeThreadRef ( ENVIRONMENT_A , SHARED_THREAD_ID ) ,
415+ {
416+ branch : BRANCH_NAME ,
417+ worktreePath : null ,
418+ } ,
419+ ) ;
420+ expect ( setThreadBranchSpy ) . not . toHaveBeenCalled ( ) ;
421+ } finally {
422+ await screen . unmount ( ) ;
423+ host . remove ( ) ;
424+ }
425+ } ) ;
346426} ) ;
0 commit comments