import { spawn } from 'child_process' ; import { accessSync , readFileSync , writeFileSync } from 'fs' ; import { dirname , join , relative } from 'path' ; import * as glob from 'glob' ; import { task } from 'gulp' ; import * as del from 'del' ; import { template } from 'lodash' ; import * as runSequence from 'run-sequence' ; import { argv } from 'yargs' ; import { DIST_E2E_COMPONENTS_ROOT , ES_2015 , PROJECT_ROOT , SRC_ROOT , SRC_COMPONENTS_ROOT , SCRIPTS_ROOT } from '../constants' ; import { createTempTsConfig , createTimestamp , getFolderInfo , readFileAsync , runAppScriptsBuild , writeFileAsync , writePolyfills } from '../util' ; import * as pAll from 'p-all' ; task ( 'e2e.prepare' , ( done ) => { runSequence ( 'e2e.clean' , 'e2e.polyfill' , 'e2e.prepareSass' , ( err ) => done ( err ) ) ; } ) ; task ( 'e2e.prepareSass' , ( done ) => { const version = `E2E-${ createTimestamp ( ) }` ; writeFileSync ( join ( SRC_ROOT , 'themes' , 'version.scss' ) , `$ionic-version: "${ version }";` ) ; done ( ) ; } ) ; task ( 'e2e.prod' , [ 'e2e.prepare' ] , ( done ) => { filterE2eTestfiles ( ) . then ( ( filePaths ) => { if ( filePaths && filePaths . length > 0 ) { console . log ( `Compiling ${ filePaths . length } E2E tests ...` ) ; return buildTests ( filePaths ) ; } } ) . then ( ( ) => { done ( ) ; } ) . catch ( ( err ) => { done ( err ) ; process . exit ( 1 ) ; } ) ; } ) ; function e2eComponentExists ( folderInfo ) { let componentPath = join ( SRC_COMPONENTS_ROOT , folderInfo . componentName , 'test' , folderInfo . componentTest , 'app' ) ; try { accessSync ( componentPath ) ; } catch ( e ) { return false ; } return true ; } function filterE2eTestfiles ( ) { return getE2eTestFiles ( ) . then ( ( filePaths ) => { const entryPoints = filePaths . map ( filePath => { const directoryName = dirname ( filePath ) ; return join ( directoryName , 'app' , 'main.ts' ) ; } ) ; return entryPoints ; } ) . then ( ( entryPoints ) => { const folderInfo = getFolderInfo ( ) ; if ( folderInfo && folderInfo . componentName && folderInfo . componentTest ) { if ( ! e2eComponentExists ( folderInfo ) ) { console . log ( 'Cannot find E2E test ' , join ( folderInfo . componentName , 'test' , folderInfo . componentTest ) , '. Make sure that the test exists and you are passing the correct folder.' ) ; return [ ] ; } const filtered = entryPoints . filter ( entryPoint => { return entryPoint . indexOf ( join ( folderInfo . componentName , 'test' , folderInfo . componentTest ) ) >= 0 ; } ) ; return filtered ; } return entryPoints ; } ) ; } function getE2eTestFiles ( ) { return new Promise ( ( resolve , reject ) => { const mainGlob = join ( SRC_COMPONENTS_ROOT , '*' , 'test' , '*' , 'e2e.ts' ) ; glob ( mainGlob , ( err , matches ) => { if ( err ) { return reject ( err ) ; } resolve ( matches ) ; } ) ; } ) ; } function buildTests ( filePaths ) { const functions = filePaths . map ( filePath => ( ) => { return buildTest ( filePath ) ; } ) ; let concurrentNumber = 2 ; if ( argv . concurrency ) { concurrentNumber = argv . concurrency ; } return pAll ( functions , { concurrency : concurrentNumber } ) . then ( ( ) => { return copyProtractorTestContent ( filePaths ) ; } ) ; } function buildTest ( filePath ) { const start = Date . now ( ) ; const ionicAngularDir = join ( process . cwd ( ) , 'src' ) ; let appEntryPoint = filePath ; let srcTestRoot = dirname ( dirname ( appEntryPoint ) ) ; try { readFileSync ( appEntryPoint ) ; } catch ( ex ) { appEntryPoint = join ( dirname ( dirname ( appEntryPoint ) ) , 'main.ts' ) ; srcTestRoot = dirname ( appEntryPoint ) ; } const relativePathFromComponents = relative ( dirname ( SRC_COMPONENTS_ROOT ) , srcTestRoot ) ; const distTestRoot = join ( process . cwd ( ) , 'dist' , 'e2e' , relativePathFromComponents ) ; const includeGlob = [ join ( ionicAngularDir , '**' , '*.ts' ) ] ; const pathToWriteFile = join ( distTestRoot , 'tsconfig.json' ) ; const pathToReadFile = join ( PROJECT_ROOT , 'tsconfig.json' ) ; createTempTsConfig ( includeGlob , ES_2015 , ES_2015 , pathToReadFile , pathToWriteFile , { removeComments : true } ) ; const sassConfigPath = join ( 'scripts' , 'e2e' , 'sass.config.js' ) ; const copyConfigPath = join ( 'scripts' , 'e2e' , 'copy.config.js' ) ; const appNgModulePath = join ( dirname ( appEntryPoint ) , 'app.module.ts' ) ; const distDir = join ( distTestRoot , 'www' ) ; const minifyCss = argv . minifyCss ? true : false ; const minifyJs = argv . minifyJs ? true : false ; const optimizeJs = argv . optimizeJs ? true : false ; return runAppScriptsBuild ( appEntryPoint , appNgModulePath , ionicAngularDir , distDir , pathToWriteFile , ionicAngularDir , sassConfigPath , copyConfigPath , argv . dev , minifyCss , minifyJs , optimizeJs ) . then ( ( ) => { const end = Date . now ( ) ; console . log ( `${ filePath } took a total of ${ ( end - start ) / 1000 } seconds to build` ) ; } ) . catch ( ( err ) => { console . log ( `${ err }` ) ; } ) ; } function copyProtractorTestContent ( filePaths ) { const e2eTestPaths = filePaths . map ( filePath => { return join ( dirname ( dirname ( filePath ) ) , 'e2e.ts' ) ; } ) ; return readE2ETestFiles ( e2eTestPaths ) . then ( ( map ) => { return applyTemplate ( map ) ; } ) . then ( ( map ) => { writeE2EJsFiles ( map ) ; } ) ; } function applyTemplate ( filePathContent ) { const buildConfig = require ( join ( '..' , '..' , 'build' , 'config' ) ) ; const templateFileContent = readFileSync ( join ( SCRIPTS_ROOT , 'e2e' , 'e2e.template.js' ) ) ; const templater = template ( templateFileContent . toString ( ) ) ; const modifiedMap = new Map < string , string > ( ) ; const platforms = [ 'android' , 'ios' , 'windows' ] ; filePathContent . forEach ( ( fileContent , filePath ) => { const srcRelativePath = relative ( SRC_ROOT , dirname ( filePath ) ) ; const wwwRelativePath = join ( srcRelativePath , 'www' ) ; platforms . forEach ( platform => { const platformContents = templater ( { contents : fileContent , buildConfig : buildConfig , relativePath : wwwRelativePath , platform : platform , relativePathBackwardsCompatibility : dirname ( wwwRelativePath ) } ) ; const newFilePath = join ( wwwRelativePath , `${ platform }.e2e.js` ) ; modifiedMap . set ( newFilePath , platformContents ) ; } ) ; } ) ; return modifiedMap ; } function writeE2EJsFiles ( map ) { const promises = [ ] ; map . forEach ( ( fileContent , filePath ) => { const destination = join ( process . cwd ( ) , 'dist' , 'e2e' , filePath ) ; promises . push ( writeFileAsync ( destination , fileContent ) ) ; } ) ; return Promise . all ( promises ) ; } function readE2ETestFiles ( mainFilePaths ) { const e2eFiles = mainFilePaths . map ( mainFilePath => { return join ( dirname ( mainFilePath ) , 'e2e.ts' ) ; } ) ; const promises = [ ] ; const map = new Map < string , string > ( ) ; for ( const e2eFile of e2eFiles ) { const promise = readE2EFile ( e2eFile ) ; promises . push ( promise ) ; promise . then ( ( content ) => { map . set ( e2eFile , content ) ; } ) ; } return Promise . all ( promises ) . then ( ( ) => { return map ; } ) ; } function readE2EFile ( filePath ) { return readFileAsync ( filePath ) . then ( ( content ) => { const purgeImportRegex = /.*?import.*?'protractor';/g ; return content . replace ( purgeImportRegex , '' ) ; } ) ; } task ( 'e2e.clean' , ( done ) => { if ( argv . skipClean ) { return done ( ) ; } del ( [ 'dist/e2e/**' ] ) . then ( ( ) => { done ( ) ; } ) . catch ( err => { done ( err ) ; } ) ; } ) ; task ( 'e2e.polyfill' , ( done ) => { if ( argv . skipPolyfill ) { return done ( ) ; } writePolyfills ( join ( 'dist' , 'e2e' , 'polyfills' ) ) . then ( ( ) => { done ( ) ; } ) . catch ( err => { done ( err ) ; } ) ; } ) ; task ( 'e2e.openProd' , ( done ) => { runSequence ( 'e2e.prod' , 'e2e.open' , ( err ) => done ( err ) ) ; } ) ; task ( 'e2e.open' , ( done ) => { const folderInfo = getFolderInfo ( ) ; if ( folderInfo && folderInfo . componentName && folderInfo . componentTest ) { const filePath = join ( DIST_E2E_COMPONENTS_ROOT , folderInfo . componentName , 'test' , folderInfo . componentTest , 'www' , 'index.html' ) ; const spawnedCommand = spawn ( 'open' , [ filePath ] ) ; spawnedCommand . on ( 'close' , ( code ) => { done ( ) ; } ) ; } else { console . log ( `Can't open without folder argument.` ) ; } } ) ;