"use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var selenium_webdriver_1 = require("selenium-webdriver"); var url = require("url"); var debugger_1 = require("./debugger"); var element_1 = require("./element"); var expectedConditions_1 = require("./expectedConditions"); var locators_1 = require("./locators"); var logger_1 = require("./logger"); var plugins_1 = require("./plugins"); var clientSideScripts = require('./clientsidescripts'); // TODO: fix the typings for selenium-webdriver/lib/command var Command = require('selenium-webdriver/lib/command').Command; var CommandName = require('selenium-webdriver/lib/command').Name; // jshint browser: true var DEFER_LABEL = 'NG_DEFER_BOOTSTRAP!'; var DEFAULT_RESET_URL = 'data:text/html,'; var DEFAULT_GET_PAGE_TIMEOUT = 10000; var logger = new logger_1.Logger('protractor'); // TODO(cnishina): either remove for loop entirely since this does not export anything // the user might need since everything is composed (with caveat that this could be a // potential breaking change) or export the types with `export * from 'selenium-webdriver'`; /* * Mix in other webdriver functionality to be accessible via protractor. */ for (var foo in require('selenium-webdriver')) { exports[foo] = require('selenium-webdriver')[foo]; } // Explicitly define webdriver.WebDriver // TODO: extend WebDriver from selenium-webdriver typings var Webdriver = (function () { function Webdriver() { } return Webdriver; }()); exports.Webdriver = Webdriver; /** * Mix a function from one object onto another. The function will still be * called in the context of the original object. Any arguments of type * `ElementFinder` will be unwrapped to their underlying `WebElement` instance * * @private * @param {Object} to * @param {Object} from * @param {string} fnName * @param {function=} setupFn */ function ptorMixin(to, from, fnName, setupFn) { to[fnName] = function () { for (var i = 0; i < arguments.length; i++) { if (arguments[i] instanceof element_1.ElementFinder) { arguments[i] = arguments[i].getWebElement(); } } if (setupFn) { setupFn(); } return from[fnName].apply(from, arguments); }; } ; /** * Build the helper 'element' function for a given instance of Browser. * * @private * @param {Browser} browser A browser instance. * @returns {function(webdriver.Locator): ElementFinder} */ function buildElementHelper(browser) { var element = (function (locator) { return new element_1.ElementArrayFinder(browser).all(locator).toElementFinder_(); }); element.all = function (locator) { return new element_1.ElementArrayFinder(browser).all(locator); }; return element; } ; /** * @alias browser * @constructor * @extends {webdriver.WebDriver} * @param {webdriver.WebDriver} webdriver * @param {string=} opt_baseUrl A base URL to run get requests against. * @param {string=} opt_rootElement Selector element that has an ng-app in * scope. * @param {boolean=} opt_untrackOutstandingTimeouts Whether Protractor should * stop tracking outstanding $timeouts. */ var ProtractorBrowser = (function (_super) { __extends(ProtractorBrowser, _super); function ProtractorBrowser(webdriverInstance, opt_baseUrl, opt_rootElement, opt_untrackOutstandingTimeouts) { var _this = _super.call(this) || this; // These functions should delegate to the webdriver instance, but should // wait for Angular to sync up before performing the action. This does not // include functions which are overridden by protractor below. var methodsToSync = ['getCurrentUrl', 'getPageSource', 'getTitle']; // Mix all other driver functionality into Protractor. Object.getOwnPropertyNames(selenium_webdriver_1.WebDriver.prototype).forEach(function (method) { if (!_this[method] && typeof webdriverInstance[method] === 'function') { if (methodsToSync.indexOf(method) !== -1) { ptorMixin(_this, webdriverInstance, method, _this.waitForAngular.bind(_this)); } else { ptorMixin(_this, webdriverInstance, method); } } }); _this.driver = webdriverInstance; _this.element = buildElementHelper(_this); _this.$ = element_1.build$(_this.element, selenium_webdriver_1.By); _this.$$ = element_1.build$$(_this.element, selenium_webdriver_1.By); _this.baseUrl = opt_baseUrl || ''; _this.rootEl = opt_rootElement || 'body'; _this.ignoreSynchronization = false; _this.getPageTimeout = DEFAULT_GET_PAGE_TIMEOUT; _this.params = {}; _this.ready = null; _this.plugins_ = new plugins_1.Plugins({}); _this.resetUrl = DEFAULT_RESET_URL; _this.ng12Hybrid = false; _this.debugHelper = new debugger_1.DebugHelper(_this); _this.driver.getCapabilities().then(function (caps) { // Internet Explorer does not accept data URLs, which are the default // reset URL for Protractor. // Safari accepts data urls, but SafariDriver fails after one is used. // PhantomJS produces a "Detected a page unload event" if we use data urls var browserName = caps.get('browserName'); if (browserName === 'internet explorer' || browserName === 'safari' || browserName === 'phantomjs' || browserName === 'MicrosoftEdge') { _this.resetUrl = 'about:blank'; } }); _this.trackOutstandingTimeouts_ = !opt_untrackOutstandingTimeouts; _this.mockModules_ = []; _this.addBaseMockModules_(); // set up expected conditions _this.ExpectedConditions = new expectedConditions_1.ProtractorExpectedConditions(_this); return _this; } /** * Get the processed configuration object that is currently being run. This * will contain the specs and capabilities properties of the current runner * instance. * * Set by the runner. * * @returns {webdriver.promise.Promise} A promise which resolves to the * capabilities object. */ ProtractorBrowser.prototype.getProcessedConfig = function () { return null; }; /** * Fork another instance of browser for use in interactive tests. * * Set by the runner. * * @param {boolean} opt_useSameUrl Whether to navigate to current url on * creation * @param {boolean} opt_copyMockModules Whether to apply same mock modules on * creation * @returns {Browser} A browser instance. */ ProtractorBrowser.prototype.forkNewDriverInstance = function (opt_useSameUrl, opt_copyMockModules) { return null; }; /** * Restart the browser instance. * * Set by the runner. */ ProtractorBrowser.prototype.restart = function () { return; }; /** * Instead of using a single root element, search through all angular apps * available on the page when finding elements or waiting for stability. * Only compatible with Angular2. */ ProtractorBrowser.prototype.useAllAngular2AppRoots = function () { // The empty string is an invalid css selector, so we use it to easily // signal to scripts to not find a root element. this.rootEl = ''; }; /** * The same as {@code webdriver.WebDriver.prototype.executeScript}, * but with a customized description for debugging. * * @private * @param {!(string|Function)} script The script to execute. * @param {string} description A description of the command for debugging. * @param {...*} var_args The arguments to pass to the script. * @returns {!webdriver.promise.Promise.} A promise that will resolve to * the scripts return value. * @template T */ ProtractorBrowser.prototype.executeScriptWithDescription = function (script, description) { var scriptArgs = []; for (var _i = 2; _i < arguments.length; _i++) { scriptArgs[_i - 2] = arguments[_i]; } if (typeof script === 'function') { script = 'return (' + script + ').apply(null, arguments);'; } return this.driver.schedule(new Command(CommandName.EXECUTE_SCRIPT) .setParameter('script', script) .setParameter('args', scriptArgs), description); }; /** * The same as {@code webdriver.WebDriver.prototype.executeAsyncScript}, * but with a customized description for debugging. * * @private * @param {!(string|Function)} script The script to execute. * @param {string} description A description for debugging purposes. * @param {...*} var_args The arguments to pass to the script. * @returns {!webdriver.promise.Promise.} A promise that will resolve to * the * scripts return value. * @template T */ ProtractorBrowser.prototype.executeAsyncScript_ = function (script, description) { var scriptArgs = []; for (var _i = 2; _i < arguments.length; _i++) { scriptArgs[_i - 2] = arguments[_i]; } if (typeof script === 'function') { script = 'return (' + script + ').apply(null, arguments);'; } return this.driver.schedule(new Command(CommandName.EXECUTE_ASYNC_SCRIPT) .setParameter('script', script) .setParameter('args', scriptArgs), description); }; /** * Instruct webdriver to wait until Angular has finished rendering and has * no outstanding $http or $timeout calls before continuing. * Note that Protractor automatically applies this command before every * WebDriver action. * * @param {string=} opt_description An optional description to be added * to webdriver logs. * @returns {!webdriver.promise.Promise} A promise that will resolve to the * scripts return value. */ ProtractorBrowser.prototype.waitForAngular = function (opt_description) { var _this = this; var description = opt_description ? ' - ' + opt_description : ''; if (this.ignoreSynchronization) { return this.driver.controlFlow().execute(function () { return true; }, 'Ignore Synchronization Protractor.waitForAngular()'); } var runWaitForAngularScript = function () { if (_this.plugins_.skipAngularStability()) { return selenium_webdriver_1.promise.fulfilled(); } else if (_this.rootEl) { return _this.executeAsyncScript_(clientSideScripts.waitForAngular, 'Protractor.waitForAngular()' + description, _this.rootEl, _this.ng12Hybrid); } else { return _this.executeAsyncScript_(clientSideScripts.waitForAllAngular2, 'Protractor.waitForAngular()' + description); } }; return runWaitForAngularScript() .then(function (browserErr) { if (browserErr) { throw new Error('Error while waiting for Protractor to ' + 'sync with the page: ' + JSON.stringify(browserErr)); } }) .then(function () { return _this.driver.controlFlow() .execute(function () { return _this.plugins_.waitForPromise(); }, 'Plugins.waitForPromise()') .then(function () { return _this.driver.wait(function () { return _this.plugins_.waitForCondition().then(function (results) { return results.reduce(function (x, y) { return x && y; }, true); }); }, _this.allScriptsTimeout, 'Plugins.waitForCondition()'); }); }, function (err) { var timeout; if (/asynchronous script timeout/.test(err.message)) { // Timeout on Chrome timeout = /-?[\d\.]*\ seconds/.exec(err.message); } else if (/Timed out waiting for async script/.test(err.message)) { // Timeout on Firefox timeout = /-?[\d\.]*ms/.exec(err.message); } else if (/Timed out waiting for an asynchronous script/.test(err.message)) { // Timeout on Safari timeout = /-?[\d\.]*\ ms/.exec(err.message); } if (timeout) { var errMsg_1 = "Timed out waiting for asynchronous Angular tasks to finish after " + (timeout + ". This may be because the current page is not an Angular ") + "application. Please see the FAQ for more details: " + "https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular"; if (description.indexOf(' - Locator: ') == 0) { errMsg_1 += '\nWhile waiting for element with locator' + description; } var pendingTimeoutsPromise = void 0; if (_this.trackOutstandingTimeouts_) { pendingTimeoutsPromise = _this.executeScriptWithDescription('return window.NG_PENDING_TIMEOUTS', 'Protractor.waitForAngular() - getting pending timeouts' + description); } else { pendingTimeoutsPromise = selenium_webdriver_1.promise.fulfilled({}); } var pendingHttpsPromise = _this.executeScriptWithDescription(clientSideScripts.getPendingHttpRequests, 'Protractor.waitForAngular() - getting pending https' + description, _this.rootEl); return selenium_webdriver_1.promise.all([pendingTimeoutsPromise, pendingHttpsPromise]) .then(function (arr) { var pendingTimeouts = arr[0] || []; var pendingHttps = arr[1] || []; var key, pendingTasks = []; for (key in pendingTimeouts) { if (pendingTimeouts.hasOwnProperty(key)) { pendingTasks.push(' - $timeout: ' + pendingTimeouts[key]); } } for (key in pendingHttps) { pendingTasks.push(' - $http: ' + pendingHttps[key].url); } if (pendingTasks.length) { errMsg_1 += '. \nThe following tasks were pending:\n'; errMsg_1 += pendingTasks.join('\n'); } err.message = errMsg_1; throw err; }, function () { err.message = errMsg_1; throw err; }); } else { throw err; } }); }; /** * Waits for Angular to finish rendering before searching for elements. * @see webdriver.WebDriver.findElement * @returns {!webdriver.promise.Promise} A promise that will be resolved to * the located {@link webdriver.WebElement}. */ ProtractorBrowser.prototype.findElement = function (locator) { return this.element(locator).getWebElement(); }; /** * Waits for Angular to finish rendering before searching for elements. * @see webdriver.WebDriver.findElements * @returns {!webdriver.promise.Promise} A promise that will be resolved to an * array of the located {@link webdriver.WebElement}s. */ ProtractorBrowser.prototype.findElements = function (locator) { return this.element.all(locator).getWebElements(); }; /** * Tests if an element is present on the page. * @see webdriver.WebDriver.isElementPresent * @returns {!webdriver.promise.Promise} A promise that will resolve to whether * the element is present on the page. */ ProtractorBrowser.prototype.isElementPresent = function (locatorOrElement) { var element = (locatorOrElement.isPresent) ? locatorOrElement : this.element(locatorOrElement); return element.isPresent(); }; /** * Add a module to load before Angular whenever Protractor.get is called. * Modules will be registered after existing modules already on the page, * so any module registered here will override preexisting modules with the * same name. * * @example * browser.addMockModule('modName', function() { * angular.module('modName', []).value('foo', 'bar'); * }); * * @param {!string} name The name of the module to load or override. * @param {!string|Function} script The JavaScript to load the module. * Note that this will be executed in the browser context, so it cannot * access variables from outside its scope. * @param {...*} varArgs Any additional arguments will be provided to * the script and may be referenced using the `arguments` object. */ ProtractorBrowser.prototype.addMockModule = function (name, script) { var moduleArgs = []; for (var _i = 2; _i < arguments.length; _i++) { moduleArgs[_i - 2] = arguments[_i]; } this.mockModules_.push({ name: name, script: script, args: moduleArgs }); }; /** * Clear the list of registered mock modules. */ ProtractorBrowser.prototype.clearMockModules = function () { this.mockModules_ = []; this.addBaseMockModules_(); }; /** * Remove a registered mock module. * * @example * browser.removeMockModule('modName'); * * @param {!string} name The name of the module to remove. */ ProtractorBrowser.prototype.removeMockModule = function (name) { for (var i = 0; i < this.mockModules_.length; ++i) { if (this.mockModules_[i].name == name) { this.mockModules_.splice(i--, 1); } } }; /** * Get a list of the current mock modules. * * @returns {Array.} The list of mock modules. */ ProtractorBrowser.prototype.getRegisteredMockModules = function () { return this.mockModules_.map(function (module) { return module.script; }); }; ; /** * Add the base mock modules used for all Protractor tests. * * @private */ ProtractorBrowser.prototype.addBaseMockModules_ = function () { this.addMockModule('protractorBaseModule_', clientSideScripts.protractorBaseModuleFn, this.trackOutstandingTimeouts_); }; /** * @see webdriver.WebDriver.get * * Navigate to the given destination and loads mock modules before * Angular. Assumes that the page being loaded uses Angular. * If you need to access a page which does not have Angular on load, use * the wrapped webdriver directly. * * @example * browser.get('https://angularjs.org/'); * expect(browser.getCurrentUrl()).toBe('https://angularjs.org/'); * * @param {string} destination Destination URL. * @param {number=} opt_timeout Number of milliseconds to wait for Angular to * start. */ ProtractorBrowser.prototype.get = function (destination, timeout) { var _this = this; if (timeout === void 0) { timeout = this.getPageTimeout; } destination = this.baseUrl.indexOf('file://') === 0 ? this.baseUrl + destination : url.resolve(this.baseUrl, destination); var msg = function (str) { return 'Protractor.get(' + destination + ') - ' + str; }; if (this.ignoreSynchronization) { this.driver.get(destination); return this.driver.controlFlow().execute(function () { return _this.plugins_.onPageLoad(); }).then(function () { }); } var deferred = selenium_webdriver_1.promise.defer(); this.driver.get(this.resetUrl).then(null, deferred.reject); this.executeScriptWithDescription('window.name = "' + DEFER_LABEL + '" + window.name;' + 'window.location.replace("' + destination + '");', msg('reset url')) .then(null, deferred.reject); // We need to make sure the new url has loaded before // we try to execute any asynchronous scripts. this.driver .wait(function () { return _this .executeScriptWithDescription('return window.location.href;', msg('get url')) .then(function (url) { return url !== _this.resetUrl; }, function (err) { if (err.code == 13) { // Ignore the error, and continue trying. This is // because IE driver sometimes (~1%) will throw an // unknown error from this execution. See // https://github.com/angular/protractor/issues/841 // This shouldn't mask errors because it will fail // with the timeout anyway. return false; } else { throw err; } }); }, timeout, 'waiting for page to load for ' + timeout + 'ms') .then(null, deferred.reject); this.driver.controlFlow().execute(function () { return _this.plugins_.onPageLoad(); }); // Make sure the page is an Angular page. this.executeAsyncScript_(clientSideScripts.testForAngular, msg('test for angular'), Math.floor(timeout / 1000), this.ng12Hybrid) .then(function (angularTestResult) { var angularVersion = angularTestResult.ver; if (!angularVersion) { var message = angularTestResult.message; logger.error("Could not find Angular on page " + destination + " : " + message); throw new Error("Angular could not be found on the page " + destination + ". If this is not an " + "Angular application, you may need to turn off waiting for Angular. Please " + "see https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular-on-page-load"); } return angularVersion; }, function (err) { throw new Error('Error while running testForAngular: ' + err.message); }) .then(loadMocks, deferred.reject); var self = this; function loadMocks(angularVersion) { if (angularVersion === 1) { // At this point, Angular will pause for us until angular.resumeBootstrap is called. var moduleNames = []; var _loop_1 = function (name_1, script, args) { moduleNames.push(name_1); var executeScriptArgs = [script, msg('add mock module ' + name_1)].concat(args); self.executeScriptWithDescription.apply(self, executeScriptArgs) .then(null, function (err) { throw new Error('Error while running module script ' + name_1 + ': ' + err.message); }) .then(null, deferred.reject); }; for (var _i = 0, _a = self.mockModules_; _i < _a.length; _i++) { var _b = _a[_i], name_1 = _b.name, script = _b.script, args = _b.args; _loop_1(name_1, script, args); } self.executeScriptWithDescription('angular.resumeBootstrap(arguments[0]);', msg('resume bootstrap'), moduleNames) .then(null, deferred.reject); } else { // TODO: support mock modules in Angular2. For now, error if someone // has tried to use one. if (self.mockModules_.length > 1) { deferred.reject('Trying to load mock modules on an Angular2 app ' + 'is not yet supported.'); } } } this.driver.controlFlow().execute(function () { return _this.plugins_.onPageStable().then(function () { deferred.fulfill(); }, deferred.reject); }); return deferred.promise; }; /** * @see webdriver.WebDriver.refresh * * Makes a full reload of the current page and loads mock modules before * Angular. Assumes that the page being loaded uses Angular. * If you need to access a page which does not have Angular on load, use * the wrapped webdriver directly. * * @param {number=} opt_timeout Number of milliseconds to wait for Angular to start. */ ProtractorBrowser.prototype.refresh = function (opt_timeout) { var _this = this; if (this.ignoreSynchronization) { return this.driver.navigate().refresh(); } return this .executeScriptWithDescription('return window.location.href', 'Protractor.refresh() - getUrl') .then(function (href) { return _this.get(href, opt_timeout); }); }; /** * Mixin navigation methods back into the navigation object so that * they are invoked as before, i.e. driver.navigate().refresh() */ ProtractorBrowser.prototype.navigate = function () { var nav = this.driver.navigate(); ptorMixin(nav, this, 'refresh'); return nav; }; /** * Browse to another page using in-page navigation. * * @example * browser.get('http://angular.github.io/protractor/#/tutorial'); * browser.setLocation('api'); * expect(browser.getCurrentUrl()) * .toBe('http://angular.github.io/protractor/#/api'); * * @param {string} url In page URL using the same syntax as $location.url() * @returns {!webdriver.promise.Promise} A promise that will resolve once * page has been changed. */ ProtractorBrowser.prototype.setLocation = function (url) { this.waitForAngular(); return this .executeScriptWithDescription(clientSideScripts.setLocation, 'Protractor.setLocation()', this.rootEl, url) .then(function (browserErr) { if (browserErr) { throw 'Error while navigating to \'' + url + '\' : ' + JSON.stringify(browserErr); } }); }; /** * Returns the current absolute url from AngularJS. * * @example * browser.get('http://angular.github.io/protractor/#/api'); * expect(browser.getLocationAbsUrl()) * .toBe('http://angular.github.io/protractor/#/api'); * @returns {webdriver.promise.Promise} The current absolute url from * AngularJS. */ ProtractorBrowser.prototype.getLocationAbsUrl = function () { this.waitForAngular(); return this.executeScriptWithDescription(clientSideScripts.getLocationAbsUrl, 'Protractor.getLocationAbsUrl()', this.rootEl); }; /** * Adds a task to the control flow to pause the test and inject helper * functions * into the browser, so that debugging may be done in the browser console. * * This should be used under node in debug mode, i.e. with * protractor debug * * @example * While in the debugger, commands can be scheduled through webdriver by * entering the repl: * debug> repl * > element(by.input('user')).sendKeys('Laura'); * > browser.debugger(); * Press Ctrl + c to leave debug repl * debug> c * * This will run the sendKeys command as the next task, then re-enter the * debugger. */ ProtractorBrowser.prototype.debugger = function () { // jshint debug: true this.driver.executeScript(clientSideScripts.installInBrowser); selenium_webdriver_1.promise.controlFlow().execute(function () { debugger; }, 'add breakpoint to control flow'); }; /** * Beta (unstable) enterRepl function for entering the repl loop from * any point in the control flow. Use browser.enterRepl() in your test. * Does not require changes to the command line (no need to add 'debug'). * Note, if you are wrapping your own instance of Protractor, you must * expose globals 'browser' and 'protractor' for pause to work. * * @example * element(by.id('foo')).click(); * browser.enterRepl(); * // Execution will stop before the next click action. * element(by.id('bar')).click(); * * @param {number=} opt_debugPort Optional port to use for the debugging * process */ ProtractorBrowser.prototype.enterRepl = function (opt_debugPort) { var debuggerClientPath = __dirname + '/debugger/clients/explorer.js'; var onStartFn = function () { logger.info(); logger.info('------- Element Explorer -------'); logger.info('Starting WebDriver debugger in a child process. Element ' + 'Explorer is still beta, please report issues at ' + 'github.com/angular/protractor'); logger.info(); logger.info('Type to see a list of locator strategies.'); logger.info('Use the `list` helper function to find elements by strategy:'); logger.info(' e.g., list(by.binding(\'\')) gets all bindings.'); logger.info(); }; this.debugHelper.init(debuggerClientPath, onStartFn, opt_debugPort); }; /** * Beta (unstable) pause function for debugging webdriver tests. Use * browser.pause() in your test to enter the protractor debugger from that * point in the control flow. * Does not require changes to the command line (no need to add 'debug'). * Note, if you are wrapping your own instance of Protractor, you must * expose globals 'browser' and 'protractor' for pause to work. * * @example * element(by.id('foo')).click(); * browser.pause(); * // Execution will stop before the next click action. * element(by.id('bar')).click(); * * @param {number=} opt_debugPort Optional port to use for the debugging * process */ ProtractorBrowser.prototype.pause = function (opt_debugPort) { if (this.debugHelper.isAttached()) { logger.info('Encountered browser.pause(), but debugger already attached.'); return selenium_webdriver_1.promise.fulfilled(true); } var debuggerClientPath = __dirname + '/debugger/clients/wddebugger.js'; var onStartFn = function (firstTime) { logger.info(); logger.info('Encountered browser.pause(). Attaching debugger...'); if (firstTime) { logger.info(); logger.info('------- WebDriver Debugger -------'); logger.info('Starting WebDriver debugger in a child process. Pause is ' + 'still beta, please report issues at github.com/angular/protractor'); logger.info(); logger.info('press c to continue to the next webdriver command'); logger.info('press ^D to detach debugger and resume code execution'); logger.info('type "repl" to enter interactive mode'); logger.info('type "exit" to break out of interactive mode'); logger.info(); } }; this.debugHelper.init(debuggerClientPath, onStartFn, opt_debugPort); }; /** * Create a new instance of Browser by wrapping a webdriver instance. * * @param {webdriver.WebDriver} webdriver The configured webdriver instance. * @param {string=} baseUrl A URL to prepend to relative gets. * @param {string=} rootElement The css selector for the element which is the * root of the Angular app. * @param {boolean=} untrackOutstandingTimeouts Whether Browser should * stop tracking outstanding $timeouts. * @returns {Browser} a new Browser instance */ ProtractorBrowser.wrapDriver = function (webdriver, baseUrl, rootElement, untrackOutstandingTimeouts) { return new ProtractorBrowser(webdriver, baseUrl, rootElement, untrackOutstandingTimeouts); }; return ProtractorBrowser; }(Webdriver)); /** * @type {ProtractorBy} */ ProtractorBrowser.By = new locators_1.ProtractorBy(); exports.ProtractorBrowser = ProtractorBrowser;