| 
									
										
										
										
											2017-08-22 21:31:15 +02:00
										 |  |  | const path = require('canonical-path'); | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  | const fs = require('fs-extra'); | 
					
						
							|  |  |  | const argv = require('yargs').argv; | 
					
						
							|  |  |  | const globby = require('globby'); | 
					
						
							|  |  |  | const xSpawn = require('cross-spawn'); | 
					
						
							|  |  |  | const treeKill = require('tree-kill'); | 
					
						
							| 
									
										
										
										
											2017-10-06 14:48:37 +01:00
										 |  |  | const shelljs = require('shelljs'); | 
					
						
							| 
									
										
										
										
											2019-03-04 20:33:25 +01:00
										 |  |  | const findFreePort = require('find-free-port'); | 
					
						
							| 
									
										
										
										
											2017-10-06 14:48:37 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | shelljs.set('-e'); | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | const AIO_PATH = path.join(__dirname, '../../'); | 
					
						
							|  |  |  | const SHARED_PATH = path.join(__dirname, '/shared'); | 
					
						
							|  |  |  | const EXAMPLES_PATH = path.join(AIO_PATH, './content/examples/'); | 
					
						
							|  |  |  | const PROTRACTOR_CONFIG_FILENAME = path.join(__dirname, './shared/protractor.config.js'); | 
					
						
							| 
									
										
										
										
											2017-08-22 21:31:15 +02:00
										 |  |  | const SJS_SPEC_FILENAME = 'e2e-spec.ts'; | 
					
						
							| 
									
										
										
										
											2018-04-12 21:20:01 -05:00
										 |  |  | const CLI_SPEC_FILENAME = 'e2e/src/app.e2e-spec.ts'; | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  | const EXAMPLE_CONFIG_FILENAME = 'example-config.json'; | 
					
						
							| 
									
										
										
										
											2019-03-04 20:33:25 +01:00
										 |  |  | const DEFAULT_CLI_EXAMPLE_PORT = 4200; | 
					
						
							|  |  |  | const DEFAULT_CLI_SPECS_CONCURRENCY = 1; | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  | const IGNORED_EXAMPLES = [ | 
					
						
							|  |  |  |   // temporary ignores
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const fixmeIvyExamples = [ | 
					
						
							|  |  |  |   // fixmeIvy('unknown') app fails at runtime due to missing external service (goog is undefined)
 | 
					
						
							| 
									
										
										
										
											2019-02-27 19:57:28 +01:00
										 |  |  |   'i18n', | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  | ]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  | if (argv.ivy) { | 
					
						
							|  |  |  |   IGNORED_EXAMPLES.push(...fixmeIvyExamples); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Run Protractor End-to-End Tests for Doc Samples | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Flags | 
					
						
							|  |  |  |  *   --filter to filter/select _example app subdir names | 
					
						
							|  |  |  |  *    e.g. --filter=foo  // all example apps with 'foo' in their folder names.
 | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2017-07-27 08:31:45 +01:00
										 |  |  |  *  --setup run yarn install, copy boilerplate and update webdriver | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  |  *    e.g. --setup | 
					
						
							| 
									
										
										
										
											2017-07-27 08:31:45 +01:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2017-07-20 21:19:01 +01:00
										 |  |  |  *  --local to use the locally built Angular packages, rather than versions from npm | 
					
						
							|  |  |  |  *    Must be used in conjunction with --setup as this is when the packages are copied. | 
					
						
							|  |  |  |  *    e.g. --setup --local | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2017-07-27 08:31:45 +01:00
										 |  |  |  *  --shard to shard the specs into groups to allow you to run them in parallel | 
					
						
							|  |  |  |  *    e.g. --shard=0/2 // the even specs: 0, 2, 4, etc
 | 
					
						
							|  |  |  |  *    e.g. --shard=1/2 // the odd specs: 1, 3, 5, etc
 | 
					
						
							|  |  |  |  *    e.g. --shard=1/3 // the second of every three specs: 1, 4, 7, etc
 | 
					
						
							| 
									
										
										
										
											2019-03-04 20:33:25 +01:00
										 |  |  |  * | 
					
						
							|  |  |  |  *  --cliSpecsConcurrency Amount of CLI example specs that should be executed concurrently. | 
					
						
							|  |  |  |  *    By default runs specs sequentially. | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  |  */ | 
					
						
							|  |  |  | function runE2e() { | 
					
						
							|  |  |  |   if (argv.setup) { | 
					
						
							|  |  |  |     // Run setup.
 | 
					
						
							| 
									
										
										
										
											2017-10-06 14:48:37 +01:00
										 |  |  |     console.log('runE2e: setup boilerplate'); | 
					
						
							|  |  |  |     const installPackagesCommand = `example-use-${argv.local ? 'local' : 'npm'}`; | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |     const addBoilerplateCommand = `boilerplate:add${argv.ivy ? ':ivy' : ''}`; | 
					
						
							|  |  |  |     shelljs.exec(`yarn ${installPackagesCommand}`, {cwd: AIO_PATH}); | 
					
						
							|  |  |  |     shelljs.exec(`yarn ${addBoilerplateCommand}`, {cwd: AIO_PATH}); | 
					
						
							| 
									
										
										
										
											2017-09-28 19:29:04 +03:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   const outputFile = path.join(AIO_PATH, './protractor-results.txt'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-06 14:48:37 +01:00
										 |  |  |   return Promise.resolve() | 
					
						
							| 
									
										
										
										
											2019-03-04 20:33:25 +01:00
										 |  |  |       .then(() => findAndRunE2eTests(argv.filter, outputFile, argv.shard, | 
					
						
							|  |  |  |           argv.cliSpecsConcurrency || DEFAULT_CLI_SPECS_CONCURRENCY)) | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |       .then((status) => { | 
					
						
							|  |  |  |         reportStatus(status, outputFile); | 
					
						
							|  |  |  |         if (status.failed.length > 0) { | 
					
						
							|  |  |  |           return Promise.reject('Some test suites failed'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       .catch(function(e) { | 
					
						
							|  |  |  |         console.log(e); | 
					
						
							|  |  |  |         process.exitCode = 1; | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Finds all of the *e2e-spec.tests under the examples folder along with the corresponding apps
 | 
					
						
							|  |  |  | // that they should run under. Then run each app/spec collection sequentially.
 | 
					
						
							| 
									
										
										
										
											2019-03-04 20:33:25 +01:00
										 |  |  | function findAndRunE2eTests(filter, outputFile, shard, cliSpecsConcurrency) { | 
					
						
							| 
									
										
										
										
											2018-08-03 22:38:03 +03:00
										 |  |  |   const shardParts = shard ? shard.split('/') : [0, 1]; | 
					
						
							| 
									
										
										
										
											2017-07-27 08:31:45 +01:00
										 |  |  |   const shardModulo = parseInt(shardParts[0], 10); | 
					
						
							|  |  |  |   const shardDivider = parseInt(shardParts[1], 10); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  |   // create an output file with header.
 | 
					
						
							|  |  |  |   const startTime = new Date().getTime(); | 
					
						
							|  |  |  |   let header = `Doc Sample Protractor Results on ${new Date().toLocaleString()}\n`; | 
					
						
							|  |  |  |   header += `  Filter: ${filter ? filter : 'All tests'}\n\n`; | 
					
						
							|  |  |  |   fs.writeFileSync(outputFile, header); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |   const status = {passed: [], failed: []}; | 
					
						
							| 
									
										
										
										
											2019-03-04 20:33:25 +01:00
										 |  |  |   const updateStatus = (specPath, passed) => { | 
					
						
							|  |  |  |     const arr = passed ? status.passed : status.failed; | 
					
						
							|  |  |  |     arr.push(specPath); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-22 21:31:15 +02:00
										 |  |  |   return getE2eSpecs(EXAMPLES_PATH, filter) | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |       .then(e2eSpecPaths => { | 
					
						
							|  |  |  |         console.log('All e2e specs:'); | 
					
						
							|  |  |  |         logSpecs(e2eSpecPaths); | 
					
						
							| 
									
										
										
										
											2017-08-22 21:31:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |         Object.keys(e2eSpecPaths).forEach(key => { | 
					
						
							|  |  |  |           const value = e2eSpecPaths[key]; | 
					
						
							|  |  |  |           e2eSpecPaths[key] = value.filter((p, index) => index % shardDivider === shardModulo); | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         console.log(`E2e specs for shard ${shardParts.join('/')}:`); | 
					
						
							|  |  |  |         logSpecs(e2eSpecPaths); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return e2eSpecPaths.systemjs | 
					
						
							|  |  |  |             .reduce( | 
					
						
							|  |  |  |                 (promise, specPath) => { | 
					
						
							|  |  |  |                   return promise.then(() => { | 
					
						
							|  |  |  |                     const examplePath = path.dirname(specPath); | 
					
						
							| 
									
										
										
										
											2019-03-04 20:33:25 +01:00
										 |  |  |                     return runE2eTestsSystemJS(examplePath, outputFile) | 
					
						
							|  |  |  |                       .then(passed => updateStatus(examplePath, passed)); | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |                   }); | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |                 Promise.resolve()) | 
					
						
							| 
									
										
										
										
											2019-03-04 20:33:25 +01:00
										 |  |  |             .then(async () => { | 
					
						
							|  |  |  |               const specQueue = [...e2eSpecPaths.cli]; | 
					
						
							|  |  |  |               // Determine free ports for the amount of pending CLI specs before starting
 | 
					
						
							|  |  |  |               // any tests. This is necessary because ports can stuck in the "TIME_WAIT"
 | 
					
						
							|  |  |  |               // state after others specs which used that port exited. This works around
 | 
					
						
							|  |  |  |               // this potential race condition which surfaces on Windows.
 | 
					
						
							|  |  |  |               const ports = await findFreePort(4000, 6000, '127.0.0.1', specQueue.length); | 
					
						
							|  |  |  |               // Enable buffering of the process output in case multiple CLI specs will
 | 
					
						
							|  |  |  |               // be executed concurrently. This means that we can can print out the full
 | 
					
						
							|  |  |  |               // output at once without interfering with other CLI specs printing as well.
 | 
					
						
							|  |  |  |               const bufferOutput = cliSpecsConcurrency > 1; | 
					
						
							|  |  |  |               while (specQueue.length) { | 
					
						
							|  |  |  |                 const chunk = specQueue.splice(0, cliSpecsConcurrency); | 
					
						
							|  |  |  |                 await Promise.all(chunk.map((testDir, index) => { | 
					
						
							|  |  |  |                   return runE2eTestsCLI(testDir, outputFile, bufferOutput, ports.pop()) | 
					
						
							|  |  |  |                     .then(passed => updateStatus(testDir, passed)); | 
					
						
							|  |  |  |                 })); | 
					
						
							|  |  |  |               } | 
					
						
							| 
									
										
										
										
											2017-08-22 21:31:15 +02:00
										 |  |  |             }); | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |       }) | 
					
						
							|  |  |  |       .then(() => { | 
					
						
							|  |  |  |         const stopTime = new Date().getTime(); | 
					
						
							|  |  |  |         status.elapsedTime = (stopTime - startTime) / 1000; | 
					
						
							|  |  |  |         return status; | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  |       }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Start the example in appDir; then run protractor with the specified
 | 
					
						
							|  |  |  | // fileName; then shut down the example.
 | 
					
						
							|  |  |  | // All protractor output is appended to the outputFile.
 | 
					
						
							| 
									
										
										
										
											2017-08-22 21:31:15 +02:00
										 |  |  | // SystemJS version
 | 
					
						
							|  |  |  | function runE2eTestsSystemJS(appDir, outputFile) { | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  |   const config = loadExampleConfig(appDir); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |   const appBuildSpawnInfo = spawnExt('yarn', [config.build], {cwd: appDir}); | 
					
						
							|  |  |  |   const appRunSpawnInfo = spawnExt('yarn', [config.run, '-s'], {cwd: appDir}, true); | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-22 21:31:15 +02:00
										 |  |  |   let run = runProtractorSystemJS(appBuildSpawnInfo.promise, appDir, appRunSpawnInfo, outputFile); | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (fs.existsSync(appDir + '/aot/index.html')) { | 
					
						
							|  |  |  |     run = run.then((ok) => ok && runProtractorAoT(appDir, outputFile)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return run; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-22 21:31:15 +02:00
										 |  |  | function runProtractorSystemJS(prepPromise, appDir, appRunSpawnInfo, outputFile) { | 
					
						
							|  |  |  |   const specFilename = path.resolve(`${appDir}/${SJS_SPEC_FILENAME}`); | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  |   return prepPromise | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |       .catch(function() { | 
					
						
							|  |  |  |         const emsg = `Application at ${appDir} failed to transpile.\n\n`; | 
					
						
							|  |  |  |         console.log(emsg); | 
					
						
							|  |  |  |         fs.appendFileSync(outputFile, emsg); | 
					
						
							|  |  |  |         return Promise.reject(emsg); | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       .then(function() { | 
					
						
							|  |  |  |         let transpileError = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Start protractor.
 | 
					
						
							|  |  |  |         console.log(`\n\n=========== Running aio example tests for: ${appDir}`); | 
					
						
							|  |  |  |         const spawnInfo = spawnExt( | 
					
						
							|  |  |  |             'yarn', | 
					
						
							|  |  |  |             [ | 
					
						
							|  |  |  |               'protractor', PROTRACTOR_CONFIG_FILENAME, `--specs=${specFilename}`, | 
					
						
							|  |  |  |               '--params.appDir=' + appDir, '--params.outputFile=' + outputFile | 
					
						
							|  |  |  |             ], | 
					
						
							|  |  |  |             {cwd: SHARED_PATH}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         spawnInfo.proc.stderr.on('data', function(data) { | 
					
						
							|  |  |  |           transpileError = transpileError || /npm ERR! Exit status 100/.test(data.toString()); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         return spawnInfo.promise.catch(function() { | 
					
						
							|  |  |  |           if (transpileError) { | 
					
						
							|  |  |  |             const emsg = `${specFilename} failed to transpile.\n\n`; | 
					
						
							|  |  |  |             console.log(emsg); | 
					
						
							|  |  |  |             fs.appendFileSync(outputFile, emsg); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           return Promise.reject(); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       .then( | 
					
						
							|  |  |  |           function() { return finish(appRunSpawnInfo.proc.pid, true); }, | 
					
						
							|  |  |  |           function() { return finish(appRunSpawnInfo.proc.pid, false); }); | 
					
						
							| 
									
										
										
										
											2017-08-22 21:31:15 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-22 21:31:15 +02:00
										 |  |  | function finish(spawnProcId, ok) { | 
					
						
							|  |  |  |   // Ugh... proc.kill does not work properly on windows with child processes.
 | 
					
						
							|  |  |  |   // appRun.proc.kill();
 | 
					
						
							|  |  |  |   treeKill(spawnProcId); | 
					
						
							|  |  |  |   return ok; | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Run e2e tests over the AOT build for projects that examples it.
 | 
					
						
							|  |  |  | function runProtractorAoT(appDir, outputFile) { | 
					
						
							|  |  |  |   fs.appendFileSync(outputFile, '++ AoT version ++\n'); | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |   const aotBuildSpawnInfo = spawnExt('yarn', ['build:aot'], {cwd: appDir}); | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  |   let promise = aotBuildSpawnInfo.promise; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const copyFileCmd = 'copy-dist-files.js'; | 
					
						
							|  |  |  |   if (fs.existsSync(appDir + '/' + copyFileCmd)) { | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |     promise = promise.then(() => spawnExt('node', [copyFileCmd], {cwd: appDir}).promise); | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |   const aotRunSpawnInfo = spawnExt('yarn', ['serve:aot'], {cwd: appDir}, true); | 
					
						
							| 
									
										
										
										
											2017-08-22 21:31:15 +02:00
										 |  |  |   return runProtractorSystemJS(promise, appDir, aotRunSpawnInfo, outputFile); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Start the example in appDir; then run protractor with the specified
 | 
					
						
							|  |  |  | // fileName; then shut down the example.
 | 
					
						
							|  |  |  | // All protractor output is appended to the outputFile.
 | 
					
						
							|  |  |  | // CLI version
 | 
					
						
							| 
									
										
										
										
											2019-03-04 20:33:25 +01:00
										 |  |  | function runE2eTestsCLI(appDir, outputFile, bufferOutput, port) { | 
					
						
							|  |  |  |   if (!bufferOutput) { | 
					
						
							|  |  |  |     console.log(`\n\n=========== Running aio example tests for: ${appDir}`); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-11 16:27:29 +02:00
										 |  |  |   // `--no-webdriver-update` is needed to preserve the ChromeDriver version already installed.
 | 
					
						
							| 
									
										
										
										
											2019-01-10 22:14:04 +02:00
										 |  |  |   const config = loadExampleConfig(appDir); | 
					
						
							| 
									
										
										
										
											2019-03-04 20:33:25 +01:00
										 |  |  |   const commands = config.e2e || [{ | 
					
						
							|  |  |  |     cmd: 'yarn', | 
					
						
							|  |  |  |     args: ['e2e', '--prod', '--no-webdriver-update', `--port=${port || DEFAULT_CLI_EXAMPLE_PORT}`] | 
					
						
							|  |  |  |   }]; | 
					
						
							|  |  |  |   let bufferedOutput = `\n\n============== AIO example output for: ${appDir}\n\n`; | 
					
						
							| 
									
										
										
										
											2019-01-10 22:14:04 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |   const e2eSpawnPromise = commands.reduce((prevSpawnPromise, {cmd, args}) => { | 
					
						
							| 
									
										
										
										
											2019-03-04 20:33:25 +01:00
										 |  |  |     // Replace the port placeholder with the specified port if present. Specs that
 | 
					
						
							|  |  |  |     // define their e2e test commands in the example config are able to use the
 | 
					
						
							|  |  |  |     // given available port. This ensures that the CLI tests can be run concurrently.
 | 
					
						
							|  |  |  |     args = args.map(a => a.replace('{PORT}', port || DEFAULT_CLI_EXAMPLE_PORT)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-10 22:14:04 +02:00
										 |  |  |     return prevSpawnPromise.then(() => { | 
					
						
							| 
									
										
										
										
											2019-03-04 20:33:25 +01:00
										 |  |  |       const currSpawn = spawnExt(cmd, args, {cwd: appDir}, false, | 
					
						
							|  |  |  |           bufferOutput ? msg => bufferedOutput += msg : undefined); | 
					
						
							| 
									
										
										
										
											2019-01-10 22:14:04 +02:00
										 |  |  |       return currSpawn.promise.then( | 
					
						
							|  |  |  |           () => Promise.resolve(finish(currSpawn.proc.pid, true)), | 
					
						
							|  |  |  |           () => Promise.reject(finish(currSpawn.proc.pid, false))); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }, Promise.resolve()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-04 20:33:25 +01:00
										 |  |  |   return e2eSpawnPromise.then(() => { | 
					
						
							|  |  |  |     fs.appendFileSync(outputFile, `Passed: ${appDir}\n\n`); | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  |   }, () => { | 
					
						
							|  |  |  |     fs.appendFileSync(outputFile, `Failed: ${appDir}\n\n`); | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   }).then(passed => { | 
					
						
							|  |  |  |     if (bufferOutput) { | 
					
						
							|  |  |  |       process.stdout.write(bufferedOutput); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return passed; | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Report final status.
 | 
					
						
							|  |  |  | function reportStatus(status, outputFile) { | 
					
						
							|  |  |  |   let log = ['']; | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   log.push('Suites ignored due to legacy guides:'); | 
					
						
							|  |  |  |   IGNORED_EXAMPLES.filter(example => !fixmeIvyExamples.find(ex => ex.startsWith(example))) | 
					
						
							|  |  |  |       .forEach(function(val) { log.push('  ' + val); }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (argv.ivy) { | 
					
						
							|  |  |  |     log.push(''); | 
					
						
							|  |  |  |     log.push('Suites ignored due to breakage with Ivy:'); | 
					
						
							|  |  |  |     fixmeIvyExamples.forEach(function(val) { log.push('  ' + val); }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   log.push(''); | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  |   log.push('Suites passed:'); | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |   status.passed.forEach(function(val) { log.push('  ' + val); }); | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (status.failed.length == 0) { | 
					
						
							|  |  |  |     log.push('All tests passed'); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     log.push('Suites failed:'); | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |     status.failed.forEach(function(val) { log.push('  ' + val); }); | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  |   } | 
					
						
							|  |  |  |   log.push('\nElapsed time: ' + status.elapsedTime + ' seconds'); | 
					
						
							|  |  |  |   log = log.join('\n'); | 
					
						
							|  |  |  |   console.log(log); | 
					
						
							|  |  |  |   fs.appendFileSync(outputFile, log); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Returns both a promise and the spawned process so that it can be killed if needed.
 | 
					
						
							| 
									
										
										
										
											2019-03-04 20:33:25 +01:00
										 |  |  | function spawnExt(command, args, options, ignoreClose = false, | 
					
						
							|  |  |  |                   printMessage = msg => process.stdout.write(msg)) { | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  |   let proc; | 
					
						
							|  |  |  |   const promise = new Promise((resolve, reject) => { | 
					
						
							| 
									
										
										
										
											2017-09-28 19:29:04 +03:00
										 |  |  |     let descr = command + ' ' + args.join(' '); | 
					
						
							| 
									
										
										
										
											2019-03-04 20:33:25 +01:00
										 |  |  |     let processOutput = ''; | 
					
						
							|  |  |  |     printMessage(`running: ${descr}\n`); | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  |     try { | 
					
						
							|  |  |  |       proc = xSpawn.spawn(command, args, options); | 
					
						
							|  |  |  |     } catch (e) { | 
					
						
							|  |  |  |       console.log(e); | 
					
						
							|  |  |  |       reject(e); | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |       return {proc: null, promise}; | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-03-04 20:33:25 +01:00
										 |  |  |     proc.stdout.on('data', printMessage); | 
					
						
							|  |  |  |     proc.stderr.on('data', printMessage); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |     proc.on('close', function(returnCode) { | 
					
						
							| 
									
										
										
										
											2019-03-04 20:33:25 +01:00
										 |  |  |       printMessage(`completed: ${descr}\n\n`); | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  |       // Many tasks (e.g., tsc) complete but are actually errors;
 | 
					
						
							|  |  |  |       // Confirm return code is zero.
 | 
					
						
							|  |  |  |       returnCode === 0 || ignoreClose ? resolve(0) : reject(returnCode); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |     proc.on('error', function(data) { | 
					
						
							| 
									
										
										
										
											2019-03-04 20:33:25 +01:00
										 |  |  |       printMessage(`completed with error: ${descr}\n\n`); | 
					
						
							|  |  |  |       printMessage(`${data.toString()}\n`); | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  |       reject(data); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |   return {proc, promise}; | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-22 21:31:15 +02:00
										 |  |  | function getE2eSpecs(basePath, filter) { | 
					
						
							|  |  |  |   let specs = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |   return getE2eSpecsFor(basePath, SJS_SPEC_FILENAME, filter) | 
					
						
							|  |  |  |       .then(sjsPaths => { specs.systemjs = sjsPaths; }) | 
					
						
							|  |  |  |       .then(() => { | 
					
						
							|  |  |  |         return getE2eSpecsFor(basePath, CLI_SPEC_FILENAME, filter).then(cliPaths => { | 
					
						
							|  |  |  |           return cliPaths.map(p => { return p.replace(`${CLI_SPEC_FILENAME}`, ''); }); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       .then(cliPaths => { specs.cli = cliPaths; }) | 
					
						
							|  |  |  |       .then(() => specs); | 
					
						
							| 
									
										
										
										
											2017-08-22 21:31:15 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  | // Find all e2e specs in a given example folder.
 | 
					
						
							| 
									
										
										
										
											2017-08-22 21:31:15 +02:00
										 |  |  | function getE2eSpecsFor(basePath, specFile, filter) { | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  |   // Only get spec file at the example root.
 | 
					
						
							| 
									
										
										
										
											2019-02-26 14:48:42 -08:00
										 |  |  |   // The formatter doesn't understand nested template string expressions (honestly, neither do I).
 | 
					
						
							|  |  |  |   // clang-format off
 | 
					
						
							| 
									
										
										
										
											2019-02-12 15:03:24 -05:00
										 |  |  |   const e2eSpecGlob = `${filter ? `*${filter}*` : '*'}/${specFile}`; | 
					
						
							| 
									
										
										
										
											2019-02-26 14:48:42 -08:00
										 |  |  |   // clang-format on
 | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |   return globby(e2eSpecGlob, {cwd: basePath, nodir: true}) | 
					
						
							|  |  |  |       .then( | 
					
						
							|  |  |  |           paths => paths.filter(file => !IGNORED_EXAMPLES.some(ignored => file.startsWith(ignored))) | 
					
						
							|  |  |  |                        .map(file => path.join(basePath, file))); | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-22 21:31:15 +02:00
										 |  |  | // Load configuration for an example. Used for SystemJS
 | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  | function loadExampleConfig(exampleFolder) { | 
					
						
							|  |  |  |   // Default config.
 | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |   let config = {build: 'build', run: 'serve:e2e'}; | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     const exampleConfig = fs.readJsonSync(`${exampleFolder}/${EXAMPLE_CONFIG_FILENAME}`); | 
					
						
							|  |  |  |     Object.assign(config, exampleConfig); | 
					
						
							| 
									
										
										
										
											2019-02-05 21:20:05 +00:00
										 |  |  |   } catch (e) { | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return config; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-03 22:38:03 +03:00
										 |  |  | // Log the specs (for debugging purposes).
 | 
					
						
							|  |  |  | // `e2eSpecPaths` is of type: `{[type: string]: string[]}`
 | 
					
						
							|  |  |  | // (where `type` is `systemjs`, `cli, etc.)
 | 
					
						
							|  |  |  | function logSpecs(e2eSpecPaths) { | 
					
						
							|  |  |  |   Object.keys(e2eSpecPaths).forEach(type => { | 
					
						
							|  |  |  |     const paths = e2eSpecPaths[type]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     console.log(`  ${type.toUpperCase()}:`); | 
					
						
							|  |  |  |     console.log(paths.map(p => `    ${p}`).join('\n')); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-13 22:35:13 +01:00
										 |  |  | runE2e(); |