@@ -562,11 +562,122 @@ describe('project generation', () => {
562562 expect ( result . failures ) . toEqual ( [
563563 expect . stringContaining ( 'Retry yarn install' ) ,
564564 ] )
565+ expect ( install ) . toHaveBeenCalledTimes ( 1 )
565566 expect (
566567 await readFile ( path . join ( destination , 'README.md' ) , 'utf8' ) ,
567568 ) . toContain ( 'PureStack' )
568569 } )
569570
571+ it . each ( [ 'yarn' , 'npm' ] as const ) (
572+ 'formats after installing with %s and before setting up Git' ,
573+ async ( manager ) => {
574+ const templateRoot = await fixture ( )
575+ if ( manager === 'npm' ) {
576+ const file = path . join ( templateRoot , 'template/package.json' )
577+ const project = JSON . parse ( await readFile ( file , 'utf8' ) )
578+ delete project . packageManager
579+ await writeFile ( file , JSON . stringify ( project ) )
580+ }
581+ const destination = path . join ( temporary , 'project' )
582+ const run = vi . spyOn ( managers , 'runPackageManager' ) . mockResolvedValue ( '' )
583+ vi . spyOn ( processes , 'runProcess' ) . mockResolvedValue ( repository )
584+ const labels : string [ ] = [ ]
585+ const signal = new AbortController ( ) . signal
586+ const result = await createSite (
587+ plan ( destination , 'starter' , { install : true , git : true } ) ,
588+ cliVersion ,
589+ repository ,
590+ async ( label , action ) => {
591+ labels . push ( label )
592+ return action ( )
593+ } ,
594+ { templateRoot, signal } ,
595+ )
596+ expect ( result . installed ) . toBe ( true )
597+ expect ( result . failures ) . toEqual ( [ ] )
598+ expect ( run . mock . calls . map ( ( call ) => call [ 1 ] ) ) . toEqual ( [
599+ manager === 'yarn' ? [ 'install' , '--no-immutable' ] : [ 'install' ] ,
600+ [ 'run' , 'format' ] ,
601+ ] )
602+ expect ( run ) . toHaveBeenLastCalledWith (
603+ manager ,
604+ [ 'run' , 'format' ] ,
605+ destination ,
606+ { capture : true , signal } ,
607+ )
608+ expect ( labels . slice ( - 3 ) ) . toEqual ( [
609+ 'Install dependencies' ,
610+ 'Format project files' ,
611+ 'Set up Git' ,
612+ ] )
613+ } ,
614+ )
615+
616+ it . each ( [ undefined , ' ' ] ) (
617+ 'skips formatting when the template has no usable format script (%s)' ,
618+ async ( format ) => {
619+ const templateRoot = await fixture ( )
620+ const file = path . join ( templateRoot , 'template/package.json' )
621+ const project = JSON . parse ( await readFile ( file , 'utf8' ) )
622+ project . scripts . format = format
623+ await writeFile ( file , JSON . stringify ( project ) )
624+ const run = vi . spyOn ( managers , 'runPackageManager' ) . mockResolvedValue ( '' )
625+ const result = await createSite (
626+ plan ( path . join ( temporary , 'project' ) , 'starter' , { install : true } ) ,
627+ cliVersion ,
628+ repository ,
629+ undefined ,
630+ { templateRoot } ,
631+ )
632+ expect ( result . installed ) . toBe ( true )
633+ expect ( result . failures ) . toEqual ( [ ] )
634+ expect ( run ) . toHaveBeenCalledTimes ( 1 )
635+ } ,
636+ )
637+
638+ it ( 'preserves the installed project and continues Git setup when formatting fails' , async ( ) => {
639+ const destination = path . join ( temporary , 'project' )
640+ vi . spyOn ( managers , 'runPackageManager' )
641+ . mockResolvedValueOnce ( '' )
642+ . mockRejectedValueOnce ( new Error ( 'Invalid markup' ) )
643+ vi . spyOn ( processes , 'runProcess' ) . mockResolvedValue ( repository )
644+ const result = await createSite (
645+ plan ( destination , 'starter' , { install : true , git : true } ) ,
646+ cliVersion ,
647+ repository ,
648+ undefined ,
649+ { templateRoot : await fixture ( ) } ,
650+ )
651+ expect ( result . installed ) . toBe ( true )
652+ expect ( result . git ) . toBe ( 'existing' )
653+ expect ( result . failures ) . toEqual ( [
654+ 'Project formatting failed: Invalid markup\nRetry yarn format in the project directory.' ,
655+ ] )
656+ expect ( await readdir ( destination ) ) . toContain ( 'README.md' )
657+ } )
658+
659+ it ( 'propagates formatting cancellation and does not start Git setup' , async ( ) => {
660+ const cancelled = new Error ( 'Cancelled' )
661+ cancelled . name = 'AbortError'
662+ vi . spyOn ( managers , 'runPackageManager' )
663+ . mockResolvedValueOnce ( '' )
664+ . mockRejectedValueOnce ( cancelled )
665+ const git = vi . spyOn ( processes , 'runProcess' )
666+ await expect (
667+ createSite (
668+ plan ( path . join ( temporary , 'project' ) , 'starter' , {
669+ install : true ,
670+ git : true ,
671+ } ) ,
672+ cliVersion ,
673+ repository ,
674+ undefined ,
675+ { templateRoot : await fixture ( ) } ,
676+ ) ,
677+ ) . rejects . toBe ( cancelled )
678+ expect ( git ) . not . toHaveBeenCalled ( )
679+ } )
680+
570681 it ( 'skips Git initialization inside an existing repository' , async ( ) => {
571682 const archive = await archiveFixture ( )
572683 vi . spyOn ( console , 'log' ) . mockImplementation ( ( ) => { } )
0 commit comments