123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- /**
- * The launcher is responsible for parsing the capabilities from the
- * input configuration and launching test runners.
- */
- const fs = require("fs");
- const q = require("q");
- const configParser_1 = require("./configParser");
- const exitCodes_1 = require("./exitCodes");
- const logger_1 = require("./logger");
- const runner_1 = require("./runner");
- const taskRunner_1 = require("./taskRunner");
- const taskScheduler_1 = require("./taskScheduler");
- const helper = require("./util");
- let logger = new logger_1.Logger('launcher');
- let RUNNERS_FAILED_EXIT_CODE = 100;
- /**
- * Keeps track of a list of task results. Provides method to add a new
- * result, aggregate the results into a summary, count failures,
- * and save results into a JSON file.
- */
- class TaskResults {
- constructor() {
- // TODO: set a type for result
- this.results_ = [];
- }
- add(result) {
- this.results_.push(result);
- }
- totalSpecFailures() {
- return this.results_.reduce((specFailures, result) => {
- return specFailures + result.failedCount;
- }, 0);
- }
- totalProcessFailures() {
- return this.results_.reduce((processFailures, result) => {
- return !result.failedCount && result.exitCode !== 0 ? processFailures + 1 : processFailures;
- }, 0);
- }
- saveResults(filepath) {
- let jsonOutput = this.results_.reduce((jsonOutput, result) => {
- return jsonOutput.concat(result.specResults);
- }, []);
- let json = JSON.stringify(jsonOutput, null, ' ');
- fs.writeFileSync(filepath, json);
- }
- reportSummary() {
- let specFailures = this.totalSpecFailures();
- let processFailures = this.totalProcessFailures();
- this.results_.forEach((result) => {
- let capabilities = result.capabilities;
- let shortName = (capabilities.browserName) ? capabilities.browserName : '';
- shortName = (capabilities.logName) ?
- capabilities.logName :
- (capabilities.browserName) ? capabilities.browserName : '';
- shortName += (capabilities.version) ? capabilities.version : '';
- shortName += (capabilities.logName && capabilities.count < 2) ? '' : ' #' + result.taskId;
- if (result.failedCount) {
- logger.info(shortName + ' failed ' + result.failedCount + ' test(s)');
- }
- else if (result.exitCode !== 0) {
- logger.info(shortName + ' failed with exit code: ' + result.exitCode);
- }
- else {
- logger.info(shortName + ' passed');
- }
- });
- if (specFailures && processFailures) {
- logger.info('overall: ' + specFailures + ' failed spec(s) and ' + processFailures +
- ' process(es) failed to complete');
- }
- else if (specFailures) {
- logger.info('overall: ' + specFailures + ' failed spec(s)');
- }
- else if (processFailures) {
- logger.info('overall: ' + processFailures + ' process(es) failed to complete');
- }
- }
- }
- let taskResults_ = new TaskResults();
- /**
- * Initialize and run the tests.
- * Exits with 1 on test failure, and RUNNERS_FAILED_EXIT_CODE on unexpected
- * failures.
- *
- * @param {string=} configFile
- * @param {Object=} additionalConfig
- */
- let initFn = function (configFile, additionalConfig) {
- let configParser = new configParser_1.ConfigParser();
- if (configFile) {
- configParser.addFileConfig(configFile);
- }
- if (additionalConfig) {
- configParser.addConfig(additionalConfig);
- }
- let config = configParser.getConfig();
- logger_1.Logger.set(config);
- logger.debug('Running with --troubleshoot');
- logger.debug('Protractor version: ' + require('../package.json').version);
- logger.debug('Your base url for tests is ' + config.baseUrl);
- // Run beforeLaunch
- helper.runFilenameOrFn_(config.configDir, config.beforeLaunch)
- .then(() => {
- return q
- .Promise((resolve, reject) => {
- // 1) If getMultiCapabilities is set, resolve that as
- // `multiCapabilities`.
- if (config.getMultiCapabilities &&
- typeof config.getMultiCapabilities === 'function') {
- if (config.multiCapabilities.length || config.capabilities) {
- logger.warn('getMultiCapabilities() will override both capabilities ' +
- 'and multiCapabilities');
- }
- // If getMultiCapabilities is defined and a function, use this.
- q(config.getMultiCapabilities())
- .then((multiCapabilities) => {
- config.multiCapabilities = multiCapabilities;
- config.capabilities = null;
- })
- .then(() => {
- resolve();
- })
- .catch(err => {
- reject(err);
- });
- }
- else {
- resolve();
- }
- })
- .then(() => {
- // 2) Set `multicapabilities` using `capabilities`,
- // `multicapabilities`,
- // or default
- if (config.capabilities) {
- if (config.multiCapabilities.length) {
- logger.warn('You have specified both capabilities and ' +
- 'multiCapabilities. This will result in capabilities being ' +
- 'ignored');
- }
- else {
- // Use capabilities if multiCapabilities is empty.
- config.multiCapabilities = [config.capabilities];
- }
- }
- else if (!config.multiCapabilities.length) {
- // Default to chrome if no capabilities given
- config.multiCapabilities = [{ browserName: 'chrome' }];
- }
- });
- })
- .then(() => {
- // 3) If we're in `elementExplorer` mode, run only that.
- if (config.elementExplorer || config.framework === 'explorer') {
- if (config.multiCapabilities.length != 1) {
- throw new Error('Must specify only 1 browser while using elementExplorer');
- }
- else {
- config.capabilities = config.multiCapabilities[0];
- }
- config.framework = 'explorer';
- let runner = new runner_1.Runner(config);
- return runner.run().then((exitCode) => {
- process.exit(exitCode);
- }, (err) => {
- logger.error(err);
- process.exit(1);
- });
- }
- })
- .then(() => {
- // 4) Run tests.
- let scheduler = new taskScheduler_1.TaskScheduler(config);
- process.on('uncaughtException', (exc) => {
- let e = (exc instanceof Error) ? exc : new Error(exc);
- if (config.ignoreUncaughtExceptions) {
- // This can be a sign of a bug in the test framework, that it may
- // not be handling WebDriver errors properly. However, we don't
- // want these errors to prevent running the tests.
- logger.warn('Ignoring uncaught error ' + exc);
- return;
- }
- let errorCode = exitCodes_1.ErrorHandler.parseError(e);
- if (errorCode) {
- let protractorError = e;
- exitCodes_1.ProtractorError.log(logger, errorCode, protractorError.message, protractorError.stack);
- process.exit(errorCode);
- }
- else {
- logger.error(e.message);
- logger.error(e.stack);
- process.exit(exitCodes_1.ProtractorError.CODE);
- }
- });
- process.on('exit', (code) => {
- if (code) {
- logger.error('Process exited with error code ' + code);
- }
- else if (scheduler.numTasksOutstanding() > 0) {
- logger.error('BUG: launcher exited with ' + scheduler.numTasksOutstanding() +
- ' tasks remaining');
- process.exit(RUNNERS_FAILED_EXIT_CODE);
- }
- });
- // Run afterlaunch and exit
- let cleanUpAndExit = (exitCode) => {
- return helper.runFilenameOrFn_(config.configDir, config.afterLaunch, [exitCode])
- .then((returned) => {
- if (typeof returned === 'number') {
- process.exit(returned);
- }
- else {
- process.exit(exitCode);
- }
- }, (err) => {
- logger.error('Error:', err);
- process.exit(1);
- });
- };
- let totalTasks = scheduler.numTasksOutstanding();
- let forkProcess = false;
- if (totalTasks > 1) {
- forkProcess = true;
- if (config.debug) {
- throw new exitCodes_1.ConfigError(logger, 'Cannot run in debug mode with multiCapabilities, count > 1, or sharding');
- }
- }
- let deferred = q.defer(); // Resolved when all tasks are completed
- let createNextTaskRunner = () => {
- let task = scheduler.nextTask();
- if (task) {
- let taskRunner = new taskRunner_1.TaskRunner(configFile, additionalConfig, task, forkProcess);
- taskRunner.run()
- .then((result) => {
- if (result.exitCode && !result.failedCount) {
- logger.error('Runner process exited unexpectedly with error code: ' + result.exitCode);
- }
- taskResults_.add(result);
- task.done();
- createNextTaskRunner();
- // If all tasks are finished
- if (scheduler.numTasksOutstanding() === 0) {
- deferred.resolve();
- }
- logger.info(scheduler.countActiveTasks() + ' instance(s) of WebDriver still running');
- })
- .catch((err) => {
- logger.error('Error:', err.stack || err.message || err);
- cleanUpAndExit(RUNNERS_FAILED_EXIT_CODE);
- });
- }
- };
- // Start `scheduler.maxConcurrentTasks()` workers for handling tasks in
- // the beginning. As a worker finishes a task, it will pick up the next
- // task
- // from the scheduler's queue until all tasks are gone.
- for (let i = 0; i < scheduler.maxConcurrentTasks(); ++i) {
- createNextTaskRunner();
- }
- logger.info('Running ' + scheduler.countActiveTasks() + ' instances of WebDriver');
- // By now all runners have completed.
- deferred.promise
- .then(function () {
- // Save results if desired
- if (config.resultJsonOutputFile) {
- taskResults_.saveResults(config.resultJsonOutputFile);
- }
- taskResults_.reportSummary();
- let exitCode = 0;
- if (taskResults_.totalProcessFailures() > 0) {
- exitCode = RUNNERS_FAILED_EXIT_CODE;
- }
- else if (taskResults_.totalSpecFailures() > 0) {
- exitCode = 1;
- }
- return cleanUpAndExit(exitCode);
- })
- .done();
- })
- .done();
- };
- exports.init = initFn;
- //# sourceMappingURL=launcher.js.map
|