@@ -34,6 +34,7 @@ const mockProgressBar = {
3434 stop : sinon . stub ( ) ,
3535 increment : sinon . stub ( ) ,
3636 update : sinon . stub ( ) ,
37+ setTotal : sinon . stub ( ) ,
3738} ;
3839
3940const mockMultiBar = {
@@ -335,6 +336,74 @@ describe('CLIProgressManager', () => {
335336 } ) ;
336337 } ) ;
337338
339+ describe ( 'Complete count reconciliation' , ( ) => {
340+ let bar : { update : sinon . SinonStub ; setTotal : sinon . SinonStub ; increment : sinon . SinonStub } ;
341+
342+ // Inject a stub bar at the rendering seam so assertions do not depend on the
343+ // load-order-sensitive cli-progress module mock (which does not bind in CI).
344+ function injectBar ( processName : string ) {
345+ bar = { update : sinon . stub ( ) , setTotal : sinon . stub ( ) , increment : sinon . stub ( ) } ;
346+ ( progressManager as any ) . processes . get ( processName ) . progressBar = bar ;
347+ }
348+
349+ beforeEach ( ( ) => {
350+ progressManager = new CLIProgressManager ( {
351+ enableNestedProgress : true ,
352+ moduleName : 'RECONCILE_TEST' ,
353+ showConsoleLogs : false ,
354+ } ) ;
355+ } ) ;
356+
357+ fancy . it ( 'renders the processed count, not the registered estimate, when fewer items were ticked' , ( ) => {
358+ progressManager . addProcess ( 'gf-update' , 37 ) ;
359+ injectBar ( 'gf-update' ) ;
360+ for ( let i = 0 ; i < 27 ; i ++ ) {
361+ progressManager . tick ( true , `item-${ i } ` , null , 'gf-update' ) ;
362+ }
363+
364+ progressManager . completeProcess ( 'gf-update' , true ) ;
365+
366+ expect ( bar . setTotal . calledWith ( 27 ) ) . to . equal ( true ) ;
367+ expect ( bar . update . lastCall . args [ 0 ] ) . to . equal ( 27 ) ;
368+ } ) ;
369+
370+ fancy . it ( 'renders the full count unchanged when every registered item was ticked' , ( ) => {
371+ progressManager . addProcess ( 'entries-create' , 59 ) ;
372+ injectBar ( 'entries-create' ) ;
373+ for ( let i = 0 ; i < 59 ; i ++ ) {
374+ progressManager . tick ( true , `item-${ i } ` , null , 'entries-create' ) ;
375+ }
376+
377+ progressManager . completeProcess ( 'entries-create' , true ) ;
378+
379+ expect ( bar . update . lastCall . args [ 0 ] ) . to . equal ( 59 ) ;
380+ } ) ;
381+
382+ fancy . it ( 'renders the registered total when a process completes with no ticks (skip case)' , ( ) => {
383+ progressManager . addProcess ( 'skipped' , 5 ) ;
384+ injectBar ( 'skipped' ) ;
385+
386+ progressManager . completeProcess ( 'skipped' , true ) ;
387+
388+ expect ( bar . update . lastCall . args [ 0 ] ) . to . equal ( 5 ) ;
389+ } ) ;
390+
391+ fancy . it ( 'reconciles the denominator for a failed process with partial ticks' , ( ) => {
392+ progressManager . addProcess ( 'entries' , 59 ) ;
393+ injectBar ( 'entries' ) ;
394+ for ( let i = 0 ; i < 38 ; i ++ ) {
395+ progressManager . tick ( true , `ok-${ i } ` , null , 'entries' ) ;
396+ }
397+ for ( let i = 0 ; i < 3 ; i ++ ) {
398+ progressManager . tick ( false , `err-${ i } ` , 'boom' , 'entries' ) ;
399+ }
400+
401+ progressManager . completeProcess ( 'entries' , false ) ;
402+
403+ expect ( bar . update . lastCall . args [ 0 ] ) . to . equal ( 41 ) ;
404+ } ) ;
405+ } ) ;
406+
338407 describe ( 'Progress Tracking' , ( ) => {
339408 beforeEach ( ( ) => {
340409 progressManager = new CLIProgressManager ( {
0 commit comments