UNPKG

voluptasmollitia

Version:
179 lines (167 loc) 5.5 kB
/** * @license * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const { spawn } = require('child-process-promise'); const { exists } = require('mz/fs'); const yargs = require('yargs'); const glob = require('glob'); const path = require('path'); const chalk = require('chalk'); // Check for 'configFiles' flag to run on specified karma.conf.js files instead // of on all files. const { configFiles } = yargs .option('configFiles', { default: [], describe: 'specify individual karma.conf.js files to run on', type: 'array' }) .version(false) .help().argv; // Get all karma.conf.js files that need to be run. // runNextTest() pulls filenames one-by-one from this queue. const testFiles = configFiles.length ? configFiles : glob .sync(`packages/*/karma.conf.js`) // Skip integration namespace tests, not very useful, introduce errors. .concat('integration/firestore/karma.conf.js') // Exclude database - currently too many failures. .filter(name => !name.includes('packages/database')); // Get CI build number or generate one if running locally. const buildNumber = process.env.TRAVIS_BUILD_NUMBER || process.env.GITHUB_RUN_ID || `local_${process.env.USER}_${new Date().getTime()}`; /** * Runs a set of SauceLabs browser tests based on this karma config file. * * @param {string} testFile Path to karma.conf.js file that defines this test * group. */ async function runTest(testFile) { if (!(await exists(testFile))) { console.error(chalk`{red ERROR: ${testFile} does not exist.}`); return 1; } // Run pretest if this dir has a package.json with a pretest script. const testFileDir = path.resolve(__dirname, '../') + '/' + path.dirname(testFile); const pkgPath = testFileDir + '/package.json'; let pkgName = testFile; if (await exists(pkgPath)) { const pkg = require(pkgPath); pkgName = pkg.name; if (pkg.scripts.pretest) { await spawn('yarn', ['--cwd', testFileDir, 'pretest'], { stdio: 'inherit' }); } } if (testFile.includes('integration/firestore')) { console.log( chalk`{blue Generating memory-only build for integration/firestore.}` ); await spawn('yarn', ['--cwd', 'integration/firestore', 'build:memory'], { stdio: 'inherit' }); console.log( chalk`{blue Running tests on memory-only build for integration/firestore.}` ); const exitCode1 = await runKarma(testFile, `${pkgName}-memory`); console.log( chalk`{blue Generating persistence build for integration/firestore.}` ); await spawn( 'yarn', ['--cwd', 'integration/firestore', 'build:persistence'], { stdio: 'inherit' } ); console.log( chalk`{blue Running tests on persistence build for integration/firestore.}` ); const exitCode2 = await runKarma(testFile, `${pkgName}-persistence`); return Math.max(exitCode1, exitCode2); } else { return runKarma(testFile, pkgName); } } /** * Runs the karma test command for one package. * * @param {string} testFile - path to karma.conf.js file * @param {string} testTag - package label for messages (usually package name) */ async function runKarma(testFile, testTag) { const karmaArgs = [ 'karma', 'start', 'config/karma.saucelabs.js', '--single-run', '--testConfigFile', testFile, '--buildNumber', buildNumber ]; const promise = spawn('npx', karmaArgs, { stdio: 'inherit' }); const childProcess = promise.childProcess; let exitCode = 0; // Capture exit code of this single package test run childProcess.on('exit', code => { exitCode = code; }); return promise .then(() => { console.log(chalk`{green [${testTag}] ******* DONE *******}`); return exitCode; }) .catch(err => { console.error(chalk`{red [${testTag}] ERROR: ${err.message}}`); return exitCode; }); } /** * Runs next file in testFiles queue as long as there are files in the queue. * * @param {number} maxExitCode Current highest exit code from all processes * run so far. When main process is complete, it will exit with highest code * of all child processes. This allows any failing test to result in a CI * build failure for the whole Saucelabs run. */ async function runNextTest(maxExitCode = 0, results = {}) { // When test queue is empty, exit with code 0 if no tests failed or // 1 if any tests failed. if (!testFiles.length) { for (const fileName of Object.keys(results)) { if (results[fileName] > 0) { console.log(`FAILED: ${fileName}`); } } process.exit(maxExitCode); } const nextFile = testFiles.shift(); let exitCode; try { exitCode = await runTest(nextFile); } catch (e) { console.error(chalk`{red [${nextFile}] ERROR: ${e.message}}`); exitCode = 1; } runNextTest(Math.max(exitCode, maxExitCode), { ...results, [nextFile]: exitCode }); } runNextTest();