import fs from 'fs' ; import path from 'path' ; import { uniqueID } from '<thisPackage>/utils' ; type DirectoryStructure = { [ filePath ] : string | DirectoryStructure ; } ; const makeFilesRecursive = ( base , structure ) => { Object . keys ( structure ) . forEach ( ( key ) => { const fullPath = path . join ( base , key ) ; const content = structure [ key ] ; if ( typeof content === 'object' ) { fs . mkdirSync ( fullPath , { recursive : true } ) ; makeFilesRecursive ( fullPath , content ) ; } else { fs . mkdirSync ( path . dirname ( fullPath ) , { recursive : true } ) ; fs . writeFileSync ( fullPath , content ) ; } } ) ; } ; const rmDirRecursive = ( dirPath , retry = true ) => { if ( ! fs . existsSync ( dirPath ) ) return ; fs . readdirSync ( dirPath ) . forEach ( ( file ) => { const fullPath = path . join ( dirPath , file ) ; let stats ; try { stats = fs . lstatSync ( fullPath ) ; } catch ( e ) { return ; } if ( stats . isDirectory ( ) ) { rmDirRecursive ( fullPath ) ; } else { fs . unlinkSync ( fullPath ) ; } } ) ; try { fs . rmdirSync ( dirPath ) ; } catch ( e ) { if ( retry ) { setTimeout ( ( ) => rmDirRecursive ( dirPath , false ) , 500 ) ; } } } ; interface Fixture { isUsed : boolean ; baseDir : string ; autoDelete : boolean ; p ( filePath ) : string ; apply ( ) : Fixture ; rollback ( ) : Fixture ; } const fixtureDir = path . join ( __dirname , '../../__fixtures__' ) ; const fixtures = new Set < Fixture > ( ) ; const clearEmptyFixtureDir = ( ) => { if ( fs . readdirSync ( fixtureDir ) . length === 0 ) { fs . rmdirSync ( fixtureDir ) ; } } ; export const createFixture = ( { $ : { name = uniqueID ( 6 ) , autoDelete = true } = { } , ... directoryStructure } ) : Fixture => { let used ; const baseDir = path . join ( fixtureDir , name ) ; const fixture = { get isUsed ( ) { return used ; } , get baseDir ( ) { return baseDir ; } , autoDelete , p ( filePath ) { return path . join ( baseDir , filePath ) ; } , apply ( ) { used = true ; makeFilesRecursive ( baseDir , directoryStructure ) ; return this ; } , rollback ( ) { rmDirRecursive ( baseDir ) ; clearEmptyFixtureDir ( ) ; return this ; } } ; fixtures . add ( fixture ) ; return fixture ; } ; afterAll ( ( ) => { fixtures . forEach ( ( fixture ) => { if ( ! fixture . isUsed ) { throw new Error ( `A fixture is left unused in test - ${ JSON . stringify ( expect . getState ( ) . currentTestName ) }. ` + 'Use `.apply()` to use it.' ) ; } if ( fixture . autoDelete ) rmDirRecursive ( fixture . baseDir ) ; } ) ; clearEmptyFixtureDir ( ) ; fixtures . clear ( ) ; } ) ;