import { Provider } from '@angular/core' ; import { flush } from '@angular/core/testing' ; import { CopyOptions } from 'fs-extra' ; import { Observable , Subject } from 'rxjs' ; import { FsService } from '../../../src/browser/shared' ; export class FsStub < R > { constructor ( readonly matchObj , private readonly stream , private mockFsService ) { } flush ( data ? ) { this . stream . next ( data ) ; this . mockFsService . _deleteStub < R > ( this ) ; flush ( ) ; } error ( error ) { this . stream . error ( error ) ; this . mockFsService . _deleteStub < R > ( this ) ; flush ( ) ; } } export interface FsMatchObject { methodName : string ; args : any [ ] ; } export enum FsMatchLiterals { ANY = '__ANY__' } interface FsStubAndStreamMap < R > { readonly matchObj : FsMatchObject ; readonly stream : Subject < R > ; } example example export class MockFsService extends FsService { private attachments : FsStubAndStreamMap < any > [ ] = [ ] ; static providers ( ) { return [ { provide : FsService , useClass : MockFsService , } , ] ; } expect < R = any > ( matchObj ) { const stub = this . findAttachment ( matchObj ) ; if ( ! stub ) { throw new Error ( `Cannot find matched stub: ${ matchObj . methodName }, ${ matchObj . args }` ) ; } return new FsStub < R > ( stub . matchObj , stub . stream , this ) ; } expectMany < R = any > ( matchObjList ) { return matchObjList . map ( obj => this . expect < R > ( obj ) ) ; } spyOn ( method ) { return spyOn ( this , method ) ; } verify ( ) { if ( this . attachments . length > 0 ) { throw new Error ( `${ this . attachments . length } not verified stubs left.` ) ; } this . attachments = [ ] ; } discardPeriodStubs ( ) { this . attachments = [ ] ; } isPathExists ( path ) { return this . createAttachment < boolean > ( 'isPathExists' , [ path ] ) ; } readFile ( fileName ) { return this . createAttachment < string > ( 'readFile' , [ fileName ] ) ; } readJsonFile < T > ( fileName ) { return this . createAttachment < T | null > ( 'readJsonFile' , [ fileName ] ) ; } readDirectory ( dirName ) { return this . createAttachment < string [ ] > ( 'readDirectory' , [ dirName ] ) ; } writeFile ( fileName , value ) { return this . createAttachment < void > ( 'writeFile' , [ fileName , value ] ) ; } writeJsonFile < T > ( fileName , value ) { return this . createAttachment < void > ( 'writeJsonFile' , [ fileName , value ] ) ; } copyFile ( src , dest , options ? ) { return this . createAttachment < void > ( 'copyFile' , [ src , dest , options ] ) ; } ensureDirectory ( dirName ) { return this . createAttachment < void > ( 'ensureDirectory' , [ dirName ] ) ; } renameFile ( oldFileName , newFileName ) { return this . createAttachment < void > ( 'renameFile' , [ oldFileName , newFileName ] ) ; } removeFile ( fileName ) { return this . createAttachment < void > ( 'removeFile' , [ fileName ] ) ; } _deleteStub < R > ( stub ) { const attach = this . findAttachment ( stub . matchObj ) ; if ( attach ) { attach . stream . complete ( ) ; const index = this . attachments . indexOf ( attach ) ; if ( index !== - 1 ) { this . attachments . splice ( index , 1 ) ; } } } private findAttachment < R > ( matchObj ) { const matchArgs = ( source , dist ) : boolean => { let allMatched = true ; for ( let i = 0 ; i < source . args . length ; i ++ ) { if ( dist . args [ i ] === FsMatchLiterals . ANY ) { continue ; } if ( source . args [ i ] !== dist . args [ i ] ) { allMatched = false ; break ; } } return allMatched ; } ; return this . attachments . find ( stub => stub . matchObj . methodName === matchObj . methodName && matchArgs ( stub . matchObj , matchObj ) ) || null ; } private createAttachment < R > ( methodName , args ) { const matchObj = { methodName , args } ; const stream = new Subject < R > ( ) ; this . attachments . push ( { matchObj , stream } ) ; return stream . asObservable ( ) ; } }