import { getSystemPath , normalize } from '@angular-devkit/core' ; import { TempScopedNodeJsSyncHost } from '@angular-devkit/core/node/testing' ; import * as virtualFs from '@angular-devkit/core/src/virtual-fs/host' ; import { HostTree , Tree } from '@angular-devkit/schematics' ; import { SchematicTestRunner , UnitTestTree } from '@angular-devkit/schematics/testing' ; import { readFileSync , rmdirSync } from 'fs-extra' ; import { basename , extname } from 'path' ; /** Create a base app used for testing. */ export async function createTestApp ( runner , appOptions = { } , tree ? ) { const workspaceTree = await runner . runExternalSchematicAsync ( '@schematics/angular' , 'workspace' , { name : 'workspace' , version : '6.0.0' , newProjectRoot : 'projects' } , tree ) . toPromise ( ) ; return runner . runExternalSchematicAsync ( '@schematics/angular' , 'application' , { name : '@ptsecurity/mosaic' , ... appOptions } , workspaceTree ) . toPromise ( ) ; } export async function createFileSystemTestApp ( runner ) { const tempFileSystemHost = new TempScopedNodeJsSyncHost ( ) ; const hostTree = new HostTree ( tempFileSystemHost as any ) ; const appTree = await createTestApp ( runner , { name : 'lib-testing' } , hostTree ) ; const tempPath = getSystemPath ( tempFileSystemHost . root ) ; appTree . files . forEach ( ( f ) => { writeFile ( f , appTree . readContent ( f ) ) ; } ) ; return { appTree , tempFileSystemHost , tempPath , writeFile , removeTempDir : ( ) => { rmdirSync ( tempPath , { recursive : true } ) ; } } ; function writeFile ( filePath , content ) { tempFileSystemHost . sync . write ( normalize ( filePath ) , virtualFs . stringToFileBuffer ( content ) ) ; } } interface ITestCaseSetup { appTree : UnitTestTree ; tempPath : string ; tempFileSystemHost ? : TempScopedNodeJsSyncHost ; runFixers ? ( ) : Promise < { logOutput : string ; } > ; removeTempDir ( ) : void ; writeFile ( filePath , content ) : void ; } export async function createTestCaseSetup ( migrationName , collectionPath , inputFiles ) { const runner = new SchematicTestRunner ( 'schematics' , collectionPath ) ; const initialWorkingDir = process . cwd ( ) ; let logOutput = '' ; runner . logger . subscribe ( ( entry ) => ( logOutput += `${ entry . message }\n` ) ) ; const { appTree , tempPath , writeFile , removeTempDir } = await createFileSystemTestApp ( runner ) ; patchTypeScriptDefaultLib ( appTree ) ; inputFiles . forEach ( ( inputFilePath ) => { const inputTestName = basename ( inputFilePath , extname ( inputFilePath ) ) ; const relativePath = `projects/lib-testing/src/tests/${ inputTestName }.ts` ; const inputContent = readFileSync ( inputFilePath , 'utf8' ) ; writeFile ( relativePath , inputContent ) ; } ) ; const testAppTsconfigPath = 'projects/lib-testing/tsconfig.app.json' ; const testAppTsconfig = JSON . parse ( appTree . readContent ( testAppTsconfigPath ) ) ; testAppTsconfig . include . push ( 'src/**/*.ts' ) ; writeFile ( testAppTsconfigPath , JSON . stringify ( testAppTsconfig , null , 4 ) ) ; const runFixers = async function ( ) : Promise < { logOutput : string ; } > { process . chdir ( tempPath ) ; await runner . runSchematicAsync ( migrationName , { } , appTree ) . toPromise ( ) ; process . chdir ( initialWorkingDir ) ; return { logOutput } ; } ; return { appTree , writeFile , tempPath , removeTempDir , runFixers } ; } /**
 * Patches the specified virtual file system tree to be able to read the TypeScript
 * default library typings. These need to be readable in unit tests because otherwise
 * type checking within migration rules is not working as in real applications.
 */ function patchTypeScriptDefaultLib ( tree ) { const originalRead = tree . read ; tree . read = function ( filePath ) : Buffer | any { if ( filePath . match ( /node_modules[/\\]typescript/ ) ) { return readFileSync ( filePath ) ; } else { return originalRead . apply ( this , arguments ) ; } } ; }