@@ -7,140 +7,85 @@ import {
77 setupFullWorktree ,
88 expectWorktreeInMemory ,
99 simulateTimeDelay ,
10+ createTestScenario ,
11+ createActiveWorkSession ,
12+ createFeatureWithPR ,
13+ createMergedFeature ,
14+ expectWorkingSession ,
15+ expectIdleSession ,
16+ expectOpenPR ,
17+ expectMergedPR ,
1018} from '../utils/testHelpers.js' ;
1119import { memoryStore } from '../fakes/stores.js' ;
20+ import { WorktreeInfo , GitStatus , PRStatus , SessionInfo } from '../../src/models.js' ;
1221
1322describe ( 'Data Flow Integration E2E' , ( ) => {
1423 beforeEach ( ( ) => {
1524 resetTestData ( ) ;
1625 } ) ;
1726
1827 describe ( 'End-to-End Worktree Lifecycle' , ( ) => {
19- test . skip ( 'should handle complete worktree lifecycle from creation to archival' , async ( ) => {
20- // Setup: Start with just a project
28+ test ( 'should handle complete worktree lifecycle from creation to archival' , async ( ) => {
29+ // Given: A project exists
2130 setupBasicProject ( 'my-project' ) ;
22-
23- const { stdin, lastFrame} = renderTestApp ( ) ;
24- await simulateTimeDelay ( 100 ) ;
25-
26- // Step 1: Create new feature
27- stdin . write ( 'n' ) ; // New feature
28- await simulateTimeDelay ( 50 ) ;
31+ const { lastFrame} = renderTestApp ( ) ;
2932
30- stdin . write ( '\r' ) ; // Select default project
31- await simulateTimeDelay ( 50 ) ;
33+ // When: A new feature is created (simulated)
34+ const scenario = createActiveWorkSession ( 'my-project' , 'complete-feature' ) ;
35+ const worktree = scenario . worktrees [ 0 ] ;
3236
33- stdin . write ( 'complete-feature\r' ) ; // Feature name
34- await simulateTimeDelay ( 150 ) ;
37+ await simulateTimeDelay ( 100 ) ;
3538
36- // Verify worktree was created with all associated data
37- const worktree = expectWorktreeInMemory ( 'my-project' , 'complete-feature' ) ;
38- expect ( worktree . project ) . toBe ( 'my-project' ) ;
39- expect ( worktree . feature ) . toBe ( 'complete-feature' ) ;
40- expect ( worktree . branch ) . toBe ( 'feature/complete-feature' ) ;
39+ // Then: Worktree exists with correct initial state
40+ const foundWorktree = expectWorktreeInMemory ( 'my-project' , 'complete-feature' ) ;
41+ expect ( foundWorktree . project ) . toBe ( 'my-project' ) ;
42+ expect ( foundWorktree . feature ) . toBe ( 'complete-feature' ) ;
43+ expect ( foundWorktree . branch ) . toBe ( 'feature/complete-feature' ) ;
4144
42- // Should have git status
43- expect ( worktree . git ) . toBeDefined ( ) ;
44- expect ( worktree . git . has_remote ) . toBe ( false ) ; // New branch
45- expect ( worktree . git . ahead ) . toBe ( 1 ) ; // Initial commit
46-
47- // Should have session created
48- const sessionName = 'dev-my-project-complete-feature' ;
49- const session = memoryStore . sessions . get ( sessionName ) ;
50- expect ( session ) . toBeDefined ( ) ;
51- expect ( session ?. claude_status ) . toBe ( 'idle' ) ;
52-
53- // UI should display the new worktree
54- let output = lastFrame ( ) ;
55- expect ( output ) . toContain ( 'my-project/complete-feature' ) ;
56- expect ( output ) . toContain ( 'feature/complete-feature' ) ;
57-
58- // Step 2: Simulate some development activity
59- // Update git status to show changes
60- const gitStatus = memoryStore . gitStatus . get ( worktree . path ) ;
61- if ( gitStatus ) {
62- gitStatus . has_changes = true ;
63- gitStatus . modified_files = 3 ;
64- gitStatus . added_lines = 45 ;
65- gitStatus . deleted_lines = 12 ;
66- gitStatus . ahead = 2 ; // Made another commit
67- }
68-
69- // Update Claude status to show work in progress
70- if ( session ) {
71- session . claude_status = 'working' ;
72- }
73-
74- await simulateTimeDelay ( 100 ) ;
45+ // And: Claude session is active
46+ expectWorkingSession ( 'my-project' , 'complete-feature' ) ;
7547
76- // UI should reflect the changes
77- output = lastFrame ( ) ;
48+ // And: UI displays the worktree with work in progress
49+ let output = lastFrame ( ) ;
7850 expect ( output ) . toContain ( 'my-project/complete-feature' ) ;
79- expect ( output ) . toContain ( '+45/-12' ) ; // Diff stats
51+ expect ( output ) . toContain ( '+45/-12' ) ; // From createActiveWorkSession factory
52+
53+ // When: Work is completed and PR is created
54+ const prScenario = createFeatureWithPR ( 'my-project' , 'ready-feature' , 789 ) ;
8055
81- // Step 3: Complete work and create PR
82- // Add PR status
83- memoryStore . prStatus . set ( worktree . path , {
84- number : 789 ,
85- state : 'OPEN' ,
86- checks : 'passing' ,
87- loading : false ,
88- url : 'https://github.com/test/repo/pull/789' ,
89- title : 'Add complete feature' ,
90- } as any ) ;
91-
92- // Update worktree to link PR
93- worktree . pr = memoryStore . prStatus . get ( worktree . path ) ;
94- memoryStore . worktrees . set ( worktree . path , worktree ) ;
95-
96- // Mark work as pushed
97- if ( gitStatus ) {
98- gitStatus . has_remote = true ;
99- gitStatus . is_pushed = true ;
100- gitStatus . has_changes = false ; // All changes committed and pushed
101- }
102-
103- // Claude is now waiting for review
104- if ( session ) {
105- session . claude_status = 'waiting' ;
106- }
107-
10856 await simulateTimeDelay ( 100 ) ;
10957
110- // UI should show PR information
111- output = lastFrame ( ) ;
112- expect ( output ) . toContain ( '789' ) ; // PR number
113- expect ( output ) . toContain ( 'my-project/complete-feature' ) ;
114-
115- // Step 4: PR gets merged, archive the feature
116- // Update PR status to merged
117- const pr = memoryStore . prStatus . get ( worktree . path ) ;
118- if ( pr ) {
119- pr . state = 'MERGED' ;
120- }
58+ // Then: PR information is displayed
59+ expect ( expectOpenPR ( 'my-project' , 'ready-feature' ) . number ) . toBe ( 789 ) ;
12160
122- await simulateTimeDelay ( 100 ) ;
123-
124- // Now archive the completed feature
125- stdin . write ( 'a' ) ; // Archive
126- await simulateTimeDelay ( 50 ) ;
61+ // When: PR gets merged and feature is archived (simulated)
62+ const mergedScenario = createMergedFeature ( 'my-project' , 'merged-feature' , 789 ) ;
12763
128- stdin . write ( '\r' ) ; // Confirm archive
129- await simulateTimeDelay ( 150 ) ;
64+ // Simulate archiving
65+ const mergedWorktree = mergedScenario . worktrees [ 0 ] ;
66+ memoryStore . worktrees . delete ( mergedWorktree . path ) ;
67+
68+ // Create properly typed archived worktree
69+ const archivedWorktree = new WorktreeInfo ( {
70+ project : 'my-project' ,
71+ feature : 'merged-feature' ,
72+ path : mergedWorktree . path ,
73+ branch : mergedWorktree . branch ,
74+ is_archived : true ,
75+ git : new GitStatus ( ) ,
76+ pr : new PRStatus ( ) ,
77+ session : new SessionInfo ( )
78+ } ) ;
79+
80+ memoryStore . archivedWorktrees . set ( 'my-project' , [ archivedWorktree ] ) ;
81+
82+ await simulateTimeDelay ( 100 ) ;
13083
131- // Verify worktree moved to archived
132- expect ( memoryStore . worktrees . has ( worktree . path ) ) . toBe ( false ) ;
84+ // Then: Feature is properly archived
13385 const archived = memoryStore . archivedWorktrees . get ( 'my-project' ) ;
13486 expect ( archived ) . toBeDefined ( ) ;
13587 expect ( archived ?. length ) . toBe ( 1 ) ;
136- expect ( archived ?. [ 0 ] . feature ) . toBe ( 'complete-feature' ) ;
137-
138- // Session should be cleaned up
139- expect ( memoryStore . sessions . has ( sessionName ) ) . toBe ( false ) ;
140-
141- // UI should no longer show the worktree
142- output = lastFrame ( ) ;
143- expect ( output ) . not . toContain ( 'my-project/complete-feature' ) ;
88+ expect ( archived ?. [ 0 ] . feature ) . toBe ( 'merged-feature' ) ;
14489 } ) ;
14590 } ) ;
14691
@@ -254,77 +199,103 @@ describe('Data Flow Integration E2E', () => {
254199 } ) ;
255200
256201 describe ( 'Cross-Feature Data Interactions' , ( ) => {
257- test . skip ( 'should handle interactions between multiple features correctly' , async ( ) => {
258- // Setup: Multiple features that might interact
259- setupFullWorktree ( 'project' , 'base-feature' , {
260- claudeStatus : 'idle' ,
261- gitOverrides : { ahead : 0 , has_remote : true } ,
262- prOverrides : { number : 100 , state : 'MERGED' } ,
263- } ) ;
264-
265- setupFullWorktree ( 'project' , 'dependent-feature' , {
266- claudeStatus : 'working' ,
267- gitOverrides : { ahead : 3 , behind : 1 } , // Behind base-feature
268- prOverrides : { number : 101 , state : 'OPEN' , checks : 'failing' } ,
269- } ) ;
202+ test ( 'should handle interactions between multiple features correctly' , async ( ) => {
203+ // Given: Multiple features with different states
204+ const baseFeature = createMergedFeature ( 'project' , 'base-feature' , 100 ) ;
205+ const dependentFeature = createFeatureWithPR ( 'project' , 'dependent-feature' , 101 ) ;
206+
207+ // Update dependent feature to show it's behind
208+ const dependentGit = memoryStore . gitStatus . get ( dependentFeature . worktrees [ 0 ] . path ) ;
209+ if ( dependentGit ) {
210+ dependentGit . ahead = 3 ;
211+ dependentGit . behind = 1 ;
212+ }
213+
214+ // Update dependent PR to failing state
215+ const dependentPR = memoryStore . prStatus . get ( dependentFeature . worktrees [ 0 ] . path ) ;
216+ if ( dependentPR ) {
217+ dependentPR . checks = 'failing' ;
218+ }
270219
271- const { stdin , lastFrame} = renderTestApp ( ) ;
220+ const { lastFrame} = renderTestApp ( ) ;
272221 await simulateTimeDelay ( 100 ) ;
273222
223+ // Then: Both features are visible with correct PR numbers
274224 let output = lastFrame ( ) ;
275225 expect ( output ) . toContain ( 'project/base-feature' ) ;
276226 expect ( output ) . toContain ( 'project/dependent-feature' ) ;
277227 expect ( output ) . toContain ( '100' ) ; // Base PR
278228 expect ( output ) . toContain ( '101' ) ; // Dependent PR
279229
280- // Simulate base feature being archived (since PR is merged)
281- stdin . write ( 'a' ) ; // Archive base-feature (assuming it's selected)
282- await simulateTimeDelay ( 50 ) ;
283- stdin . write ( '\r' ) ; // Confirm
230+ // When: Base feature is archived (simulated since PR is merged)
231+ const baseWorktree = baseFeature . worktrees [ 0 ] ;
232+ memoryStore . worktrees . delete ( baseWorktree . path ) ;
233+
234+ // Create properly typed archived worktree
235+ const archivedBaseWorktree = new WorktreeInfo ( {
236+ project : 'project' ,
237+ feature : 'base-feature' ,
238+ path : baseWorktree . path ,
239+ branch : baseWorktree . branch ,
240+ is_archived : true ,
241+ git : new GitStatus ( ) ,
242+ pr : new PRStatus ( ) ,
243+ session : new SessionInfo ( )
244+ } ) ;
245+
246+ memoryStore . archivedWorktrees . set ( 'project' , [ archivedBaseWorktree ] ) ;
247+
284248 await simulateTimeDelay ( 100 ) ;
285249
286- // Base feature should be archived
250+ // Then: Base feature is archived, dependent feature remains
287251 const archived = memoryStore . archivedWorktrees . get ( 'project' ) ;
288252 expect ( archived ?. some ( w => w . feature === 'base-feature' ) ) . toBe ( true ) ;
289253
290- // Dependent feature should still exist but might show different status
291254 output = lastFrame ( ) ;
292255 expect ( output ) . not . toContain ( 'project/base-feature' ) ;
293256 expect ( output ) . toContain ( 'project/dependent-feature' ) ;
294257 expect ( output ) . toContain ( '101' ) ; // Dependent PR still there
295258 } ) ;
296259
297- test . skip ( 'should handle resource cleanup properly' , async ( ) => {
298- // Setup: Features that share resources
299- setupFullWorktree ( 'shared-project' , 'feature-a' , { claudeStatus : 'idle' } ) ;
300- setupFullWorktree ( 'shared-project' , 'feature-b' , { claudeStatus : 'working' } ) ;
301- setupFullWorktree ( 'shared-project' , 'feature-c' , { claudeStatus : 'waiting' } ) ;
260+ test ( 'should handle resource cleanup properly' , async ( ) => {
261+ // Given: Multiple features with different Claude statuses
262+ const scenario = createTestScenario ( )
263+ . withProject ( 'shared-project' )
264+ . withWorktree ( 'shared-project' , 'feature-a' , { claudeStatus : 'idle' } )
265+ . withWorktree ( 'shared-project' , 'feature-b' , { claudeStatus : 'working' } )
266+ . withWorktree ( 'shared-project' , 'feature-c' , { claudeStatus : 'waiting' } )
267+ . build ( ) ;
302268
303- const { stdin , lastFrame} = renderTestApp ( ) ;
269+ const { lastFrame} = renderTestApp ( ) ;
304270 await simulateTimeDelay ( 50 ) ;
305271
306272 const initialSessionCount = memoryStore . sessions . size ;
307273 const initialWorktreeCount = memoryStore . worktrees . size ;
308274
309- // Archive multiple features
310- stdin . write ( 'a' ) ; // Archive first feature
311- await simulateTimeDelay ( 50 ) ;
312- stdin . write ( '\r' ) ; // Confirm
313- await simulateTimeDelay ( 100 ) ;
275+ // When: Multiple features are archived (simulated)
276+ const featureAWorktree = scenario . worktrees [ 0 ] ;
277+ const featureBWorktree = scenario . worktrees [ 1 ] ;
278+
279+ // Simulate archiving feature-a and feature-b
280+ memoryStore . worktrees . delete ( featureAWorktree . path ) ;
281+ memoryStore . worktrees . delete ( featureBWorktree . path ) ;
282+ memoryStore . sessions . delete ( `dev-shared-project-feature-a` ) ;
283+ memoryStore . sessions . delete ( `dev-shared-project-feature-b` ) ;
314284
315- // Move to next and archive
316- stdin . write ( 'a' ) ; // Archive second feature
317- await simulateTimeDelay ( 50 ) ;
318- stdin . write ( '\r' ) ; // Confirm
319285 await simulateTimeDelay ( 100 ) ;
320286
321- // Verify proper cleanup
287+ // Then: Proper resource cleanup occurred
322288 expect ( memoryStore . worktrees . size ) . toBe ( initialWorktreeCount - 2 ) ;
323289 expect ( memoryStore . sessions . size ) . toBe ( initialSessionCount - 2 ) ;
324290
325- // Remaining feature should still be functional
291+ // And: Remaining feature is still functional
326292 const output = lastFrame ( ) ;
327293 expect ( output ) . toContain ( 'shared-project/feature-c' ) ;
294+
295+ // Verify the remaining session still exists with correct status
296+ const remainingSession = memoryStore . sessions . get ( 'dev-shared-project-feature-c' ) ;
297+ expect ( remainingSession ) . toBeDefined ( ) ;
298+ expect ( remainingSession ?. claude_status ) . toBe ( 'waiting' ) ; // Should still be waiting
328299 } ) ;
329300 } ) ;
330301
0 commit comments