123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- const events_1 = require("events");
- const q = require("q");
- const selenium_webdriver_1 = require("selenium-webdriver");
- const util = require("util");
- const browser_1 = require("./browser");
- const driverProviders_1 = require("./driverProviders");
- const logger_1 = require("./logger");
- const plugins_1 = require("./plugins");
- const ptor_1 = require("./ptor");
- const helper = require("./util");
- let logger = new logger_1.Logger('runner');
- /*
- * Runner is responsible for starting the execution of a test run and triggering
- * setup, teardown, managing config, etc through its various dependencies.
- *
- * The Protractor Runner is a node EventEmitter with the following events:
- * - testPass
- * - testFail
- * - testsDone
- *
- * @param {Object} config
- * @constructor
- */
- class Runner extends events_1.EventEmitter {
- constructor(config) {
- super();
- /**
- * Responsible for cleaning up test run and exiting the process.
- * @private
- * @param {int} Standard unix exit code
- */
- this.exit_ = function (exitCode) {
- return helper.runFilenameOrFn_(this.config_.configDir, this.config_.onCleanUp, [exitCode])
- .then((returned) => {
- if (typeof returned === 'number') {
- return returned;
- }
- else {
- return exitCode;
- }
- });
- };
- this.config_ = config;
- if (config.v8Debug) {
- // Call this private function instead of sending SIGUSR1 because Windows.
- process['_debugProcess'](process.pid);
- }
- if (config.nodeDebug) {
- process['_debugProcess'](process.pid);
- let flow = selenium_webdriver_1.promise.controlFlow();
- this.ready_ = flow.execute(() => {
- let nodedebug = require('child_process').fork('debug', ['localhost:5858']);
- process.on('exit', function () {
- nodedebug.kill('SIGTERM');
- });
- nodedebug.on('exit', function () {
- process.exit(1);
- });
- }, 'start the node debugger').then(() => {
- return flow.timeout(1000, 'waiting for debugger to attach');
- });
- }
- if (config.capabilities && config.capabilities.seleniumAddress) {
- config.seleniumAddress = config.capabilities.seleniumAddress;
- }
- this.loadDriverProvider_(config);
- this.setTestPreparer(config.onPrepare);
- }
- /**
- * Registrar for testPreparers - executed right before tests run.
- * @public
- * @param {string/Fn} filenameOrFn
- */
- setTestPreparer(filenameOrFn) {
- this.preparer_ = filenameOrFn;
- }
- /**
- * Executor of testPreparer
- * @public
- * @param {string[]=} An optional list of command line arguments the framework will accept.
- * @return {q.Promise} A promise that will resolve when the test preparers
- * are finished.
- */
- runTestPreparer(extraFlags) {
- let unknownFlags = this.config_.unknownFlags_ || [];
- if (extraFlags) {
- unknownFlags = unknownFlags.filter((f) => extraFlags.indexOf(f) === -1);
- }
- if (unknownFlags.length > 0 && !this.config_.disableChecks) {
- // TODO: Make this throw a ConfigError in Protractor 6.
- logger.warn('Ignoring unknown extra flags: ' + unknownFlags.join(', ') + '. This will be' +
- ' an error in future versions, please use --disableChecks flag to disable the ' +
- ' Protractor CLI flag checks. ');
- }
- return this.plugins_.onPrepare().then(() => {
- return helper.runFilenameOrFn_(this.config_.configDir, this.preparer_);
- });
- }
- /**
- * Called after each test finishes.
- *
- * Responsible for `restartBrowserBetweenTests`
- *
- * @public
- * @return {q.Promise} A promise that will resolve when the work here is done
- */
- afterEach() {
- let ret;
- this.frameworkUsesAfterEach = true;
- if (this.config_.restartBrowserBetweenTests) {
- this.restartPromise = this.restartPromise || q(ptor_1.protractor.browser.restart());
- ret = this.restartPromise;
- this.restartPromise = undefined;
- }
- return ret || q();
- }
- /**
- * Grab driver provider based on type
- * @private
- *
- * Priority
- * 1) if directConnect is true, use that
- * 2) if seleniumAddress is given, use that
- * 3) if a Sauce Labs account is given, use that
- * 4) if a seleniumServerJar is specified, use that
- * 5) try to find the seleniumServerJar in protractor/selenium
- */
- loadDriverProvider_(config) {
- this.config_ = config;
- this.driverprovider_ = driverProviders_1.buildDriverProvider(this.config_);
- }
- /**
- * Getter for the Runner config object
- * @public
- * @return {Object} config
- */
- getConfig() {
- return this.config_;
- }
- /**
- * Get the control flow used by this runner.
- * @return {Object} WebDriver control flow.
- */
- controlFlow() {
- return selenium_webdriver_1.promise.controlFlow();
- }
- /**
- * Sets up convenience globals for test specs
- * @private
- */
- setupGlobals_(browser_) {
- // Keep $, $$, element, and by/By under the global protractor namespace
- ptor_1.protractor.browser = browser_;
- ptor_1.protractor.$ = browser_.$;
- ptor_1.protractor.$$ = browser_.$$;
- ptor_1.protractor.element = browser_.element;
- ptor_1.protractor.by = ptor_1.protractor.By = browser_1.ProtractorBrowser.By;
- ptor_1.protractor.ExpectedConditions = browser_.ExpectedConditions;
- if (!this.config_.noGlobals) {
- // Export protractor to the global namespace to be used in tests.
- global.browser = browser_;
- global.$ = browser_.$;
- global.$$ = browser_.$$;
- global.element = browser_.element;
- global.by = global.By = ptor_1.protractor.By;
- global.ExpectedConditions = ptor_1.protractor.ExpectedConditions;
- }
- global.protractor = ptor_1.protractor;
- if (!this.config_.skipSourceMapSupport) {
- // Enable sourcemap support for stack traces.
- require('source-map-support').install();
- }
- // Required by dart2js machinery.
- // https://code.google.com/p/dart/source/browse/branches/bleeding_edge/dart/sdk/lib/js/dart2js/js_dart2js.dart?spec=svn32943&r=32943#487
- global.DartObject = function (o) {
- this.o = o;
- };
- }
- /**
- * Create a new driver from a driverProvider. Then set up a
- * new protractor instance using this driver.
- * This is used to set up the initial protractor instances and any
- * future ones.
- *
- * @param {Plugin} plugins The plugin functions
- * @param {ProtractorBrowser=} parentBrowser The browser which spawned this one
- *
- * @return {Protractor} a protractor instance.
- * @public
- */
- createBrowser(plugins, parentBrowser) {
- let config = this.config_;
- let driver = this.driverprovider_.getNewDriver();
- let blockingProxyUrl;
- if (config.useBlockingProxy) {
- blockingProxyUrl = this.driverprovider_.getBPUrl();
- }
- let initProperties = {
- baseUrl: config.baseUrl,
- rootElement: config.rootElement,
- untrackOutstandingTimeouts: config.untrackOutstandingTimeouts,
- params: config.params,
- getPageTimeout: config.getPageTimeout,
- allScriptsTimeout: config.allScriptsTimeout,
- debuggerServerPort: config.debuggerServerPort,
- ng12Hybrid: config.ng12Hybrid,
- waitForAngularEnabled: true
- };
- if (parentBrowser) {
- initProperties.baseUrl = parentBrowser.baseUrl;
- initProperties.rootElement = parentBrowser.angularAppRoot();
- initProperties.untrackOutstandingTimeouts = !parentBrowser.trackOutstandingTimeouts_;
- initProperties.params = parentBrowser.params;
- initProperties.getPageTimeout = parentBrowser.getPageTimeout;
- initProperties.allScriptsTimeout = parentBrowser.allScriptsTimeout;
- initProperties.debuggerServerPort = parentBrowser.debuggerServerPort;
- initProperties.ng12Hybrid = parentBrowser.ng12Hybrid;
- initProperties.waitForAngularEnabled = parentBrowser.waitForAngularEnabled();
- }
- let browser_ = new browser_1.ProtractorBrowser(driver, initProperties.baseUrl, initProperties.rootElement, initProperties.untrackOutstandingTimeouts, blockingProxyUrl);
- browser_.params = initProperties.params;
- browser_.plugins_ = plugins || new plugins_1.Plugins({});
- if (initProperties.getPageTimeout) {
- browser_.getPageTimeout = initProperties.getPageTimeout;
- }
- if (initProperties.allScriptsTimeout) {
- browser_.allScriptsTimeout = initProperties.allScriptsTimeout;
- }
- if (initProperties.debuggerServerPort) {
- browser_.debuggerServerPort = initProperties.debuggerServerPort;
- }
- if (initProperties.ng12Hybrid) {
- browser_.ng12Hybrid = initProperties.ng12Hybrid;
- }
- browser_.ready =
- browser_.ready
- .then(() => {
- return browser_.waitForAngularEnabled(initProperties.waitForAngularEnabled);
- })
- .then(() => {
- return driver.manage().timeouts().setScriptTimeout(initProperties.allScriptsTimeout || 0);
- })
- .then(() => {
- return browser_;
- });
- browser_.getProcessedConfig = () => {
- return selenium_webdriver_1.promise.when(config);
- };
- browser_.forkNewDriverInstance =
- (useSameUrl, copyMockModules, copyConfigUpdates = true) => {
- let newBrowser = this.createBrowser(plugins);
- if (copyMockModules) {
- newBrowser.mockModules_ = browser_.mockModules_;
- }
- if (useSameUrl) {
- newBrowser.ready = newBrowser.ready
- .then(() => {
- return browser_.driver.getCurrentUrl();
- })
- .then((url) => {
- return newBrowser.get(url);
- })
- .then(() => {
- return newBrowser;
- });
- }
- return newBrowser;
- };
- let replaceBrowser = () => {
- let newBrowser = browser_.forkNewDriverInstance(false, true);
- if (browser_ === ptor_1.protractor.browser) {
- this.setupGlobals_(newBrowser);
- }
- return newBrowser;
- };
- browser_.restart = () => {
- // Note: because tests are not paused at this point, any async
- // calls here are not guaranteed to complete before the tests resume.
- // Seperate solutions depending on if the control flow is enabled (see lib/browser.ts)
- if (browser_.controlFlowIsEnabled()) {
- return browser_.restartSync().ready;
- }
- else {
- return this.driverprovider_.quitDriver(browser_.driver)
- .then(replaceBrowser)
- .then(newBrowser => newBrowser.ready);
- }
- };
- browser_.restartSync = () => {
- if (!browser_.controlFlowIsEnabled()) {
- throw TypeError('Unable to use `browser.restartSync()` when the control flow is disabled');
- }
- this.driverprovider_.quitDriver(browser_.driver);
- return replaceBrowser();
- };
- return browser_;
- }
- /**
- * Final cleanup on exiting the runner.
- *
- * @return {q.Promise} A promise which resolves on finish.
- * @private
- */
- shutdown_() {
- return driverProviders_1.DriverProvider.quitDrivers(this.driverprovider_, this.driverprovider_.getExistingDrivers());
- }
- /**
- * The primary workhorse interface. Kicks off the test running process.
- *
- * @return {q.Promise} A promise which resolves to the exit code of the tests.
- * @public
- */
- run() {
- let testPassed;
- let plugins = this.plugins_ = new plugins_1.Plugins(this.config_);
- let pluginPostTestPromises;
- let browser_;
- let results;
- if (this.config_.framework !== 'explorer' && !this.config_.specs.length) {
- throw new Error('Spec patterns did not match any files.');
- }
- if (this.config_.SELENIUM_PROMISE_MANAGER != null) {
- selenium_webdriver_1.promise.USE_PROMISE_MANAGER = this.config_.SELENIUM_PROMISE_MANAGER;
- }
- if (this.config_.webDriverLogDir || this.config_.highlightDelay) {
- this.config_.useBlockingProxy = true;
- }
- // 0) Wait for debugger
- return q(this.ready_)
- .then(() => {
- // 1) Setup environment
- // noinspection JSValidateTypes
- return this.driverprovider_.setupEnv();
- })
- .then(() => {
- // 2) Create a browser and setup globals
- browser_ = this.createBrowser(plugins);
- this.setupGlobals_(browser_);
- return browser_.ready.then(browser_.getSession)
- .then((session) => {
- logger.debug('WebDriver session successfully started with capabilities ' +
- util.inspect(session.getCapabilities()));
- }, (err) => {
- logger.error('Unable to start a WebDriver session.');
- throw err;
- });
- // 3) Setup plugins
- })
- .then(() => {
- return plugins.setup();
- // 4) Execute test cases
- })
- .then(() => {
- // Do the framework setup here so that jasmine and mocha globals are
- // available to the onPrepare function.
- let frameworkPath = '';
- if (this.config_.framework === 'jasmine' || this.config_.framework === 'jasmine2') {
- frameworkPath = './frameworks/jasmine.js';
- }
- else if (this.config_.framework === 'mocha') {
- frameworkPath = './frameworks/mocha.js';
- }
- else if (this.config_.framework === 'debugprint') {
- // Private framework. Do not use.
- frameworkPath = './frameworks/debugprint.js';
- }
- else if (this.config_.framework === 'explorer') {
- // Private framework. Do not use.
- frameworkPath = './frameworks/explorer.js';
- }
- else if (this.config_.framework === 'custom') {
- if (!this.config_.frameworkPath) {
- throw new Error('When config.framework is custom, ' +
- 'config.frameworkPath is required.');
- }
- frameworkPath = this.config_.frameworkPath;
- }
- else {
- throw new Error('config.framework (' + this.config_.framework + ') is not a valid framework.');
- }
- if (this.config_.restartBrowserBetweenTests) {
- // TODO(sjelin): replace with warnings once `afterEach` support is required
- let restartDriver = () => {
- if (!this.frameworkUsesAfterEach) {
- this.restartPromise = q(browser_.restart());
- }
- };
- this.on('testPass', restartDriver);
- this.on('testFail', restartDriver);
- }
- // We need to save these promises to make sure they're run, but we
- // don't
- // want to delay starting the next test (because we can't, it's just
- // an event emitter).
- pluginPostTestPromises = [];
- this.on('testPass', (testInfo) => {
- pluginPostTestPromises.push(plugins.postTest(true, testInfo));
- });
- this.on('testFail', (testInfo) => {
- pluginPostTestPromises.push(plugins.postTest(false, testInfo));
- });
- logger.debug('Running with spec files ' + this.config_.specs);
- return require(frameworkPath).run(this, this.config_.specs);
- // 5) Wait for postTest plugins to finish
- })
- .then((testResults) => {
- results = testResults;
- return q.all(pluginPostTestPromises);
- // 6) Teardown plugins
- })
- .then(() => {
- return plugins.teardown();
- // 7) Teardown
- })
- .then(() => {
- results = helper.joinTestLogs(results, plugins.getResults());
- this.emit('testsDone', results);
- testPassed = results.failedCount === 0;
- if (this.driverprovider_.updateJob) {
- return this.driverprovider_.updateJob({ 'passed': testPassed }).then(() => {
- return this.driverprovider_.teardownEnv();
- });
- }
- else {
- return this.driverprovider_.teardownEnv();
- }
- // 8) Let plugins do final cleanup
- })
- .then(() => {
- return plugins.postResults();
- // 9) Exit process
- })
- .then(() => {
- let exitCode = testPassed ? 0 : 1;
- return this.exit_(exitCode);
- })
- .fin(() => {
- return this.shutdown_();
- });
- }
- }
- exports.Runner = Runner;
- //# sourceMappingURL=runner.js.map
|