11import { SingleTask } from './SingleTask.js'
22import { render } from '../../testing/ui.js'
3+ import { TokenizedString } from '../../../../public/node/output.js'
34import React from 'react'
45import { describe , expect , test } from 'vitest'
56
67describe ( 'SingleTask' , ( ) => {
78 test ( 'unmounts when promise resolves successfully' , async ( ) => {
89 // Given
9- const title = 'Uploading files'
10+ const title = new TokenizedString ( 'Uploading files' )
1011 let resolvePromise : ( value : string ) => void
11- const taskPromise = new Promise < string > ( ( resolve ) => {
12- resolvePromise = resolve
13- } )
12+ const task = ( ) =>
13+ new Promise < string > ( ( resolve ) => {
14+ resolvePromise = resolve
15+ } )
1416
1517 // When
16- const renderInstance = render ( < SingleTask title = { title } taskPromise = { taskPromise } /> )
18+ const renderInstance = render ( < SingleTask title = { title } task = { task } /> )
1719
1820 // Wait for initial render
1921 await new Promise ( ( resolve ) => setTimeout ( resolve , 10 ) )
@@ -30,14 +32,15 @@ describe('SingleTask', () => {
3032
3133 test ( 'unmounts when promise rejects' , async ( ) => {
3234 // Given
33- const title = 'Failed task'
35+ const title = new TokenizedString ( 'Failed task' )
3436 let rejectPromise : ( error : Error ) => void
35- const taskPromise = new Promise < string > ( ( resolve , reject ) => {
36- rejectPromise = reject
37- } )
37+ const task = ( ) =>
38+ new Promise < string > ( ( resolve , reject ) => {
39+ rejectPromise = reject
40+ } )
3841
3942 // When
40- const renderInstance = render ( < SingleTask title = { title } taskPromise = { taskPromise } /> )
43+ const renderInstance = render ( < SingleTask title = { title } task = { task } /> )
4144
4245 // Wait for initial render
4346 await new Promise ( ( resolve ) => setTimeout ( resolve , 10 ) )
@@ -51,11 +54,11 @@ describe('SingleTask', () => {
5154
5255 test ( 'handles promise that resolves immediately' , async ( ) => {
5356 // Given
54- const title = 'Instant task'
55- const taskPromise = Promise . resolve ( 'immediate success' )
57+ const title = new TokenizedString ( 'Instant task' )
58+ const task = ( ) => Promise . resolve ( 'immediate success' )
5659
5760 // When
58- const renderInstance = render ( < SingleTask title = { title } taskPromise = { taskPromise } /> )
61+ const renderInstance = render ( < SingleTask title = { title } task = { task } /> )
5962 await renderInstance . waitUntilExit ( )
6063
6164 // Then - component should complete successfully
@@ -64,51 +67,83 @@ describe('SingleTask', () => {
6467
6568 test ( 'handles promise that rejects immediately' , async ( ) => {
6669 // Given
67- const title = 'Instant failure'
68- const taskPromise = Promise . reject ( new Error ( 'Immediate error' ) )
70+ const title = new TokenizedString ( 'Instant failure' )
71+ const task = ( ) => Promise . reject ( new Error ( 'Immediate error' ) )
6972
7073 // When
71- const renderInstance = render ( < SingleTask title = { title } taskPromise = { taskPromise } /> )
74+ const renderInstance = render ( < SingleTask title = { title } task = { task } /> )
7275
7376 // Then - should exit with error
7477 await expect ( renderInstance . waitUntilExit ( ) ) . rejects . toThrow ( 'Immediate error' )
7578 } )
7679
7780 test ( 'handles different types of promise return values' , async ( ) => {
7881 // Test with string
79- const stringTask = Promise . resolve ( 'task completed' )
80- const stringRender = render ( < SingleTask title = "String task" taskPromise = { stringTask } /> )
82+ let stringResult : string | undefined
83+ const stringTask = ( ) => Promise . resolve ( 'task completed' )
84+ const stringRender = render (
85+ < SingleTask
86+ title = { new TokenizedString ( 'String task' ) }
87+ task = { stringTask }
88+ onComplete = { ( result ) => ( stringResult = result ) }
89+ /> ,
90+ )
8191 await stringRender . waitUntilExit ( )
8292 expect ( stringRender . lastFrame ( ) ) . toBeDefined ( )
93+ expect ( stringResult ) . toBe ( 'task completed' )
8394
8495 // Test with object
85- const objectTask = Promise . resolve ( { id : 1 , name : 'test' } )
86- const objectRender = render ( < SingleTask title = "Object task" taskPromise = { objectTask } /> )
96+ let objectResult : { id : number ; name : string } | undefined
97+ const objectTask = ( ) => Promise . resolve ( { id : 1 , name : 'test' } )
98+ const objectRender = render (
99+ < SingleTask
100+ title = { new TokenizedString ( 'Object task' ) }
101+ task = { objectTask }
102+ onComplete = { ( result ) => ( objectResult = result ) }
103+ /> ,
104+ )
87105 await objectRender . waitUntilExit ( )
88106 expect ( objectRender . lastFrame ( ) ) . toBeDefined ( )
89107
90108 // Test with number
91- const numberTask = Promise . resolve ( 42 )
92- const numberRender = render ( < SingleTask title = "Number task" taskPromise = { numberTask } /> )
109+ let numberResult : number | undefined
110+ const numberTask = ( ) => Promise . resolve ( 42 )
111+ const numberRender = render (
112+ < SingleTask
113+ title = { new TokenizedString ( 'Number task' ) }
114+ task = { numberTask }
115+ onComplete = { ( result ) => ( numberResult = result ) }
116+ /> ,
117+ )
93118 await numberRender . waitUntilExit ( )
94119 expect ( numberRender . lastFrame ( ) ) . toBeDefined ( )
120+ expect ( numberResult ) . toBe ( 42 )
95121
96122 // Test with boolean
97- const booleanTask = Promise . resolve ( true )
98- const booleanRender = render ( < SingleTask title = "Boolean task" taskPromise = { booleanTask } /> )
123+ let booleanResult : boolean | undefined
124+ const booleanTask = ( ) => Promise . resolve ( true )
125+ const booleanRender = render (
126+ < SingleTask
127+ title = { new TokenizedString ( 'Boolean task' ) }
128+ task = { booleanTask }
129+ onComplete = { ( result ) => ( booleanResult = result ) }
130+ /> ,
131+ )
99132 await booleanRender . waitUntilExit ( )
100133 expect ( booleanRender . lastFrame ( ) ) . toBeDefined ( )
134+ expect ( booleanResult ) . toBe ( true )
101135 } )
102136
103137 test ( 'handles promise with delayed resolution' , async ( ) => {
104138 // Given
105- const title = 'Delayed task'
106- const taskPromise = new Promise < string > ( ( resolve ) => {
107- setTimeout ( ( ) => resolve ( 'completed' ) , 100 )
108- } )
139+ const title = new TokenizedString ( 'Delayed task' )
140+ const task = ( ) =>
141+ new Promise < string > ( ( resolve ) => {
142+ setTimeout ( ( ) => resolve ( 'completed' ) , 100 )
143+ } )
109144
110145 // When
111- const renderInstance = render ( < SingleTask title = { title } taskPromise = { taskPromise } /> )
146+ const renderInstance = render ( < SingleTask title = { title } task = { task } /> )
112147
113148 // Wait for completion
114149 await renderInstance . waitUntilExit ( )
@@ -119,13 +154,14 @@ describe('SingleTask', () => {
119154
120155 test ( 'handles promise with delayed rejection' , async ( ) => {
121156 // Given
122- const title = 'Delayed failure'
123- const taskPromise = new Promise < string > ( ( resolve , reject ) => {
124- setTimeout ( ( ) => reject ( new Error ( 'delayed error' ) ) , 100 )
125- } )
157+ const title = new TokenizedString ( 'Delayed failure' )
158+ const task = ( ) =>
159+ new Promise < string > ( ( resolve , reject ) => {
160+ setTimeout ( ( ) => reject ( new Error ( 'delayed error' ) ) , 100 )
161+ } )
126162
127163 // When
128- const renderInstance = render ( < SingleTask title = { title } taskPromise = { taskPromise } /> )
164+ const renderInstance = render ( < SingleTask title = { title } task = { task } /> )
129165
130166 // Wait for completion - should throw error
131167 await expect ( renderInstance . waitUntilExit ( ) ) . rejects . toThrow ( 'delayed error' )
@@ -141,23 +177,23 @@ describe('SingleTask', () => {
141177 }
142178
143179 const customError = new CustomError ( 'Custom error message' , 'CUSTOM_CODE' )
144- const taskPromise = Promise . reject ( customError )
180+ const task = ( ) => Promise . reject ( customError )
145181
146182 // When
147- const renderInstance = render ( < SingleTask title = " Custom error task" taskPromise = { taskPromise } /> )
183+ const renderInstance = render ( < SingleTask title = { new TokenizedString ( ' Custom error task' ) } task = { task } /> )
148184
149185 // Then - should preserve the exact error
150186 await expect ( renderInstance . waitUntilExit ( ) ) . rejects . toThrow ( 'Custom error message' )
151187 } )
152188
153189 test ( 'handles concurrent promise operations' , async ( ) => {
154190 // Given - Multiple SingleTask components with different promises
155- const fastPromise = new Promise ( ( resolve ) => setTimeout ( ( ) => resolve ( 'fast' ) , 50 ) )
156- const slowPromise = new Promise ( ( resolve ) => setTimeout ( ( ) => resolve ( 'slow' ) , 150 ) )
191+ const fastPromise = ( ) => new Promise ( ( resolve ) => setTimeout ( ( ) => resolve ( 'fast' ) , 50 ) )
192+ const slowPromise = ( ) => new Promise ( ( resolve ) => setTimeout ( ( ) => resolve ( 'slow' ) , 150 ) )
157193
158194 // When
159- const fastRender = render ( < SingleTask title = " Fast task" taskPromise = { fastPromise } /> )
160- const slowRender = render ( < SingleTask title = " Slow task" taskPromise = { slowPromise } /> )
195+ const fastRender = render ( < SingleTask title = { new TokenizedString ( ' Fast task' ) } task = { fastPromise } /> )
196+ const slowRender = render ( < SingleTask title = { new TokenizedString ( ' Slow task' ) } task = { slowPromise } /> )
161197
162198 // Then - Both should complete successfully
163199 await fastRender . waitUntilExit ( )
@@ -169,14 +205,69 @@ describe('SingleTask', () => {
169205
170206 test ( 'passes noColor prop to LoadingBar component' , async ( ) => {
171207 // Given
172- const title = 'No color task'
173- const taskPromise = Promise . resolve ( )
208+ const title = new TokenizedString ( 'No color task' )
209+ const task = ( ) => Promise . resolve ( )
174210
175211 // When - Test that noColor prop doesn't break the component
176- const renderInstance = render ( < SingleTask title = { title } taskPromise = { taskPromise } noColor /> )
212+ const renderInstance = render ( < SingleTask title = { title } task = { task } noColor /> )
177213 await renderInstance . waitUntilExit ( )
178214
179215 // Then - Component should complete successfully with noColor prop
180216 expect ( renderInstance . lastFrame ( ) ) . toBeDefined ( )
181217 } )
218+
219+ test ( 'updates status message during task execution' , async ( ) => {
220+ // Given
221+ const initialTitle = new TokenizedString ( 'Starting task' )
222+ let step1Resolve : ( ) => void
223+ let step2Resolve : ( ) => void
224+ let step3Resolve : ( ) => void
225+
226+ const step1Promise = new Promise < void > ( ( resolve ) => {
227+ step1Resolve = resolve
228+ } )
229+ const step2Promise = new Promise < void > ( ( resolve ) => {
230+ step2Resolve = resolve
231+ } )
232+ const step3Promise = new Promise < void > ( ( resolve ) => {
233+ step3Resolve = resolve
234+ } )
235+
236+ const task = async ( updateStatus : ( status : TokenizedString ) => void ) => {
237+ updateStatus ( new TokenizedString ( 'Running (1 complete)...' ) )
238+ await step1Promise
239+
240+ updateStatus ( new TokenizedString ( 'Running (2 complete)...' ) )
241+ await step2Promise
242+
243+ updateStatus ( new TokenizedString ( 'Running (3 complete)...' ) )
244+ await step3Promise
245+
246+ return 'completed'
247+ }
248+
249+ // When
250+ const renderInstance = render ( < SingleTask title = { initialTitle } task = { task } /> )
251+
252+ // Wait for component to render with first status
253+ await new Promise ( ( resolve ) => setTimeout ( resolve , 10 ) )
254+ const frame1 = renderInstance . lastFrame ( )
255+ expect ( frame1 ) . toContain ( '1 complete' )
256+
257+ // Progress to step 2
258+ step1Resolve ! ( )
259+ await new Promise ( ( resolve ) => setTimeout ( resolve , 10 ) )
260+ const frame2 = renderInstance . lastFrame ( )
261+ expect ( frame2 ) . toContain ( '2 complete' )
262+
263+ // Progress to step 3
264+ step2Resolve ! ( )
265+ await new Promise ( ( resolve ) => setTimeout ( resolve , 10 ) )
266+ const frame3 = renderInstance . lastFrame ( )
267+ expect ( frame3 ) . toContain ( '3 complete' )
268+
269+ // Complete the task
270+ step3Resolve ! ( )
271+ await renderInstance . waitUntilExit ( )
272+ } )
182273} )
0 commit comments