diff --git a/.jshintignore b/.jshintignore deleted file mode 100644 index 2442be841..000000000 --- a/.jshintignore +++ /dev/null @@ -1 +0,0 @@ -./spec/built/* diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index a48f2aa66..000000000 --- a/.jshintrc +++ /dev/null @@ -1,28 +0,0 @@ -{ - "unused": true, - "node": true, - "bitwise": true, - "immed": true, - "newcap": true, - "noarg": true, - "noempty": true, - "nonew": true, - "trailing": true, - "maxlen": 100, - "boss": true, - "eqnull": true, - "expr": true, - "laxbreak": true, - "loopfunc": true, - "sub": true, - "undef": true, - "quotmark": "single", - "evil": true, - "curly": true, - "esversion": 6, - "predef": ["$", "$$", "angular", "after", "afterAll", "afterEach", - "beforeAll", "beforeEach", "browser", "by", "By", "DartObject", - "describe", "document", "element", "expect", "ExpectedConditions", - "fdescribe", "fit", "it", "jasmine", "protractor", "spyOn", - "xdescribe", "xit"] -} diff --git a/.travis.yml b/.travis.yml index 46cfc14f5..271ac9f88 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: node_js sudo: false node_js: - - "6" - - "8" + - "9" + - "10" env: global: @@ -24,9 +24,9 @@ matrix: - env: "JOB=bstack" exclude: - env: JOB=smoke - node_js: "7" + node_js: "9" - env: JOB=bstack - node_js: "7" + node_js: "9" addons: apt: @@ -36,17 +36,18 @@ addons: - g++-4.8 before_install: - - g++-4.8 --version + - travis_wait g++-4.8 --version before_script: - npm run install_testapp - npm run pretest - mkdir -p $LOGS_DIR - - ./scripts/travis_setup.sh + - travis_wait ./scripts/travis_setup.sh + script: - ./scripts/testserver.sh - ./scripts/test_on_travis.sh after_script: - - ./scripts/print_logs.sh + - ./scripts/print_logs.sh \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 90f540011..9b0523f7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,153 @@ +# 6.0.0-beta + +Selenium 4 removes the control flow and most of these changes are based on those changes. To see the full list of changes, please refer to selenium-webdriver's [CHANGELOG](https://github.com/SeleniumHQ/selenium/blob/master/javascript/node/selenium-webdriver/CHANGES.md) + +## Breaking changes + +- Control flow is removed and you should use async await to run your tests. +- Other control flow related items: + - debugger, explore and element explorer have been removed + - jasminewd is no longer a dependency +- ignoreSynchronization has been deprecated and you should use `waitForAngularEnabled` +- Types for selenium-webdriver are currently in the types/ directory and are not complete. We are still missing some type definitions for selenium 4. +- Actions API in selenium-webdriver have changed and they will break your test. Also we have not exported it yet since the type definitions are not complete. + +## Features + +- ([8420cfa](https://github.com/angular/protractor/commit/8420cfa10fa5e32b037db933d40e84726a895f3c)) + chore(debugprint): convert debugprint to TypeScript (#5074) + +- ([1e12445](https://github.com/angular/protractor/commit/1e124454d2926a9d2328c1a4516186f533b42b71)) + chore(browser): remove timing issues with restart and fork (#5085) + + - remove .ready since forking should automatically return a browser + - getNewDriver should return a promised WebDriver that can be awaited + - fix interaction tests and local driver tests + - update unit tests for async await due to getNewDriver fix + closes #5031 +- ([d6bbf09](https://github.com/angular/protractor/commit/d6bbf09af0220f683ac7db51ade5cada45876776)) + chore(elementexplorer): remove explorer bin file (#5094) + + closes #5092 + +- ([0c325c5](https://github.com/angular/protractor/commit/0c325c56f0aef3b0016890b074938db875ae7a62)) + chore(ignoreSynchornization): clean up to use waitForAngularEnabled (#5071) + + +- ([591653d](https://github.com/angular/protractor/commit/591653d46b390d5b16087b5fb5b65cc680090fed)) + chore(debugger): remove debugger and explore methods (#5070) + +- ([0541775](https://github.com/angular/protractor/commit/0541775980f0314fc36590eae1791f478588b619)) + chore(promises): remove q promises and webdriver promises (#5052) + + - remove q promises and webdriver promises from the runner, launcher, plugins, and taskRunner + - add deprecated message to element explorer. + - add unhandledRejection + - update browser versions used in travis tests + +- ([7ca7df2](https://github.com/angular/protractor/commit/7ca7df2708b847b9d002bef6d1ed0fe075154aeb)) + chore(promises): clean up driver providers and browser control flow (#5034) + + Driver providers and tests: + + - Use native promises over q promises in driver providers + - Remove driverProviderUseExistingWebDriver since the generation of the selenium server is already + accomplished when providing a selenium address in driverProvider.ts. Also clean up docs and tests. + + - Enabled the driverProviderLocal tests + - Clean up JSDocs for q.promise + Basic lib spec: + + - Remove auto unwrap test for a WebElement. Reference PR #3471 + Browser: + + - Remove control flow from waitForAngularEnabled, waitForAngular, and angularAppRoot in the + Browser class. + +## Dependencies + +- ([93930ff](https://github.com/angular/protractor/commit/93930fff33e94942c8ff6c6c8089dcd70acd6b7b)) + deps(jasmine): upgrade jasmine 3.3 (#5102) + +- ([373ba02](https://github.com/angular/protractor/commit/373ba029420a5f3f0c05bbb8f739f9fd96ac598b)) + deps(selenium): upgrade to selenium 4 (#5095) + + - elements workaround for WebElement.equals + - added a better unhandled rejection warning message in the launcher + control flow)bal function wrappers for mocha (these wrappers went away with + - fix the attach to session driver provider + Typing exported from Protractor: + + - removed ActionSequence and EventEmitter (actions is currently missing) + - removed promise.Promise + fulfilled, filter, whener, delayed, createFlow, controlFlow, all, + Typings exported from WebDriver: + + - removed attachToSession + - removed WebDriver instance methods: touchActions, call + - removed WebElement getSize and getLocation for getRect + - removed redefined global vars for testing + - In the typings, we are missing Options.setScriptTimeout method. This should not impact users + unless they are using the driver.manage() method. + Tests: + + - fix element equals test + - add missing 'await' in colorList test that is causing unhandled promise rejections. + - remove control flow related tests + - disable the install test. Installing from "file:../../" is not working. + - fix the attach to session driver provider test to exit with a 1 if errors are encountered + +- ([736bbf7](https://github.com/angular/protractor/commit/736bbf7df04b36d1d0fcc7383be2022aa14234b0)) + deps(latest): upgrade to the gulp and typescript (#5089) + + * deps(latest): upgrade to the gulp and typescript + + - add in @types/loglevel and @types/yargs for webdriver-manager + - upgrade tslint clean up for tslint + supported by gulpp 4 and remove run sequence since this feature is + - remove compile to es5 + +- ([658a1fb](https://github.com/angular/protractor/commit/658a1fb3020ce6a2316113cbbb6f84e513158c82)) + deps(webdriver-manager): use replacement (#5088) + + publish a beta release of use webdriver-manager-replacement until we + webdriver-manager + closes #5087 + + +# 5.4.2 + +## Features + +- ([db1b638](https://github.com/angular/protractor/commit/db1b6381d463c7cecf11dece2bf9412fecbd6f4d)) + feat(saucelabs): add sauceRegion support for eu datacenters (#5083) + + This change allows user to define the backend region from sauce via the `sauceRegion` property, + e.g. + + ```js + sauceUser: process.env.SAUCE_USERNAME, + sauceKey: process.env.SAUCE_ACCESS_KEY, + sauceRegion: 'eu', + ``` + Will run the test against `https://ondemand.eu-central-1.saucelabs.com:443/wd/hub/.` + + ```js + sauceUser: process.env.SAUCE_USERNAME, + sauceKey: process.env.SAUCE_ACCESS_KEY, + sauceRegion: 'us', + + // the default + sauceUser: process.env.SAUCE_USERNAME, + sauceKey: process.env.SAUCE_ACCESS_KEY, + ``` + Will run the test against https://ondemand.saucelabs.com:443/wd/hub/ + +## Fixes + +- ([f5dbe13](https://github.com/angular/protractor/commit/f5dbe13ad6755ae812627d8056527e351db8b34c)) + fix(deps): @types/node is now a dev dependency + # 5.4.1 ## Features diff --git a/bin/elementexplorer.js b/bin/elementexplorer.js deleted file mode 100755 index 069b7552c..000000000 --- a/bin/elementexplorer.js +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env node - -/** - * This is an explorer to help get the right element locators, and test out what - * Protractor commands will do on your site without running a full test suite. - * - * This beta version only uses the Chrome browser. - * - * Usage: - * - * Expects a selenium standalone server to be running at http://localhost:4444 - * from protractor directory, run with: - * - * ./bin/elementexplorer.js - * - * This will load up the URL on webdriver and put the terminal into a REPL loop. - * You will see a > prompt. The `browser`, `element` and `protractor` variables - * will be available. Enter a command such as: - * - * > element(by.id('foobar')).getText() - * - * or - * - * > browser.get('http://www.angularjs.org') - * - * try just - * - * > browser - * - * to get a list of functions you can call. - * - * Typing tab at a blank prompt will fill in a suggestion for finding - * elements. - */ -console.log('Please use "protractor [configFile] [options] --elementExplorer"' + - ' for full functionality\n'); - -if (process.argv.length > 3) { - console.log('usage: elementexplorer.js [url]'); - process.exit(1); -} else if (process.argv.length === 3) { - process.argv[2] = ('--baseUrl=' + process.argv[2]); -} - -process.argv.push('--elementExplorer'); -require('../built/cli.js'); diff --git a/bin/webdriver-manager b/bin/webdriver-manager index 1792e6e59..3f8533a21 100755 --- a/bin/webdriver-manager +++ b/bin/webdriver-manager @@ -1,3 +1,3 @@ #!/usr/bin/env node -require('webdriver-manager'); +require('webdriver-manager/dist/lib/cli'); diff --git a/circle.yml b/circle.yml index 9a92e3224..7c1bb9179 100644 --- a/circle.yml +++ b/circle.yml @@ -2,7 +2,7 @@ version: 2 jobs: build: docker: - - image: circleci/node:6.14-browsers + - image: circleci/node:8.11-browsers environment: # Fix issue with selenium-server in containers. # See http://github.com/SeleniumHQ/docker-selenium/issues/87 @@ -32,6 +32,8 @@ jobs: - restore_cache: key: node_modules-{{ .Branch }}-{{ checksum "package-lock.json" }} + - run: google-chrome --version + - run: name: NPM Install command: | diff --git a/docs/server-setup.md b/docs/server-setup.md index aa15e0e26..296722d2f 100644 --- a/docs/server-setup.md +++ b/docs/server-setup.md @@ -108,37 +108,3 @@ Protractor can test directly against Chrome and Firefox without using a Selenium - `directConnect: true` - Your test script communicates directly Chrome Driver or Firefox Driver, bypassing any Selenium Server. If this is true, settings for `seleniumAddress` and `seleniumServerJar` will be ignored. If you attempt to use a browser other than Chrome or Firefox an error will be thrown. The advantage of directly connecting to browser drivers is that your test scripts may start up and run faster. - -Re-using an Existing WebDriver ------------------------------- - -The use case for re-using an existing WebDriver is when you have existing -`selenium-webdriver` code and are already in control of how the WebDriver is -created, but would also like Protractor to use the same browser, so you can -use protractor's element locators and the rest of its API. This could be -done with the `attachSession` driver provider, but the `attachSession` API is -being removed in `selenium-webdriver` 4.0.0. - -Instead of a protractor config file, you create a config object in your test -setup code, and add your already-created WebDriver object and base URL. - -```javascript -const ProtractorConfigParser = require('protractor/built/configParser').ConfigParser; -const ProtractorRunner = require('protractor/built/runner').Runner; - -const ptorConfig = new ProtractorConfigParser().config_; -ptorConfig.baseUrl = myExistingBaseUrl; -ptorConfig.seleniumWebDriver = myExistingWebDriver; -ptorConfig.noGlobals = true; // local preference - -// looks similar to protractor/built/runner.js run() -const ptorRunner = new ProtractorRunner(ptorConfig); -ptorRunner.driverProvider_.setupEnv(); -const browser = ptorRunner.createBrowser(); -ptorRunner.setupGlobals_(browser); // now you can access protractor.$, etc. -``` - -Note that this driver provider leaves you in control of quitting the driver, -but that also means Protractor API calls that expect the driver to properly -quit and/or restart the browser, e.g. `restart`, `restartSync`, and -`forkNewDriverInstance`, will not behave as documented. diff --git a/gulpfile.js b/gulpfile.js index 541f58900..ba83286d6 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,32 +1,29 @@ 'use strict'; -var gulp = require('gulp'); -var clangFormat = require('clang-format'); -var gulpFormat = require('gulp-clang-format'); -var runSequence = require('run-sequence'); -var spawn = require('child_process').spawn; -var spawnSync = require('child_process').spawnSync; -var tslint = require('gulp-tslint'); -var fs = require('fs'); -var path = require('path'); -var glob = require('glob'); -var semver = require('semver'); - -var runSpawn = function(done, task, opt_arg, opt_io) { +const gulp = require('gulp'); +const format = require('gulp-clang-format'); +const clangFormat = require('clang-format'); +const spawn = require('child_process').spawn; +const tslint = require('gulp-tslint'); +const fs = require('fs'); +const path = require('path'); +const semver = require('semver'); + +const runSpawn = (done, task, opt_arg, opt_io) => { opt_arg = typeof opt_arg !== 'undefined' ? opt_arg : []; - var stdio = 'inherit'; + const stdio = 'inherit'; if (opt_io === 'ignore') { stdio = 'ignore'; } - var child = spawn(task, opt_arg, {stdio: stdio}); - var running = false; - child.on('close', function() { + const child = spawn(task, opt_arg, {stdio: stdio}); + let running = false; + child.on('close', () => { if (!running) { running = true; done(); } }); - child.on('error', function() { + child.on('error', () => { if (!running) { console.error('gulp encountered a child error'); running = true; @@ -35,21 +32,25 @@ var runSpawn = function(done, task, opt_arg, opt_io) { }); }; -gulp.task('tslint', function() { +gulp.task('tslint', () => { return gulp.src(['lib/**/*.ts', 'spec/**/*.ts', '!spec/install/**/*.ts']) - .pipe(tslint()).pipe(tslint.report()); + .pipe(tslint()) + .pipe(tslint.report()); }); -gulp.task('lint', function(done) { - runSequence('tslint', 'jshint', 'format:enforce', done); +gulp.task('format:enforce', () => { + return gulp.src(['lib/**/*.ts']) + .pipe(format.checkFormat('file', clangFormat, {verbose: true, fail: true})); }); +gulp.task('lint', gulp.series('tslint', 'format:enforce')); + // prevent contributors from using the wrong version of node -gulp.task('checkVersion', function(done) { +gulp.task('checkVersion', (done) => { // read minimum node on package.json - var packageJson = JSON.parse(fs.readFileSync(path.resolve('package.json'))); - var protractorVersion = packageJson.version; - var nodeVersion = packageJson.engines.node; + const packageJson = JSON.parse(fs.readFileSync(path.resolve('package.json'))); + const protractorVersion = packageJson.version; + const nodeVersion = packageJson.engines.node; if (semver.satisfies(process.version, nodeVersion)) { done(); @@ -59,60 +60,35 @@ gulp.task('checkVersion', function(done) { } }); -gulp.task('built:copy', function(done) { +gulp.task('built:copy', () => { return gulp.src(['lib/**/*.js']) .pipe(gulp.dest('built/')); - done(); }); -gulp.task('webdriver:update', function(done) { +gulp.task('webdriver:update', (done) => { runSpawn(done, 'node', ['bin/webdriver-manager', 'update']); }); -gulp.task('jshint', function(done) { - runSpawn(done, 'node', ['node_modules/jshint/bin/jshint', '-c', - '.jshintrc', 'lib', 'spec', 'scripts', - '--exclude=lib/selenium-webdriver/**/*.js,lib/webdriver-js-extender/**/*.js,' + - 'spec/dependencyTest/*.js,spec/install/**/*.js']); -}); - -gulp.task('format:enforce', function() { - var format = require('gulp-clang-format'); - var clangFormat = require('clang-format'); - return gulp.src(['lib/**/*.ts']).pipe( - format.checkFormat('file', clangFormat, {verbose: true, fail: true})); -}); - -gulp.task('format', function() { - var format = require('gulp-clang-format'); - var clangFormat = require('clang-format'); - return gulp.src(['lib/**/*.ts'], { base: '.' }).pipe( - format.format('file', clangFormat)).pipe(gulp.dest('.')); +gulp.task('format', () => { + return gulp.src(['lib/**/*.ts'], { base: '.' }) + .pipe(format.format('file', clangFormat)) + .pipe(gulp.dest('.')); }); -gulp.task('tsc', function(done) { +gulp.task('tsc', (done) => { runSpawn(done, 'node', ['node_modules/typescript/bin/tsc']); }); -gulp.task('tsc:spec', function(done) { +gulp.task('tsc:spec', (done) => { runSpawn(done, 'node', ['node_modules/typescript/bin/tsc', '-p', 'ts_spec_config.json']); }); -gulp.task('tsc:es5', function(done) { - runSpawn(done, './scripts/compile_to_es5.sh'); -}); +gulp.task('prepublish', gulp.series('checkVersion', 'tsc', 'built:copy')); -gulp.task('compile_to_es5', function(done) { - runSequence('checkVersion', 'tsc:es5', 'built:copy', done); -}); +gulp.task('pretest', gulp.series( + 'checkVersion', + gulp.parallel('webdriver:update', 'tslint', 'format'), + 'tsc', 'built:copy', 'tsc:spec')); -gulp.task('prepublish', function(done) { - runSequence('checkVersion', 'jshint', 'tsc', 'built:copy', done); -}); - -gulp.task('pretest', function(done) { - runSequence('checkVersion', - ['webdriver:update', 'jshint', 'tslint', 'format'], 'tsc', 'built:copy', 'tsc:spec', done); -}); +gulp.task('default', gulp.series('prepublish')); -gulp.task('default',['prepublish']); diff --git a/lib/bpRunner.ts b/lib/bpRunner.ts index 7c124097a..e003abfaf 100644 --- a/lib/bpRunner.ts +++ b/lib/bpRunner.ts @@ -1,5 +1,4 @@ import {ChildProcess, fork} from 'child_process'; -import * as q from 'q'; import {Config} from './config'; import {Logger} from './logger'; @@ -15,7 +14,7 @@ export class BlockingProxyRunner { constructor(private config: Config) {} start() { - return q.Promise((resolve, reject) => { + return new Promise((resolve, reject) => { this.checkSupportedConfig(); let args = [ diff --git a/lib/browser.ts b/lib/browser.ts index 6e98c21a3..68e101f93 100644 --- a/lib/browser.ts +++ b/lib/browser.ts @@ -1,9 +1,8 @@ import {BPClient} from 'blocking-proxy'; -import {ActionSequence, By, Capabilities, Command as WdCommand, FileDetector, ICommandName, Navigation, Options, promise as wdpromise, Session, TargetLocator, TouchSequence, until, WebDriver, WebElement, WebElementPromise} from 'selenium-webdriver'; +import {By, Command as WdCommand, ICommandName, Navigation, promise as wdpromise, Session, WebDriver, WebElement, WebElementPromise} from 'selenium-webdriver'; import * as url from 'url'; import {extend as extendWD, ExtendedWebDriver} from 'webdriver-js-extender'; -import {DebugHelper} from './debugger'; import {build$, build$$, ElementArrayFinder, ElementFinder} from './element'; import {IError} from './exitCodes'; import {ProtractorExpectedConditions} from './expectedConditions'; @@ -22,7 +21,7 @@ const DEFER_LABEL = 'NG_DEFER_BOOTSTRAP!'; const DEFAULT_RESET_URL = 'data:text/html,'; const DEFAULT_GET_PAGE_TIMEOUT = 10000; -let logger = new Logger('protractor'); +let logger = new Logger('browser'); // 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 @@ -73,7 +72,7 @@ function ptorMixin(to: any, from: any, fnName: string, setupFn?: Function) { } return run(); }; -}; +} export interface ElementHelper extends Function { (locator: Locator): ElementFinder; @@ -97,7 +96,7 @@ function buildElementHelper(browser: ProtractorBrowser): ElementHelper { }; return element; -}; +} /** * @alias browser @@ -105,7 +104,7 @@ function buildElementHelper(browser: ProtractorBrowser): ElementHelper { * @extends {webdriver_extensions.ExtendedWebDriver} * @param {webdriver.WebDriver} webdriver * @param {string=} opt_baseUrl A base URL to run get requests against. - * @param {string|webdriver.promise.Promise=} opt_rootElement Selector element that has an + * @param {string|Promise=} opt_rootElement Selector element that has an * ng-app in scope. * @param {boolean=} opt_untrackOutstandingTimeouts Whether Protractor should * stop tracking outstanding $timeouts. @@ -189,28 +188,18 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { * 'body' but if your ng-app is on a subsection of the page it may be * a subelement. * - * The change will be made within WebDriver's control flow, so that commands after - * this method is called use the new app root. Pass nothing to get a promise that - * resolves to the value of the selector. - * - * @param {string|webdriver.promise.Promise} value The new selector. + * @param {string|Promise} valuePromise The new selector. * @returns A promise that resolves with the value of the selector. */ - angularAppRoot(value: string|wdpromise.Promise = null): wdpromise.Promise { - return this.driver.controlFlow().execute(() => { - if (value != null) { - return wdpromise.when(value).then((value: string) => { - this.internalRootEl = value; - if (this.bpClient) { - const bpCommandPromise = this.bpClient.setWaitParams(value); - // Convert to webdriver promise as best as possible - return wdpromise.when(bpCommandPromise as any).then(() => this.internalRootEl); - } - return this.internalRootEl; - }); + async angularAppRoot(valuePromise: string|Promise = null): Promise { + if (valuePromise != null) { + const value = await valuePromise; + this.internalRootEl = value; + if (this.bpClient) { + await this.bpClient.setWaitParams(value); } - return wdpromise.when(this.internalRootEl); - }, `Set angular root selector to ${value}`); + } + return this.internalRootEl; } /** @@ -222,19 +211,13 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { * * Initialized to `false` by the runner. * - * This property is deprecated - please use waitForAngularEnabled instead. + * ignoreSynchornization is deprecated. + * + * Please use waitForAngularEnabled instead. * * @deprecated * @type {boolean} */ - set ignoreSynchronization(value) { - this.waitForAngularEnabled(!value); - } - - get ignoreSynchronization() { - return this.internalIgnoreSynchronization; - } - private internalIgnoreSynchronization: boolean; /** @@ -255,13 +238,13 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { * Resolved when the browser is ready for use. Resolves to the browser, so * you can do: * - * forkedBrowser = await browser.forkNewDriverInstance().ready; + * forkedBrowser = await browser.forkNewDriverInstance(); * * Set by the runner. * - * @type {webdriver.promise.Promise.} + * @type {Promise} */ - ready: wdpromise.Promise; + ready: Promise; /* * Set by the runner. @@ -315,18 +298,12 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { */ ng12Hybrid: boolean; - /** - * A helper that manages debugging tests. - */ - debugHelper: DebugHelper; - // This index type allows looking up methods by name so we can do mixins. [key: string]: any; constructor( - webdriverInstance: WebDriver, opt_baseUrl?: string, - opt_rootElement?: string|wdpromise.Promise, opt_untrackOutstandingTimeouts?: boolean, - opt_blockingProxyUrl?: string) { + webdriverInstance: WebDriver, opt_baseUrl?: string, opt_rootElement?: string|Promise, + opt_untrackOutstandingTimeouts?: boolean, opt_blockingProxyUrl?: string) { super(); // These functions should delegate to the webdriver instance, but should // wait for Angular to sync up before performing the action. This does not @@ -364,7 +341,6 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { this.getPageTimeout = DEFAULT_GET_PAGE_TIMEOUT; this.params = {}; this.resetUrl = DEFAULT_RESET_URL; - this.debugHelper = new DebugHelper(this); let ng12Hybrid_ = false; Object.defineProperty(this, 'ng12Hybrid', { @@ -383,22 +359,6 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { ng12Hybrid_ = ng12Hybrid; } }); - this.ready = this.angularAppRoot(opt_rootElement || '') - .then(() => { - return this.driver.getSession(); - }) - .then((session: Session) => { - // 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 - let browserName = session.getCapabilities().get('browserName'); - if (browserName === 'internet explorer' || browserName === 'safari' || - browserName === 'phantomjs' || browserName === 'MicrosoftEdge') { - this.resetUrl = 'about:blank'; - } - return this; - }); this.trackOutstandingTimeouts_ = !opt_untrackOutstandingTimeouts; this.mockModules_ = []; @@ -417,23 +377,16 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { * Call waitForAngularEnabled() without passing a value to read the current * state without changing it. */ - waitForAngularEnabled(enabled: boolean|wdpromise.Promise = null): - wdpromise.Promise { - if (enabled != null) { - const ret = this.driver.controlFlow().execute(() => { - return wdpromise.when(enabled).then((enabled: boolean) => { - if (this.bpClient) { - logger.debug('Setting waitForAngular' + !enabled); - const bpCommandPromise = this.bpClient.setWaitEnabled(enabled); - // Convert to webdriver promise as best as possible - return wdpromise.when(bpCommandPromise as any).then(() => enabled); - } - }); - }, `Set proxy synchronization enabled to ${enabled}`); + async waitForAngularEnabled(enabledPromise: boolean|Promise = null): Promise { + if (enabledPromise != null) { + const enabled = await enabledPromise; + if (this.bpClient) { + logger.debug('Setting waitForAngular' + !enabled); + await this.bpClient.setWaitEnabled(enabled); + } this.internalIgnoreSynchronization = !enabled; - return ret; } - return wdpromise.when(!this.ignoreSynchronization); + return !this.internalIgnoreSynchronization; } /** @@ -443,10 +396,10 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { * * Set by the runner. * - * @returns {webdriver.promise.Promise} A promise which resolves to the + * @returns {Promise} A promise which resolves to the * capabilities object. */ - getProcessedConfig(): wdpromise.Promise { + getProcessedConfig(): Promise { return null; } @@ -454,12 +407,7 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { * Fork another instance of browser for use in interactive tests. * * @example - * // Running with control flow enabled - * var fork = browser.forkNewDriverInstance(); - * fork.get('page1'); // 'page1' gotten by forked browser - * - * // Running with control flow disabled - * var forked = await browser.forkNewDriverInstance().ready; + * var forked = await browser.forkNewDriverInstance(); * await forked.get('page1'); // 'page1' gotten by forked browser * * @param {boolean=} useSameUrl Whether to navigate to current url on creation @@ -469,8 +417,9 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { * * @returns {ProtractorBrowser} A browser instance. */ - forkNewDriverInstance(useSameUrl?: boolean, copyMockModules?: boolean, copyConfigUpdates = true): - ProtractorBrowser { + async forkNewDriverInstance( + useSameUrl?: boolean, copyMockModules?: boolean, + copyConfigUpdates = true): Promise { return null; } @@ -483,34 +432,16 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { * When restarting a forked browser, it is the caller's job to overwrite references to the old * instance. * - * This function behaves slightly differently depending on if the webdriver control flow is - * enabled. If the control flow is enabled, the global `browser` object is synchronously - * replaced. If the control flow is disabled, the global `browser` is replaced asynchronously - * after the old driver quits. - * * Set by the runner. * * @example - * // Running against global browser, with control flow enabled - * browser.get('page1'); - * browser.restart(); - * browser.get('page2'); // 'page2' gotten by restarted browser - * - * // Running against global browser, with control flow disabled + * // Running against global browser * await browser.get('page1'); * await browser.restart(); * await browser.get('page2'); // 'page2' gotten by restarted browser * - * // Running against forked browsers, with the control flow enabled - * // In this case, you may prefer `restartSync` (documented below) - * var forked = browser.forkNewDriverInstance(); - * fork.get('page1'); - * fork.restart().then(function(fork) { - * fork.get('page2'); // 'page2' gotten by restarted fork - * }); - * - * // Running against forked browsers, with the control flow disabled - * var forked = await browser.forkNewDriverInstance().ready; + * // Running against forked browsers + * var forked = await browser.forkNewDriverInstance(); * await fork.get('page1'); * fork = await fork.restart(); * await fork.get('page2'); // 'page2' gotten by restarted fork @@ -522,33 +453,10 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { * }); * browser.restart(); * - * @returns {webdriver.promise.Promise} A promise resolving to the restarted + * @returns {Promise} A promise resolving to the restarted * browser */ - restart(): wdpromise.Promise { - return; - } - - /** - * Like `restart`, but instead of returning a promise resolving to the new browser instance, - * returns the new browser instance directly. Can only be used when the control flow is enabled. - * - * @example - * // Running against global browser - * browser.get('page1'); - * browser.restartSync(); - * browser.get('page2'); // 'page2' gotten by restarted browser - * - * // Running against forked browsers - * var forked = browser.forkNewDriverInstance(); - * fork.get('page1'); - * fork = fork.restartSync(); - * fork.get('page2'); // 'page2' gotten by restarted fork - * - * @throws {TypeError} Will throw an error if the control flow is not enabled - * @returns {ProtractorBrowser} The restarted browser - */ - restartSync(): ProtractorBrowser { + restart(): Promise { return; } @@ -571,21 +479,21 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { * @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 + * @returns {!Promise} A promise that will resolve to * the scripts return value. * @template T */ public executeScriptWithDescription( - script: string|Function, description: string, ...scriptArgs: any[]): wdpromise.Promise { + script: string|Function, description: string, ...scriptArgs: any[]): Promise { 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); + // TODO(selenium4): schedule does not exist on driver. Should use execute instead. + return (this.driver as any) + .execute(new Command(CommandName.EXECUTE_SCRIPT) + .setParameter('script', script) + .setParameter('args', scriptArgs)); } /** @@ -596,21 +504,22 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { * @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 + * @returns {!Promise} A promise that will resolve to * the * scripts return value. * @template T */ private executeAsyncScript_(script: string|Function, description: string, ...scriptArgs: any[]): - wdpromise.Promise { + Promise { + // TODO(selenium4): decide what to do with description. 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); + // TODO(selenium4): fix typings. driver.execute should exist + return (this.driver as any) + .execute(new Command(CommandName.EXECUTE_ASYNC_SCRIPT) + .setParameter('script', script) + .setParameter('args', scriptArgs)); } /** @@ -621,119 +530,93 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { * * @param {string=} opt_description An optional description to be added * to webdriver logs. - * @returns {!webdriver.promise.Promise} A promise that will resolve to the + * @returns {!Promise} A promise that will resolve to the * scripts return value. */ - waitForAngular(opt_description?: string): wdpromise.Promise { + async waitForAngular(opt_description?: string): Promise { let description = opt_description ? ' - ' + opt_description : ''; - if (this.ignoreSynchronization) { - return this.driver.controlFlow().execute(() => { - return true; - }, 'Ignore Synchronization Protractor.waitForAngular()'); + if (!await this.waitForAngularEnabled()) { + return true; } - let runWaitForAngularScript: () => wdpromise.Promise = () => { + let runWaitForAngularScript = async(): Promise => { if (this.plugins_.skipAngularStability() || this.bpClient) { - return this.driver.controlFlow().execute(() => { - return wdpromise.when(null); - }, 'bpClient or plugin stability override'); + return null; } else { - // Need to wrap this so that we read rootEl in the control flow, not synchronously. - return this.angularAppRoot().then((rootEl: string) => { - return this.executeAsyncScript_( - clientSideScripts.waitForAngular, 'Protractor.waitForAngular()' + description, - rootEl); - }); + let rootEl = await this.angularAppRoot(); + return this.executeAsyncScript_( + clientSideScripts.waitForAngular, `Protractor.waitForAngular() ${description}`, rootEl); } }; - return runWaitForAngularScript() - .then((browserErr: Function) => { - if (browserErr) { - throw new Error( - 'Error while waiting for Protractor to ' + - 'sync with the page: ' + JSON.stringify(browserErr)); + try { + let browserErr = await runWaitForAngularScript(); + if (browserErr) { + throw new Error( + 'Error while waiting for Protractor to ' + + 'sync with the page: ' + JSON.stringify(browserErr)); + } + await this.plugins_.waitForPromise(this); + + await this.driver.wait(async () => { + let results = await this.plugins_.waitForCondition(this); + return results.reduce((x, y) => x && y, true); + }, this.allScriptsTimeout, 'Plugins.waitForCondition()'); + } catch (err) { + let timeout: RegExpExecArray; + 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) { + let errMsg = `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 += '\nWhile waiting for element with locator' + description; + } + let pendingTimeoutsPromise: Promise; + if (this.trackOutstandingTimeouts_) { + pendingTimeoutsPromise = this.executeScriptWithDescription( + 'return window.NG_PENDING_TIMEOUTS', + 'Protractor.waitForAngular() - getting pending timeouts' + description); + } else { + pendingTimeoutsPromise = Promise.resolve(); + } + let pendingHttpsPromise = this.executeScriptWithDescription( + clientSideScripts.getPendingHttpRequests, + 'Protractor.waitForAngular() - getting pending https' + description, + this.internalRootEl); + + let arr = await Promise.all([pendingTimeoutsPromise, pendingHttpsPromise]); + + let pendingTimeouts = arr[0] || []; + let pendingHttps = arr[1] || []; + + let key: string, pendingTasks: string[] = []; + for (key in pendingTimeouts) { + if (pendingTimeouts.hasOwnProperty(key)) { + pendingTasks.push(' - $timeout: ' + pendingTimeouts[key]); } - }) - .then( - () => { - return this.driver.controlFlow() - .execute( - () => { - return this.plugins_.waitForPromise(this); - }, - 'Plugins.waitForPromise()') - .then(() => { - return this.driver.wait(() => { - return this.plugins_.waitForCondition(this).then((results: boolean[]) => { - return results.reduce((x, y) => x && y, true); - }); - }, this.allScriptsTimeout, 'Plugins.waitForCondition()'); - }); - }, - (err: Error) => { - let timeout: RegExpExecArray; - 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) { - let errMsg = `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 += '\nWhile waiting for element with locator' + description; - } - let pendingTimeoutsPromise: wdpromise.Promise; - if (this.trackOutstandingTimeouts_) { - pendingTimeoutsPromise = this.executeScriptWithDescription( - 'return window.NG_PENDING_TIMEOUTS', - 'Protractor.waitForAngular() - getting pending timeouts' + description); - } else { - pendingTimeoutsPromise = wdpromise.when({}); - } - let pendingHttpsPromise = this.executeScriptWithDescription( - clientSideScripts.getPendingHttpRequests, - 'Protractor.waitForAngular() - getting pending https' + description, - this.internalRootEl); - - return wdpromise.all([pendingTimeoutsPromise, pendingHttpsPromise]) - .then( - (arr: any[]) => { - let pendingTimeouts = arr[0] || []; - let pendingHttps = arr[1] || []; - - let key: string, pendingTasks: string[] = []; - 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 += '. \nThe following tasks were pending:\n'; - errMsg += pendingTasks.join('\n'); - } - err.message = errMsg; - throw err; - }, - () => { - err.message = errMsg; - throw err; - }); - } else { - throw err; - } - }); + } + for (key in pendingHttps) { + pendingTasks.push(' - $http: ' + pendingHttps[key].url); + } + if (pendingTasks.length) { + errMsg += '. \nThe following tasks were pending:\n'; + errMsg += pendingTasks.join('\n'); + } + err.message = errMsg; + } + throw err; + } } /** @@ -749,20 +632,20 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { /** * 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 + * @returns {!Promise} A promise that will be resolved to an * array of the located {@link webdriver.WebElement}s. */ - findElements(locator: Locator): wdpromise.Promise { + findElements(locator: Locator): Promise { 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 + * @returns {!Promise} A promise that will resolve to whether * the element is present on the page. */ - isElementPresent(locatorOrElement: Locator|WebElement|ElementFinder): wdpromise.Promise { + isElementPresent(locatorOrElement: Locator|WebElement|ElementFinder): Promise { let element: ElementFinder; if (locatorOrElement instanceof ElementFinder) { element = locatorOrElement; @@ -827,7 +710,7 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { */ getRegisteredMockModules(): Array { return this.mockModules_.map(module => module.script); - }; + } /** * Add the base mock modules used for all Protractor tests. @@ -849,147 +732,117 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { * the wrapped webdriver directly. * * @example - * browser.get('https://angularjs.org/'); - * expect(browser.getCurrentUrl()).toBe('https://angularjs.org/'); + * await browser.get('https://angularjs.org/'); + * expect(await browser.getCurrentUrl()).toBe('https://angularjs.org/'); * * @param {string} destination Destination URL. * @param {number=} opt_timeout Number of milliseconds to wait for Angular to * start. */ - get(destination: string, timeout = this.getPageTimeout) { + async get(destination: string, timeout = this.getPageTimeout) { destination = this.baseUrl.indexOf('file://') === 0 ? this.baseUrl + destination : url.resolve(this.baseUrl, destination); - if (this.ignoreSynchronization) { - return this.driver.get(destination) - .then(() => this.driver.controlFlow().execute(() => this.plugins_.onPageLoad(this))) - .then(() => null); + if (!await this.waitForAngularEnabled()) { + await this.driver.get(destination); + await this.plugins_.onPageLoad(this); + return; } let msg = (str: string) => { return 'Protractor.get(' + destination + ') - ' + str; }; - return this.driver.controlFlow() - .execute(() => { - return wdpromise.when(null); - }) - .then(() => { - if (this.bpClient) { - return this.driver.controlFlow().execute(() => { - return this.bpClient.setWaitEnabled(false); - }); - } - }) - .then(() => { - // Go to reset url - return this.driver.get(this.resetUrl); - }) - .then(() => { - // Set defer label and navigate - return this.executeScriptWithDescription( - 'window.name = "' + DEFER_LABEL + '" + window.name;' + - 'window.location.replace("' + destination + '");', - msg('reset url')); - }) - .then(() => { - // We need to make sure the new url has loaded before - // we try to execute any asynchronous scripts. - return this.driver.wait(() => { - return this.executeScriptWithDescription('return window.location.href;', msg('get url')) - .then( - (url: any) => { - return url !== this.resetUrl; - }, - (err: IError) => { - if (err.code == 13 || err.name === 'JavascriptError') { - // 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(() => { - // Run Plugins - return this.driver.controlFlow().execute(() => { - return this.plugins_.onPageLoad(this); - }); - }) - .then(() => { - // Make sure the page is an Angular page. - return this - .executeAsyncScript_( - clientSideScripts.testForAngular, msg('test for angular'), - Math.floor(timeout / 1000), this.ng12Hybrid) - .then( - (angularTestResult: {ver: number, message: string}) => { - let angularVersion = angularTestResult.ver; - if (!angularVersion) { - let 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; - }, - (err: Error) => { - throw new Error('Error while running testForAngular: ' + err.message); - }); - }) - .then((angularVersion) => { - // Load Angular Mocks - if (angularVersion === 1) { - // At this point, Angular will pause for us until angular.resumeBootstrap is called. - let moduleNames: string[] = []; - let modulePromise: wdpromise.Promise = wdpromise.when(null); - for (const {name, script, args} of this.mockModules_) { - moduleNames.push(name); - let executeScriptArgs = [script, msg('add mock module ' + name), ...args]; - modulePromise = modulePromise.then( - () => this.executeScriptWithDescription.apply(this, executeScriptArgs) - .then(null, (err: Error) => { - throw new Error( - 'Error while running module script ' + name + ': ' + err.message); - })); - } - - return modulePromise.then( - () => this.executeScriptWithDescription( - 'window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__ = ' + - 'angular.resumeBootstrap(arguments[0]);', - msg('resume bootstrap'), moduleNames)); - } else { - // TODO: support mock modules in Angular2. For now, error if someone - // has tried to use one. - if (this.mockModules_.length > 1) { - throw 'Trying to load mock modules on an Angular v2+ app is not yet supported.'; - } - } - }) - .then(() => { - // Reset bpClient sync - if (this.bpClient) { - return this.driver.controlFlow().execute(() => { - return this.bpClient.setWaitEnabled(!this.internalIgnoreSynchronization); + if (this.bpClient) { + await this.bpClient.setWaitEnabled(false); + } + // Go to reset url + await this.driver.get(this.resetUrl); + + // Set defer label and navigate + await this.executeScriptWithDescription( + 'window.name = "' + DEFER_LABEL + '" + window.name;' + + 'window.location.replace("' + destination + '");', + msg('reset url')); + + // We need to make sure the new url has loaded before + // we try to execute any asynchronous scripts. + await this.driver.wait(() => { + return this.executeScriptWithDescription('return window.location.href;', msg('get url')) + .then( + (url: any) => { + return url !== this.resetUrl; + }, + (err: IError) => { + if (err.code == 13 || err.name === 'JavascriptError') { + // 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'); + + // Run Plugins + await this.plugins_.onPageLoad(this); + + let angularVersion: number; + try { + // Make sure the page is an Angular page. + const angularTestResult: {ver: number, message: string} = await this.executeAsyncScript_( + clientSideScripts.testForAngular, msg('test for angular'), Math.floor(timeout / 1000), + this.ng12Hybrid); + angularVersion = angularTestResult.ver; + + if (!angularVersion) { + let 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`); + } + } catch (err) { + throw new Error('Error while running testForAngular: ' + err.message); + } + + // Load Angular Mocks + if (angularVersion === 1) { + // At this point, Angular will pause for us until angular.resumeBootstrap is called. + let moduleNames: string[] = []; + for (const {name, script, args} of this.mockModules_) { + moduleNames.push(name); + let executeScriptArgs = [script, msg('add mock module ' + name), ...args]; + await this.executeScriptWithDescription.apply(this, executeScriptArgs) + .then(null, (err: Error) => { + throw new Error('Error while running module script ' + name + ': ' + err.message); }); - } - }) - .then(() => { - // Run Plugins - return this.driver.controlFlow().execute(() => { - return this.plugins_.onPageStable(this); - }); - }) - .then(() => null); + } + + await this.executeScriptWithDescription( + 'window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__ = ' + + 'angular.resumeBootstrap(arguments[0]);', + msg('resume bootstrap'), moduleNames); + } else { + // TODO: support mock modules in Angular2. For now, error if someone + // has tried to use one. + if (this.mockModules_.length > 1) { + throw 'Trying to load mock modules on an Angular v2+ app is not yet supported.'; + } + } + + // Reset bpClient sync + if (this.bpClient) { + await this.bpClient.setWaitEnabled(!this.internalIgnoreSynchronization); + } + + // Run Plugins + await this.plugins_.onPageStable(this); } /** @@ -1002,17 +855,14 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { * * @param {number=} opt_timeout Number of milliseconds to wait for Angular to start. */ - refresh(opt_timeout?: number) { - if (this.ignoreSynchronization) { + async refresh(opt_timeout?: number) { + if (!await this.waitForAngularEnabled()) { return this.driver.navigate().refresh(); } - return this - .executeScriptWithDescription( - 'return window.location.href', 'Protractor.refresh() - getUrl') - .then((href: string) => { - return this.get(href, opt_timeout); - }); + const href = await this.executeScriptWithDescription( + 'return window.location.href', 'Protractor.refresh() - getUrl'); + return this.get(href, opt_timeout); } /** @@ -1029,28 +879,23 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { * Browse to another page using in-page navigation. * * @example - * browser.get('http://angular.github.io/protractor/#/tutorial'); - * browser.setLocation('api'); - * expect(browser.getCurrentUrl()) + * await browser.get('http://angular.github.io/protractor/#/tutorial'); + * await browser.setLocation('api'); + * expect(await 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 + * @returns {!Promise} A promise that will resolve once * page has been changed. */ - setLocation(url: string): wdpromise.Promise { - return this.waitForAngular() - .then(() => this.angularAppRoot()) - .then( - (rootEl) => - this.executeScriptWithDescription( - clientSideScripts.setLocation, 'Protractor.setLocation()', rootEl, url) - .then((browserErr: Error) => { - if (browserErr) { - throw 'Error while navigating to \'' + url + - '\' : ' + JSON.stringify(browserErr); - } - })); + async setLocation(url: string): Promise { + await this.waitForAngular(); + const rootEl = await this.angularAppRoot(); + const browserErr = await this.executeScriptWithDescription( + clientSideScripts.setLocation, 'Protractor.setLocation()', rootEl, url); + if (browserErr) { + throw 'Error while navigating to \'' + url + '\' : ' + JSON.stringify(browserErr); + } } /** @@ -1062,132 +907,19 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { * * @deprecated Please use `browser.getCurrentUrl()` * @example - * browser.get('http://angular.github.io/protractor/#/api'); - * expect(browser.getLocationAbsUrl()) + * await browser.get('http://angular.github.io/protractor/#/api'); + * expect(await browser.getLocationAbsUrl()) * .toBe('http://angular.github.io/protractor/#/api'); - * @returns {webdriver.promise.Promise} The current absolute url from + * @returns {Promise} The current absolute url from * AngularJS. */ - getLocationAbsUrl(): wdpromise.Promise { + async getLocationAbsUrl(): Promise { logger.warn( '`browser.getLocationAbsUrl()` is deprecated, please use `browser.getCurrentUrl` instead.'); - return this.waitForAngular() - .then(() => this.angularAppRoot()) - .then( - (rootEl) => this.executeScriptWithDescription( - clientSideScripts.getLocationAbsUrl, 'Protractor.getLocationAbsUrl()', 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. - */ - debugger() { - // jshint debug: true - return this.driver.executeScript(clientSideScripts.installInBrowser) - .then(() => wdpromise.controlFlow().execute(() => { - debugger; - }, 'add breakpoint to control flow')); - } - - /** - * See browser.explore(). - */ - enterRepl(opt_debugPort?: number) { - return this.explore(opt_debugPort); - } - - /** - * Beta (unstable) explore function for entering the repl loop from - * any point in the control flow. Use browser.explore() 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.explore(); - * // 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 - */ - explore(opt_debugPort?: number) { - let debuggerClientPath = __dirname + '/debugger/clients/explorer.js'; - let onStartFn = (firstTime: boolean) => { - logger.info(); - if (firstTime) { - 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.initBlocking(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 - */ - pause(opt_debugPort?: number): wdpromise.Promise { - if (this.debugHelper.isAttached()) { - logger.info('Encountered browser.pause(), but debugger already attached.'); - return wdpromise.when(true); - } - let debuggerClientPath = __dirname + '/debugger/clients/wddebugger.js'; - let onStartFn = (firstTime: boolean) => { - 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(); - } - }; - this.debugHelper.init(debuggerClientPath, onStartFn, opt_debugPort); + await this.waitForAngular(); + const rootEl = await this.angularAppRoot(); + return await this.executeScriptWithDescription( + clientSideScripts.getLocationAbsUrl, 'Protractor.getLocationAbsUrl()', rootEl); } /** diff --git a/lib/cli.ts b/lib/cli.ts index a84888fa8..225dd728f 100644 --- a/lib/cli.ts +++ b/lib/cli.ts @@ -201,7 +201,7 @@ function processFilePatterns_(list: string): Array { return list.split(',').map(function(spec) { return path.resolve(process.cwd(), spec); }); -}; +} if (argv.specs) { argv.specs = processFilePatterns_(argv.specs); diff --git a/lib/config.ts b/lib/config.ts index 0e817ec31..c04849088 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -1,5 +1,3 @@ -import {WebDriver} from 'selenium-webdriver'; - import {PluginConfig} from './plugins'; export interface Config { @@ -238,12 +236,6 @@ export interface Config { */ firefoxPath?: string; - // ---- 8. To re-use an existing WebDriver object --------------------------- - - // This would not appear in a configuration file. Instead a configuration - // object would be created that includes an existing webdriver. - seleniumWebDriver?: WebDriver; - // --------------------------------------------------------------------------- // ----- What tests to run --------------------------------------------------- // --------------------------------------------------------------------------- @@ -731,6 +723,11 @@ export interface Config { nodeDebug?: boolean; debuggerServerPort?: number; frameworkPath?: string; + + /** + * Deprecated: Element explorer depends on the WebDriver control flow, and + * thus is no longer supported. + */ elementExplorer?: any; debug?: boolean; unknownFlags_?: string[]; diff --git a/lib/debugger.ts b/lib/debugger.ts deleted file mode 100644 index b22cfa0dd..000000000 --- a/lib/debugger.ts +++ /dev/null @@ -1,280 +0,0 @@ -import * as net from 'net'; -import {promise as wdpromise, WebElement} from 'selenium-webdriver'; -import * as util from 'util'; - -import {ProtractorBrowser} from './browser'; -import {Locator} from './locators'; -import {Logger} from './logger'; -import {Ptor} from './ptor'; -import * as helper from './util'; -let breakpointHook = require('./breakpointhook.js'); - -declare var global: any; -declare var process: any; - -let logger = new Logger('protractor'); - -export class DebugHelper { - /** - * Set to true when we validate that the debug port is open. Since the debug - * port is held open forever once the debugger is attached, it's important - * we only do validation once. - */ - debuggerValidated_: boolean; - - dbgCodeExecutor: any; - - constructor(private browserUnderDebug_: ProtractorBrowser) {} - - - initBlocking(debuggerClientPath: string, onStartFn: Function, opt_debugPort?: number) { - this.init_(debuggerClientPath, true, onStartFn, opt_debugPort); - } - - init(debuggerClientPath: string, onStartFn: Function, opt_debugPort?: number) { - this.init_(debuggerClientPath, false, onStartFn, opt_debugPort); - } - - /** - * 1) Set up helper functions for debugger clients to call on (e.g. - * execute code, get autocompletion). - * 2) Enter process into debugger mode. (i.e. process._debugProcess). - * 3) Invoke the debugger client specified by debuggerClientPath. - * - * @param {string} debuggerClientPath Absolute path of debugger client to use. - * @param {boolean} blockUntilExit Whether to block the flow until process exit or resume - * immediately. - * @param {Function} onStartFn Function to call when the debugger starts. The - * function takes a single parameter, which represents whether this is the - * first time that the debugger is called. - * @param {number=} opt_debugPort Optional port to use for the debugging - * process. - * - * @return {Promise} If blockUntilExit, a promise resolved when the debugger process - * exits. Otherwise, resolved when the debugger process is ready to begin. - */ - init_( - debuggerClientPath: string, blockUntilExit: boolean, onStartFn: Function, - opt_debugPort?: number) { - const vm_ = require('vm'); - let flow = wdpromise.controlFlow(); - - interface Context { - require: any; - [key: string]: any; - } - let context: Context = {require: require}; - global.list = (locator: Locator) => { - return (global.protractor).browser.findElements(locator).then((arr: WebElement[]) => { - let found: string[] = []; - for (let i = 0; i < arr.length; ++i) { - arr[i].getText().then((text: string) => { - found.push(text); - }); - } - return found; - }); - }; - for (let key in global) { - context[key] = global[key]; - } - let sandbox = vm_.createContext(context); - - let debuggingDone = wdpromise.defer(); - - // We run one flow.execute block for the debugging session. All - // subcommands should be scheduled under this task. - let executePromise = flow.execute(() => { - process['debugPort'] = opt_debugPort || process['debugPort']; - this.validatePortAvailability_(process['debugPort']).then((firstTime: boolean) => { - onStartFn(firstTime); - - let args = [process.pid, process['debugPort']]; - if (this.browserUnderDebug_.debuggerServerPort) { - args.push(this.browserUnderDebug_.debuggerServerPort); - } - let nodedebug = require('child_process').fork(debuggerClientPath, args); - process.on('exit', function() { - nodedebug.kill('SIGTERM'); - }); - nodedebug - .on('message', - (m: string) => { - if (m === 'ready') { - breakpointHook(); - if (!blockUntilExit) { - debuggingDone.fulfill(); - } - } - }) - .on('exit', () => { - // Clear this so that we know it's ok to attach a debugger - // again. - this.dbgCodeExecutor = null; - debuggingDone.fulfill(); - }); - }); - return debuggingDone.promise; - }, 'debugging tasks'); - - // Helper used only by debuggers at './debugger/modes/*.js' to insert code - // into the control flow, via debugger 'evaluate' protocol. - // In order to achieve this, we maintain a task at the top of the control - // flow, so that we can insert frames into it. - // To be able to simulate callback/asynchronous code, we poll this object - // whenever `breakpointHook` is called. - this.dbgCodeExecutor = { - execPromise_: undefined, // Promise pointing to currently executing command. - execPromiseResult_: undefined, // Return value of promise. - execPromiseError_: undefined, // Error from promise. - - // A dummy repl server to make use of its completion function. - replServer_: require('repl').start({ - input: {on: function() {}, resume: function() {}}, - // dummy readable stream - output: {write: function() {}}, // dummy writable stream - useGlobal: true - }), - - // Execute a function, which could yield a value or a promise, - // and allow its result to be accessed synchronously - execute_: function(execFn_: Function) { - this.execPromiseResult_ = this.execPromiseError_ = undefined; - - this.execPromise_ = execFn_(); - // Note: This needs to be added after setting execPromise to execFn, - // or else we cause this.execPromise_ to get stuck in pending mode - // at our next breakpoint. - this.execPromise_.then( - (result: Object) => { - this.execPromiseResult_ = result; - breakpointHook(); - }, - (err: Error) => { - this.execPromiseError_ = err; - breakpointHook(); - }); - }, - - // Execute a piece of code. - // Result is a string representation of the evaluation. - execute: function(code: Function) { - let execFn_ = () => { - // Run code through vm so that we can maintain a local scope which is - // isolated from the rest of the execution. - let res: wdpromise.Promise; - try { - res = vm_.runInContext(code, sandbox); - } catch (e) { - res = wdpromise.when('Error while evaluating command: ' + e); - } - if (!wdpromise.isPromise(res)) { - res = wdpromise.when(res); - } - - return res.then((res: any) => { - if (res === undefined) { - return undefined; - } else { - // The '' forces res to be expanded into a string instead of just - // '[Object]'. Then we remove the extra space caused by the '' - // using substring. - return util.format.apply(this, ['', res]).substring(1); - } - }); - }; - this.execute_(execFn_); - }, - - // Autocomplete for a line. - // Result is a JSON representation of the autocomplete response. - complete: function(line: string) { - let execFn_ = () => { - let deferred = wdpromise.defer(); - this.replServer_.complete(line, (err: any, res: any) => { - if (err) { - deferred.reject(err); - } else { - deferred.fulfill(JSON.stringify(res)); - } - }); - return deferred.promise; - }; - this.execute_(execFn_); - }, - - // Code finished executing. - resultReady: function() { - return !(this.execPromise_.state_ === 'pending'); - }, - - // Get asynchronous results synchronously. - // This will throw if result is not ready. - getResult: function() { - if (!this.resultReady()) { - throw new Error('Result not ready'); - } - if (this.execPromiseError_) { - throw this.execPromiseError_; - } - return this.execPromiseResult_; - } - }; - - return executePromise; - } - - /** - * Validates that the port is free to use. This will only validate the first - * time it is called. The reason is that on subsequent calls, the port will - * already be bound to the debugger, so it will not be available, but that is - * okay. - * - * @returns {Promise} A promise that becomes ready when the - * validation - * is done. The promise will resolve to a boolean which represents whether - * this is the first time that the debugger is called. - */ - private validatePortAvailability_(port: number): wdpromise.Promise { - if (this.debuggerValidated_) { - return wdpromise.when(false); - } - - let doneDeferred = wdpromise.defer(); - - // Resolve doneDeferred if port is available. - let tester = net.connect({port: port}, () => { - doneDeferred.reject( - 'Port ' + port + ' is already in use. Please specify ' + - 'another port to debug.'); - }); - tester.once('error', (err: NodeJS.ErrnoException) => { - if (err.code === 'ECONNREFUSED') { - tester - .once( - 'close', - () => { - doneDeferred.fulfill(true); - }) - .end(); - } else { - doneDeferred.reject( - 'Unexpected failure testing for port ' + port + ': ' + JSON.stringify(err)); - } - }); - - return doneDeferred.promise.then( - (firstTime: boolean) => { - this.debuggerValidated_ = true; - return firstTime; - }, - (err: string) => { - console.error(err); - return process.exit(1) as never; - }); - } - - public isAttached(): boolean { - return !!this.dbgCodeExecutor; - } -} diff --git a/lib/debugger/clients/explorer.js b/lib/debugger/clients/explorer.js deleted file mode 100644 index b0dee20c2..000000000 --- a/lib/debugger/clients/explorer.js +++ /dev/null @@ -1,157 +0,0 @@ -var repl = require('repl'); -var debuggerCommons = require('../debuggerCommons'); -var CommandRepl = require('../modes/commandRepl'); - -/** - * BETA BETA BETA - * Custom explorer to test protractor commands. - * - * @constructor - */ -var WdRepl = function() { - this.client; -}; - -/** - * Instantiate a server to handle IO. - * @param {number} port The port to start the server. - * @private - */ -WdRepl.prototype.initServer_ = function(port) { - var net = require('net'); - var self = this; - var cmdRepl = new CommandRepl(this.client); - - var received = ''; - net.createServer(function(sock) { - sock.on('data', function(data) { - received += data.toString(); - var eolIndex = received.indexOf('\r\n'); - if (eolIndex === 0) { - return; - } - var input = received.substring(0, eolIndex); - received = received.substring(eolIndex + 2); - if (data[0] === 0x1D) { - // '^]': term command - self.client.req({command: 'disconnect'}, function() { - // Intentionally blank. - }); - sock.end(); - // TODO(juliemr): Investigate why this is necessary. At this point, there - // should be no active listeners so this process should just exit - // by itself. - process.exit(0); - } else if (input[input.length - 1] === '\t') { - // If the last character is the TAB key, this is an autocomplete - // request. We use everything before the TAB as the init data to feed - // into autocomplete. - input = input.substring(0, input.length - 1); - cmdRepl.complete(input, function(err, res) { - if (err) { - sock.write('ERROR: ' + err + '\r\n'); - } else { - sock.write(JSON.stringify(res) + '\r\n'); - } - }); - } else { - // Normal input - input = input.trim(); - cmdRepl.stepEval(input, function(err, res) { - if (err) { - sock.write('ERROR: ' + err + '\r\n'); - return; - } - if (res === undefined) { - res = ''; - } - sock.write(res + '\r\n'); - }); - } - }); - }).listen(port); - - console.log('Server listening on 127.0.0.1:' + port); -}; - -/** - * Instantiate a repl to handle IO. - * @private - */ -WdRepl.prototype.initRepl_ = function() { - var self = this; - var cmdRepl = new CommandRepl(this.client); - - // Eval function for processing a single step in repl. - var stepEval = function(cmd, context, filename, callback) { - // The command that eval feeds is of the form '(CMD\n)', so we trim the - // double quotes and new line. - cmd = debuggerCommons.trimReplCmd(cmd); - cmdRepl.stepEval(cmd, function(err, res) { - // Result is a string representation of the evaluation. - if (res !== undefined) { - console.log(res); - } - callback(err, undefined); - }); - }; - - var replServer = repl.start({ - prompt: cmdRepl.prompt, - input: process.stdin, - output: process.stdout, - eval: stepEval, - useGlobal: false, - ignoreUndefined: true, - completer: cmdRepl.complete.bind(cmdRepl) - }); - - replServer.on('exit', function() { - console.log('Element Explorer Exiting...'); - self.client.req({command: 'disconnect'}, function() { - // TODO(juliemr): Investigate why this is necessary. At this point, there - // should be no active listeners so this process should just exit - // by itself. - process.exit(0); - }); - }); -}; - -/** - * Instantiate a repl or a server. - * @private - */ -WdRepl.prototype.initReplOrServer_ = function() { - // Note instead of starting either repl or server, another approach is to - // feed the server socket into the repl as the input/output streams. The - // advantage is that the process becomes much more realistic because now we're - // using the normal repl. However, it was not possible to test autocomplete - // this way since we cannot immitate the TAB key over the wire. - var debuggerServerPort = process.argv[4]; - if (debuggerServerPort) { - this.initServer_(debuggerServerPort); - } else { - this.initRepl_(); - } -}; - -/** - * Initiate the debugger. - * @public - */ -WdRepl.prototype.init = function() { - var self = this; - this.client = debuggerCommons.attachDebugger(process.argv[2], process.argv[3]); - this.client.once('ready', function() { - debuggerCommons.setEvaluateBreakpoint(self.client, function() { - process.send('ready'); - self.client.reqContinue(function() { - // Intentionally blank. - }); - }); - self.initReplOrServer_(); - }); -}; - -var wdRepl = new WdRepl(); -wdRepl.init(); diff --git a/lib/debugger/clients/wddebugger.js b/lib/debugger/clients/wddebugger.js deleted file mode 100644 index f082e376d..000000000 --- a/lib/debugger/clients/wddebugger.js +++ /dev/null @@ -1,83 +0,0 @@ -var repl = require('repl'); -var debuggerCommons = require('../debuggerCommons'); -var DebuggerRepl = require('../modes/debuggerRepl'); - -/** - * Custom protractor debugger which steps through one control flow task at a time. - * - * @constructor - */ -var WdDebugger = function() { - this.client; - this.replServer; - this.dbgRepl; -}; - -/** - * Eval function for processing a single step in repl. - * @private - * @param {string} cmd - * @param {object} context - * @param {string} filename - * @param {function} callback - */ -WdDebugger.prototype.stepEval_ = function(cmd, context, filename, callback) { - // The loop won't come back until 'callback' is called. - // Note - node's debugger gets around this by adding custom objects - // named 'c', 's', etc to the REPL context. They have getters which - // perform the desired function, and the callback is stored for later use. - // Think about whether this is a better pattern. - - cmd = debuggerCommons.trimReplCmd(cmd); - this.dbgRepl.stepEval(cmd, callback); -}; - -/** - * Instantiate all repl objects, and debuggerRepl as current and start repl. - * @private - */ -WdDebugger.prototype.initRepl_ = function() { - var self = this; - this.dbgRepl = new DebuggerRepl(this.client); - - // We want the prompt to show up only after the controlflow text prints. - this.dbgRepl.printControlFlow_(function() { - self.replServer = repl.start({ - prompt: self.dbgRepl.prompt, - input: process.stdin, - output: process.stdout, - eval: self.stepEval_.bind(self), - useGlobal: false, - ignoreUndefined: true, - completer: self.dbgRepl.complete.bind(self.dbgRepl) - }); - - self.replServer.on('exit', function() { - console.log('Resuming code execution'); - self.client.req({command: 'disconnect'}, function() { - process.exit(); - }); - }); - }); -}; - -/** - * Initiate the debugger. - * @public - */ -WdDebugger.prototype.init = function() { - var self = this; - this.client = debuggerCommons.attachDebugger(process.argv[2], process.argv[3]); - this.client.once('ready', function() { - debuggerCommons.setWebDriverCommandBreakpoint(self.client, function() { - process.send('ready'); - self.client.reqContinue(function() { - // Intentionally blank. - }); - }); - self.initRepl_(); - }); -}; - -var wdDebugger = new WdDebugger(); -wdDebugger.init(); diff --git a/lib/debugger/debuggerCommons.js b/lib/debugger/debuggerCommons.js deleted file mode 100644 index f7b3b7833..000000000 --- a/lib/debugger/debuggerCommons.js +++ /dev/null @@ -1,113 +0,0 @@ -var baseDebugger; -try { - baseDebugger = require('_debugger'); -} catch (e) { - if (e.code == 'MODULE_NOT_FOUND') { - console.log('***********************************************************'); - console.log('* WARNING: _debugger module not available on Node.js 8 *'); - console.log('* and higher. *'); - console.log('* *'); - console.log('* Use \'debugger\' keyword instead: *'); - console.log('* https://goo.gl/MvWqFh *'); - console.log('***********************************************************'); - } - throw e; -} -var path = require('path'); - -/** - * Create a debugger client and attach to a running protractor process. - * @param {number} pid Pid of the process to attach the debugger to. - * @param {number=} opt_port Port to set up the debugger connection over. - * @return {!baseDebugger.Client} The connected debugger client. - */ -exports.attachDebugger = function(pid, opt_port) { - var client = new baseDebugger.Client(); - var port = opt_port || process.debugPort; - - // Call this private function instead of sending SIGUSR1 because Windows. - process._debugProcess(pid); - - // Connect to debugger on port with retry 200ms apart. - var connectWithRetry = function(attempts) { - client.connect(port, 'localhost') - .on('error', function(e) { - if (attempts === 1) { - throw e; - } else { - setTimeout(function() { - connectWithRetry(attempts - 1); - }, 200); - } - }); - }; - connectWithRetry(10); - - return client; -}; - - -/** - * Set a breakpoint for evaluating REPL statements. - * This sets a breakpoint in Protractor's breakpointhook.js, so that we'll - * break after executing a command from the REPL. - */ -exports.setEvaluateBreakpoint = function(client, cb) { - client.setBreakpoint({ - type: 'scriptRegExp', - target: prepareDebuggerPath('built', 'breakpointhook.js'), - line: 2 - }, function(err, response) { - if (err) { - throw new Error(err); - } - cb(response.breakpoint); - }); -}; - -/** - * Set a breakpoint for moving forward by one webdriver command. - * This sets a breakpoint in selenium-webdriver/lib/http.js, and is - * extremely sensitive to the selenium version. It works for - * selenium-webdriver 3.0.1 - * This breaks on the following line in http.js: - * let request = buildRequest(this.customCommands_, this.w3c, command); - * And will need to break at a similar point in future selenium-webdriver - * versions. - */ -exports.setWebDriverCommandBreakpoint = function(client, cb) { - client.setBreakpoint({ - type: 'scriptRegExp', - target: prepareDebuggerPath('lib', 'http.js'), - line: 433 - }, function(err, response) { - if (err) { - throw new Error(err); - } - cb(response.breakpoint); - }); -}; - -/** - * Create a cross-platform friendly path for setting scriptRegExp breakpoints. - */ -function prepareDebuggerPath(...parts) { - return path.join(...parts) - .replace('\\', '\\\\') - .replace('.', '\\.'); -} - -/** - * Trim excess symbols from the repl command so that it is consistent with - * the user input. - * @param {string} cmd Cmd provided by the repl server. - * @return {string} The trimmed cmd. - */ -exports.trimReplCmd = function(cmd) { - // Given user input 'foobar', some versions of node provide '(foobar\n)', - // while other versions of node provide 'foobar\n'. - if (cmd.length >= 2 && cmd[0] === '(' && cmd[cmd.length - 1] === ')') { - cmd = cmd.substring(1, cmd.length - 1); - } - return cmd.slice(0, cmd.length - 1); -}; diff --git a/lib/debugger/modes/commandRepl.js b/lib/debugger/modes/commandRepl.js deleted file mode 100644 index 6608e5970..000000000 --- a/lib/debugger/modes/commandRepl.js +++ /dev/null @@ -1,127 +0,0 @@ -var REPL_INITIAL_SUGGESTIONS = [ - 'element(by.id(\'\'))', - 'element(by.css(\'\'))', - 'element(by.name(\'\'))', - 'element(by.binding(\'\'))', - 'element(by.xpath(\'\'))', - 'element(by.tagName(\'\'))', - 'element(by.className(\'\'))' -]; - -/** - * Repl to interactively run commands in the context of the test. - * - * @param {Client} node debugger client. - * @constructor - */ -var CommandRepl = function(client) { - this.client = client; - this.prompt = '> '; -}; - -/** - * Eval function for processing a single step in repl. - * Call callback with the result when complete. - * - * @public - * @param {string} expression - * @param {function} callback - */ -CommandRepl.prototype.stepEval = function(expression, callback) { - expression = expression.replace(/"/g, '\\\"'); - - var expr = 'browser.debugHelper.dbgCodeExecutor.execute("' + expression + '")'; - this.evaluate_(expr, callback); -}; - -/** - * Autocomplete user entries. - * Call callback with the suggestions. - * - * @public - * @param {string} line Initial user entry - * @param {function} callback - */ -CommandRepl.prototype.complete = function(line, callback) { - if (line === '') { - callback(null, [REPL_INITIAL_SUGGESTIONS, '']); - } else { - // TODO(juliemr): This is freezing the program! - line = line.replace(/"/g, '\\\"'); - var expr = 'browser.debugHelper.dbgCodeExecutor.complete("' + line + '")'; - this.evaluate_(expr, function(err, res) { - // Result is a JSON representation of the autocomplete response. - var result = res === undefined ? undefined : JSON.parse(res); - callback(err, result); - }); - } -}; - -/** - * Helper function to evaluate an expression remotely, and callback with - * the result. The expression can be a promise, in which case, the method - * will wait for the result and callback with the resolved value. - * - * @private - * @param {string} expression Expression to evaluate - * @param {function} callback - */ -CommandRepl.prototype.evaluate_ = function(expression, callback) { - var self = this; - var onbreak_ = function() { - self.client.req({ - command: 'evaluate', - arguments: { - frame: 0, - maxStringLength: 1000, - expression: 'browser.debugHelper.dbgCodeExecutor.resultReady()' - } - }, function(err, res) { - if (err) { - throw new Error('Error while checking if debugger expression result was ready.' + - 'Expression: ' + expression + ' Error: ' + err); - } - // If code finished executing, get result. - if (res.value) { - self.client.req({ - command: 'evaluate', - arguments: { - frame: 0, - maxStringLength: -1, - expression: 'browser.debugHelper.dbgCodeExecutor.getResult()' - } - }, function(err, res) { - try { - callback(err, res.value); - } catch (e) { - callback(e, undefined); - } - self.client.removeListener('break', onbreak_); - }); - } else { - // If we need more loops for the code to finish executing, continue - // until the next execute step. - self.client.reqContinue(function() { - // Intentionally blank. - }); - } - }); - }; - - this.client.on('break', onbreak_); - - this.client.req({ - command: 'evaluate', - arguments: { - frame: 0, - maxStringLength: 1000, - expression: expression - } - }, function() { - self.client.reqContinue(function() { - // Intentionally blank. - }); - }); -}; - -module.exports = CommandRepl; diff --git a/lib/debugger/modes/debuggerRepl.js b/lib/debugger/modes/debuggerRepl.js deleted file mode 100644 index 0d1c46266..000000000 --- a/lib/debugger/modes/debuggerRepl.js +++ /dev/null @@ -1,143 +0,0 @@ -var util = require('util'); - -var DBG_INITIAL_SUGGESTIONS = - ['repl', 'c', 'frame', 'scopes', 'scripts', 'source', 'backtrace']; - -/** - * Repl to step through webdriver test code. - * - * @param {Client} node debugger client. - * @constructor - */ -var DebuggerRepl = function(client) { - this.client = client; - this.prompt = '>>> '; -}; - -/** - * Eval function for processing a single step in repl. - * Call callback with the result when complete. - * - * @public - * @param {string} cmd - * @param {function} callback - */ -DebuggerRepl.prototype.stepEval = function(cmd, callback) { - switch (cmd) { - case 'c': - this.printNextStep_(callback); - this.client.reqContinue(function() { - // Intentionally blank. - }); - break; - case 'repl': - console.log('Error: using repl from browser.pause() has been removed. ' + - 'Please use browser.enterRepl instead.'); - callback(); - break; - case 'schedule': - this.printControlFlow_(callback); - break; - case 'frame': - this.client.req({command: 'frame'}, function(err, res) { - console.log(util.inspect(res, {colors: true})); - callback(); - }); - break; - case 'scopes': - this.client.req({command: 'scopes'}, function(err, res) { - console.log(util.inspect(res, {depth: 4, colors: true})); - callback(); - }); - break; - case 'scripts': - this.client.req({command: 'scripts'}, function(err, res) { - console.log(util.inspect(res, {depth: 4, colors: true})); - callback(); - }); - break; - case 'source': - this.client.req({command: 'source'}, function(err, res) { - console.log(util.inspect(res, {depth: 4, colors: true})); - callback(); - }); - break; - case 'backtrace': - this.client.req({command: 'backtrace'}, function(err, res) { - console.log(util.inspect(res, {depth: 4, colors: true})); - callback(); - }); - break; - default: - console.log('Unrecognized command.'); - callback(); - break; - } -}; - -/** - * Autocomplete user entries. - * Call callback with the suggestions. - * - * @public - * @param {string} line Initial user entry - * @param {function} callback - */ -DebuggerRepl.prototype.complete = function(line, callback) { - var suggestions = DBG_INITIAL_SUGGESTIONS.filter(function(suggestion) { - return suggestion.indexOf(line) === 0; - }); - console.log('suggestions'); - callback(null, [suggestions, line]); -}; - -/** - * Print the next command and setup the next breakpoint. - * - * @private - * @param {function} callback - */ -DebuggerRepl.prototype.printNextStep_ = function(callback) { - var self = this; - var onBreak_ = function() { - self.client.req({ - command: 'evaluate', - arguments: { - frame: 0, - maxStringLength: 1000, - expression: 'command.getName()' - } - }, function(err, res) { - // We ignore errors here because we'll get one from the initial break. - if (res.value) { - console.log('-- Next command: ' + res.value); - } - callback(); - }); - }; - this.client.once('break', onBreak_); -}; - -/** - * Print the controlflow. - * - * @private - * @param {function} callback - */ -DebuggerRepl.prototype.printControlFlow_ = function(callback) { - this.client.req({ - command: 'evaluate', - arguments: { - frame: 0, - maxStringLength: 4000, - expression: 'protractor.promise.controlFlow().getSchedule()' - } - }, function(err, controlFlowResponse) { - if (controlFlowResponse.value) { - console.log(controlFlowResponse.value); - } - callback(); - }); -}; - -module.exports = DebuggerRepl; diff --git a/lib/driverProviders/attachSession.ts b/lib/driverProviders/attachSession.ts index dd342a77e..ca385161b 100644 --- a/lib/driverProviders/attachSession.ts +++ b/lib/driverProviders/attachSession.ts @@ -3,8 +3,7 @@ * It is responsible for setting up the account object, tearing * it down, and setting up the driver correctly. */ -import * as q from 'q'; -import {promise as wdpromise, WebDriver} from 'selenium-webdriver'; +import {Session, WebDriver} from 'selenium-webdriver'; import {Config} from '../config'; import {Logger} from '../logger'; @@ -22,13 +21,12 @@ export class AttachSession extends DriverProvider { /** * Configure and launch (if applicable) the object's environment. - * @return {q.promise} A promise which will resolve when the environment is + * @return {Promise} A promise which will resolve when the environment is * ready to test. */ - protected setupDriverEnv(): q.Promise { + protected async setupDriverEnv(): Promise { logger.info('Using the selenium server at ' + this.config_.seleniumAddress); logger.info('Using session id - ' + this.config_.seleniumSessionId); - return q(undefined); } /** @@ -37,10 +35,12 @@ export class AttachSession extends DriverProvider { * @public * @return {WebDriver} webdriver instance */ - getNewDriver(): WebDriver { + async getNewDriver(): Promise { const httpClient = new http.HttpClient(this.config_.seleniumAddress); const executor = new http.Executor(httpClient); - const newDriver = WebDriver.attachToSession(executor, this.config_.seleniumSessionId); + const session = new Session(this.config_.seleniumSessionId, null); + + const newDriver = new WebDriver(session, executor); this.drivers_.push(newDriver); return newDriver; } @@ -50,7 +50,5 @@ export class AttachSession extends DriverProvider { * * @public */ - quitDriver(): wdpromise.Promise { - return wdpromise.when(undefined); - } + async quitDriver(): Promise {} } diff --git a/lib/driverProviders/browserStack.ts b/lib/driverProviders/browserStack.ts index 0994df7ba..8c85a840b 100644 --- a/lib/driverProviders/browserStack.ts +++ b/lib/driverProviders/browserStack.ts @@ -3,9 +3,7 @@ * It is responsible for setting up the account object, tearing * it down, and setting up the driver correctly. */ -import * as https from 'https'; -import * as q from 'q'; -import {Session, WebDriver} from 'selenium-webdriver'; +import {WebDriver} from 'selenium-webdriver'; import * as util from 'util'; import {Config} from '../config'; @@ -29,59 +27,54 @@ export class BrowserStack extends DriverProvider { * Hook to update the BrowserStack job status. * @public * @param {Object} update - * @return {q.promise} A promise that will resolve when the update is complete. + * @return {Promise} A promise that will resolve when the update is complete. */ - updateJob(update: any): q.Promise { - let deferredArray = this.drivers_.map((driver: WebDriver) => { - let deferred = q.defer(); + async updateJob(update: any): Promise { + let mappedDrivers = this.drivers_.map(async (driver: WebDriver) => { + let session = await driver.getSession(); - driver.getSession().then((session: Session) => { - - // Fetching BrowserStack session details. - this.browserstackClient.getSession( - session.getId(), function(error: Error, automate_session: any) { - if (error) { + // Fetching BrowserStack session details. + this.browserstackClient.getSession( + session.getId(), function(error: Error, automate_session: any) { + if (error) { + logger.info( + 'BrowserStack results available at ' + + 'https://www.browserstack.com/automate'); + } else { + if (automate_session && automate_session.browser_url) { + logger.info('BrowserStack results available at ' + automate_session.browser_url); + } else { logger.info( 'BrowserStack results available at ' + 'https://www.browserstack.com/automate'); - } else { - if (automate_session && automate_session.browser_url) { - logger.info('BrowserStack results available at ' + automate_session.browser_url); - } else { - logger.info( - 'BrowserStack results available at ' + - 'https://www.browserstack.com/automate'); - } } - }); + } + }); - let jobStatus = update.passed ? 'completed' : 'error'; - let statusObj = {status: jobStatus}; + let jobStatus = update.passed ? 'completed' : 'error'; + let statusObj = {status: jobStatus}; - // Updating status of BrowserStack session. - this.browserstackClient.updateSession( - session.getId(), statusObj, function(error: Error, automate_session: any) { - if (error) { - throw new BrowserError( - logger, 'Error updating BrowserStack pass/fail status: ' + util.inspect(error)); - } else { - logger.info(automate_session); - deferred.resolve(); - } - }); - }); - return deferred.promise; + // Updating status of BrowserStack session. + this.browserstackClient.updateSession( + session.getId(), statusObj, function(error: Error, automate_session: any) { + if (error) { + throw new BrowserError( + logger, 'Error updating BrowserStack pass/fail status: ' + util.inspect(error)); + } else { + logger.info(automate_session); + } + }); }); - return q.all(deferredArray); + + return Promise.all(mappedDrivers); } /** * Configure and launch (if applicable) the object's environment. - * @return {q.promise} A promise which will resolve when the environment is + * @return {promise} A promise which will resolve when the environment is * ready to test. */ - protected setupDriverEnv(): q.Promise { - let deferred = q.defer(); + protected async setupDriverEnv(): Promise { this.config_.capabilities['browserstack.user'] = this.config_.browserstackUser; this.config_.capabilities['browserstack.key'] = this.config_.browserstackKey; this.config_.seleniumAddress = 'http://hub.browserstack.com/wd/hub'; @@ -99,8 +92,6 @@ export class BrowserStack extends DriverProvider { (':' + this.config_.specs.toString().replace(/^.*[\\\/]/, '')); } - logger.info('Using BrowserStack selenium server at ' + this.config_.seleniumAddress); - deferred.resolve(); - return deferred.promise; + logger.info(`Using BrowserStack selenium server at ${this.config_.seleniumAddress}`); } } diff --git a/lib/driverProviders/direct.ts b/lib/driverProviders/direct.ts index 71f0d49e9..e7b0dc9db 100644 --- a/lib/driverProviders/direct.ts +++ b/lib/driverProviders/direct.ts @@ -4,11 +4,10 @@ * it down, and setting up the driver correctly. */ import * as fs from 'fs'; -import * as path from 'path'; -import * as q from 'q'; import {Capabilities, WebDriver} from 'selenium-webdriver'; -import {Driver as ChromeDriver, ServiceBuilder as ChromeServiceBuilder} from 'selenium-webdriver/chrome'; -import {Driver as FirefoxDriver} from 'selenium-webdriver/firefox'; +import {Driver as DriverForChrome, ServiceBuilder as ChromeServiceBuilder} from 'selenium-webdriver/chrome'; +import {Driver as DriverForFirefox, ServiceBuilder as FirefoxServiceBuilder} from 'selenium-webdriver/firefox'; +import {ChromeDriver, GeckoDriver} from 'webdriver-manager'; import {Config} from '../config'; import {BrowserError} from '../exitCodes'; @@ -16,8 +15,6 @@ import {Logger} from '../logger'; import {DriverProvider} from './driverProvider'; -const SeleniumConfig = require('webdriver-manager/built/lib/config').Config; - let logger = new Logger('direct'); export class Direct extends DriverProvider { constructor(config: Config) { @@ -26,10 +23,10 @@ export class Direct extends DriverProvider { /** * Configure and launch (if applicable) the object's environment. - * @return {q.promise} A promise which will resolve when the environment is + * @return {Promise} A promise which will resolve when the environment is * ready to test. */ - protected setupDriverEnv(): q.Promise { + protected async setupDriverEnv(): Promise { switch (this.config_.capabilities.browserName) { case 'chrome': logger.info('Using ChromeDriver directly...'); @@ -43,7 +40,6 @@ export class Direct extends DriverProvider { 'browserName ' + this.config_.capabilities.browserName + ' is not supported with directConnect.'); } - return q.fcall(function() {}); } /** @@ -53,7 +49,7 @@ export class Direct extends DriverProvider { * @override * @return webdriver instance */ - getNewDriver(): WebDriver { + async getNewDriver(): Promise { let driver: WebDriver; switch (this.config_.capabilities.browserName) { @@ -63,14 +59,10 @@ export class Direct extends DriverProvider { chromeDriverFile = this.config_.chromeDriver; } else { try { - let updateJson = path.resolve(SeleniumConfig.getSeleniumDir(), 'update-config.json'); - let updateConfig = JSON.parse(fs.readFileSync(updateJson).toString()); - chromeDriverFile = updateConfig.chrome.last; + chromeDriverFile = new ChromeDriver().getBinaryPath(); } catch (e) { throw new BrowserError( - logger, - 'Could not find update-config.json. ' + - 'Run \'webdriver-manager update\' to download binaries.'); + logger, 'Run \'webdriver-manager update\' to download binaries.'); } } @@ -82,12 +74,8 @@ export class Direct extends DriverProvider { } let chromeService = new ChromeServiceBuilder(chromeDriverFile).build(); - // driver = ChromeDriver.createSession(new Capabilities(this.config_.capabilities), - // chromeService); - // TODO(ralphj): fix typings - driver = - require('selenium-webdriver/chrome') - .Driver.createSession(new Capabilities(this.config_.capabilities), chromeService); + driver = await DriverForChrome.createSession( + new Capabilities(this.config_.capabilities), chromeService); break; case 'firefox': let geckoDriverFile: string; @@ -95,14 +83,10 @@ export class Direct extends DriverProvider { geckoDriverFile = this.config_.geckoDriver; } else { try { - let updateJson = path.resolve(SeleniumConfig.getSeleniumDir(), 'update-config.json'); - let updateConfig = JSON.parse(fs.readFileSync(updateJson).toString()); - geckoDriverFile = updateConfig.gecko.last; + geckoDriverFile = new GeckoDriver().getBinaryPath(); } catch (e) { throw new BrowserError( - logger, - 'Could not find update-config.json. ' + - 'Run \'webdriver-manager update\' to download binaries.'); + logger, 'Run \'webdriver-manager update\' to download binaries.'); } } @@ -113,14 +97,9 @@ export class Direct extends DriverProvider { '. Run \'webdriver-manager update\' to download binaries.'); } - // TODO (mgiambalvo): Turn this into an import when the selenium typings are updated. - const FirefoxServiceBuilder = require('selenium-webdriver/firefox').ServiceBuilder; - let firefoxService = new FirefoxServiceBuilder(geckoDriverFile).build(); - // TODO(mgiambalvo): Fix typings. - driver = - require('selenium-webdriver/firefox') - .Driver.createSession(new Capabilities(this.config_.capabilities), firefoxService); + driver = await DriverForFirefox.createSession( + new Capabilities(this.config_.capabilities), firefoxService); break; default: throw new BrowserError( diff --git a/lib/driverProviders/driverProvider.ts b/lib/driverProviders/driverProvider.ts index 35a7616d4..f562db5e1 100644 --- a/lib/driverProviders/driverProvider.ts +++ b/lib/driverProviders/driverProvider.ts @@ -3,12 +3,14 @@ * It is responsible for setting up the account object, tearing * it down, and setting up the driver correctly. */ -import * as q from 'q'; -import {Builder, promise as wdpromise, Session, WebDriver} from 'selenium-webdriver'; +import {Builder, WebDriver} from 'selenium-webdriver'; import {BlockingProxyRunner} from '../bpRunner'; import {Config} from '../config'; +import {BrowserError} from '../exitCodes'; +import {Logger} from '../logger'; +let logger = new Logger('driverProvider'); export abstract class DriverProvider { drivers_: WebDriver[]; config_: Config; @@ -43,7 +45,7 @@ export abstract class DriverProvider { * @public * @return webdriver instance */ - getNewDriver() { + async getNewDriver() { let builder: Builder; if (this.config_.useBlockingProxy) { builder = @@ -57,7 +59,12 @@ export abstract class DriverProvider { if (this.config_.disableEnvironmentOverrides === true) { builder.disableEnvironmentOverrides(); } - let newDriver = builder.build() as WebDriver; + let newDriver: WebDriver; + try { + newDriver = await builder.build(); + } catch (e) { + throw new BrowserError(logger, (e as Error).message); + } this.drivers_.push(newDriver); return newDriver; } @@ -68,81 +75,62 @@ export abstract class DriverProvider { * @public * @param webdriver instance */ - quitDriver(driver: WebDriver): wdpromise.Promise { + async quitDriver(driver: WebDriver): Promise { let driverIndex = this.drivers_.indexOf(driver); if (driverIndex >= 0) { this.drivers_.splice(driverIndex, 1); - } - - if (driver.getSession() === undefined) { - return wdpromise.when(undefined); - } else { - return driver.getSession() - .then((session_: Session) => { - if (session_) { - return driver.quit(); - } - }) - .catch(function(err: Error) {}); + try { + await driver.close(); + await driver.quit(); + } catch (err) { + // This happens when Protractor keeps track of all the webdrivers + // created and calls quit. If a user calls driver.quit, then this will + // throw an error. This catch will swallow the error. + } } } - /** * Quits an array of drivers and returns a q promise instead of a webdriver one * * @param drivers {webdriver.WebDriver[]} The webdriver instances */ - static quitDrivers(provider: DriverProvider, drivers: WebDriver[]): q.Promise { - let deferred = q.defer(); - wdpromise - .all(drivers.map((driver: WebDriver) => { - return provider.quitDriver(driver); - })) - .then( - () => { - deferred.resolve(); - }, - () => { - deferred.resolve(); - }); - return deferred.promise; + static async quitDrivers(provider: DriverProvider, drivers: WebDriver[]): Promise { + await Promise.all(drivers.map((driver: WebDriver) => { + return provider.quitDriver(driver); + })); } /** * Default update job method. * @return a promise */ - updateJob(update: any): q.Promise { - return q.fcall(function() {}); - }; + async updateJob(update: any): Promise {} /** * Default setup environment method, common to all driver providers. */ - setupEnv(): q.Promise { - let driverPromise = this.setupDriverEnv(); + async setupEnv(): Promise { + await this.setupDriverEnv(); if (this.config_.useBlockingProxy && !this.config_.blockingProxyUrl) { - // TODO(heathkit): If set, pass the webDriverProxy to BP. - return driverPromise.then(() => this.bpRunner.start()); + await this.bpRunner.start(); } - return driverPromise; - }; + } /** * Set up environment specific to a particular driver provider. Overridden * by each driver provider. */ - protected abstract setupDriverEnv(): q.Promise; + protected async abstract setupDriverEnv(): Promise; /** * Teardown and destroy the environment and do any associated cleanup. * Shuts down the drivers. * * @public - * @return {q.Promise} A promise which will resolve when the environment is down. + * @return {Promise} A promise which will resolve when the environment is down. */ - teardownEnv(): q.Promise { - return DriverProvider.quitDrivers(this, this.drivers_); + async teardownEnv(): Promise { + await DriverProvider.quitDrivers(this, this.drivers_); } } diff --git a/lib/driverProviders/hosted.ts b/lib/driverProviders/hosted.ts index f6778787a..22a3e6258 100644 --- a/lib/driverProviders/hosted.ts +++ b/lib/driverProviders/hosted.ts @@ -3,8 +3,6 @@ * It is responsible for setting up the account object, tearing * it down, and setting up the driver correctly. */ -import * as q from 'q'; - import {Config} from '../config'; import {Logger} from '../logger'; @@ -19,11 +17,10 @@ export class Hosted extends DriverProvider { /** * Configure and launch (if applicable) the object's environment. * @public - * @return {q.promise} A promise which will resolve when the environment is + * @return {Promise} A promise which will resolve when the environment is * ready to test. */ - protected setupDriverEnv(): q.Promise { + protected async setupDriverEnv(): Promise { logger.info('Using the selenium server at ' + this.config_.seleniumAddress); - return q.fcall(function() {}); } } diff --git a/lib/driverProviders/index.ts b/lib/driverProviders/index.ts index 87bc431a1..25fcabcf6 100644 --- a/lib/driverProviders/index.ts +++ b/lib/driverProviders/index.ts @@ -8,7 +8,6 @@ export * from './mock'; export * from './sauce'; export * from './testObject'; export * from './kobiton'; -export * from './useExistingWebDriver'; import {AttachSession} from './attachSession'; @@ -21,7 +20,6 @@ import {Mock} from './mock'; import {Sauce} from './sauce'; import {TestObject} from './testObject'; import {Kobiton} from './kobiton'; -import {UseExistingWebDriver} from './useExistingWebDriver'; import {Config} from '../config'; import {Logger} from '../logger'; @@ -34,9 +32,6 @@ export let buildDriverProvider = (config: Config): DriverProvider => { if (config.directConnect) { driverProvider = new Direct(config); logWarnings('directConnect', config); - } else if (config.seleniumWebDriver) { - driverProvider = new UseExistingWebDriver(config); - logWarnings('useExistingWebDriver', config); } else if (config.seleniumAddress) { if (config.seleniumSessionId) { driverProvider = new AttachSession(config); @@ -114,9 +109,6 @@ export let logWarnings = (providerType: string, config: Config): void => { if ('mock' !== providerType && config.mockSelenium) { warnList.push('mockSelenium'); } - if ('useExistingWebDriver' !== providerType && config.seleniumWebDriver) { - warnList.push('seleniumWebDriver'); - } if (warnList.length !== 0) { logger.warn(warnInto + warnList.join(', ')); } diff --git a/lib/driverProviders/kobiton.ts b/lib/driverProviders/kobiton.ts index 8bfc53ddc..1a7a6bbe9 100644 --- a/lib/driverProviders/kobiton.ts +++ b/lib/driverProviders/kobiton.ts @@ -3,7 +3,6 @@ * It is responsible for setting up the account object, tearing * it down, and setting up the driver correctly. */ -import * as q from 'q'; import {Config} from '../config'; import {Logger} from '../logger'; import {DriverProvider} from './driverProvider'; @@ -17,18 +16,15 @@ export class Kobiton extends DriverProvider { /** * Configure and launch (if applicable) the object's environment. - * @return {q.promise} A promise which will resolve when the environment is + * @return {Promise} A promise which will resolve when the environment is * ready to test. */ - protected setupDriverEnv(): q.Promise { - let deferred = q.defer(); + protected async setupDriverEnv(): Promise { this.config_.capabilities['kobitonUser'] = this.config_.kobitonUser; this.config_.capabilities['kobitonKey'] = this.config_.kobitonKey; this.config_.seleniumAddress = 'https://' + this.config_.kobitonUser + ':' + this.config_.kobitonKey + '@api.kobiton.com/wd/hub'; logger.info('Using Kobiton selenium server at ' + this.config_.seleniumAddress); - deferred.resolve(); - return deferred.promise; } } diff --git a/lib/driverProviders/local.ts b/lib/driverProviders/local.ts index d766e2780..078631efa 100644 --- a/lib/driverProviders/local.ts +++ b/lib/driverProviders/local.ts @@ -7,8 +7,8 @@ * so that we only start the local selenium once per entire launch. */ import * as fs from 'fs'; -import * as path from 'path'; -import * as q from 'q'; +import {SeleniumServer} from 'selenium-webdriver/remote'; +import {ChromeDriver, GeckoDriver, SeleniumServer as WdmSeleniumServer} from 'webdriver-manager'; import {Config} from '../config'; import {BrowserError, ConfigError} from '../exitCodes'; @@ -16,9 +16,6 @@ import {Logger} from '../logger'; import {DriverProvider} from './driverProvider'; -const SeleniumConfig = require('webdriver-manager/built/lib/config').Config; -const remote = require('selenium-webdriver/remote'); - let logger = new Logger('local'); export class Local extends DriverProvider { @@ -38,14 +35,9 @@ export class Local extends DriverProvider { 'Attempting to find the SeleniumServerJar in the default ' + 'location used by webdriver-manager'); try { - let updateJson = path.resolve(SeleniumConfig.getSeleniumDir(), 'update-config.json'); - let updateConfig = JSON.parse(fs.readFileSync(updateJson).toString()); - this.config_.seleniumServerJar = updateConfig.standalone.last; + this.config_.seleniumServerJar = new WdmSeleniumServer().getBinaryPath(); } catch (err) { - throw new BrowserError( - logger, - 'No update-config.json found.' + - ' Run \'webdriver-manager update\' to download binaries.'); + throw new BrowserError(logger, 'Run \'webdriver-manager update\' to download binaries.'); } } if (!fs.existsSync(this.config_.seleniumServerJar)) { @@ -61,14 +53,9 @@ export class Local extends DriverProvider { 'location used by webdriver-manager'); try { - let updateJson = path.resolve(SeleniumConfig.getSeleniumDir(), 'update-config.json'); - let updateConfig = JSON.parse(fs.readFileSync(updateJson).toString()); - this.config_.chromeDriver = updateConfig.chrome.last; + this.config_.chromeDriver = new ChromeDriver().getBinaryPath(); } catch (err) { - throw new BrowserError( - logger, - 'No update-config.json found. ' + - 'Run \'webdriver-manager update\' to download binaries.'); + throw new BrowserError(logger, 'Run \'webdriver-manager update\' to download binaries.'); } } @@ -92,14 +79,9 @@ export class Local extends DriverProvider { 'location used by webdriver-manager'); try { - let updateJson = path.resolve(SeleniumConfig.getSeleniumDir(), 'update-config.json'); - let updateConfig = JSON.parse(fs.readFileSync(updateJson).toString()); - this.config_.geckoDriver = updateConfig.gecko.last; + this.config_.geckoDriver = new GeckoDriver().getBinaryPath(); } catch (err) { - throw new BrowserError( - logger, - 'No update-config.json found. ' + - 'Run \'webdriver-manager update\' to download binaries.'); + throw new BrowserError(logger, 'Run \'webdriver-manager update\' to download binaries.'); } } @@ -120,10 +102,10 @@ export class Local extends DriverProvider { /** * Configure and launch (if applicable) the object's environment. * @public - * @return {q.promise} A promise which will resolve when the environment is + * @return {Promise} A promise which will resolve when the environment is * ready to test. */ - setupDriverEnv(): q.Promise { + async setupDriverEnv(): Promise { this.addDefaultBinaryLocs_(); logger.info('Starting selenium standalone server...'); @@ -153,39 +135,13 @@ export class Local extends DriverProvider { serverConf.jvmArgs.push('-Dwebdriver.gecko.driver=' + this.config_.geckoDriver); } - this.server_ = new remote.SeleniumServer(this.config_.seleniumServerJar, serverConf); + this.server_ = new SeleniumServer(this.config_.seleniumServerJar, serverConf); - let deferred = q.defer(); // start local server, grab hosted address, and resolve promise - this.server_.start(this.config_.seleniumServerStartTimeout) - .then((url: string) => { - logger.info('Selenium standalone server started at ' + url); - return this.server_.address(); - }) - .then((address: string) => { - this.config_.seleniumAddress = address; - deferred.resolve(); - }) - .catch((err: string) => { - deferred.reject(err); - }); - - return deferred.promise; - } + const url = await this.server_.start(this.config_.seleniumServerStartTimeout); - /** - * Teardown and destroy the environment and do any associated cleanup. - * Shuts down the drivers and server. - * - * @public - * @override - * @return {q.promise} A promise which will resolve when the environment - * is down. - */ - teardownEnv(): q.Promise { - return super.teardownEnv().then(() => { - logger.info('Shutting down selenium standalone server.'); - return this.server_.stop(); - }); + logger.info('Selenium standalone server started at ' + url); + const address = await this.server_.address(); + this.config_.seleniumAddress = address; } } diff --git a/lib/driverProviders/mock.ts b/lib/driverProviders/mock.ts index c5e3a130a..b183feb81 100644 --- a/lib/driverProviders/mock.ts +++ b/lib/driverProviders/mock.ts @@ -3,7 +3,6 @@ * It returns a fake webdriver and never actually contacts a selenium * server. */ -import * as q from 'q'; import {Session, WebDriver} from 'selenium-webdriver'; import {Config} from '../config'; @@ -21,20 +20,16 @@ export class Mock extends DriverProvider { /** * An execute function that returns a promise with a test value. */ - execute(): q.Promise { - let deferred = q.defer(); - deferred.resolve({value: 'test_response'}); - return deferred.promise; + async execute(): Promise { + return {value: 'test_response'}; } /** * Configure and launch (if applicable) the object's environment. * @public - * @return {q.promise} A promise which will resolve immediately. + * @return {Promise} A promise which will resolve immediately. */ - protected setupDriverEnv(): q.Promise { - return q.fcall(function() {}); - } + protected async setupDriverEnv(): Promise {} /** * Create a new driver. @@ -43,7 +38,7 @@ export class Mock extends DriverProvider { * @override * @return webdriver instance */ - getNewDriver(): WebDriver { + async getNewDriver(): Promise { let mockSession = new Session('test_session_id', {}); let newDriver = new WebDriver(mockSession, new MockExecutor()); this.drivers_.push(newDriver); diff --git a/lib/driverProviders/sauce.ts b/lib/driverProviders/sauce.ts index 3f43a6c46..6816a24a5 100644 --- a/lib/driverProviders/sauce.ts +++ b/lib/driverProviders/sauce.ts @@ -4,7 +4,6 @@ * it down, and setting up the driver correctly. */ -import * as q from 'q'; import {Session, WebDriver} from 'selenium-webdriver'; import * as util from 'util'; @@ -31,33 +30,29 @@ export class Sauce extends DriverProvider { * Hook to update the sauce job. * @public * @param {Object} update - * @return {q.promise} A promise that will resolve when the update is complete. + * @return {Promise} A promise that will resolve when the update is complete. */ - updateJob(update: any): q.Promise { - let deferredArray = this.drivers_.map((driver: WebDriver) => { - let deferred = q.defer(); + updateJob(update: any): Promise { + let mappedDrivers = this.drivers_.map((driver: WebDriver) => { driver.getSession().then((session: Session) => { logger.info('SauceLabs results available at http://saucelabs.com/jobs/' + session.getId()); this.sauceServer_.updateJob(session.getId(), update, (err: Error) => { if (err) { throw new Error('Error updating Sauce pass/fail status: ' + util.inspect(err)); } - deferred.resolve(); }); }); - return deferred.promise; }); - return q.all(deferredArray); + return Promise.all(mappedDrivers); } /** * Configure and launch (if applicable) the object's environment. * @public - * @return {q.promise} A promise which will resolve when the environment is + * @return {Promise} A promise which will resolve when the environment is * ready to test. */ - protected setupDriverEnv(): q.Promise { - let deferred = q.defer(); + protected async setupDriverEnv(): Promise { this.sauceServer_ = new SauceLabs({ hostname: this.getSauceEndpoint(this.config_.sauceRegion), username: this.config_.sauceUser, @@ -85,8 +80,6 @@ export class Sauce extends DriverProvider { logger.info( 'Using SauceLabs selenium server at ' + this.config_.seleniumAddress.replace(/\/\/.+@/, '//')); - deferred.resolve(); - return deferred.promise; } /** diff --git a/lib/driverProviders/testObject.ts b/lib/driverProviders/testObject.ts index 9e0a4266f..d510e84c8 100644 --- a/lib/driverProviders/testObject.ts +++ b/lib/driverProviders/testObject.ts @@ -3,7 +3,6 @@ * It is responsible for setting up the account object, tearing * it down, and setting up the driver correctly. */ -import * as q from 'q'; import {Config} from '../config'; import {Logger} from '../logger'; import {DriverProvider} from './driverProvider'; @@ -17,17 +16,14 @@ export class TestObject extends DriverProvider { /** * Configure and launch (if applicable) the object's environment. - * @return {q.promise} A promise which will resolve when the environment is + * @return {Promise} A promise which will resolve when the environment is * ready to test. */ - protected setupDriverEnv(): q.Promise { - let deferred = q.defer(); + protected async setupDriverEnv(): Promise { this.config_.capabilities['testobject.user'] = this.config_.testobjectUser; this.config_.capabilities['testobject_api_key'] = this.config_.testobjectKey; this.config_.seleniumAddress = 'https://us1.appium.testobject.com/wd/hub'; logger.info('Using TestObject selenium server at ' + this.config_.seleniumAddress); - deferred.resolve(); - return deferred.promise; } } diff --git a/lib/driverProviders/useExistingWebDriver.ts b/lib/driverProviders/useExistingWebDriver.ts deleted file mode 100644 index 36b279455..000000000 --- a/lib/driverProviders/useExistingWebDriver.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This is an implementation of the Use Existing WebDriver Driver Provider. - * It is responsible for setting up the account object, tearing it down, and - * setting up the driver correctly. - */ -import * as q from 'q'; -import {promise as wdpromise, WebDriver} from 'selenium-webdriver'; - -import {Config} from '../config'; -import {Logger} from '../logger'; - -import {DriverProvider} from './driverProvider'; - -const http = require('selenium-webdriver/http'); - -let logger = new Logger('useExistingWebDriver'); - -export class UseExistingWebDriver extends DriverProvider { - constructor(config: Config) { - super(config); - } - - /** - * Configure and launch (if applicable) the object's environment. - * @return {q.promise} A promise which will resolve when the environment is - * ready to test. - */ - protected setupDriverEnv(): q.Promise { - const defer = q.defer(); - this.config_.seleniumWebDriver.getSession().then((session) => { - logger.info('Using session id - ' + session.getId()); - return defer.resolve(); - }); - return q(undefined); - } - - /** - * Getting a new driver by attaching an existing session. - * - * @public - * @return {WebDriver} webdriver instance - */ - getNewDriver(): WebDriver { - const newDriver = this.config_.seleniumWebDriver; - this.drivers_.push(newDriver); - return newDriver; - } - - /** - * Maintains the existing session and does not quit the driver. - * - * @public - */ - quitDriver(): wdpromise.Promise { - return wdpromise.when(undefined); - } -} diff --git a/lib/element.ts b/lib/element.ts index 151553478..bdde04fff 100644 --- a/lib/element.ts +++ b/lib/element.ts @@ -1,7 +1,6 @@ -import {By, error as wderror, ILocation, ISize, promise as wdpromise, WebDriver, WebElement, WebElementPromise} from 'selenium-webdriver'; +import {By, error as wderror, WebElement, WebElementPromise} from 'selenium-webdriver'; import {ElementHelper, ProtractorBrowser} from './browser'; -import {IError} from './exitCodes'; import {isProtractorLocator, Locator} from './locators'; import {Logger} from './logger'; import {falseIfMissing} from './util'; @@ -56,34 +55,31 @@ let WEB_ELEMENT_FUNCTIONS = [ * * * @example - * element.all(by.css('.items li')).then(function(items) { - * expect(items.length).toBe(3); - * expect(items[0].getText()).toBe('First'); - * }); + * const items = await element.all(by.css('.items li')); + * expect(items.length).toBe(3); + * expect(await items[0].getText()).toBe('First'); * * // Or using the shortcut $$() notation instead of element.all(by.css()): * - * $$('.items li').then(function(items) { - * expect(items.length).toBe(3); - * expect(items[0].getText()).toBe('First'); - * }); + * const items = await $$('.items li'); + * expect(items.length).toBe(3); + * expect(await items[0].getText()).toBe('First'); * * @constructor * @param {ProtractorBrowser} browser A browser instance. * @param {function(): Array.} getWebElements A function * that returns a list of the underlying Web Elements. - * @param {webdriver.Locator} locator The most relevant locator. It is only + * @param {Locator} locator The most relevant locator. It is only * used for error reporting and ElementArrayFinder.locator. - * @param {Array.} opt_actionResults An array + * @param {Array} opt_actionResults An array * of promises which will be retrieved with then. Resolves to the latest * action result, or null if no action has been called. * @returns {ElementArrayFinder} */ export class ElementArrayFinder extends WebdriverWebElement { constructor( - public browser_: ProtractorBrowser, - public getWebElements: () => wdpromise.Promise = null, public locator_?: any, - public actionResults_: wdpromise.Promise = null) { + public browser_: ProtractorBrowser, public getWebElements: () => Promise = null, + public locator_?: any, public actionResults_: Promise = null) { super(); // TODO(juliemr): might it be easier to combine this with our docs and just @@ -134,57 +130,53 @@ export class ElementArrayFinder extends WebdriverWebElement { * * @example * let foo = element.all(by.css('.parent')).all(by.css('.foo')); - * expect(foo.getText()).toEqual(['1a', '2a']); + * expect(await foo.getText()).toEqual(['1a', '2a']); * let baz = element.all(by.css('.parent')).all(by.css('.baz')); - * expect(baz.getText()).toEqual(['1b']); + * expect(await baz.getText()).toEqual(['1b']); * let nonexistent = element.all(by.css('.parent')) * .all(by.css('.NONEXISTENT')); - * expect(nonexistent.getText()).toEqual(['']); + * expect(await nonexistent.getText()).toEqual(['']); * * // Or using the shortcut $$() notation instead of element.all(by.css()): * * let foo = $$('.parent').$$('.foo'); - * expect(foo.getText()).toEqual(['1a', '2a']); + * expect(await foo.getText()).toEqual(['1a', '2a']); * let baz = $$('.parent').$$('.baz'); - * expect(baz.getText()).toEqual(['1b']); + * expect(await baz.getText()).toEqual(['1b']); * let nonexistent = $$('.parent').$$('.NONEXISTENT'); - * expect(nonexistent.getText()).toEqual(['']); + * expect(await nonexistent.getText()).toEqual(['']); * - * @param {webdriver.Locator} subLocator + * @param {Locator} locator * @returns {ElementArrayFinder} */ all(locator: Locator): ElementArrayFinder { - let ptor = this.browser_; - let getWebElements = (): wdpromise.Promise => { + const ptor = this.browser_; + const getWebElements = async(): Promise => { if (this.getWebElements === null) { // This is the first time we are looking for an element - return ptor.waitForAngular('Locator: ' + locator) - .then((): wdpromise.Promise => { - if (isProtractorLocator(locator)) { - return locator.findElementsOverride(ptor.driver, null, ptor.rootEl); - } else { - return ptor.driver.findElements(locator); - } - }); - } else { - return this.getWebElements().then((parentWebElements: WebElement[]) => { - // For each parent web element, find their children and construct - // a list of Promise> - let childrenPromiseList = parentWebElements.map((parentWebElement: WebElement) => { - return isProtractorLocator(locator) ? - locator.findElementsOverride(ptor.driver, parentWebElement, ptor.rootEl) : - parentWebElement.findElements(locator); - }); + await ptor.waitForAngular('Locator: ' + locator); - // Resolve the list of Promise> and merge - // into a single list - return wdpromise.all(childrenPromiseList) - .then((resolved: WebElement[][]) => { - return resolved.reduce((childrenList, resolvedE) => { - return childrenList.concat(resolvedE); - }, []); - }); + if (isProtractorLocator(locator)) { + return locator.findElementsOverride(ptor.driver, null, ptor.rootEl); + } else { + return ptor.driver.findElements(locator); + } + } else { + const parentWebElements = await this.getWebElements(); + // For each parent web element, find their children and construct + // a list of Promise> + const childrenPromiseList = parentWebElements.map((parentWebElement: WebElement) => { + return isProtractorLocator(locator) ? + locator.findElementsOverride(ptor.driver, parentWebElement, ptor.rootEl) : + parentWebElement.findElements(locator); }); + + // Resolve the list of Promise> and merge + // into a single list + const resolved = await Promise.all(childrenPromiseList); + return resolved.reduce((childrenList, resolvedE) => { + return childrenList.concat(resolvedE); + }, []); } }; return new ElementArrayFinder(this.browser_, getWebElements, locator); @@ -206,53 +198,46 @@ export class ElementArrayFinder extends WebdriverWebElement { * * * @example - * element.all(by.css('.items li')).filter(function(elem, index) { - * return elem.getText().then(function(text) { - * return text === 'Third'; - * }); - * }).first().click(); + * await element.all(by.css('.items li')) + * .filter(async (elem, index) => await elem.getText() === 'Third') + * .first() + * .click(); * * // Or using the shortcut $$() notation instead of element.all(by.css()): * - * $$('.items li').filter(function(elem, index) { - * return elem.getText().then(function(text) { - * return text === 'Third'; - * }); - * }).first().click(); + * await $$('.items li') + * .filter(async (elem, index) => await elem.getText() === 'Third') + * .first() + * .click(); * - * @param {function(ElementFinder, number): webdriver.WebElement.Promise} - * filterFn + * @param {function(ElementFinder, number): boolean|Promise} filterFn * Filter function that will test if an element should be returned. * filterFn can either return a boolean or a promise that resolves to a - * boolean + * boolean. * @returns {!ElementArrayFinder} A ElementArrayFinder that represents an * array * of element that satisfy the filter function. */ - filter( - filterFn: (element: ElementFinder, index?: number) => boolean | - wdpromise.Promise): ElementArrayFinder { - let getWebElements = (): wdpromise.Promise => { - return this.getWebElements().then((parentWebElements: WebElement[]) => { - let list = parentWebElements.map((parentWebElement: WebElement, index: number) => { - let elementFinder = - ElementFinder.fromWebElement_(this.browser_, parentWebElement, this.locator_); - - return filterFn(elementFinder, index); - }); - return wdpromise.all(list).then((resolvedList: any) => { - return parentWebElements.filter((parentWebElement: WebElement, index: number) => { - return resolvedList[index]; - }); - }); + filter(filterFn: (element: ElementFinder, index?: number) => boolean | Promise): + ElementArrayFinder { + const getWebElements = async(): Promise => { + const parentWebElements = await this.getWebElements(); + const list = parentWebElements.map((parentWebElement: WebElement, index: number) => { + let elementFinder = + ElementFinder.fromWebElement_(this.browser_, parentWebElement, this.locator_); + return filterFn(elementFinder, index); + }); + const resolvedList = await Promise.all(list); + return parentWebElements.filter((_: WebElement, index: number) => { + return resolvedList[index]; }); }; return new ElementArrayFinder(this.browser_, getWebElements, this.locator_); } /** - * Get an element within the ElementArrayFinder by index. The index starts at 0. - * Negative indices are wrapped (i.e. -i means ith element from last) + * Get an element within the ElementArrayFinder by index. The index starts at + * 0. Negative indices are wrapped (i.e. -i means ith element from last) * This does not actually retrieve the underlying element. * * @alias element.all(locator).get(index) @@ -265,32 +250,32 @@ export class ElementArrayFinder extends WebdriverWebElement { * * @example * let list = element.all(by.css('.items li')); - * expect(list.get(0).getText()).toBe('First'); - * expect(list.get(1).getText()).toBe('Second'); + * expect(await list.get(0).getText()).toBe('First'); + * expect(await list.get(1).getText()).toBe('Second'); * * // Or using the shortcut $$() notation instead of element.all(by.css()): * * let list = $$('.items li'); - * expect(list.get(0).getText()).toBe('First'); - * expect(list.get(1).getText()).toBe('Second'); + * expect(await list.get(0).getText()).toBe('First'); + * expect(await list.get(1).getText()).toBe('Second'); * - * @param {number|webdriver.promise.Promise} index Element index. + * @param {number|Promise} indexPromise Element index. * @returns {ElementFinder} finder representing element at the given index. */ - get(index: number|wdpromise.Promise): ElementFinder { - let getWebElements = (): wdpromise.Promise => { - return wdpromise.all([index, this.getWebElements()]).then(([i, parentWebElements]) => { - if (i < 0) { - i += parentWebElements.length; - } - if (i < 0 || i >= parentWebElements.length) { - throw new wderror.NoSuchElementError( - 'Index out of bound. Trying to access element at index: ' + index + - ', but there are only ' + parentWebElements.length + ' elements that match ' + - 'locator ' + this.locator_.toString()); - } - return [parentWebElements[i]]; - }); + get(indexPromise: number|Promise): ElementFinder { + const getWebElements = async(): Promise => { + let index = await indexPromise; + const parentWebElements = await this.getWebElements(); + if (index < 0) { + index += parentWebElements.length; + } + if (index < 0 || index >= parentWebElements.length) { + throw new wderror.NoSuchElementError( + `Index out of bound. Trying to access element at index: ` + + `${index}, but there are only ${parentWebElements.length} ` + + `elements that match locator ${this.locator_.toString()}`); + } + return [parentWebElements[index]]; }; return new ElementArrayFinder(this.browser_, getWebElements, this.locator_).toElementFinder_(); } @@ -309,18 +294,18 @@ export class ElementArrayFinder extends WebdriverWebElement { * * @example * let first = element.all(by.css('.items li')).first(); - * expect(first.getText()).toBe('First'); + * expect(await first.getText()).toBe('First'); * * // Or using the shortcut $$() notation instead of element.all(by.css()): * * let first = $$('.items li').first(); - * expect(first.getText()).toBe('First'); + * expect(await first.getText()).toBe('First'); * * @returns {ElementFinder} finder representing the first matching element */ first(): ElementFinder { return this.get(0); - }; + } /** * Get the last matching element for the ElementArrayFinder. This does not @@ -336,12 +321,12 @@ export class ElementArrayFinder extends WebdriverWebElement { * * @example * let last = element.all(by.css('.items li')).last(); - * expect(last.getText()).toBe('Third'); + * expect(await last.getText()).toBe('Third'); * * // Or using the shortcut $$() notation instead of element.all(by.css()): * * let last = $$('.items li').last(); - * expect(last.getText()).toBe('Third'); + * expect(await last.getText()).toBe('Third'); * * @returns {ElementFinder} finder representing the last matching element */ @@ -363,16 +348,16 @@ export class ElementArrayFinder extends WebdriverWebElement { * @example * // The following two blocks of code are equivalent. * let list = element.all(by.css('.count span')); - * expect(list.count()).toBe(2); - * expect(list.get(0).getText()).toBe('First'); - * expect(list.get(1).getText()).toBe('Second'); + * expect(await list.count()).toBe(2); + * expect(await list.get(0).getText()).toBe('First'); + * expect(await list.get(1).getText()).toBe('Second'); * * // Or using the shortcut $$() notation instead of element.all(by.css()): * * let list = $$('.count span'); - * expect(list.count()).toBe(2); - * expect(list.get(0).getText()).toBe('First'); - * expect(list.get(1).getText()).toBe('Second'); + * expect(await list.count()).toBe(2); + * expect(await list.get(0).getText()).toBe('First'); + * expect(await list.get(1).getText()).toBe('Second'); * * @param {string} selector a css selector * @returns {ElementArrayFinder} which identifies the @@ -407,28 +392,27 @@ export class ElementArrayFinder extends WebdriverWebElement { * * @example * let list = element.all(by.css('.items li')); - * expect(list.count()).toBe(3); + * expect(await list.count()).toBe(3); * * // Or using the shortcut $$() notation instead of element.all(by.css()): * * let list = $$('.items li'); - * expect(list.count()).toBe(3); + * expect(await list.count()).toBe(3); * - * @returns {!webdriver.promise.Promise} A promise which resolves to the + * @returns {!Promise} A promise which resolves to the * number of elements matching the locator. */ - count(): wdpromise.Promise { - return this.getWebElements().then( - (arr: WebElement[]) => { - return arr.length; - }, - (err: Error) => { - if (err instanceof wderror.NoSuchElementError) { - return 0; - } else { - throw err; - } - }); + async count(): Promise { + try { + const arr = await this.getWebElements(); + return arr.length; + } catch (err) { + if (err instanceof wderror.NoSuchElementError) { + return 0; + } else { + throw err; + } + } } /** @@ -437,14 +421,13 @@ export class ElementArrayFinder extends WebdriverWebElement { * @alias element.all(locator).isPresent() * * @example - * expect($('.item').isPresent()).toBeTruthy(); + * expect(await $('.item').isPresent()).toBeTruthy(); * * @returns {Promise} */ - isPresent(): wdpromise.Promise { - return this.count().then((count) => { - return count > 0; - }); + async isPresent(): Promise { + const count = await this.count(); + return count > 0; } /** @@ -460,7 +443,7 @@ export class ElementArrayFinder extends WebdriverWebElement { * // returns by.css('#ID1') * $$('#ID1').filter(filterFn).get(0).click().locator(); * - * @returns {webdriver.Locator} + * @returns {Locator} */ locator(): Locator { return this.locator_; @@ -479,17 +462,17 @@ export class ElementArrayFinder extends WebdriverWebElement { // map(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[]; private applyAction_(actionFn: (value: WebElement, index: number, array: WebElement[]) => any): ElementArrayFinder { - let callerError = new Error(); + const callerError = new Error(); let actionResults = this.getWebElements() - .then((arr: any) => wdpromise.all(arr.map(actionFn))) + .then((arr: any) => Promise.all(arr.map(actionFn))) .then( (value: any) => { - return {passed: true, value: value}; + return {passed: true, value}; }, (error: any) => { return {passed: false, value: error}; }); - let getWebElements = () => actionResults.then(() => this.getWebElements()); + const getWebElements = () => actionResults.then(() => this.getWebElements()); actionResults = actionResults.then((result: {passed: boolean, value: any}) => { if (result.passed) { return result.value; @@ -511,14 +494,13 @@ export class ElementArrayFinder extends WebdriverWebElement { /** * Represents the ElementArrayFinder as an array of ElementFinders. * - * @returns {Array.} Return a promise, which resolves to a list - * of ElementFinders specified by the locator. + * @returns {Promise} Return a promise, which resolves to a + * list of ElementFinders specified by the locator. */ - asElementFinders_(): wdpromise.Promise { - return this.getWebElements().then((arr: WebElement[]) => { - return arr.map((webElem: WebElement) => { - return ElementFinder.fromWebElement_(this.browser_, webElem, this.locator_); - }); + async asElementFinders_(): Promise { + const arr = await this.getWebElements(); + return arr.map((webElem: WebElement) => { + return ElementFinder.fromWebElement_(this.browser_, webElem, this.locator_); }); } @@ -536,27 +518,24 @@ export class ElementArrayFinder extends WebdriverWebElement { * * * @example - * element.all(by.css('.items li')).then(function(arr) { - * expect(arr.length).toEqual(3); - * }); + * const arr = await element.all(by.css('.items li')); + * expect(arr.length).toEqual(3); * * // Or using the shortcut $$() notation instead of element.all(by.css()): * - * $$('.items li').then(function(arr) { - * expect(arr.length).toEqual(3); - * }); + * const arr = $$('.items li'); + * expect(arr.length).toEqual(3); * * @param {function(Array.)} fn * @param {function(Error)} errorFn * - * @returns {!webdriver.promise.Promise} A promise which will resolve to + * @returns {!Promise} A promise which will resolve to * an array of ElementFinders represented by the ElementArrayFinder. */ - then( - fn?: (value: ElementFinder[]|any[]) => T | wdpromise.IThenable, - errorFn?: (error: any) => any): wdpromise.Promise { + then(fn?: (value: ElementFinder[]|any[]) => T | Promise, errorFn?: (error: any) => any): + Promise { if (this.actionResults_) { - return this.actionResults_.then(fn, errorFn); + return this.actionResults_.then(fn, errorFn) as Promise; } else { return this.asElementFinders_().then(fn, errorFn); } @@ -575,32 +554,26 @@ export class ElementArrayFinder extends WebdriverWebElement { * * * @example - * element.all(by.css('.items li')).each(function(element, index) { + * await element.all(by.css('.items li')).each(async (element, index) => { * // Will print 0 First, 1 Second, 2 Third. - * element.getText().then(function (text) { - * console.log(index, text); - * }); + * console.log(index, await element.getText()); * }); * * // Or using the shortcut $$() notation instead of element.all(by.css()): * - * $$('.items li').each(function(element, index) { + * $$('.items li').each(async (element, index) => { * // Will print 0 First, 1 Second, 2 Third. - * element.getText().then(function (text) { - * console.log(index, text); - * }); + * console.log(index, await element.getText()); * }); * * @param {function(ElementFinder)} fn Input function * - * @returns {!webdriver.promise.Promise} A promise that will resolve when the + * @returns {!Promise} A promise that will resolve when the * function has been called on all the ElementFinders. The promise will * resolve to null. */ - each(fn: (elementFinder?: ElementFinder, index?: number) => any): wdpromise.Promise { - return this.map(fn).then((): any => { - return null; - }); + async each(fn: (elementFinder?: ElementFinder, index?: number) => any): Promise { + return await this.map(fn); } /** @@ -617,13 +590,14 @@ export class ElementArrayFinder extends WebdriverWebElement { * * * @example - * let items = element.all(by.css('.items li')).map(function(elm, index) { - * return { - * index: index, - * text: elm.getText(), - * class: elm.getAttribute('class') - * }; - * }); + * let items = await element.all(by.css('.items li')) + * .map(async (elm, index) => { + * return { + * index: index, + * text: await elm.getText(), + * class: await elm.getAttribute('class') + * }; + * }); * expect(items).toEqual([ * {index: 0, text: 'First', class: 'one'}, * {index: 1, text: 'Second', class: 'two'}, @@ -632,11 +606,11 @@ export class ElementArrayFinder extends WebdriverWebElement { * * // Or using the shortcut $$() notation instead of element.all(by.css()): * - * let items = $$('.items li').map(function(elm, index) { + * let items = await $$('.items li').map(async (elm, index) => { * return { * index: index, - * text: elm.getText(), - * class: elm.getAttribute('class') + * text: await elm.getText(), + * class: await elm.getAttribute('class') * }; * }); * expect(items).toEqual([ @@ -647,20 +621,19 @@ export class ElementArrayFinder extends WebdriverWebElement { * * @param {function(ElementFinder, number)} mapFn Map function that * will be applied to each element. - * @returns {!webdriver.promise.Promise} A promise that resolves to an array + * @returns {!Promise} A promise that resolves to an array * of values returned by the map function. */ - map(mapFn: (elementFinder?: ElementFinder, index?: number) => T | any): - wdpromise.Promise { - return this.asElementFinders_().then((arr: ElementFinder[]) => { - let list = arr.map((elementFinder?: ElementFinder, index?: number) => { - let mapResult = mapFn(elementFinder, index); - // All nested arrays and objects will also be fully resolved. - return wdpromise.fullyResolved(mapResult) as wdpromise.Promise; - }); - return wdpromise.all(list); + async map(mapFn: (elementFinder?: ElementFinder, index?: number) => T | any): Promise { + const arr = await this.asElementFinders_(); + + const list = arr.map(async (elementFinder?: ElementFinder, index?: number) => { + let mapResult = mapFn(elementFinder, index); + // All nested arrays and objects will also be fully resolved. + return await mapResult; }); - }; + return Promise.all(list); + } /** * Apply a reduce function against an accumulator and every element found @@ -679,21 +652,15 @@ export class ElementArrayFinder extends WebdriverWebElement { * * * @example - * let value = element.all(by.css('.items li')).reduce(function(acc, elem) { - * return elem.getText().then(function(text) { - * return acc + text + ' '; - * }); - * }, ''); + * let value = await element.all(by.css('.items li')) + * .reduce(async (acc, elem) => acc + (await elem.getText()) + ' ', ''); * * expect(value).toEqual('First Second Third '); * * // Or using the shortcut $$() notation instead of element.all(by.css()): * - * let value = $$('.items li').reduce(function(acc, elem) { - * return elem.getText().then(function(text) { - * return acc + text + ' '; - * }); - * }, ''); + * let value = await $$('.items li') + * .reduce(async (acc, elem) => acc + (await elem.getText()) + ' ', ''); * * expect(value).toEqual('First Second Third '); * @@ -701,18 +668,15 @@ export class ElementArrayFinder extends WebdriverWebElement { * reduceFn Reduce function that reduces every element into a single * value. * @param {*} initialValue Initial value of the accumulator. - * @returns {!webdriver.promise.Promise} A promise that resolves to the final + * @returns {!Promise} A promise that resolves to the final * value of the accumulator. */ - reduce(reduceFn: Function, initialValue: any): wdpromise.Promise { - let valuePromise = wdpromise.when(initialValue); - return this.asElementFinders_().then((arr: ElementFinder[]) => { - return arr.reduce((valuePromise: any, elementFinder: ElementFinder, index: number) => { - return valuePromise.then((value: any) => { - return reduceFn(value, elementFinder, index, arr); - }); - }, valuePromise); - }); + async reduce(reduceFn: Function, initialValue: any): Promise { + const valuePromise = await initialValue; + const arr = await this.asElementFinders_(); + return arr.reduce(async (valuePromise: any, elementFinder: ElementFinder, index: number) => { + return reduceFn(await valuePromise, elementFinder, index, arr); + }, valuePromise); } /** @@ -739,7 +703,7 @@ export class ElementArrayFinder extends WebdriverWebElement { * will be returned as a WebElement. */ evaluate(expression: string): ElementArrayFinder { - let evaluationFn = (webElem: WebElement) => { + const evaluationFn = (webElem: WebElement) => { return webElem.getDriver().executeScript(clientSideScripts.evaluate, webElem, expression); }; return this.applyAction_(evaluationFn); @@ -761,7 +725,7 @@ export class ElementArrayFinder extends WebdriverWebElement { * allowed. */ allowAnimations(value: boolean): ElementArrayFinder { - let allowAnimationsTestFn = (webElem: WebElement) => { + const allowAnimationsTestFn = (webElem: WebElement) => { return webElem.getDriver().executeScript(clientSideScripts.allowAnimations, webElem, value); }; return this.applyAction_(allowAnimationsTestFn); @@ -794,17 +758,17 @@ export class ElementArrayFinder extends WebdriverWebElement { * * @example * // Find element with {{scopelet}} syntax. - * element(by.binding('person.name')).getText().then(function(name) { - * expect(name).toBe('Foo'); - * }); + * const name = await element(by.binding('person.name')).getText(); + * expect(name).toBe('Foo'); * * // Find element with ng-bind="scopelet" syntax. - * expect(element(by.binding('person.email')).getText()).toBe('foo@bar.com'); + * const email = await element(by.binding('person.email')).getText(); + * expect(email).toBe('foo@bar.com'); * * // Find by model. * let input = element(by.model('person.name')); - * input.sendKeys('123'); - * expect(input.getAttribute('value')).toBe('Foo123'); + * await input.sendKeys('123'); + * expect(await input.getAttribute('value')).toBe('Foo123'); * * @constructor * @extends {webdriver.WebElement} @@ -817,8 +781,8 @@ export class ElementFinder extends WebdriverWebElement { parentElementArrayFinder: ElementArrayFinder; elementArrayFinder_: ElementArrayFinder; then?: - (fn: (value: any) => any | wdpromise.IThenable, - errorFn?: (error: any) => any) => wdpromise.Promise = null; + (fn: (value: any) => any | Promise, + errorFn?: (error: any) => any) => Promise = null; constructor(public browser_: ProtractorBrowser, elementArrayFinder: ElementArrayFinder) { super(); @@ -831,34 +795,32 @@ export class ElementFinder extends WebdriverWebElement { // has action results. if (this.parentElementArrayFinder.actionResults_) { // Access the underlying actionResult of ElementFinder. - this.then = - (fn: (value: any) => any | wdpromise.IThenable, errorFn?: (error: any) => any) => { - return this.elementArrayFinder_.then((actionResults: any) => { - if (!fn) { - return actionResults[0]; - } - return fn(actionResults[0]); - }, errorFn); - }; + this.then = (fn: (value: any) => any | Promise, errorFn?: (error: any) => any) => { + return this.elementArrayFinder_.then((actionResults: any) => { + if (!fn) { + return actionResults[0]; + } + return fn(actionResults[0]); + }, errorFn); + }; } // This filter verifies that there is only 1 element returned by the // elementArrayFinder. It will warn if there are more than 1 element and // throw an error if there are no elements. - let getWebElements = (): wdpromise.Promise => { - return elementArrayFinder.getWebElements().then((webElements: WebElement[]) => { - if (webElements.length === 0) { - throw new wderror.NoSuchElementError( - 'No element found using locator: ' + elementArrayFinder.locator().toString()); - } else { - if (webElements.length > 1) { - logger.warn( - 'more than one element found for locator ' + - elementArrayFinder.locator().toString() + ' - the first result will be used'); - } - return [webElements[0]]; + const getWebElements = async(): Promise => { + const webElements = await elementArrayFinder.getWebElements(); + if (webElements.length === 0) { + throw new wderror.NoSuchElementError( + 'No element found using locator: ' + elementArrayFinder.locator().toString()); + } else { + if (webElements.length > 1) { + logger.warn( + 'more than one element found for locator ' + elementArrayFinder.locator().toString() + + ' - the first result will be used'); } - }); + return [webElements[0]]; + } }; // Store a copy of the underlying elementArrayFinder, but with the more @@ -878,8 +840,8 @@ export class ElementFinder extends WebdriverWebElement { static fromWebElement_(browser: ProtractorBrowser, webElem: WebElement, locator?: Locator): ElementFinder { - let getWebElements = () => { - return wdpromise.when([webElem]); + const getWebElements = () => { + return Promise.resolve([webElem]); }; return new ElementArrayFinder(browser, getWebElements, locator).toElementFinder_(); } @@ -898,7 +860,7 @@ export class ElementFinder extends WebdriverWebElement { /** * @see ElementArrayFinder.prototype.locator * - * @returns {webdriver.Locator} + * @returns {Locator} */ locator(): any { return this.elementArrayFinder_.locator(); @@ -921,10 +883,10 @@ export class ElementFinder extends WebdriverWebElement { * browser.driver.findElement(by.css('.parent')); * browser.findElement(by.css('.parent')); * - * @returns {webdriver.WebElementPromise} + * @returns {webdriver.WebElement} */ getWebElement(): WebElementPromise { - let id = this.elementArrayFinder_.getWebElements().then((parentWebElements: WebElement[]) => { + const id = this.elementArrayFinder_.getWebElements().then((parentWebElements: WebElement[]) => { return parentWebElements[0]; }); return new WebElementPromise(this.browser_.driver, id); @@ -951,7 +913,7 @@ export class ElementFinder extends WebdriverWebElement { * * let items = $('.parent').all(by.tagName('li')); * - * @param {webdriver.Locator} subLocator + * @param {Locator} subLocator * @returns {ElementArrayFinder} */ all(subLocator: Locator): ElementArrayFinder { @@ -974,26 +936,26 @@ export class ElementFinder extends WebdriverWebElement { * // Chain 2 element calls. * let child = element(by.css('.parent')). * element(by.css('.child')); - * expect(child.getText()).toBe('Child text\n555-123-4567'); + * expect(await child.getText()).toBe('Child text\n555-123-4567'); * * // Chain 3 element calls. * let triple = element(by.css('.parent')). * element(by.css('.child')). * element(by.binding('person.phone')); - * expect(triple.getText()).toBe('555-123-4567'); + * expect(await triple.getText()).toBe('555-123-4567'); * * // Or using the shortcut $() notation instead of element(by.css()): * * // Chain 2 element calls. * let child = $('.parent').$('.child'); - * expect(child.getText()).toBe('Child text\n555-123-4567'); + * expect(await child.getText()).toBe('Child text\n555-123-4567'); * * // Chain 3 element calls. * let triple = $('.parent').$('.child'). * element(by.binding('person.phone')); - * expect(triple.getText()).toBe('555-123-4567'); + * expect(await triple.getText()).toBe('555-123-4567'); * - * @param {webdriver.Locator} subLocator + * @param {Locator} subLocator * @returns {ElementFinder} */ element(subLocator: Locator): ElementFinder { @@ -1044,24 +1006,24 @@ export class ElementFinder extends WebdriverWebElement { * // Chain 2 element calls. * let child = element(by.css('.parent')). * $('.child'); - * expect(child.getText()).toBe('Child text\n555-123-4567'); + * expect(await child.getText()).toBe('Child text\n555-123-4567'); * * // Chain 3 element calls. * let triple = element(by.css('.parent')). * $('.child'). * element(by.binding('person.phone')); - * expect(triple.getText()).toBe('555-123-4567'); + * expect(await triple.getText()).toBe('555-123-4567'); * * // Or using the shortcut $() notation instead of element(by.css()): * * // Chain 2 element calls. * let child = $('.parent').$('.child'); - * expect(child.getText()).toBe('Child text\n555-123-4567'); + * expect(await child.getText()).toBe('Child text\n555-123-4567'); * * // Chain 3 element calls. * let triple = $('.parent').$('.child'). * element(by.binding('person.phone')); - * expect(triple.getText()).toBe('555-123-4567'); + * expect(await triple.getText()).toBe('555-123-4567'); * * @param {string} selector A css selector * @returns {ElementFinder} @@ -1078,23 +1040,25 @@ export class ElementFinder extends WebdriverWebElement { * * @example * // Element exists. - * expect(element(by.binding('person.name')).isPresent()).toBe(true); + * expect(await element(by.binding('person.name')).isPresent()).toBe(true); * * // Element not present. - * expect(element(by.binding('notPresent')).isPresent()).toBe(false); + * expect(await element(by.binding('notPresent')).isPresent()).toBe(false); * - * @returns {webdriver.promise.Promise} which resolves to whether + * @returns {Promise} which resolves to whether * the element is present on the page. */ - isPresent(): wdpromise.Promise { - return this.parentElementArrayFinder.getWebElements().then((arr: any[]) => { + async isPresent(): Promise { + try { + const arr = await this.parentElementArrayFinder.getWebElements(); if (arr.length === 0) { return false; } - return arr[0].isEnabled().then(() => { - return true; // is present, whether it is enabled or not - }, falseIfMissing); - }, falseIfMissing); + // is present, whether it is enabled or not + return await arr[0].isEnabled(); + } catch (err) { + return falseIfMissing(err); + } } /** @@ -1110,11 +1074,11 @@ export class ElementFinder extends WebdriverWebElement { * * @see ElementFinder.isPresent * - * @param {webdriver.Locator} subLocator Locator for element to look for. - * @returns {webdriver.promise.Promise} which resolves to whether + * @param {Locator} subLocator Locator for element to look for. + * @returns {Promise} which resolves to whether * the subelement is present on the page. */ - isElementPresent(subLocator: Locator): wdpromise.Promise { + isElementPresent(subLocator: Locator): Promise { if (!subLocator) { throw new Error( 'SubLocator is not supplied as a parameter to ' + @@ -1155,16 +1119,18 @@ export class ElementFinder extends WebdriverWebElement { /** * Compares an element to this one for equality. * - * @param {!ElementFinder|!webdriver.WebElement} The element to compare to. + * @param {!ElementFinder|!webdriver.WebElement} element The element to compare to. * - * @returns {!webdriver.promise.Promise.} A promise that will be + * @returns {!Promise} A promise that will be * resolved to whether the two WebElements are equal. */ - equals(element: ElementFinder|WebElement): wdpromise.Promise { - return WebElement.equals( - this.getWebElement(), - (element as any).getWebElement ? (element as ElementFinder).getWebElement() : - element as WebElement); + async equals(element: ElementFinder|WebElement): Promise { + const a = await this.getWebElement(); + const b = (element as any).getWebElement ? await(element as ElementFinder).getWebElement() : + element as WebElement; + // TODO(selenium4): Use `return WebElement.equals(a, b);` when + // https://github.com/SeleniumHQ/selenium/pull/6749 is fixed. + return a.getDriver().executeScript('return arguments[0] === arguments[1]', a, b); } } @@ -1181,13 +1147,13 @@ export class ElementFinder extends WebdriverWebElement { * * @example * let item = $('.count .two'); - * expect(item.getText()).toBe('Second'); + * expect(await item.getText()).toBe('Second'); * * @param {string} selector A css selector * @returns {ElementFinder} which identifies the located * {@link webdriver.WebElement} */ -export let build$ = (element: ElementHelper, by: typeof By) => { +export const build$ = (element: ElementHelper, by: typeof By) => { return (selector: string) => { return element(by.css(selector)); }; @@ -1207,18 +1173,18 @@ export let build$ = (element: ElementHelper, by: typeof By) => { * @example * // The following protractor expressions are equivalent. * let list = element.all(by.css('.count span')); - * expect(list.count()).toBe(2); + * expect(await list.count()).toBe(2); * * list = $$('.count span'); - * expect(list.count()).toBe(2); - * expect(list.get(0).getText()).toBe('First'); - * expect(list.get(1).getText()).toBe('Second'); + * expect(await list.count()).toBe(2); + * expect(await list.get(0).getText()).toBe('First'); + * expect(await list.get(1).getText()).toBe('Second'); * * @param {string} selector a css selector * @returns {ElementArrayFinder} which identifies the * array of the located {@link webdriver.WebElement}s. */ -export let build$$ = (element: ElementHelper, by: typeof By) => { +export const build$$ = (element: ElementHelper, by: typeof By) => { return (selector: string) => { return element.all(by.css(selector)); }; diff --git a/lib/expectedConditions.ts b/lib/expectedConditions.ts index 209713608..139dcfbd9 100644 --- a/lib/expectedConditions.ts +++ b/lib/expectedConditions.ts @@ -16,56 +16,53 @@ import {falseIfMissing, passBoolean} from './util'; * * * @example - * var EC = protractor.ExpectedConditions; - * var button = $('#xyz'); - * var isClickable = EC.elementToBeClickable(button); + * const EC = protractor.ExpectedConditions; + * const button = $('#xyz'); + * const isClickable = EC.elementToBeClickable(button); * - * browser.get(URL); - * browser.wait(isClickable, 5000); //wait for an element to become clickable - * button.click(); + * await browser.get(URL); + * await browser.wait(isClickable, 5000); //wait for an element to become clickable + * await button.click(); * * // You can define your own expected condition, which is a function that * // takes no parameter and evaluates to a promise of a boolean. - * var urlChanged = function() { - * return browser.getCurrentUrl().then(function(url) { - * return url === 'http://www.angularjs.org'; - * }); - * }; + * const urlChanged = async () => { + * return await browser.getCurrentUrl() === 'http://www.angularjs.org'; + * } * * // You can customize the conditions with EC.and, EC.or, and EC.not. * // Here's a condition to wait for url to change, $('abc') element to contain * // text 'bar', and button becomes clickable. - * var condition = EC.and(urlChanged, EC.textToBePresentInElement($('abc'), + * const condition = EC.and(urlChanged, EC.textToBePresentInElement($('abc'), * 'bar'), isClickable); - * browser.get(URL); - * browser.wait(condition, 5000); //wait for condition to be true. - * button.click(); + * await browser.get(URL); + * await browser.wait(condition, 5000); //wait for condition to be true. + * await button.click(); * * @alias ExpectedConditions * @constructor */ export class ProtractorExpectedConditions { - constructor(public browser: ProtractorBrowser){}; + constructor(public browser: ProtractorBrowser) {} /** * Negates the result of a promise. * * @example - * var EC = protractor.ExpectedConditions; - * var titleIsNotFoo = EC.not(EC.titleIs('Foo')); + * const EC = protractor.ExpectedConditions; + * const titleIsNotFoo = EC.not(EC.titleIs('Foo')); * // Waits for title to become something besides 'foo'. - * browser.wait(titleIsNotFoo, 5000); + * await browser.wait(titleIsNotFoo, 5000); * * @alias ExpectedConditions.not * @param {!function} expectedCondition * * @returns {!function} An expected condition that returns the negated value. */ - not(expectedCondition: Function): Function { - return (): Function => { - return expectedCondition().then((bool: boolean): boolean => { - return !bool; - }); + not(expectedCondition: Function): (() => Promise) { + return async(): Promise => { + const bool = await expectedCondition(); + return !bool; }; } @@ -80,20 +77,19 @@ export class ProtractorExpectedConditions { * @returns {!function} An expected condition that returns a promise which * evaluates to the result of the logical chain. */ - logicalChain_(defaultRet: boolean, fns: Array): Function { + logicalChain_(defaultRet: boolean, fns: Array): (() => Promise) { let self = this; - return (): boolean => { + return async(): Promise => { if (fns.length === 0) { return defaultRet; } - let fn = fns[0]; - return fn().then((bool: boolean): boolean => { - if (bool === defaultRet) { - return self.logicalChain_(defaultRet, fns.slice(1))(); - } else { - return !defaultRet; - } - }); + const fn = fns[0]; + const bool = await fn(); + if (bool === defaultRet) { + return self.logicalChain_(defaultRet, fns.slice(1))(); + } else { + return !defaultRet; + } }; } @@ -102,20 +98,20 @@ export class ProtractorExpectedConditions { * at the first expected condition that evaluates to false. * * @example - * var EC = protractor.ExpectedConditions; - * var titleContainsFoo = EC.titleContains('Foo'); - * var titleIsNotFooBar = EC.not(EC.titleIs('FooBar')); + * const EC = protractor.ExpectedConditions; + * const titleContainsFoo = EC.titleContains('Foo'); + * const titleIsNotFooBar = EC.not(EC.titleIs('FooBar')); * // Waits for title to contain 'Foo', but is not 'FooBar' - * browser.wait(EC.and(titleContainsFoo, titleIsNotFooBar), 5000); + * await browser.wait(EC.and(titleContainsFoo, titleIsNotFooBar), 5000); * * @alias ExpectedConditions.and - * @param {Array.} fns An array of expected conditions to 'and' + * @param {Array.} args An array of expected conditions to 'and' * together. * * @returns {!function} An expected condition that returns a promise which * evaluates to the result of the logical and. */ - and(...args: Function[]): Function { + and(...args: Function[]): (() => Promise) { return this.logicalChain_(true, args); } @@ -125,19 +121,19 @@ export class ProtractorExpectedConditions { * * @alias ExpectedConditions.or * @example - * var EC = protractor.ExpectedConditions; - * var titleContainsFoo = EC.titleContains('Foo'); - * var titleContainsBar = EC.titleContains('Bar'); + * const EC = protractor.ExpectedConditions; + * const titleContainsFoo = EC.titleContains('Foo'); + * const titleContainsBar = EC.titleContains('Bar'); * // Waits for title to contain either 'Foo' or 'Bar' - * browser.wait(EC.or(titleContainsFoo, titleContainsBar), 5000); + * await browser.wait(EC.or(titleContainsFoo, titleContainsBar), 5000); * - * @param {Array.} fns An array of expected conditions to 'or' + * @param {Array.} args An array of expected conditions to 'or' * together. * * @returns {!function} An expected condition that returns a promise which * evaluates to the result of the logical or. */ - or(...args: Function[]): Function { + or(...args: Function[]): (() => Promise) { return this.logicalChain_(false, args); } @@ -145,28 +141,26 @@ export class ProtractorExpectedConditions { * Expect an alert to be present. * * @example - * var EC = protractor.ExpectedConditions; + * const EC = protractor.ExpectedConditions; * // Waits for an alert pops up. - * browser.wait(EC.alertIsPresent(), 5000); + * await browser.wait(EC.alertIsPresent(), 5000); * * @alias ExpectedConditions.alertIsPresent * @returns {!function} An expected condition that returns a promise * representing whether an alert is present. */ - alertIsPresent(): Function { - return () => { - return this.browser.driver.switchTo().alert().then( - (): - boolean => { - return true; - }, - (err: any) => { - if (err instanceof wderror.NoSuchAlertError) { - return false; - } else { - throw err; - } - }); + alertIsPresent(): (() => Promise) { + return async(): Promise => { + try { + await this.browser.driver.switchTo().alert(); + return true; + } catch (e) { + if (e instanceof wderror.NoSuchAlertError) { + return false; + } else { + throw e; + } + } }; } @@ -175,9 +169,9 @@ export class ProtractorExpectedConditions { * can click it. * * @example - * var EC = protractor.ExpectedConditions; + * const EC = protractor.ExpectedConditions; * // Waits for the element with id 'abc' to be clickable. - * browser.wait(EC.elementToBeClickable($('#abc')), 5000); + * await browser.wait(EC.elementToBeClickable($('#abc')), 5000); * * @alias ExpectedConditions.elementToBeClickable * @param {!ElementFinder} elementFinder The element to check @@ -185,7 +179,7 @@ export class ProtractorExpectedConditions { * @returns {!function} An expected condition that returns a promise * representing whether the element is clickable. */ - elementToBeClickable(elementFinder: ElementFinder): Function { + elementToBeClickable(elementFinder: ElementFinder): (() => Promise) { return this.and(this.visibilityOf(elementFinder), () => { return elementFinder.isEnabled().then(passBoolean, falseIfMissing); }); @@ -196,9 +190,9 @@ export class ProtractorExpectedConditions { * element. Returns false if the elementFinder does not find an element. * * @example - * var EC = protractor.ExpectedConditions; + * const EC = protractor.ExpectedConditions; * // Waits for the element with id 'abc' to contain the text 'foo'. - * browser.wait(EC.textToBePresentInElement($('#abc'), 'foo'), 5000); + * await browser.wait(EC.textToBePresentInElement($('#abc'), 'foo'), 5000); * * @alias ExpectedConditions.textToBePresentInElement * @param {!ElementFinder} elementFinder The element to check @@ -207,13 +201,16 @@ export class ProtractorExpectedConditions { * @returns {!function} An expected condition that returns a promise * representing whether the text is present in the element. */ - textToBePresentInElement(elementFinder: ElementFinder, text: string): Function { - let hasText = () => { - return elementFinder.getText().then((actualText: string): boolean => { + textToBePresentInElement(elementFinder: ElementFinder, text: string): (() => Promise) { + let hasText = async () => { + try { + const actualText = await elementFinder.getText(); // MSEdge does not properly remove newlines, which causes false // negatives return actualText.replace(/\r?\n|\r/g, '').indexOf(text) > -1; - }, falseIfMissing); + } catch (e) { + return falseIfMissing(e); + } }; return this.and(this.presenceOf(elementFinder), hasText); } @@ -223,9 +220,9 @@ export class ProtractorExpectedConditions { * value. Returns false if the elementFinder does not find an element. * * @example - * var EC = protractor.ExpectedConditions; + * const EC = protractor.ExpectedConditions; * // Waits for the element with id 'myInput' to contain the input 'foo'. - * browser.wait(EC.textToBePresentInElementValue($('#myInput'), 'foo'), 5000); + * await browser.wait(EC.textToBePresentInElementValue($('#myInput'), 'foo'), 5000); * * @alias ExpectedConditions.textToBePresentInElementValue * @param {!ElementFinder} elementFinder The element to check @@ -234,11 +231,15 @@ export class ProtractorExpectedConditions { * @returns {!function} An expected condition that returns a promise * representing whether the text is present in the element's value. */ - textToBePresentInElementValue(elementFinder: ElementFinder, text: string): Function { - let hasText = () => { - return elementFinder.getAttribute('value').then((actualText: string): boolean => { + textToBePresentInElementValue(elementFinder: ElementFinder, text: string): + (() => Promise) { + let hasText = async () => { + try { + const actualText = await elementFinder.getAttribute('value'); return actualText.indexOf(text) > -1; - }, falseIfMissing); + } catch (e) { + return falseIfMissing(e); + } }; return this.and(this.presenceOf(elementFinder), hasText); } @@ -248,9 +249,9 @@ export class ProtractorExpectedConditions { * substring. * * @example - * var EC = protractor.ExpectedConditions; + * const EC = protractor.ExpectedConditions; * // Waits for the title to contain 'foo'. - * browser.wait(EC.titleContains('foo'), 5000); + * await browser.wait(EC.titleContains('foo'), 5000); * * @alias ExpectedConditions.titleContains * @param {!string} title The fragment of title expected @@ -258,11 +259,10 @@ export class ProtractorExpectedConditions { * @returns {!function} An expected condition that returns a promise * representing whether the title contains the string. */ - titleContains(title: string): Function { - return () => { - return this.browser.driver.getTitle().then((actualTitle: string): boolean => { - return actualTitle.indexOf(title) > -1; - }); + titleContains(title: string): (() => Promise) { + return async(): Promise => { + const actualTitle = await this.browser.driver.getTitle(); + return actualTitle.indexOf(title) > -1; }; } @@ -270,9 +270,9 @@ export class ProtractorExpectedConditions { * An expectation for checking the title of a page. * * @example - * var EC = protractor.ExpectedConditions; + * const EC = protractor.ExpectedConditions; * // Waits for the title to be 'foo'. - * browser.wait(EC.titleIs('foo'), 5000); + * await browser.wait(EC.titleIs('foo'), 5000); * * @alias ExpectedConditions.titleIs * @param {!string} title The expected title, which must be an exact match. @@ -280,11 +280,10 @@ export class ProtractorExpectedConditions { * @returns {!function} An expected condition that returns a promise * representing whether the title equals the string. */ - titleIs(title: string): Function { - return () => { - return this.browser.driver.getTitle().then((actualTitle: string): boolean => { - return actualTitle === title; - }); + titleIs(title: string): (() => Promise) { + return async(): Promise => { + const actualTitle = await this.browser.driver.getTitle(); + return actualTitle === title; }; } @@ -293,9 +292,9 @@ export class ProtractorExpectedConditions { * substring. * * @example - * var EC = protractor.ExpectedConditions; + * const EC = protractor.ExpectedConditions; * // Waits for the URL to contain 'foo'. - * browser.wait(EC.urlContains('foo'), 5000); + * await browser.wait(EC.urlContains('foo'), 5000); * * @alias ExpectedConditions.urlContains * @param {!string} url The fragment of URL expected @@ -303,11 +302,10 @@ export class ProtractorExpectedConditions { * @returns {!function} An expected condition that returns a promise * representing whether the URL contains the string. */ - urlContains(url: string): Function { - return () => { - return this.browser.driver.getCurrentUrl().then((actualUrl: string): boolean => { - return actualUrl.indexOf(url) > -1; - }); + urlContains(url: string): (() => Promise) { + return async(): Promise => { + const actualUrl = await this.browser.driver.getCurrentUrl(); + return actualUrl.indexOf(url) > -1; }; } @@ -315,9 +313,9 @@ export class ProtractorExpectedConditions { * An expectation for checking the URL of a page. * * @example - * var EC = protractor.ExpectedConditions; + * const EC = protractor.ExpectedConditions; * // Waits for the URL to be 'foo'. - * browser.wait(EC.urlIs('foo'), 5000); + * await browser.wait(EC.urlIs('foo'), 5000); * * @alias ExpectedConditions.urlIs * @param {!string} url The expected URL, which must be an exact match. @@ -325,11 +323,10 @@ export class ProtractorExpectedConditions { * @returns {!function} An expected condition that returns a promise * representing whether the url equals the string. */ - urlIs(url: string): Function { - return () => { - return this.browser.driver.getCurrentUrl().then((actualUrl: string): boolean => { - return actualUrl === url; - }); + urlIs(url: string): (() => Promise) { + return async(): Promise => { + const actualUrl = await this.browser.driver.getCurrentUrl(); + return actualUrl === url; }; } @@ -339,9 +336,9 @@ export class ProtractorExpectedConditions { * This is the opposite of 'stalenessOf'. * * @example - * var EC = protractor.ExpectedConditions; + * const EC = protractor.ExpectedConditions; * // Waits for the element with id 'abc' to be present on the dom. - * browser.wait(EC.presenceOf($('#abc')), 5000); + * await browser.wait(EC.presenceOf($('#abc')), 5000); * * @alias ExpectedConditions.presenceOf * @param {!ElementFinder} elementFinder The element to check @@ -349,18 +346,18 @@ export class ProtractorExpectedConditions { * @returns {!function} An expected condition that returns a promise * representing whether the element is present. */ - presenceOf(elementFinder: ElementFinder): Function { + presenceOf(elementFinder: ElementFinder): (() => Promise) { return elementFinder.isPresent.bind(elementFinder); - }; + } /** * An expectation for checking that an element is not attached to the DOM * of a page. This is the opposite of 'presenceOf'. * * @example - * var EC = protractor.ExpectedConditions; + * const EC = protractor.ExpectedConditions; * // Waits for the element with id 'abc' to be no longer present on the dom. - * browser.wait(EC.stalenessOf($('#abc')), 5000); + * await browser.wait(EC.stalenessOf($('#abc')), 5000); * * @alias ExpectedConditions.stalenessOf * @param {!ElementFinder} elementFinder The element to check @@ -368,7 +365,7 @@ export class ProtractorExpectedConditions { * @returns {!function} An expected condition that returns a promise * representing whether the element is stale. */ - stalenessOf(elementFinder: ElementFinder): Function { + stalenessOf(elementFinder: ElementFinder): (() => Promise) { return this.not(this.presenceOf(elementFinder)); } @@ -380,9 +377,9 @@ export class ProtractorExpectedConditions { * of 'invisibilityOf'. * * @example - * var EC = protractor.ExpectedConditions; + * const EC = protractor.ExpectedConditions; * // Waits for the element with id 'abc' to be visible on the dom. - * browser.wait(EC.visibilityOf($('#abc')), 5000); + * await browser.wait(EC.visibilityOf($('#abc')), 5000); * * @alias ExpectedConditions.visibilityOf * @param {!ElementFinder} elementFinder The element to check @@ -390,7 +387,7 @@ export class ProtractorExpectedConditions { * @returns {!function} An expected condition that returns a promise * representing whether the element is visible. */ - visibilityOf(elementFinder: ElementFinder): Function { + visibilityOf(elementFinder: ElementFinder): (() => Promise) { return this.and(this.presenceOf(elementFinder), () => { return elementFinder.isDisplayed().then(passBoolean, falseIfMissing); }); @@ -401,9 +398,9 @@ export class ProtractorExpectedConditions { * present on the DOM. This is the opposite of 'visibilityOf'. * * @example - * var EC = protractor.ExpectedConditions; + * const EC = protractor.ExpectedConditions; * // Waits for the element with id 'abc' to be no longer visible on the dom. - * browser.wait(EC.invisibilityOf($('#abc')), 5000); + * await browser.wait(EC.invisibilityOf($('#abc')), 5000); * * @alias ExpectedConditions.invisibilityOf * @param {!ElementFinder} elementFinder The element to check @@ -411,7 +408,7 @@ export class ProtractorExpectedConditions { * @returns {!function} An expected condition that returns a promise * representing whether the element is invisible. */ - invisibilityOf(elementFinder: ElementFinder): Function { + invisibilityOf(elementFinder: ElementFinder): (() => Promise) { return this.not(this.visibilityOf(elementFinder)); } @@ -419,9 +416,9 @@ export class ProtractorExpectedConditions { * An expectation for checking the selection is selected. * * @example - * var EC = protractor.ExpectedConditions; + * const EC = protractor.ExpectedConditions; * // Waits for the element with id 'myCheckbox' to be selected. - * browser.wait(EC.elementToBeSelected($('#myCheckbox')), 5000); + * await browser.wait(EC.elementToBeSelected($('#myCheckbox')), 5000); * * @alias ExpectedConditions.elementToBeSelected * @param {!ElementFinder} elementFinder The element to check @@ -429,7 +426,7 @@ export class ProtractorExpectedConditions { * @returns {!function} An expected condition that returns a promise * representing whether the element is selected. */ - elementToBeSelected(elementFinder: ElementFinder): Function { + elementToBeSelected(elementFinder: ElementFinder): (() => Promise) { return this.and(this.presenceOf(elementFinder), () => { return elementFinder.isSelected().then(passBoolean, falseIfMissing); }); diff --git a/lib/frameworks/__protractor_internal_afterEach_setup_spec.js b/lib/frameworks/__protractor_internal_afterEach_setup_spec.js index a325c9b2b..b47932b47 100644 --- a/lib/frameworks/__protractor_internal_afterEach_setup_spec.js +++ b/lib/frameworks/__protractor_internal_afterEach_setup_spec.js @@ -1,10 +1,10 @@ // This is spec file is automatically added by protractor to implement our // `afterEach` functionality for jasmine and mocha. -var hooks = require('./setupAfterEach').hooks; +const hooks = require('../../built/frameworks/setupAfterEach').hooks; afterEach(function() { - if (hooks.afterEach) { + if (hooks.afterEach) { return hooks.afterEach(); } }); diff --git a/lib/frameworks/debugprint.js b/lib/frameworks/debugprint.js deleted file mode 100644 index 79c28267c..000000000 --- a/lib/frameworks/debugprint.js +++ /dev/null @@ -1,22 +0,0 @@ -var util = require('util'), - q = require('q'), - Logger = require('../logger').Logger; - -var logger = new Logger('debugger'); - -/** - * A debug framework which does not actually run any tests, just spits - * out the list that would be run. - * - * @param {Runner} runner The current Protractor Runner. - * @param {Array} specs Array of Directory Path Strings. - * @return {q.Promise} Promise resolved with the test results - */ -exports.run = function(runner, specs) { - return q.promise(function(resolve) { - logger.info('Resolved spec files: ' + util.inspect(specs)); - resolve({ - failedCount: 0 - }); - }); -}; diff --git a/lib/frameworks/debugprint.ts b/lib/frameworks/debugprint.ts new file mode 100644 index 000000000..4585d6393 --- /dev/null +++ b/lib/frameworks/debugprint.ts @@ -0,0 +1,21 @@ +import * as util from 'util'; +import {Logger} from '../logger'; +import {Runner} from '../runner'; +import {RunResults} from '../taskRunner'; + +const logger = new Logger('debugger'); + +/** + * A debug framework which does not actually run any tests, just spits + * out the list that would be run. + * + * @param {Runner} runner The current Protractor Runner. + * @param {Array} specs Array of Directory Path Strings. + * @return {Promise} Promise resolved with the test results + */ +export const run = (runner: Runner, specs: Array): Promise => { + return new Promise(resolve => { + logger.info(`Resolved spec files: ${util.inspect(specs)}`); + resolve({failedCount: 0}); + }); +}; diff --git a/lib/frameworks/explorer.js b/lib/frameworks/explorer.js deleted file mode 100644 index 4a60adb4a..000000000 --- a/lib/frameworks/explorer.js +++ /dev/null @@ -1,24 +0,0 @@ -var q = require('q'); - -/** - * A framework which does not actually run any tests. It allows users to drop - * into a repl loop to experiment with protractor commands. - * - * @param {Runner} runner The current Protractor Runner. - * @return {q.Promise} Promise resolved with the test results - */ -exports.run = function(runner) { - /* globals browser */ - return q.promise(function(resolve) { - if (runner.getConfig().baseUrl) { - browser.get(runner.getConfig().baseUrl); - } - browser.executeScriptWithDescription('var e = 0', 'starting explorer hook'); - browser.enterRepl(); - browser.executeScriptWithDescription('var e = 1', 'done with explorer hook').then(function() { - resolve({ - failedCount: 0 - }); - }); - }); -}; diff --git a/lib/frameworks/jasmine.js b/lib/frameworks/jasmine.js index 03251729b..18be1b761 100644 --- a/lib/frameworks/jasmine.js +++ b/lib/frameworks/jasmine.js @@ -1,7 +1,4 @@ -var q = require('q'); -var webdriver = require('selenium-webdriver'); - -var RunnerReporter = function(emitter) { +let RunnerReporter = function(emitter) { this.emitter = emitter; this.testResult = [], this.failedCount = 0; @@ -18,7 +15,7 @@ RunnerReporter.prototype.specStarted = function() { }; RunnerReporter.prototype.specDone = function(result) { - var specInfo = { + const specInfo = { name: result.description, category: result.fullName.slice(0, -result.description.length).trim() }; @@ -29,7 +26,7 @@ RunnerReporter.prototype.specDone = function(result) { this.failedCount++; } - var entry = { + const entry = { description: result.fullName, assertions: [], duration: new Date().getTime() - this.startTime.getTime() @@ -41,7 +38,7 @@ RunnerReporter.prototype.specDone = function(result) { }); } - result.failedExpectations.forEach(function(item) { + result.failedExpectations.forEach(item => { entry.assertions.push({ passed: item.passed, errorMsg: item.passed ? undefined : item.message, @@ -56,27 +53,24 @@ RunnerReporter.prototype.specDone = function(result) { * * @param {Runner} runner The current Protractor Runner. * @param {Array} specs Array of Directory Path Strings. - * @return {q.Promise} Promise resolved with the test results + * @return {Promise} Promise resolved with the test results */ -exports.run = function(runner, specs) { - var JasmineRunner = require('jasmine'); - var jrunner = new JasmineRunner(); - /* global jasmine */ - - require('jasminewd2').init(webdriver.promise.controlFlow(), webdriver); +exports.run = async function(runner, specs) { + const JasmineRunner = require('jasmine'); + const jrunner = new JasmineRunner(); - var jasmineNodeOpts = runner.getConfig().jasmineNodeOpts; + const jasmineNodeOpts = runner.getConfig().jasmineNodeOpts; // On timeout, the flow should be reset. This will prevent webdriver tasks // from overflowing into the next test and causing it to fail or timeout // as well. This is done in the reporter instead of an afterEach block // to ensure that it runs after any afterEach() blocks with webdriver tasks // get to complete first. - var reporter = new RunnerReporter(runner); + const reporter = new RunnerReporter(runner); jasmine.getEnv().addReporter(reporter); // Add hooks for afterEach - require('./setupAfterEach').setup(runner, specs); + require('../../built/frameworks/setupAfterEach').setup(runner, specs); // Filter specs to run based on jasmineNodeOpts.grep and jasmineNodeOpts.invert. jasmine.getEnv().specFilter = function(spec) { @@ -100,36 +94,32 @@ exports.run = function(runner, specs) { } } - return runner.runTestPreparer().then(function() { - return q.promise(function(resolve, reject) { - if (jasmineNodeOpts && jasmineNodeOpts.defaultTimeoutInterval) { - jasmine.DEFAULT_TIMEOUT_INTERVAL = jasmineNodeOpts.defaultTimeoutInterval; - } + await runner.runTestPreparer(); + return new Promise((resolve, reject) => { + if (jasmineNodeOpts && jasmineNodeOpts.defaultTimeoutInterval) { + jasmine.DEFAULT_TIMEOUT_INTERVAL = jasmineNodeOpts.defaultTimeoutInterval; + } - var originalOnComplete = runner.getConfig().onComplete; - - jrunner.onComplete(function(passed) { - try { - var completed = q(); - if (originalOnComplete) { - completed = q(originalOnComplete(passed)); - } - completed.then(function() { - resolve({ - failedCount: reporter.failedCount, - specResults: reporter.testResult - }); - }); - } catch (err) { - reject(err); - } - }); + const originalOnComplete = runner.getConfig().onComplete; - jrunner.configureDefaultReporter(jasmineNodeOpts); - jrunner.projectBaseDir = ''; - jrunner.specDir = ''; - jrunner.addSpecFiles(specs); - jrunner.execute(); + jrunner.onComplete(async(passed) => { + try { + if (originalOnComplete) { + await originalOnComplete(passed); + } + resolve({ + failedCount: reporter.failedCount, + specResults: reporter.testResult + }); + } catch (err) { + reject(err); + } }); + + jrunner.configureDefaultReporter(jasmineNodeOpts); + jrunner.projectBaseDir = ''; + jrunner.specDir = ''; + jrunner.addSpecFiles(specs); + jrunner.execute(); }); }; diff --git a/lib/frameworks/mocha.js b/lib/frameworks/mocha.js index d7d7f1d16..9c8e5868a 100644 --- a/lib/frameworks/mocha.js +++ b/lib/frameworks/mocha.js @@ -1,5 +1,3 @@ -var q = require('q'); - /** * Execute the Runner's test cases through Mocha. * @@ -7,134 +5,70 @@ var q = require('q'); * @param {Array} specs Array of Directory Path Strings. * @return {q.Promise} Promise resolved with the test results */ -exports.run = function(runner, specs) { - var Mocha = require('mocha'), - mocha = new Mocha(runner.getConfig().mochaOpts); +exports.run = (runner, specs) => { + const Mocha = require('mocha'); + const mocha = new Mocha(runner.getConfig().mochaOpts); // Add hooks for afterEach require('./setupAfterEach').setup(runner, specs); - var deferred = q.defer(); + return new Promise(async (resolve, reject) => { + mocha.loadFiles(); - // Mocha doesn't set up the ui until the pre-require event, so - // wait until then to load mocha-webdriver adapters as well. - mocha.suite.on('pre-require', function() { try { - // We need to re-wrap all of the global functions, which `selenium-webdriver/testing` only - // does when it is required. So first we must remove it from the cache. - delete require.cache[require.resolve('selenium-webdriver/testing')]; - var seleniumAdapter = require('selenium-webdriver/testing'); - - // Save unwrapped version - var unwrappedFns = {}; - ['after', 'afterEach', 'before', 'beforeEach', 'it', 'xit', 'iit'].forEach(function(fnName) { - unwrappedFns[fnName] = global[fnName] || Mocha[fnName]; + await runner.runTestPreparer(); + specs.forEach((file) => { + mocha.addFile(file); }); + let testResult = []; - var wrapFn = function(seleniumWrappedFn, opt_fnName) { - // This does not work on functions that can be nested (e.g. `describe`) - return function() { - // Set globals to unwrapped version to avoid circular reference - var wrappedFns = {}; - for (var fnName in unwrappedFns) { - wrappedFns[fnName] = global[fnName]; - global[fnName] = unwrappedFns[fnName]; - } - - var args = arguments; - // Allow before/after hooks to use names - if (opt_fnName && (arguments.length > 1) && (seleniumWrappedFn.length < 2)) { - global[opt_fnName] = global[opt_fnName].bind(this, args[0]); - args = Array.prototype.slice.call(arguments, 1); + const mochaRunner = mocha.run(async (failures) => { + try { + if (runner.getConfig().onComplete) { + await runner.getConfig().onComplete(); } - - try { - seleniumWrappedFn.apply(this, args); - } finally { - // Restore wrapped version - for (fnName in wrappedFns) { - global[fnName] = wrappedFns[fnName]; - } - } - }; - }; - - // Wrap functions - global.after = wrapFn(seleniumAdapter.after, 'after'); - global.afterEach = wrapFn(seleniumAdapter.afterEach, 'afterEach'); - global.before = wrapFn(seleniumAdapter.before, 'before'); - global.beforeEach = wrapFn(seleniumAdapter.beforeEach, 'beforeEach'); - - global.it = wrapFn(seleniumAdapter.it); - global.iit = wrapFn(seleniumAdapter.it.only); - global.xit = wrapFn(seleniumAdapter.xit); - global.it.only = wrapFn(seleniumAdapter.it.only); - global.it.skip = wrapFn(seleniumAdapter.it.skip); - } catch (err) { - deferred.reject(err); - } - }); - - mocha.loadFiles(); - - runner.runTestPreparer().then(function() { - specs.forEach(function(file) { - mocha.addFile(file); - }); - - var testResult = []; - - var mochaRunner = mocha.run(function(failures) { - try { - var completed = q(); - if (runner.getConfig().onComplete) { - completed = q(runner.getConfig().onComplete()); - } - completed.then(function() { - deferred.resolve({ + resolve({ failedCount: failures, specResults: testResult }); - }); - } catch (err) { - deferred.reject(err); - } - }); + } catch (err) { + reject(err); + } + }); - mochaRunner.on('pass', function(test) { - var testInfo = { - name: test.title, - category: test.fullTitle().slice(0, -test.title.length).trim() - }; - runner.emit('testPass', testInfo); - testResult.push({ - description: test.title, - assertions: [{ - passed: true - }], - duration: test.duration + mochaRunner.on('pass', (test) => { + const testInfo = { + name: test.title, + category: test.fullTitle().slice(0, -test.title.length).trim() + }; + runner.emit('testPass', testInfo); + testResult.push({ + description: test.title, + assertions: [{ + passed: true + }], + duration: test.duration + }); }); - }); - mochaRunner.on('fail', function(test) { - var testInfo = { - name: test.title, - category: test.fullTitle().slice(0, -test.title.length).trim() - }; - runner.emit('testFail', testInfo); - testResult.push({ - description: test.title, - assertions: [{ - passed: false, - errorMsg: test.err.message, - stackTrace: test.err.stack - }], - duration: test.duration + mochaRunner.on('fail', (test) => { + const testInfo = { + name: test.title, + category: test.fullTitle().slice(0, -test.title.length).trim() + }; + runner.emit('testFail', testInfo); + testResult.push({ + description: test.title, + assertions: [{ + passed: false, + errorMsg: test.err.message, + stackTrace: test.err.stack + }], + duration: test.duration + }); }); - }); - }).catch (function(reason) { - deferred.reject(reason); + } catch (err) { + reject(err); + } }); - - return deferred.promise; }; diff --git a/lib/frameworks/setupAfterEach.js b/lib/frameworks/setupAfterEach.ts similarity index 74% rename from lib/frameworks/setupAfterEach.js rename to lib/frameworks/setupAfterEach.ts index 98be559d3..3950dfb07 100644 --- a/lib/frameworks/setupAfterEach.js +++ b/lib/frameworks/setupAfterEach.ts @@ -1,3 +1,8 @@ +import * as path from 'path'; +import {Runner} from '../runner'; + +export interface Hooks { afterEach: () => Promise; } + /** * Setup afterEach hook for jasmine/mocha tests. * @@ -7,23 +12,19 @@ * file is not prematurely executed. */ -var path = require('path'); - // Queried by `protractor_internal_afterEach_setup_spec.js` for the `afterEach` hook -var hooks = { +export const hooks: Hooks = { afterEach: null }; -exports.hooks = hooks; - /** - * Setup `runner.afterEach` to be called after every spec. + * Setup `runner.afterEach` to be called after every spec. * * @param {Runner} runner The current Protractor Runner. * @param {Array} specs Array of Directory Path Strings. Must be a reference to the same array * instance used by the framework */ -exports.setup = function(runner, specs) { +export const setup = function(runner: Runner, specs: Array) { hooks.afterEach = runner.afterEach.bind(runner); specs.push(path.resolve(__dirname, '__protractor_internal_afterEach_setup_spec.js')); }; diff --git a/lib/index.ts b/lib/index.ts index f1a0cae5f..479dd26a3 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -6,7 +6,8 @@ import {PluginConfig, ProtractorPlugin} from './plugins'; import {Ptor} from './ptor'; // Re-export selenium-webdriver types. -export {ActionSequence, Browser, Builder, Button, Capabilities, Capability, error, EventEmitter, FileDetector, Key, logging, promise, Session, until, WebDriver, WebElement, WebElementPromise} from 'selenium-webdriver'; +// TODO(selenium4): Actions class typings missing. ActionSequence is deprecated. +export {/*Actions,*/ Browser, Builder, Button, Capabilities, Capability, error, EventEmitter, FileDetector, Key, logging, promise, Session, until, WebDriver, WebElement, WebElementPromise} from 'selenium-webdriver'; // Re-export public types. export {ElementHelper, ProtractorBrowser} from './browser'; export {Config} from './config'; diff --git a/lib/launcher.ts b/lib/launcher.ts index 6a92e20ea..2ce5d6ed3 100644 --- a/lib/launcher.ts +++ b/lib/launcher.ts @@ -3,16 +3,15 @@ * input configuration and launching test runners. */ import * as fs from 'fs'; -import * as q from 'q'; import {Config} from './config'; import {ConfigParser} from './configParser'; import {ConfigError, ErrorHandler, ProtractorError} from './exitCodes'; import {Logger} from './logger'; -import {Runner} from './runner'; import {TaskRunner} from './taskRunner'; import {TaskScheduler} from './taskScheduler'; -import * as helper from './util'; +import {runFilenameOrFn_} from './util'; + let logger = new Logger('launcher'); let RUNNERS_FAILED_EXIT_CODE = 100; @@ -93,7 +92,7 @@ let taskResults_ = new TaskResults(); * @param {string=} configFile * @param {Object=} additionalConfig */ -let initFn = function(configFile: string, additionalConfig: Config) { +let initFn = async function(configFile: string, additionalConfig: Config) { let configParser = new ConfigParser(); if (configFile) { configParser.addFileConfig(configFile); @@ -108,197 +107,168 @@ let initFn = function(configFile: string, additionalConfig: Config) { logger.debug('Your base url for tests is ' + config.baseUrl); // Run beforeLaunch - helper.runFilenameOrFn_(config.configDir, config.beforeLaunch) - .then(() => { + await runFilenameOrFn_(config.configDir, config.beforeLaunch); + // 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. + const waitMultiConfig = await config.getMultiCapabilities(); + config.multiCapabilities = waitMultiConfig; + config.capabilities = null; + } - return q - .Promise((resolve: Function, reject: Function) => { - // 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'; + // 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'}]; + } - let runner = new Runner(config); - return runner.run().then( - (exitCode: number) => { - process.exit(exitCode); - }, - (err: Error) => { - logger.error(err); - process.exit(1); - }); - } - }) - .then(() => { - // 4) Run tests. - let scheduler = new TaskScheduler(config); + // 3) If we're in `elementExplorer` mode, throw an error and exit. + if (config.elementExplorer || config.framework === 'explorer') { + const err = new Error( + 'Deprecated: Element explorer depends on the ' + + 'WebDriver control flow, and thus is no longer supported.'); + logger.error(err); + process.exit(1); + } - process.on('uncaughtException', (exc: (Error|string)) => { - 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; - } + // 4) Run tests. + let scheduler = new TaskScheduler(config); - let errorCode = ErrorHandler.parseError(e); - if (errorCode) { - let protractorError = e as ProtractorError; - ProtractorError.log(logger, errorCode, protractorError.message, protractorError.stack); - process.exit(errorCode); - } else { - logger.error(e.message); - logger.error(e.stack); - process.exit(ProtractorError.CODE); - } - }); + process.on('uncaughtException', (exc: (Error|string)) => { + 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; + } + logger.error(e.message); + logger.error(e.stack); + if (e instanceof ProtractorError) { + let protractorError = e as ProtractorError; + process.exit(protractorError.code); + } else { + process.exit(1); + } + }); - process.on('exit', (code: number) => { - 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); - } - }); + process.on('unhandledRejection', (reason: Error | any, p: Promise) => { + if (reason.stack.match('angular testability are undefined') || + reason.stack.match('angular is not defined')) { + logger.warn( + 'Unhandled promise rejection error: This is usually occurs ' + + 'when a browser.get call is made and a previous async call was ' + + 'not awaited'); + } + logger.warn(p); + }); - // Run afterlaunch and exit - let cleanUpAndExit = (exitCode: number) => { - return helper.runFilenameOrFn_(config.configDir, config.afterLaunch, [exitCode]) - .then( - (returned) => { - if (typeof returned === 'number') { - process.exit(returned); - } else { - process.exit(exitCode); - } - }, - (err: Error) => { - logger.error('Error:', err); - process.exit(1); - }); - }; + process.on('exit', (code: number) => { + 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); + } + }); - let totalTasks = scheduler.numTasksOutstanding(); - let forkProcess = false; - if (totalTasks > 1) { // Start new processes only if there are >1 tasks. - forkProcess = true; - if (config.debug) { - throw new ConfigError( - logger, 'Cannot run in debug mode with multiCapabilities, count > 1, or sharding'); - } - } + // Run afterlaunch and exit + const cleanUpAndExit = async (exitCode: number) => { + try { + const returned = await runFilenameOrFn_(config.configDir, config.afterLaunch, [exitCode]); + if (typeof returned === 'number') { + process.exit(returned); + } else { + process.exit(exitCode); + } + } catch (err) { + logger.error('Error:', err); + process.exit(1); + } + }; - let deferred = q.defer(); // Resolved when all tasks are completed - let createNextTaskRunner = () => { - let task = scheduler.nextTask(); - if (task) { - let taskRunner = new 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: Error) => { - logger.error('Error:', (err as any).stack || err.message || err); - cleanUpAndExit(RUNNERS_FAILED_EXIT_CODE); - }); + const totalTasks = scheduler.numTasksOutstanding(); + let forkProcess = false; + if (totalTasks > 1) { // Start new processes only if there are >1 tasks. + forkProcess = true; + if (config.debug) { + throw new ConfigError( + logger, 'Cannot run in debug mode with multiCapabilities, count > 1, or sharding'); + } + } + + const createNextTaskRunner = async () => { + return new Promise(async (resolve) => { + const task = scheduler.nextTask(); + if (task) { + const taskRunner = new TaskRunner(configFile, additionalConfig, task, forkProcess); + try { + const result = await taskRunner.run(); + if (result.exitCode && !result.failedCount) { + logger.error('Runner process exited unexpectedly with error code: ' + result.exitCode); + } + taskResults_.add(result); + task.done(); + await createNextTaskRunner(); + // If all tasks are finished + if (scheduler.numTasksOutstanding() === 0) { + resolve(); } - }; - // 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(scheduler.countActiveTasks() + ' instance(s) of WebDriver still running'); + } catch (err) { + const errorCode = ErrorHandler.parseError(err); + logger.error('Error:', (err as any).stack || err.message || err); + await cleanUpAndExit(errorCode ? errorCode : RUNNERS_FAILED_EXIT_CODE); } - logger.info('Running ' + scheduler.countActiveTasks() + ' instances of WebDriver'); + } else { + resolve(); + } + }); + }; + + const maxConcurrentTasks = scheduler.maxConcurrentTasks(); + for (let i = 0; i < maxConcurrentTasks; ++i) { + await createNextTaskRunner(); + } + logger.info('Running ' + scheduler.countActiveTasks() + ' instances of WebDriver'); + + // By now all runners have completed. + // Save results if desired + if (config.resultJsonOutputFile) { + taskResults_.saveResults(config.resultJsonOutputFile); + } - // 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; + } + await cleanUpAndExit(exitCode); + // Start `const 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. - 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(); }; export let init = initFn; diff --git a/lib/locators.ts b/lib/locators.ts index f6852ea02..2fe317df8 100644 --- a/lib/locators.ts +++ b/lib/locators.ts @@ -1,4 +1,4 @@ -import {By, ByHash, promise as wdpromise, WebDriver, WebElement} from 'selenium-webdriver'; +import {By, ByHash, WebDriver, WebElement} from 'selenium-webdriver'; let clientSideScripts = require('./clientsidescripts'); @@ -21,8 +21,7 @@ export type WebDriverLocator = By | ByHash | Function; // Protractor locator strategy export interface ProtractorLocator { findElementsOverride: - (driver: WebDriver, using: WebElement, - rootSelector: string) => wdpromise.Promise; + (driver: WebDriver, using: WebElement, rootSelector: string) => Promise; row?: (index: number) => Locator; column?: (index: string) => Locator; toString?: () => string; @@ -67,7 +66,7 @@ export class ProtractorBy extends WebdriverBy { * }); * * // Use the custom locator. - * element(by.buttonTextSimple('Go!')).click(); + * await element(by.buttonTextSimple('Go!')).click(); * * @alias by.addLocator(locatorName, functionOrScript) * @param {string} name The name of the new locator. @@ -79,25 +78,25 @@ export class ProtractorBy extends WebdriverBy { */ addLocator(name: string, script: Function|string) { this[name] = (...args: any[]): ProtractorLocator => { - let locatorArguments = args; + const locatorArguments = args; return { - findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string): - wdpromise.Promise => { - let findElementArguments: any[] = [script]; - for (let i = 0; i < locatorArguments.length; i++) { - findElementArguments.push(locatorArguments[i]); - } - findElementArguments.push(using); - findElementArguments.push(rootSelector); + findElementsOverride: async(driver: WebDriver, using: WebElement, rootSelector: string): + Promise => { + let findElementArguments: any[] = [script]; + for (let i = 0; i < locatorArguments.length; i++) { + findElementArguments.push(locatorArguments[i]); + } + findElementArguments.push(using); + findElementArguments.push(rootSelector); - return driver.findElements(By.js.apply(By, findElementArguments)); - }, + return await driver.findElements(By.js.apply(By, findElementArguments)); + }, toString: (): string => { return 'by.' + name + '("' + Array.prototype.join.call(locatorArguments, '", "') + '")'; } }; }; - }; + } /** * Find an element by text binding. Does a partial match, so any elements @@ -114,14 +113,14 @@ export class ProtractorBy extends WebdriverBy { * * @example * var span1 = element(by.binding('person.name')); - * expect(span1.getText()).toBe('Foo'); + * expect(await span1.getText()).toBe('Foo'); * * var span2 = element(by.binding('person.email')); - * expect(span2.getText()).toBe('foo@bar.com'); + * expect(await span2.getText()).toBe('foo@bar.com'); * * // You can also use a substring for a partial match * var span1alt = element(by.binding('name')); - * expect(span1alt.getText()).toBe('Foo'); + * expect(await span1alt.getText()).toBe('Foo'); * * // This works for sites using Angular 1.2 but NOT 1.3 * var deprecatedSyntax = element(by.binding('{{person.name}}')); @@ -131,16 +130,16 @@ export class ProtractorBy extends WebdriverBy { */ binding(bindingDescriptor: string): ProtractorLocator { return { - findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string): - wdpromise.Promise => { - return driver.findElements( - By.js(clientSideScripts.findBindings, bindingDescriptor, false, using, rootSelector)); - }, + findElementsOverride: async(driver: WebDriver, using: WebElement, rootSelector: string): + Promise => { + return await driver.findElements(By.js( + clientSideScripts.findBindings, bindingDescriptor, false, using, rootSelector)); + }, toString: (): string => { return 'by.binding("' + bindingDescriptor + '")'; } }; - }; + } /** * Find an element by exact binding. @@ -151,28 +150,28 @@ export class ProtractorBy extends WebdriverBy { * {{person_phone|uppercase}} * * @example - * expect(element(by.exactBinding('person.name')).isPresent()).toBe(true); - * expect(element(by.exactBinding('person-email')).isPresent()).toBe(true); - * expect(element(by.exactBinding('person')).isPresent()).toBe(false); - * expect(element(by.exactBinding('person_phone')).isPresent()).toBe(true); - * expect(element(by.exactBinding('person_phone|uppercase')).isPresent()).toBe(true); - * expect(element(by.exactBinding('phone')).isPresent()).toBe(false); + * expect(await element(by.exactBinding('person.name')).isPresent()).toBe(true); + * expect(await element(by.exactBinding('person-email')).isPresent()).toBe(true); + * expect(await element(by.exactBinding('person')).isPresent()).toBe(false); + * expect(await element(by.exactBinding('person_phone')).isPresent()).toBe(true); + * expect(await element(by.exactBinding('person_phone|uppercase')).isPresent()).toBe(true); + * expect(await element(by.exactBinding('phone')).isPresent()).toBe(false); * * @param {string} bindingDescriptor * @returns {ProtractorLocator} location strategy */ exactBinding(bindingDescriptor: string): ProtractorLocator { return { - findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string): - wdpromise.Promise => { - return driver.findElements( - By.js(clientSideScripts.findBindings, bindingDescriptor, true, using, rootSelector)); - }, + findElementsOverride: async(driver: WebDriver, using: WebElement, rootSelector: string): + Promise => { + return await driver.findElements(By.js( + clientSideScripts.findBindings, bindingDescriptor, true, using, rootSelector)); + }, toString: (): string => { return 'by.exactBinding("' + bindingDescriptor + '")'; } }; - }; + } /** * Find an element by ng-model expression. @@ -183,24 +182,24 @@ export class ProtractorBy extends WebdriverBy { * * @example * var input = element(by.model('person.name')); - * input.sendKeys('123'); - * expect(input.getAttribute('value')).toBe('Foo123'); + * await input.sendKeys('123'); + * expect(await input.getAttribute('value')).toBe('Foo123'); * * @param {string} model ng-model expression. * @returns {ProtractorLocator} location strategy */ model(model: string): ProtractorLocator { return { - findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string): - wdpromise.Promise => { - return driver.findElements( - By.js(clientSideScripts.findByModel, model, using, rootSelector)); - }, + findElementsOverride: async(driver: WebDriver, using: WebElement, rootSelector: string): + Promise => { + return await driver.findElements( + By.js(clientSideScripts.findByModel, model, using, rootSelector)); + }, toString: (): string => { return 'by.model("' + model + '")'; } }; - }; + } /** * Find a button by text. @@ -216,16 +215,16 @@ export class ProtractorBy extends WebdriverBy { */ buttonText(searchText: string): ProtractorLocator { return { - findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string): - wdpromise.Promise => { - return driver.findElements( - By.js(clientSideScripts.findByButtonText, searchText, using, rootSelector)); - }, + findElementsOverride: async(driver: WebDriver, using: WebElement, rootSelector: string): + Promise => { + return driver.findElements( + By.js(clientSideScripts.findByButtonText, searchText, using, rootSelector)); + }, toString: (): string => { return 'by.buttonText("' + searchText + '")'; } }; - }; + } /** * Find a button by partial text. @@ -241,23 +240,23 @@ export class ProtractorBy extends WebdriverBy { */ partialButtonText(searchText: string): ProtractorLocator { return { - findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string): - wdpromise.Promise => { - return driver.findElements( - By.js(clientSideScripts.findByPartialButtonText, searchText, using, rootSelector)); - }, + findElementsOverride: async(driver: WebDriver, using: WebElement, rootSelector: string): + Promise => { + return driver.findElements( + By.js(clientSideScripts.findByPartialButtonText, searchText, using, rootSelector)); + }, toString: (): string => { return 'by.partialButtonText("' + searchText + '")'; } }; - }; + } // Generate either by.repeater or by.exactRepeater private byRepeaterInner(exact: boolean, repeatDescriptor: string): ProtractorLocator { let name = 'by.' + (exact ? 'exactR' : 'r') + 'epeater'; return { - findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string): - wdpromise.Promise => { + findElementsOverride: async( + driver: WebDriver, using: WebElement, rootSelector: string): Promise => { return driver.findElements(By.js( clientSideScripts.findAllRepeaterRows, repeatDescriptor, exact, using, rootSelector)); }, @@ -266,23 +265,24 @@ export class ProtractorBy extends WebdriverBy { }, row: (index: number): ProtractorLocator => { return { - findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string): - wdpromise.Promise => { - return driver.findElements(By.js( - clientSideScripts.findRepeaterRows, repeatDescriptor, exact, index, using, - rootSelector)); - }, + findElementsOverride: async(driver: WebDriver, using: WebElement, rootSelector: string): + Promise => { + return await driver.findElements(By.js( + clientSideScripts.findRepeaterRows, repeatDescriptor, exact, index, using, + rootSelector)); + }, toString: (): string => { return name + '(' + repeatDescriptor + '").row("' + index + '")"'; }, column: (binding: string): ProtractorLocator => { return { - findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string): - wdpromise.Promise => { - return driver.findElements(By.js( - clientSideScripts.findRepeaterElement, repeatDescriptor, exact, index, binding, - using, rootSelector)); - }, + findElementsOverride: + async(driver: WebDriver, using: WebElement, rootSelector: string): + Promise => { + return driver.findElements(By.js( + clientSideScripts.findRepeaterElement, repeatDescriptor, exact, index, + binding, using, rootSelector)); + }, toString: (): string => { return name + '("' + repeatDescriptor + '").row("' + index + '").column("' + binding + '")'; @@ -293,23 +293,24 @@ export class ProtractorBy extends WebdriverBy { }, column: (binding: string): ProtractorLocator => { return { - findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string): - wdpromise.Promise => { - return driver.findElements(By.js( - clientSideScripts.findRepeaterColumn, repeatDescriptor, exact, binding, using, - rootSelector)); - }, + findElementsOverride: async(driver: WebDriver, using: WebElement, rootSelector: string): + Promise => { + return driver.findElements(By.js( + clientSideScripts.findRepeaterColumn, repeatDescriptor, exact, binding, using, + rootSelector)); + }, toString: (): string => { return name + '("' + repeatDescriptor + '").column("' + binding + '")'; }, row: (index: number): ProtractorLocator => { return { - findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string): - wdpromise.Promise => { - return driver.findElements(By.js( - clientSideScripts.findRepeaterElement, repeatDescriptor, exact, index, binding, - using, rootSelector)); - }, + findElementsOverride: + async(driver: WebDriver, using: WebElement, rootSelector: string): + Promise => { + return driver.findElements(By.js( + clientSideScripts.findRepeaterElement, repeatDescriptor, exact, index, + binding, using, rootSelector)); + }, toString: (): string => { return name + '("' + repeatDescriptor + '").column("' + binding + '").row("' + index + '")'; @@ -340,37 +341,36 @@ export class ProtractorBy extends WebdriverBy { * * @example * // Returns the DIV for the second cat. - * var secondCat = element(by.repeater('cat in pets').row(1)); + * let secondCat = element(by.repeater('cat in pets').row(1)); * * // Returns the SPAN for the first cat's name. - * var firstCatName = element(by.repeater('cat in pets'). + * let firstCatName = element(by.repeater('cat in pets'). * row(0).column('cat.name')); * * // Returns a promise that resolves to an array of WebElements from a column - * var ages = element.all( - * by.repeater('cat in pets').column('cat.age')); + * let ages = element.all(by.repeater('cat in pets').column('cat.age')); * * // Returns a promise that resolves to an array of WebElements containing * // all top level elements repeated by the repeater. For 2 pets rows * // resolves to an array of 2 elements. - * var rows = element.all(by.repeater('cat in pets')); + * let rows = element.all(by.repeater('cat in pets')); * * // Returns a promise that resolves to an array of WebElements containing * // all the elements with a binding to the book's name. - * var divs = element.all(by.repeater('book in library').column('book.name')); + * let divs = element.all(by.repeater('book in library').column('book.name')); * * // Returns a promise that resolves to an array of WebElements containing * // the DIVs for the second book. - * var bookInfo = element.all(by.repeater('book in library').row(1)); + * let bookInfo = element.all(by.repeater('book in library').row(1)); * * // Returns the H4 for the first book's name. - * var firstBookName = element(by.repeater('book in library'). + * let firstBookName = element(by.repeater('book in library'). * row(0).column('book.name')); * * // Returns a promise that resolves to an array of WebElements containing * // all top level elements repeated by the repeater. For 2 books divs * // resolves to an array of 4 elements. - * var divs = element.all(by.repeater('book in library')); + * let divs = element.all(by.repeater('book in library')); * * @param {string} repeatDescriptor * @returns {ProtractorLocator} location strategy @@ -387,12 +387,9 @@ export class ProtractorBy extends WebdriverBy { *
  • * * @example - * expect(element(by.exactRepeater('person in - * peopleWithRedHair')).isPresent()) - * .toBe(true); - * expect(element(by.exactRepeater('person in - * people')).isPresent()).toBe(false); - * expect(element(by.exactRepeater('car in cars')).isPresent()).toBe(true); + * expect(await element(by.exactRepeater('person in peopleWithRedHair')).isPresent()).toBe(true); + * expect(await element(by.exactRepeater('person in people')).isPresent()).toBe(false); + * expect(await element(by.exactRepeater('car in cars')).isPresent()).toBe(true); * * @param {string} repeatDescriptor * @returns {ProtractorLocator} location strategy @@ -415,23 +412,23 @@ export class ProtractorBy extends WebdriverBy { * var dog = element(by.cssContainingText('.pet', 'Dog')); * * @param {string} cssSelector css selector - * @param {string|RegExp} searchString text search + * @param {string|RegExp} searchText text search * @returns {ProtractorLocator} location strategy */ cssContainingText(cssSelector: string, searchText: string|RegExp): ProtractorLocator { searchText = (searchText instanceof RegExp) ? '__REGEXP__' + searchText.toString() : searchText; return { - findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string): - wdpromise.Promise => { - return driver.findElements(By.js( - clientSideScripts.findByCssContainingText, cssSelector, searchText, using, - rootSelector)); - }, + findElementsOverride: async(driver: WebDriver, using: WebElement, rootSelector: string): + Promise => { + return await driver.findElements(By.js( + clientSideScripts.findByCssContainingText, cssSelector, searchText, using, + rootSelector)); + }, toString: (): string => { return 'by.cssContainingText("' + cssSelector + '", "' + searchText + '")'; } }; - }; + } /** * Find an element by ng-options expression. @@ -445,25 +442,25 @@ export class ProtractorBy extends WebdriverBy { * * @example * var allOptions = element.all(by.options('c for c in colors')); - * expect(allOptions.count()).toEqual(2); + * expect(await allOptions.count()).toEqual(2); * var firstOption = allOptions.first(); - * expect(firstOption.getText()).toEqual('red'); + * expect(await firstOption.getText()).toEqual('red'); * * @param {string} optionsDescriptor ng-options expression. * @returns {ProtractorLocator} location strategy */ options(optionsDescriptor: string): ProtractorLocator { return { - findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string): - wdpromise.Promise => { - return driver.findElements( - By.js(clientSideScripts.findByOptions, optionsDescriptor, using, rootSelector)); - }, + findElementsOverride: async(driver: WebDriver, using: WebElement, rootSelector: string): + Promise => { + return await driver.findElements( + By.js(clientSideScripts.findByOptions, optionsDescriptor, using, rootSelector)); + }, toString: (): string => { return 'by.option("' + optionsDescriptor + '")'; } }; - }; + } /** * Find an element by css selector within the Shadow DOM. @@ -481,7 +478,7 @@ export class ProtractorBy extends WebdriverBy { * * @example * var spans = element.all(by.deepCss('span')); - * expect(spans.count()).toEqual(3); + * expect(await spans.count()).toEqual(3); * * @param {string} selector a css selector within the Shadow DOM. * @returns {Locator} location strategy @@ -490,5 +487,5 @@ export class ProtractorBy extends WebdriverBy { // TODO(julie): syntax will change from /deep/ to >>> at some point. // When that is supported, switch it here. return By.css('* /deep/ ' + selector); - }; + } } diff --git a/lib/plugins.ts b/lib/plugins.ts index 1eaf88b1d..bccd742e2 100644 --- a/lib/plugins.ts +++ b/lib/plugins.ts @@ -1,19 +1,12 @@ -import * as q from 'q'; import * as webdriver from 'selenium-webdriver'; import {ProtractorBrowser} from './browser'; import {Config} from './config'; import {ConfigParser} from './configParser'; import {Logger} from './logger'; -import {protractor} from './ptor'; let logger = new Logger('plugins'); -export enum PromiseType { - Q, - WEBDRIVER -} - export interface PluginConfig { path?: string; package?: string; @@ -124,8 +117,8 @@ export interface ProtractorPlugin { /** * This is called inside browser.get() directly after angular is done - * bootstrapping/synchronizing. If `browser.ignoreSynchronization` is `true`, - * this will not be called. + * bootstrapping/synchronizing. If `await browser.waitForAngularEnabled()` + * is `false`, this will not be called. * * @param {ProtractorBrowser} browser The browser instance which is loading a page. * @@ -317,7 +310,7 @@ export class Plugins { this.pluginObjs.push(pluginObj); }); } - }; + } /** * Adds properties to a plugin's object @@ -405,7 +398,7 @@ export class Plugins { this.printPluginResults(results.specResults); this.resultsReported = true; return results; - }; + } /** * Returns true if any loaded plugin has skipAngularStability enabled. @@ -415,20 +408,20 @@ export class Plugins { skipAngularStability() { const result = this.pluginObjs.some(pluginObj => pluginObj.skipAngularStability); return result; - }; + } /** * @see docs/plugins.md#writing-plugins for information on these functions */ - setup = this.pluginFunFactory('setup', PromiseType.Q); - onPrepare = this.pluginFunFactory('onPrepare', PromiseType.Q); - teardown = this.pluginFunFactory('teardown', PromiseType.Q); - postResults = this.pluginFunFactory('postResults', PromiseType.Q); - postTest = this.pluginFunFactory('postTest', PromiseType.Q); - onPageLoad = this.pluginFunFactory('onPageLoad', PromiseType.WEBDRIVER); - onPageStable = this.pluginFunFactory('onPageStable', PromiseType.WEBDRIVER); - waitForPromise = this.pluginFunFactory('waitForPromise', PromiseType.WEBDRIVER); - waitForCondition = this.pluginFunFactory('waitForCondition', PromiseType.WEBDRIVER, true); + setup = this.pluginFunFactory('setup'); + onPrepare = this.pluginFunFactory('onPrepare'); + teardown = this.pluginFunFactory('teardown'); + postResults = this.pluginFunFactory('postResults'); + postTest = this.pluginFunFactory('postTest'); + onPageLoad = this.pluginFunFactory('onPageLoad'); + onPageStable = this.pluginFunFactory('onPageStable'); + waitForPromise = this.pluginFunFactory('waitForPromise'); + waitForCondition = this.pluginFunFactory('waitForCondition', true); /** * Calls a function from a plugin safely. If the plugin's function throws an @@ -439,18 +432,15 @@ export class Plugins { * @param {Object} pluginObj The plugin object containing the function to be run * @param {string} funName The name of the function we want to run * @param {*[]} args The arguments we want to invoke the function with - * @param {PromiseType} promiseType The type of promise (WebDriver or Q) that - * should be used * @param {boolean} resultsReported If the results have already been reported * @param {*} failReturnVal The value to return if the function fails * - * @return {webdriver.promise.Promise|Q.Promise} A promise which resolves to the + * @return {Promise} A promise which resolves to the * function's return value */ private safeCallPluginFun( - pluginObj: ProtractorPlugin, funName: string, args: any[], promiseType: PromiseType, - failReturnVal: any): q.Promise|Promise|webdriver.promise.Promise { - const resolver = (done: (result: any) => void) => { + pluginObj: ProtractorPlugin, funName: string, args: any[], failReturnVal: any): Promise { + const resolver = async (done: (result: any) => void) => { const logError = (e: any) => { if (this.resultsReported) { this.printPluginResults([{ @@ -468,47 +458,33 @@ export class Plugins { done(failReturnVal); }; try { - const result = (pluginObj as any)[funName].apply(pluginObj, args); - if (webdriver.promise.isPromise(result)) { - (result as PromiseLike).then(done, logError); - } else { - done(result); - } + const result = await(pluginObj as any)[funName].apply(pluginObj, args); + done(result); } catch (e) { logError(e); } }; - if (promiseType == PromiseType.Q) { - return q.Promise(resolver); - } else if (protractor.browser.controlFlowIsEnabled()) { - return new webdriver.promise.Promise(resolver); - } else { - return new Promise(resolver); - } + return new Promise(resolver); } /** * Generates the handler for a plugin function (e.g. the setup() function) * * @param {string} funName The name of the function to make a handler for - * @param {PromiseType} promiseType The type of promise (WebDriver or Q) that should be used * @param {boolean=} failReturnVal The value that the function should return if the plugin crashes * * @return The handler */ - private pluginFunFactory(funName: string, promiseType: PromiseType.Q, failReturnVal?: boolean): - (...args: any[]) => q.Promise; - private pluginFunFactory( - funName: string, promiseType: PromiseType.WEBDRIVER, - failReturnVal?: boolean): (...args: any[]) => webdriver.promise.Promise; - private pluginFunFactory(funName: string, promiseType: PromiseType, failReturnVal?: boolean) { + private pluginFunFactory(funName: string, failReturnVal?: boolean): + (...args: any[]) => Promise; + private pluginFunFactory(funName: string, failReturnVal?: boolean): + (...args: any[]) => webdriver.promise.Promise; + private pluginFunFactory(funName: string, failReturnVal?: boolean) { return (...args: any[]) => { const promises = this.pluginObjs.filter(pluginObj => typeof(pluginObj as any)[funName] === 'function') - .map( - pluginObj => - this.safeCallPluginFun(pluginObj, funName, args, promiseType, failReturnVal)); - return promiseType == PromiseType.Q ? q.all(promises) : webdriver.promise.all(promises); + .map(pluginObj => this.safeCallPluginFun(pluginObj, funName, args, failReturnVal)); + return Promise.all(promises); }; } } diff --git a/lib/runner.ts b/lib/runner.ts index 6d36e7540..de0bfff8b 100644 --- a/lib/runner.ts +++ b/lib/runner.ts @@ -1,16 +1,15 @@ import {EventEmitter} from 'events'; -import * as q from 'q'; -import {promise as wdpromise, Session} from 'selenium-webdriver'; +// TODO(cnishina): remove when selenium webdriver is upgraded. +import {promise as wdpromise, WebDriver} from 'selenium-webdriver'; import * as util from 'util'; import {ProtractorBrowser} from './browser'; import {Config} from './config'; import {buildDriverProvider, DriverProvider} from './driverProviders'; -import {ConfigError} from './exitCodes'; import {Logger} from './logger'; import {Plugins} from './plugins'; import {protractor} from './ptor'; -import * as helper from './util'; +import {joinTestLogs, runFilenameOrFn_} from './util'; declare let global: any; declare let process: any; @@ -34,9 +33,9 @@ export class Runner extends EventEmitter { driverprovider_: DriverProvider; o: any; plugins_: Plugins; - restartPromise: q.Promise; + restartPromise: Promise; frameworkUsesAfterEach: boolean; - ready_?: wdpromise.Promise; + ready_?: Promise; constructor(config: Config) { super(); @@ -48,19 +47,12 @@ export class Runner extends EventEmitter { if (config.nodeDebug) { process['_debugProcess'](process.pid); - let flow = wdpromise.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'); + const nodedebug = require('child_process').fork('debug', ['localhost:5858']); + process.on('exit', () => { + nodedebug.kill('SIGTERM'); + }); + nodedebug.on('exit', () => { + process.exit(1); }); } @@ -84,10 +76,10 @@ export class Runner extends EventEmitter { * 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 + * @return {Promise} A promise that will resolve when the test preparers * are finished. */ - runTestPreparer(extraFlags?: string[]): q.Promise { + runTestPreparer(extraFlags?: string[]): Promise { let unknownFlags = this.config_.unknownFlags_ || []; if (extraFlags) { unknownFlags = unknownFlags.filter((f) => extraFlags.indexOf(f) === -1); @@ -100,7 +92,7 @@ export class Runner extends EventEmitter { ' Protractor CLI flag checks. '); } return this.plugins_.onPrepare().then(() => { - return helper.runFilenameOrFn_(this.config_.configDir, this.preparer_); + return runFilenameOrFn_(this.config_.configDir, this.preparer_); }); } @@ -110,17 +102,17 @@ export class Runner extends EventEmitter { * Responsible for `restartBrowserBetweenTests` * * @public - * @return {q.Promise} A promise that will resolve when the work here is done + * @return {Promise} A promise that will resolve when the work here is done */ - afterEach(): q.Promise { - let ret: q.Promise; + afterEach(): Promise { + let ret: Promise; this.frameworkUsesAfterEach = true; if (this.config_.restartBrowserBetweenTests) { - this.restartPromise = this.restartPromise || q(protractor.browser.restart()); + this.restartPromise = this.restartPromise || Promise.resolve(protractor.browser.restart()); ret = this.restartPromise; this.restartPromise = undefined; } - return ret || q(); + return ret || Promise.resolve(); } /** @@ -144,16 +136,15 @@ export class Runner extends EventEmitter { * @private * @param {int} Standard unix exit code */ - exit_ = function(exitCode: number): any { - return helper.runFilenameOrFn_(this.config_.configDir, this.config_.onCleanUp, [exitCode]) - .then((returned): number | any => { - if (typeof returned === 'number') { - return returned; - } else { - return exitCode; - } - }); - }; + async exit_(exitCode: number): Promise { + const returned = + await runFilenameOrFn_(this.config_.configDir, this.config_.onCleanUp, [exitCode]); + if (typeof returned === 'number') { + return returned; + } else { + return exitCode; + } + } /** * Getter for the Runner config object @@ -164,14 +155,6 @@ export class Runner extends EventEmitter { return this.config_; } - /** - * Get the control flow used by this runner. - * @return {Object} WebDriver control flow. - */ - controlFlow(): any { - return wdpromise.controlFlow(); - } - /** * Sets up convenience globals for test specs * @private @@ -220,9 +203,9 @@ export class Runner extends EventEmitter { * @return {Protractor} a protractor instance. * @public */ - createBrowser(plugins: any, parentBrowser?: ProtractorBrowser): any { + async createBrowser(plugins: any, parentBrowser?: ProtractorBrowser): Promise { let config = this.config_; - let driver = this.driverprovider_.getNewDriver(); + let driver = await this.driverprovider_.getNewDriver(); let blockingProxyUrl: string; if (config.useBlockingProxy) { @@ -231,14 +214,14 @@ export class Runner extends EventEmitter { let initProperties = { baseUrl: config.baseUrl, - rootElement: config.rootElement as string | wdpromise.Promise, + rootElement: config.rootElement as string | Promise, untrackOutstandingTimeouts: config.untrackOutstandingTimeouts, params: config.params, getPageTimeout: config.getPageTimeout, allScriptsTimeout: config.allScriptsTimeout, debuggerServerPort: config.debuggerServerPort, ng12Hybrid: config.ng12Hybrid, - waitForAngularEnabled: true as boolean | wdpromise.Promise + waitForAngularEnabled: true as boolean | Promise }; if (parentBrowser) { @@ -272,73 +255,42 @@ export class Runner extends EventEmitter { 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_; - }); + await browser_.waitForAngularEnabled(initProperties.waitForAngularEnabled); + // TODO(selenium4): Options does not have a setScriptTimeout method. + await(driver.manage() as any).setTimeouts({script: initProperties.allScriptsTimeout || 0}); + browser_.getProcessedConfig = () => { - return wdpromise.when(config); + return Promise.resolve(config); + }; + + browser_.forkNewDriverInstance = async( + useSameUrl: boolean, copyMockModules: boolean, copyConfigUpdates = true): Promise => { + let newBrowser = await this.createBrowser(plugins); + if (copyMockModules) { + newBrowser.mockModules_ = browser_.mockModules_; + } + if (useSameUrl) { + const currentUrl = await browser_.driver.getCurrentUrl(); + await newBrowser.get(currentUrl); + } + return newBrowser; }; - browser_.forkNewDriverInstance = - (useSameUrl: boolean, copyMockModules: boolean, 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: string) => { - return newBrowser.get(url); - }) - .then(() => { - return newBrowser; - }); - } - return newBrowser; - }; - - let replaceBrowser = () => { - let newBrowser = browser_.forkNewDriverInstance(false, true); + let replaceBrowser = async () => { + let newBrowser = await browser_.forkNewDriverInstance(false, true); if (browser_ === protractor.browser) { this.setupGlobals_(newBrowser); } return newBrowser; }; - browser_.restart = () => { + browser_.restart = async () => { // 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(); + const restartedBrowser = await replaceBrowser(); + await this.driverprovider_.quitDriver(browser_.driver); + return restartedBrowser; }; return browser_; @@ -347,10 +299,10 @@ export class Runner extends EventEmitter { /** * Final cleanup on exiting the runner. * - * @return {q.Promise} A promise which resolves on finish. + * @return {Promise} A promise which resolves on finish. * @private */ - shutdown_(): q.Promise { + shutdown_(): Promise { return DriverProvider.quitDrivers( this.driverprovider_, this.driverprovider_.getExistingDrivers()); } @@ -358,10 +310,10 @@ export class Runner extends EventEmitter { /** * The primary workhorse interface. Kicks off the test running process. * - * @return {q.Promise} A promise which resolves to the exit code of the tests. + * @return {Promise} A promise which resolves to the exit code of the tests. * @public */ - run(): q.Promise { + async run(): Promise { let testPassed: boolean; let plugins = this.plugins_ = new Plugins(this.config_); let pluginPostTestPromises: any; @@ -372,6 +324,7 @@ export class Runner extends EventEmitter { throw new Error('Spec patterns did not match any files.'); } + // TODO(selenium4): Remove when selenium is upgraded. if (this.config_.SELENIUM_PROMISE_MANAGER != null) { (wdpromise as any).USE_PROMISE_MANAGER = this.config_.SELENIUM_PROMISE_MANAGER; } @@ -380,121 +333,102 @@ export class Runner extends EventEmitter { 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: Session) => { - logger.debug( - 'WebDriver session successfully started with capabilities ' + - util.inspect(session.getCapabilities())); - }, - (err: Error) => { - 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: any) => { - pluginPostTestPromises.push(plugins.postTest(true, testInfo)); - }); - this.on('testFail', (testInfo: any) => { - 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: any) => { - 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_(); - }); + // 1) Setup environment + // noinspection JSValidateTypes + await this.driverprovider_.setupEnv(); + + // 2) Create a browser and setup globals + browser_ = await this.createBrowser(plugins); + this.setupGlobals_(browser_); + try { + const session = await browser_.getSession(); + logger.debug( + 'WebDriver session successfully started with capabilities ' + + util.inspect(session.getCapabilities())); + } catch (err) { + logger.error('Unable to start a WebDriver session.'); + throw err; + } + + // 3) Setup plugins + await plugins.setup(); + + // 4) Execute test cases + // 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 = async () => { + if (!this.frameworkUsesAfterEach) { + this.restartPromise = await 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: any) => { + pluginPostTestPromises.push(plugins.postTest(true, testInfo)); + }); + this.on('testFail', (testInfo: any) => { + pluginPostTestPromises.push(plugins.postTest(false, testInfo)); + }); + logger.debug('Running with spec files ' + this.config_.specs); + let testResults = await require(frameworkPath).run(this, this.config_.specs); + + // 5) Wait for postTest plugins to finish + results = testResults; + await Promise.all(pluginPostTestPromises); + + // 6) Teardown plugins + await plugins.teardown(); + + // 7) Teardown + results = joinTestLogs(results, plugins.getResults()); + this.emit('testsDone', results); + testPassed = results.failedCount === 0; + if (this.driverprovider_.updateJob) { + await this.driverprovider_.updateJob({'passed': testPassed}); + } + await this.driverprovider_.teardownEnv(); + + // 8) Let plugins do final cleanup + await plugins.postResults(); + + // 9) Exit process + const exitCode = testPassed ? 0 : 1; + + await this.shutdown_(); + + return this.exit_(exitCode); } } diff --git a/lib/taskRunner.ts b/lib/taskRunner.ts index bdef6f953..3c5579b29 100644 --- a/lib/taskRunner.ts +++ b/lib/taskRunner.ts @@ -1,6 +1,5 @@ import * as child_process from 'child_process'; import {EventEmitter} from 'events'; -import * as q from 'q'; import {Config} from './config'; import {ConfigParser} from './configParser'; @@ -8,12 +7,12 @@ import {Runner} from './runner'; import {TaskLogger} from './taskLogger'; export interface RunResults { - taskId: number; - specs: Array; - capabilities: any; - failedCount: number; - exitCode: number; - specResults: Array; + taskId?: number; + specs?: Array; + capabilities?: any; + failedCount?: number; + exitCode?: number; + specResults?: Array; } /** @@ -37,12 +36,12 @@ export class TaskRunner extends EventEmitter { /** * Sends the run command. - * @return {q.Promise} A promise that will resolve when the task finishes + * @return {Promise} A promise that will resolve when the task finishes * running. The promise contains the following parameters representing the * result of the run: * taskId, specs, capabilities, failedCount, exitCode, specResults */ - public run(): q.Promise { + public async run(): Promise { let runResults: RunResults = { taskId: this.task.taskId, specs: this.task.specs, @@ -65,61 +64,59 @@ export class TaskRunner extends EventEmitter { config.specs = this.task.specs; if (this.runInFork) { - let deferred = q.defer(); + return new Promise((resolve, reject) => { + let childProcess = child_process.fork( + __dirname + '/runnerCli.js', process.argv.slice(2), {cwd: process.cwd(), silent: true}); + let taskLogger = new TaskLogger(this.task, childProcess.pid); - let childProcess = child_process.fork( - __dirname + '/runnerCli.js', process.argv.slice(2), {cwd: process.cwd(), silent: true}); - let taskLogger = new TaskLogger(this.task, childProcess.pid); + // stdout pipe + childProcess.stdout.on('data', (data: string) => { + taskLogger.log(data); + }); - // stdout pipe - childProcess.stdout.on('data', (data: string) => { - taskLogger.log(data); - }); - - // stderr pipe - childProcess.stderr.on('data', (data: string) => { - taskLogger.log(data); - }); + // stderr pipe + childProcess.stderr.on('data', (data: string) => { + taskLogger.log(data); + }); - childProcess - .on('message', - (m: any) => { - if (config.verboseMultiSessions) { - taskLogger.peek(); - } - switch (m.event) { - case 'testPass': - process.stdout.write('.'); - break; - case 'testFail': - process.stdout.write('F'); - break; - case 'testsDone': - runResults.failedCount = m.results.failedCount; - runResults.specResults = m.results.specResults; - break; - } - }) - .on('error', - (err: any) => { - taskLogger.flush(); - deferred.reject(err); - }) - .on('exit', (code: number) => { - taskLogger.flush(); - runResults.exitCode = code; - deferred.resolve(runResults); - }); + childProcess + .on('message', + (m: any) => { + if (config.verboseMultiSessions) { + taskLogger.peek(); + } + switch (m.event) { + case 'testPass': + process.stdout.write('.'); + break; + case 'testFail': + process.stdout.write('F'); + break; + case 'testsDone': + runResults.failedCount = m.results.failedCount; + runResults.specResults = m.results.specResults; + break; + } + }) + .on('error', + (err: any) => { + taskLogger.flush(); + reject(err); + }) + .on('exit', (code: number) => { + taskLogger.flush(); + runResults.exitCode = code; + resolve(runResults); + }); - childProcess.send({ - command: 'run', - configFile: this.configFile, - additionalConfig: this.additionalConfig, - capabilities: this.task.capabilities, - specs: this.task.specs + childProcess.send({ + command: 'run', + configFile: this.configFile, + additionalConfig: this.additionalConfig, + capabilities: this.task.capabilities, + specs: this.task.specs + }); }); - - return deferred.promise; } else { let runner = new Runner(config); @@ -128,10 +125,9 @@ export class TaskRunner extends EventEmitter { runResults.specResults = results.specResults; }); - return runner.run().then((exitCode: number) => { - runResults.exitCode = exitCode; - return runResults; - }); + const exitCode = await runner.run(); + runResults.exitCode = exitCode; + return runResults; } } } diff --git a/lib/util.ts b/lib/util.ts index 346051702..a090e2fb1 100644 --- a/lib/util.ts +++ b/lib/util.ts @@ -1,10 +1,9 @@ -import {resolve} from 'path'; -import {Promise, when} from 'q'; +import * as path from 'path'; import {error as wderror} from 'selenium-webdriver'; let STACK_SUBSTRINGS_TO_FILTER = [ 'node_modules/jasmine/', 'node_modules/selenium-webdriver', 'at Module.', 'at Object.Module.', - 'at Function.Module', '(timers.js:', 'jasminewd2/index.js', 'protractor/lib/' + 'at Function.Module', '(timers.js:', 'protractor/lib/' ]; @@ -33,35 +32,37 @@ export function filterStackTrace(text: string): string { * Internal helper for abstraction of polymorphic filenameOrFn properties. * @param {object} filenameOrFn The filename or function that we will execute. * @param {Array.}} args The args to pass into filenameOrFn. - * @return {q.Promise} A promise that will resolve when filenameOrFn completes. + * @return {Promise} A promise that will resolve when filenameOrFn completes. */ -export function runFilenameOrFn_(configDir: string, filenameOrFn: any, args?: any[]): Promise { - return Promise((resolvePromise) => { - if (filenameOrFn && !(typeof filenameOrFn === 'string' || typeof filenameOrFn === 'function')) { - throw new Error('filenameOrFn must be a string or function'); - } +export async function runFilenameOrFn_( + configDir: string, filenameOrFn: any, args?: any[]): Promise { + if (filenameOrFn && !(typeof filenameOrFn === 'string' || typeof filenameOrFn === 'function')) { + throw new Error('filenameOrFn must be a string or function'); + } - if (typeof filenameOrFn === 'string') { - filenameOrFn = require(resolve(configDir, filenameOrFn)); - } - if (typeof filenameOrFn === 'function') { - let results = when(filenameOrFn.apply(null, args), null, (err) => { - if (typeof err === 'string') { - err = new Error(err); - } else { - err = err as Error; - if (!err.stack) { - err.stack = new Error().stack; - } + if (typeof filenameOrFn === 'string') { + filenameOrFn = require(path.resolve(configDir, filenameOrFn)); + } + if (typeof filenameOrFn === 'function') { + let results; + try { + results = await filenameOrFn.apply(null, args); + } catch (err) { + if (typeof err === 'string') { + err = new Error(err); + } else { + err = err as Error; + if (!err.stack) { + err.stack = new Error().stack; } - err.stack = exports.filterStackTrace(err.stack); - throw err; - }); - resolvePromise(results); - } else { - resolvePromise(undefined); + } + err.stack = exports.filterStackTrace(err.stack); + throw err; } - }); + return results; + } else { + return undefined; + } } /** diff --git a/package-lock.json b/package-lock.json index 12b48bbcc..4eba36724 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "protractor", - "version": "5.4.1", + "version": "6.0.0-beta", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -16,6 +16,12 @@ "integrity": "sha512-y3bR98mzYOo0pAZuiLari+cQyiKk3UXRuT45h1RjhfeCzqkjaVsfZJNaxdgtk7/3tzOm1ozLTqEqMP3VbI48jw==", "dev": true }, + "@types/fancy-log": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@types/fancy-log/-/fancy-log-1.3.0.tgz", + "integrity": "sha512-mQjDxyOM1Cpocd+vm1kZBP7smwKZ4TNokFeds9LV7OZibmPJFEzY3+xZMrKfUdNT71lv8GoCPD6upKwHxubClw==", + "dev": true + }, "@types/glob": { "version": "5.0.35", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-5.0.35.tgz", @@ -28,19 +34,16 @@ } }, "@types/jasmine": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.5.tgz", - "integrity": "sha512-mkrHFZTgOXkZhau36K628iKFkjbp11t/bHCkY4Mefu4R6McMg2FD9P3naBv/0Ygyn4sz8baColJp2gdmSekgiw==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.3.4.tgz", + "integrity": "sha512-543S+ZCJfN4jKWzRkptbJqTY2vc4h7+lPVqU2hXb1XFofDcUxNANAimdZPYaH6/yhezVAsNeujoZjAFU06bfmA==", "dev": true }, - "@types/jasminewd2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.3.tgz", - "integrity": "sha512-hYDVmQZT5VA2kigd4H4bv7vl/OhlympwREUemqBdOqtrYTo5Ytm12a5W5/nGgGYdanGVxj0x/VhZ7J3hOg/YKg==", - "dev": true, - "requires": { - "@types/jasmine": "*" - } + "@types/loglevel": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@types/loglevel/-/loglevel-1.5.3.tgz", + "integrity": "sha512-TzzIZihV+y9kxSg5xJMkyIkaoGkXi50isZTtGHObNHRqAAwjGNjSCNPI7AUAv0tZUKTq9f2cdkCUd/2JVZUTrA==", + "dev": true }, "@types/minimatch": { "version": "2.0.29", @@ -66,16 +69,17 @@ "integrity": "sha1-qIc1gLOoS2msHmhzI7Ffu+uQR5o=", "dev": true }, - "@types/q": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", - "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=" - }, "@types/selenium-webdriver": { "version": "3.0.10", "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.10.tgz", "integrity": "sha512-ikB0JHv6vCR1KYUQAzTO4gi/lXLElT4Tx+6De2pc/OZwizE9LRNiTa+U8TBFKBD/nntPnr/MPSHSnOTybjhqNA==" }, + "@types/yargs": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-12.0.1.tgz", + "integrity": "sha512-UVjo2oH79aRNcsDlFlnQ/iJ67Jd7j6uSg7jUJP/RZ/nUjAh5ElmnwlD5K/6eGgETJUgCHkiWn91B8JjXQ6ubAw==", + "dev": true + }, "accepts": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", @@ -86,6 +90,11 @@ "negotiator": "0.6.1" } }, + "adm-zip": { + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.13.tgz", + "integrity": "sha512-fERNJX8sOXfel6qCBCMPvZLzENBEhZTzKqg6vrOW5pvoEaQuJhRU4ndTAh6lHOxn1I6jnz2NHra56ZODM751uw==" + }, "agent-base": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.0.tgz", @@ -95,14 +104,14 @@ } }, "ajv": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.1.tgz", - "integrity": "sha1-s4u4h22ehr7plJVqBOch6IskjrI=", + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.1.tgz", + "integrity": "sha512-ZoJjft5B+EJBjUyu9C9Hc0OZyPZSSlOF+plzouTrg6UlA8f+e/n8NIgBFG/9tppJtpPWfthHakK7juJdNDODww==", "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", + "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, "ansi-align": { @@ -114,6 +123,15 @@ "string-width": "^2.0.0" } }, + "ansi-colors": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "requires": { + "ansi-wrap": "^0.1.0" + } + }, "ansi-gray": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", @@ -139,19 +157,53 @@ "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", "dev": true }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "append-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", + "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", + "dev": true, + "requires": { + "buffer-equal": "^1.0.0" + } + }, "archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-filter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", + "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=", "dev": true, "requires": { - "arr-flatten": "^1.0.1" + "make-iterator": "^1.0.0" } }, "arr-flatten": { @@ -160,6 +212,21 @@ "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", "dev": true }, + "arr-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", + "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=", + "dev": true, + "requires": { + "make-iterator": "^1.0.0" + } + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, "array-differ": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", @@ -178,40 +245,85 @@ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, + "array-initial": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", + "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", + "dev": true, + "requires": { + "array-slice": "^1.0.0", + "is-number": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "array-last": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", + "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", + "dev": true, + "requires": { + "is-number": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, "array-slice": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", "dev": true }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "array-sort": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", + "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", + "dev": true, "requires": { - "array-uniq": "^1.0.1" + "default-compare": "^1.0.0", + "get-value": "^2.0.6", + "kind-of": "^5.0.2" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } } }, "array-uniq": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true }, "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" - }, "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } }, "assert-plus": { "version": "1.0.0", @@ -224,26 +336,65 @@ "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=", "dev": true }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, "async": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true }, + "async-done": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.1.tgz", + "integrity": "sha512-R1BaUeJ4PMoLNJuk+0tLJgjmEqVsdN118+Z8O+alhnQDQgy0kmD5Mqi0DNEmMx2LM0Ed5yekKu+ZXYvIHceicg==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.2", + "process-nextick-args": "^1.0.7", + "stream-exhaust": "^1.0.1" + } + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "dev": true + }, + "async-settle": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", + "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", + "dev": true, + "requires": { + "async-done": "^1.2.2" + } + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, "babel-code-frame": { "version": "6.26.0", @@ -256,16 +407,87 @@ "js-tokens": "^3.0.2" } }, + "bach": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", + "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", + "dev": true, + "requires": { + "arr-filter": "^1.1.1", + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "array-each": "^1.0.0", + "array-initial": "^1.0.0", + "array-last": "^1.1.1", + "async-done": "^1.2.2", + "async-settle": "^1.0.0", + "now-and-later": "^2.0.0" + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "optional": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "requires": { "tweetnacl": "^0.14.3" } @@ -276,6 +498,12 @@ "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", "dev": true }, + "binary-extensions": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.12.0.tgz", + "integrity": "sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg==", + "dev": true + }, "blocking-proxy": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz", @@ -325,14 +553,6 @@ } } }, - "boom": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", - "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", - "requires": { - "hoek": "4.x.x" - } - }, "boxen": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", @@ -349,32 +569,32 @@ }, "dependencies": { "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" } }, "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^3.1.0", + "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", - "supports-color": "^4.0.0" + "supports-color": "^5.3.0" } }, "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "has-flag": "^2.0.0" + "has-flag": "^3.0.0" } } } @@ -389,14 +609,32 @@ } }, "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } } }, "browserstack": { @@ -407,12 +645,47 @@ "https-proxy-agent": "^2.2.1" } }, + "buffer-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", + "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, "bytes": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz", "integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk=", "dev": true }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, "camelcase": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", @@ -420,9 +693,9 @@ "dev": true }, "capture-stack-trace": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", - "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", + "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", "dev": true }, "caseless": { @@ -459,6 +732,38 @@ "supports-color": "^2.0.0" } }, + "chokidar": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", + "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.0", + "braces": "^2.3.0", + "fsevents": "^1.2.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "lodash.debounce": "^4.0.8", + "normalize-path": "^2.1.1", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0", + "upath": "^1.0.5" + } + }, + "chownr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==" + }, + "ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "dev": true + }, "clang-format": { "version": "1.0.49", "resolved": "https://registry.npmjs.org/clang-format/-/clang-format-1.0.49.tgz", @@ -470,14 +775,27 @@ "resolve": "^1.1.6" } }, - "cli": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", - "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, "requires": { - "exit": "0.1.2", - "glob": "^7.1.1" + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } } }, "cli-boxes": { @@ -487,23 +805,67 @@ "dev": true }, "cli-color": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-1.2.0.tgz", - "integrity": "sha1-OlrnT9drYmevZm5p4q+70B3vNNE=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-1.4.0.tgz", + "integrity": "sha512-xu6RvQqqrWEo6MPR1eixqGPywhYBHRs653F9jfXB2Hx4jdM/3WxiNE1vppRmxtMIfl16SFYTpYlrnqH/HsK/2w==", "dev": true, "requires": { "ansi-regex": "^2.1.1", "d": "1", - "es5-ext": "^0.10.12", - "es6-iterator": "2", - "memoizee": "^0.4.3", - "timers-ext": "0.1" + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "memoizee": "^0.4.14", + "timers-ext": "^0.1.5" + }, + "dependencies": { + "es5-ext": { + "version": "0.10.46", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz", + "integrity": "sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw==", + "dev": true, + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "1" + } + } + } + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + } } }, "clone": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz", - "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8=", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", "dev": true }, "clone-stats": { @@ -512,18 +874,82 @@ "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", "dev": true }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + "cloneable-readable": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.2.tgz", + "integrity": "sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + }, + "dependencies": { + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "collection-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", + "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=", + "dev": true, + "requires": { + "arr-map": "^2.0.2", + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } }, "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { - "color-name": "^1.1.1" + "color-name": "1.1.3" } }, "color-name": { @@ -532,16 +958,22 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true + }, "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", + "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", "dev": true }, "combined-stream": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", "requires": { "delayed-stream": "~1.0.0" } @@ -552,40 +984,73 @@ "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=", "dev": true }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, - "configstore": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.1.tgz", - "integrity": "sha512-5oNkD/L++l0O6xGXxb1EWS7SivtjfGQlRyxJsYgE0Z495/L81e2h4/d3r969hoPXuFItzNOKMtsXgYG4c7dYvw==", + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, "requires": { - "dot-prop": "^4.1.0", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" }, "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } } } }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "configstore": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", + "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", "dev": true, "requires": { - "date-now": "^0.1.4" + "dot-prop": "^4.1.0", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" } }, "content-disposition": { @@ -600,6 +1065,15 @@ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", "dev": true }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, "cookie": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", @@ -612,6 +1086,22 @@ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", "dev": true }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-props": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz", + "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==", + "dev": true, + "requires": { + "each-props": "^1.3.0", + "is-plain-object": "^2.0.1" + } + }, "core-js": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz", @@ -643,9 +1133,9 @@ }, "dependencies": { "lru-cache": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", - "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "dev": true, "requires": { "pseudomap": "^1.0.2", @@ -654,24 +1144,6 @@ } } }, - "cryptiles": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", - "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", - "requires": { - "boom": "5.x.x" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "requires": { - "hoek": "4.x.x" - } - } - } - }, "crypto-random-string": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", @@ -695,12 +1167,6 @@ "assert-plus": "^1.0.0" } }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true - }, "dateformat": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", @@ -715,6 +1181,17 @@ "ms": "2.0.0" } }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, "deep-eql": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", @@ -733,40 +1210,80 @@ } }, "deep-extend": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", - "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "default-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", + "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", + "dev": true, + "requires": { + "kind-of": "^5.0.2" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "default-resolution": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", + "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, "requires": { - "clone": "^1.0.2" + "object-keys": "^1.0.12" } }, - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, "requires": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" }, "dependencies": { - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, "requires": { - "glob": "^7.0.5" + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } } } @@ -782,12 +1299,6 @@ "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", "dev": true }, - "deprecated": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/deprecated/-/deprecated-0.0.1.tgz", - "integrity": "sha1-+cmvVGSvoeepcUWKi97yqpTVuxk=", - "dev": true - }, "destroy": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", @@ -795,13 +1306,10 @@ "dev": true }, "detect-file": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-0.1.0.tgz", - "integrity": "sha1-STXe39lIhkjgBrASlWbpOGcR6mM=", - "dev": true, - "requires": { - "fs-exists-sync": "^0.1.0" - } + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true }, "diff": { "version": "2.2.3", @@ -833,55 +1341,6 @@ } } }, - "dom-serializer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", - "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", - "dev": true, - "requires": { - "domelementtype": "~1.1.1", - "entities": "~1.1.1" - }, - "dependencies": { - "domelementtype": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", - "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", - "dev": true - }, - "entities": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", - "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", - "dev": true - } - } - }, - "domelementtype": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", - "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", - "dev": true - }, - "domhandler": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", - "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", - "dev": true, - "requires": { - "domelementtype": "1" - } - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, "dot-prop": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", @@ -893,7 +1352,7 @@ }, "duplexer": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, @@ -914,7 +1373,7 @@ }, "readable-stream": { "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "dev": true, "requires": { @@ -932,13 +1391,35 @@ "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", "dev": true }, + "duplexify": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.1.tgz", + "integrity": "sha512-vM58DwdnKmty+FSPzT14K9JXb90H+j5emaR4KYbr2KTIz00WHGbWOe5ghQTx233ZCLZtrGDALzKwcjEtSt35mA==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "each-props": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", + "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.1", + "object.defaults": "^1.1.0" + } + }, "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "optional": true, + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "requires": { - "jsbn": "~0.1.0" + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" } }, "ee-first": { @@ -954,30 +1435,22 @@ "dev": true }, "end-of-stream": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz", - "integrity": "sha1-jhdyBsPICDfYVjLouTWd/osvbq8=", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "dev": true, "requires": { - "once": "~1.3.0" - }, - "dependencies": { - "once": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", - "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", - "dev": true, - "requires": { - "wrappy": "1" - } - } + "once": "^1.4.0" } }, - "entities": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", - "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", - "dev": true + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } }, "es5-ext": { "version": "0.10.38", @@ -1053,6 +1526,12 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, "esutils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", @@ -1076,18 +1555,18 @@ } }, "event-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.5.tgz", + "integrity": "sha512-vyibDcu5JL20Me1fP734QBH/kenBGLZap2n0+XXM7mvuUPzJ20Ydqj1aKcIeMdri1p+PU+4yAKugjN8KCVst+g==", "dev": true, "requires": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" + "duplexer": "^0.1.1", + "from": "^0.1.7", + "map-stream": "0.0.7", + "pause-stream": "^0.0.11", + "split": "^1.0.1", + "stream-combiner": "^0.2.2", + "through": "^2.3.8" } }, "execa": { @@ -1105,36 +1584,57 @@ "strip-eof": "^1.0.0" } }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=" - }, "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "^0.1.0" - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dev": true, - "requires": { - "fill-range": "^2.1.0" + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } } }, "expand-tilde": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz", - "integrity": "sha1-C4HrqJflo9MdHD0QL48BRB5VlEk=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", "dev": true, "requires": { - "os-homedir": "^1.0.1" + "homedir-polyfill": "^1.0.1" } }, "expect.js": { @@ -1201,17 +1701,94 @@ } }, "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } } }, "extsprintf": { @@ -1220,42 +1797,48 @@ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fancy-log": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.1.tgz", - "integrity": "sha1-xKNGK6FK3137q3lzH9OESiBpy7s=", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", "dev": true, "requires": { "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", "time-stamp": "^1.0.0" } }, "fast-deep-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", - "dev": true - }, "fill-range": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", - "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^1.1.3", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } } }, "finalhandler": { @@ -1294,28 +1877,41 @@ } } }, - "find-index": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz", - "integrity": "sha1-Z101iyyjiS15Whq0cjL4tuLg3eQ=", - "dev": true + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } }, "findup-sync": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.4.3.tgz", - "integrity": "sha1-QAQ5Kee8YK3wt/SCfExudaDeyhI=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", "dev": true, "requires": { - "detect-file": "^0.1.0", - "is-glob": "^2.0.1", - "micromatch": "^2.3.7", - "resolve-dir": "^0.1.0" + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } } }, "fined": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.0.tgz", - "integrity": "sha1-s33IRLdqL15wgeiE98CuNE8VNHY=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.1.tgz", + "integrity": "sha512-jQp949ZmEbiYHk3gkbdtpJ0G1+kgtLQBNdP5edFP7Fh+WAYceLQz6yO1SBj72Xkg8GVyTB3bBzAYrHJVh5Xd5g==", "dev": true, "requires": { "expand-tilde": "^2.0.2", @@ -1323,31 +1919,24 @@ "object.defaults": "^1.1.0", "object.pick": "^1.2.0", "parse-filepath": "^1.0.1" - }, - "dependencies": { - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" - } - } } }, - "first-chunk-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz", - "integrity": "sha1-Wb+1DNkF9g18OUzT2ayqtOatk04=", - "dev": true - }, "flagged-respawn": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-0.3.2.tgz", - "integrity": "sha1-/xke3c1wiKZ1smEP/8l2vpuAdLU=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", "dev": true }, + "flush-write-stream": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", + "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.4" + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -1355,9 +1944,9 @@ "dev": true }, "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", "dev": true, "requires": { "for-in": "^1.0.1" @@ -1369,12 +1958,12 @@ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "form-data": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", - "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "requires": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.5", + "combined-stream": "^1.0.6", "mime-types": "^2.1.12" } }, @@ -1384,6 +1973,15 @@ "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", "dev": true }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, "fresh": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz", @@ -1396,30 +1994,644 @@ "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", "dev": true }, - "fs-exists-sync": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", - "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=", - "dev": true + "fs-minipass": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "requires": { + "minipass": "^2.2.1" + } + }, + "fs-mkdirp-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", + "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "through2": "^2.0.3" + } }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, - "gaze": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.2.tgz", - "integrity": "sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8=", + "fsevents": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", + "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", "dev": true, + "optional": true, "requires": { - "globule": "~0.1.0" + "nan": "^2.9.2", + "node-pre-gyp": "^0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": false, + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": false, + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "aproba": { + "version": "1.2.0", + "resolved": false, + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "resolved": false, + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": false, + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": false, + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.0.1", + "resolved": false, + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": false, + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": false, + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": false, + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": false, + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "resolved": false, + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.5.1", + "resolved": false, + "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "resolved": false, + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "resolved": false, + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "resolved": false, + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": false, + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "resolved": false, + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": false, + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": false, + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.21", + "resolved": false, + "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==", + "dev": true, + "optional": true, + "requires": { + "safer-buffer": "^2.1.0" + } + }, + "ignore-walk": { + "version": "3.0.1", + "resolved": false, + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": false, + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": false, + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": false, + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": false, + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": false, + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": false, + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": false, + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "minipass": { + "version": "2.2.4", + "resolved": false, + "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.1", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.1.0", + "resolved": false, + "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==", + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": false, + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "resolved": false, + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.0", + "resolved": false, + "integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==", + "dev": true, + "optional": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.0", + "resolved": false, + "integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==", + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.0", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.1.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "resolved": false, + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.3", + "resolved": false, + "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==", + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.1.10", + "resolved": false, + "integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==", + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": false, + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": false, + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": false, + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "resolved": false, + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": false, + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": false, + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "resolved": false, + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": false, + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": false, + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.7", + "resolved": false, + "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==", + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.5.1", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": false, + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": false, + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": false, + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "optional": true, + "requires": { + "glob": "^7.0.5" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": false, + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": false, + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "resolved": false, + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true, + "optional": true + }, + "semver": { + "version": "5.5.0", + "resolved": false, + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": false, + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": false, + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "resolved": false, + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": false, + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": false, + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": false, + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.1", + "resolved": false, + "integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==", + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.0.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.2.4", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.1", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": false, + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.2", + "resolved": false, + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": false, + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "yallist": { + "version": "3.0.2", + "resolved": false, + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", + "dev": true + } } }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "dev": true }, "getpass": { @@ -1443,106 +2655,89 @@ "path-is-absolute": "^1.0.0" } }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true, - "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" - } - }, "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "^2.0.0" - } - }, - "glob-stream": { - "version": "3.1.18", - "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-3.1.18.tgz", - "integrity": "sha1-kXCl8St5Awb9/lmPMT+PeVT9FDs=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "glob": "^4.3.1", - "glob2base": "^0.0.12", - "minimatch": "^2.0.1", - "ordered-read-streams": "^0.1.0", - "through2": "^0.6.1", - "unique-stream": "^1.0.0" + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" }, "dependencies": { - "glob": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-4.5.3.tgz", - "integrity": "sha1-xstz0yJsHv7wTePFbQEvAzd+4V8=", + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^2.0.1", - "once": "^1.3.0" + "is-extglob": "^2.1.0" } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + } + } + }, + "glob-stream": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", + "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", + "dev": true, + "requires": { + "extend": "^3.0.0", + "glob": "^7.1.1", + "glob-parent": "^3.1.0", + "is-negated-glob": "^1.0.0", + "ordered-read-streams": "^1.0.0", + "pumpify": "^1.3.5", + "readable-stream": "^2.1.5", + "remove-trailing-separator": "^1.0.1", + "to-absolute-glob": "^2.0.0", + "unique-stream": "^2.0.2" + }, + "dependencies": { + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true }, - "minimatch": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", - "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", - "dev": true, - "requires": { - "brace-expansion": "^1.0.0" - } - }, "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "through2": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", - "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" + "safe-buffer": "~5.1.0" } } } }, "glob-watcher": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-0.0.6.tgz", - "integrity": "sha1-uVtKjfdLOcgymLDAXJeLTZo7cQs=", - "dev": true, - "requires": { - "gaze": "^0.5.1" - } - }, - "glob2base": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz", - "integrity": "sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY=", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.3.tgz", + "integrity": "sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg==", "dev": true, "requires": { - "find-index": "^0.1.1" + "anymatch": "^2.0.0", + "async-done": "^1.2.0", + "chokidar": "^2.0.0", + "is-negated-glob": "^1.0.0", + "just-debounce": "^1.0.0", + "object.defaults": "^1.1.0" } }, "global-dirs": { @@ -1555,90 +2750,27 @@ } }, "global-modules": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz", - "integrity": "sha1-6lo77ULG1s6ZWk+KEmm12uIjgo0=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, "requires": { - "global-prefix": "^0.1.4", - "is-windows": "^0.2.0" + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" } }, "global-prefix": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-0.1.5.tgz", - "integrity": "sha1-jTvGuNo8qBEqFg2NSW/wRiv+948=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", "dev": true, "requires": { - "homedir-polyfill": "^1.0.0", + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", "ini": "^1.3.4", - "is-windows": "^0.2.0", - "which": "^1.2.12" - } - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "globule": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", - "integrity": "sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU=", - "dev": true, - "requires": { - "glob": "~3.1.21", - "lodash": "~1.0.1", - "minimatch": "~0.2.11" - }, - "dependencies": { - "glob": { - "version": "3.1.21", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", - "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", - "dev": true, - "requires": { - "graceful-fs": "~1.2.0", - "inherits": "1", - "minimatch": "~0.2.11" - } - }, - "graceful-fs": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", - "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=", - "dev": true - }, - "inherits": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", - "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=", - "dev": true - }, - "lodash": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz", - "integrity": "sha1-j1dWDIO1n8JwvT1WG2kAQ0MOJVE=", - "dev": true - }, - "minimatch": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", - "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", - "dev": true, - "requires": { - "lru-cache": "2", - "sigmund": "~1.0.0" - } - } + "is-windows": "^1.0.1", + "which": "^1.2.14" } }, "glogg": { @@ -1670,13 +2802,10 @@ } }, "graceful-fs": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", - "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", - "dev": true, - "requires": { - "natives": "^1.1.0" - } + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true }, "growl": { "version": "1.9.2", @@ -1685,31 +2814,157 @@ "dev": true }, "gulp": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/gulp/-/gulp-3.9.1.tgz", - "integrity": "sha1-VxzkWSjdQK9lFPxAEYZgFsE4RbQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.0.tgz", + "integrity": "sha1-lXZsYB2t5Kd+0+eyttwDiBtZY2Y=", "dev": true, "requires": { - "archy": "^1.0.0", - "chalk": "^1.0.0", - "deprecated": "^0.0.1", - "gulp-util": "^3.0.0", - "interpret": "^1.0.0", - "liftoff": "^2.1.0", - "minimist": "^1.1.0", - "orchestrator": "^0.3.0", - "pretty-hrtime": "^1.0.0", - "semver": "^4.1.0", - "tildify": "^1.0.0", - "v8flags": "^2.0.2", - "vinyl-fs": "^0.3.0" + "glob-watcher": "^5.0.0", + "gulp-cli": "^2.0.0", + "undertaker": "^1.0.0", + "vinyl-fs": "^3.0.0" }, "dependencies": { - "semver": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", - "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "dev": true, + "requires": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + } + }, + "gulp-cli": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.0.1.tgz", + "integrity": "sha512-RxujJJdN8/O6IW2nPugl7YazhmrIEjmiVfPKrWt68r71UCaLKS71Hp0gpKT+F6qOUFtr7KqtifDKaAJPRVvMYQ==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "archy": "^1.0.0", + "array-sort": "^1.0.0", + "color-support": "^1.1.3", + "concat-stream": "^1.6.0", + "copy-props": "^2.0.1", + "fancy-log": "^1.3.2", + "gulplog": "^1.0.0", + "interpret": "^1.1.0", + "isobject": "^3.0.1", + "liftoff": "^2.5.0", + "matchdep": "^2.0.0", + "mute-stdout": "^1.0.0", + "pretty-hrtime": "^1.0.0", + "replace-homedir": "^1.0.0", + "semver-greatest-satisfied-range": "^1.1.0", + "v8flags": "^3.0.1", + "yargs": "^7.1.0" + } + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "os-locale": { + "version": "1.4.0", + "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", "dev": true + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + } + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "dev": true, + "requires": { + "camelcase": "^3.0.0" + } } } }, @@ -1736,7 +2991,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -1748,7 +3003,7 @@ }, "through2": { "version": "0.6.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "resolved": "http://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, "requires": { @@ -1760,7 +3015,7 @@ }, "gulp-diff": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gulp-diff/-/gulp-diff-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/gulp-diff/-/gulp-diff-1.0.0.tgz", "integrity": "sha1-EBsjcS3WsQe9B9BauI6jrEhf7Xc=", "dev": true, "requires": { @@ -1772,14 +3027,59 @@ } }, "gulp-tslint": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/gulp-tslint/-/gulp-tslint-7.1.0.tgz", - "integrity": "sha1-m9P/T7wW1MvZq7CP94bbibVj6T0=", + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/gulp-tslint/-/gulp-tslint-8.1.3.tgz", + "integrity": "sha512-KEP350N5B9Jg6o6jnyCyKVBPemJePYpMsGfIQq0G0ErvY7tw4Lrfb/y3L4WRf7ek0OsaE8nnj86w+lcLXW8ovw==", "dev": true, "requires": { - "gulp-util": "~3.0.8", - "map-stream": "~0.1.0", + "@types/fancy-log": "1.3.0", + "chalk": "2.3.1", + "fancy-log": "1.3.2", + "map-stream": "~0.0.7", + "plugin-error": "1.0.1", "through": "~2.3.8" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.3.1", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", + "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.2.0" + } + }, + "fancy-log": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.2.tgz", + "integrity": "sha1-9BEl49hPLn2JpD0G2VjI94vha+E=", + "dev": true, + "requires": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "time-stamp": "^1.0.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "gulp-util": { @@ -1806,14 +3106,6 @@ "replace-ext": "0.0.1", "through2": "^2.0.0", "vinyl": "^0.5.0" - }, - "dependencies": { - "object-assign": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", - "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", - "dev": true - } } }, "gulplog": { @@ -1831,11 +3123,11 @@ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", "requires": { - "ajv": "^5.1.0", + "ajv": "^6.5.5", "har-schema": "^2.0.0" } }, @@ -1848,9 +3140,9 @@ } }, "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, "has-gulplog": { @@ -1862,64 +3154,59 @@ "sparkles": "^1.0.0" } }, - "hawk": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", - "requires": { - "boom": "4.x.x", - "cryptiles": "3.x.x", - "hoek": "4.x.x", - "sntp": "2.x.x" - } - }, - "hoek": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", - "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==" + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true }, - "homedir-polyfill": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", - "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, "requires": { - "parse-passwd": "^1.0.0" + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" } }, - "htmlparser2": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", - "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, "requires": { - "domelementtype": "1", - "domhandler": "2.3", - "domutils": "1.5", - "entities": "1.0", - "readable-stream": "1.1" + "is-number": "^3.0.0", + "kind-of": "^4.0.0" }, "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "is-buffer": "^1.1.5" } } } }, + "homedir-polyfill": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", + "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "dev": true + }, "http-errors": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.5.1.tgz", @@ -1990,7 +3277,8 @@ "ini": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true }, "interpret": { "version": "1.1.0", @@ -1998,6 +3286,11 @@ "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", "dev": true }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" + }, "ipaddr.js": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.4.0.tgz", @@ -2005,13 +3298,48 @@ "dev": true }, "is-absolute": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.2.6.tgz", - "integrity": "sha1-IN5p89uULvLYe5wto28XIjWxtes=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "dev": true, + "requires": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "requires": { - "is-relative": "^0.2.1", - "is-windows": "^0.2.0" + "binary-extensions": "^1.0.0" } }, "is-buffer": { @@ -2020,19 +3348,61 @@ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true + "is-builtin-module": { + "version": "1.0.0", + "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-ci": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "dev": true, + "requires": { + "ci-info": "^1.5.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-primitive": "^2.0.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } } }, "is-extendable": { @@ -2042,24 +3412,23 @@ "dev": true }, "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "^2.1.1" } }, "is-installed-globally": { @@ -2072,6 +3441,12 @@ "is-path-inside": "^1.0.0" } }, + "is-negated-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", + "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=", + "dev": true + }, "is-npm": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", @@ -2079,12 +3454,23 @@ "dev": true }, "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "is-obj": { @@ -2093,23 +3479,11 @@ "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=" - }, - "is-path-in-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", - "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", - "requires": { - "is-path-inside": "^1.0.0" - } - }, "is-path-inside": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, "requires": { "path-is-inside": "^1.0.1" } @@ -2121,28 +3495,8 @@ "dev": true, "requires": { "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } } }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true - }, "is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", @@ -2156,12 +3510,12 @@ "dev": true }, "is-relative": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.2.1.tgz", - "integrity": "sha1-0n9MfVFtF1+2ENuEu+7yPDvJeqU=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "dev": true, "requires": { - "is-unc-path": "^0.1.1" + "is-unc-path": "^1.0.0" } }, "is-retry-allowed": { @@ -2173,8 +3527,7 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, "is-typedarray": { "version": "1.0.0", @@ -2182,12 +3535,12 @@ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, "is-unc-path": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-0.1.2.tgz", - "integrity": "sha1-arBTpyVzwQJQ/0FqOBTDUXivObk=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", "dev": true, "requires": { - "unc-path-regex": "^0.1.0" + "unc-path-regex": "^0.1.2" } }, "is-utf8": { @@ -2196,10 +3549,16 @@ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", "dev": true }, + "is-valid-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", + "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=", + "dev": true + }, "is-windows": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", - "integrity": "sha1-3hqm1j6indJIc3tp8f+LgALSEIw=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, "isarray": { @@ -2210,17 +3569,13 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true }, "isstream": { "version": "0.1.2", @@ -2252,24 +3607,18 @@ } }, "jasmine": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", - "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.3.1.tgz", + "integrity": "sha512-/vU3/H7U56XsxIXHwgEuWpCgQ0bRi2iiZeUpx7Nqo8n1TpoDHfZhkPIc7CO8I4pnMzYsi3XaSZEiy8cnTfujng==", "requires": { - "exit": "^0.1.2", "glob": "^7.0.6", - "jasmine-core": "~2.8.0" + "jasmine-core": "~3.3.0" } }, "jasmine-core": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", - "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=" - }, - "jasminewd2": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", - "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=" + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.3.0.tgz", + "integrity": "sha512-3/xSmG/d35hf80BEN66Y6g9Ca5l/Isdeg/j6zvbTYlTzeKinzmaTM4p9am5kYqOmE05D7s1t8FGjzdSnbUbceA==" }, "js-tokens": { "version": "3.0.2", @@ -2277,51 +3626,51 @@ "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", "dev": true }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "optional": true - }, - "jshint": { - "version": "2.9.5", - "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.9.5.tgz", - "integrity": "sha1-HnJSkVzmgbQIJ+4UJIxG006apiw=", + "js-yaml": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", "dev": true, "requires": { - "cli": "~1.0.0", - "console-browserify": "1.1.x", - "exit": "0.1.x", - "htmlparser2": "3.8.x", - "lodash": "3.7.x", - "minimatch": "~3.0.2", - "shelljs": "0.3.x", - "strip-json-comments": "1.0.x" - }, - "dependencies": { - "lodash": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.7.0.tgz", - "integrity": "sha1-Nni9irmVBXwHreg27S7wh9qBHUU=", - "dev": true - } + "argparse": "^1.0.7", + "esprima": "^4.0.0" } }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" }, "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "~0.0.0" + } }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -2345,13 +3694,26 @@ "readable-stream": "~2.0.6" } }, + "just-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz", + "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=", + "dev": true + }, "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "last-run": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", + "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "default-resolution": "^2.0.0", + "es6-weak-map": "^2.0.1" } }, "latest-version": { @@ -2363,6 +3725,32 @@ "package-json": "^4.0.0" } }, + "lazystream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", + "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "dev": true, + "requires": { + "readable-stream": "^2.0.5" + } + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "requires": { + "invert-kv": "^2.0.0" + } + }, + "lead": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", + "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", + "dev": true, + "requires": { + "flush-write-stream": "^1.0.2" + } + }, "lie": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", @@ -2372,22 +3760,43 @@ } }, "liftoff": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.3.0.tgz", - "integrity": "sha1-qY8v9nGD2Lp8+soQVIvX/wVQs4U=", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz", + "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=", "dev": true, "requires": { "extend": "^3.0.0", - "findup-sync": "^0.4.2", + "findup-sync": "^2.0.0", "fined": "^1.0.1", - "flagged-respawn": "^0.3.2", - "lodash.isplainobject": "^4.0.4", - "lodash.isstring": "^4.0.1", - "lodash.mapvalues": "^4.4.0", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", "rechoir": "^0.6.2", "resolve": "^1.1.7" } }, + "load-json-file": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, "lodash": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", @@ -2448,6 +3857,12 @@ "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", "dev": true }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, "lodash.escape": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", @@ -2469,18 +3884,6 @@ "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", "dev": true }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", - "dev": true - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", - "dev": true - }, "lodash.keys": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", @@ -2492,12 +3895,6 @@ "lodash.isarray": "^3.0.0" } }, - "lodash.mapvalues": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", - "integrity": "sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw=", - "dev": true - }, "lodash.restparam": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", @@ -2531,10 +3928,15 @@ "lodash.escape": "^3.0.0" } }, + "loglevel": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", + "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=" + }, "lowercase-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", - "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "dev": true }, "lru-cache": { @@ -2553,9 +3955,9 @@ } }, "make-dir": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.1.0.tgz", - "integrity": "sha512-0Pkui4wLJ7rxvmfUvs87skoEaxmu0hCUApF8nonzpl7q//FWp9zu8W61Scz4sd/kUiqDxvUhtoam2efDyiBzcA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", "dev": true, "requires": { "pify": "^3.0.0" @@ -2569,6 +3971,23 @@ } } }, + "make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "requires": { + "p-defer": "^1.0.0" + } + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -2576,37 +3995,81 @@ "dev": true }, "map-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", "dev": true }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, "marked": { "version": "0.3.12", "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.12.tgz", "integrity": "sha512-k4NaW+vS7ytQn6MgJn3fYpQt20/mOgYM5Ft9BYMfQJDz2QT6yEeS9XJ8k2Nw8JTeWK/znPPW2n3UJGzyYEiMoA==", "dev": true }, + "matchdep": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", + "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", + "dev": true, + "requires": { + "findup-sync": "^2.0.0", + "micromatch": "^3.0.4", + "resolve": "^1.4.0", + "stack-trace": "0.0.10" + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", "dev": true }, + "mem": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.0.0.tgz", + "integrity": "sha512-WQxG/5xYc3tMbYLXoXPm81ET2WDULiU5FxbuIoNbJqLOOI8zehXFdZuiUEgfdrU2mVB1pxBZUGlYORSrpuJreA==", + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^1.0.0", + "p-is-promise": "^1.1.0" + } + }, "memoizee": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.11.tgz", - "integrity": "sha1-vemBdmPJ5A/bKk6hw2cpYIeujI8=", + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.14.tgz", + "integrity": "sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg==", "dev": true, "requires": { "d": "1", - "es5-ext": "^0.10.30", + "es5-ext": "^0.10.45", "es6-weak-map": "^2.0.2", "event-emitter": "^0.3.5", "is-promise": "^2.1", "lru-queue": "0.1", "next-tick": "1", - "timers-ext": "^0.1.2" + "timers-ext": "^0.1.5" + }, + "dependencies": { + "es5-ext": { + "version": "0.10.46", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz", + "integrity": "sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw==", + "dev": true, + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "1" + } + } } }, "merge-descriptors": { @@ -2622,24 +4085,24 @@ "dev": true }, "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" } }, "mime": { @@ -2661,6 +4124,11 @@ "mime-db": "~1.30.0" } }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -2674,11 +4142,60 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, + "minipass": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + } + } + }, + "minizlib": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", + "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", + "requires": { + "minipass": "^2.2.1" + } + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, "requires": { "minimist": "0.0.8" }, @@ -2686,8 +4203,7 @@ "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" } } }, @@ -2771,19 +4287,45 @@ }, "multipipe": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "resolved": "http://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", "dev": true, "requires": { "duplexer2": "0.0.2" } }, - "natives": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.3.tgz", - "integrity": "sha512-BZGSYV4YOLxzoTK73l0/s/0sH9l8SHs2ocReMH1f8JYSh5FUWu4ZrKCpJdRkWXV6HFR/pZDz7bwWOVAY07q77g==", + "mute-stdout": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", + "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", "dev": true }, + "nan": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", + "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, "negotiator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", @@ -2796,34 +4338,123 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "now-and-later": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.0.tgz", + "integrity": "sha1-vGHLtFbXnLMiB85HygUTb/Ln1u4=", + "dev": true, + "requires": { + "once": "^1.3.2" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-keys": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", + "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, "requires": { - "remove-trailing-separator": "^1.0.1" + "isobject": "^3.0.0" } }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", "dev": true, "requires": { - "path-key": "^2.0.0" + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" } }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, "object.defaults": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", @@ -2834,33 +4465,16 @@ "array-slice": "^1.0.0", "for-own": "^1.0.0", "isobject": "^3.0.0" - }, - "dependencies": { - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "dev": true, - "requires": { - "for-in": "^1.0.1" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } } }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", "dev": true, "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" } }, "object.pick": { @@ -2870,14 +4484,16 @@ "dev": true, "requires": { "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } + } + }, + "object.reduce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", + "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=", + "dev": true, + "requires": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" } }, "on-finished": { @@ -2913,39 +4529,93 @@ } } }, - "orchestrator": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/orchestrator/-/orchestrator-0.3.8.tgz", - "integrity": "sha1-FOfp4nZPcxX7rBhOUGx6pt+UrX4=", + "ordered-read-streams": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", + "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", "dev": true, "requires": { - "end-of-stream": "~0.1.5", - "sequencify": "~0.0.7", - "stream-consume": "~0.1.0" + "readable-stream": "^2.0.1" } }, - "ordered-read-streams": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz", - "integrity": "sha1-/VZamvjrRHO6abbtijQ1LLVS8SY=", - "dev": true - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true + "os-locale": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.0.1.tgz", + "integrity": "sha512-7g5e7dmXPtzcP4bgsZ8ixDVqA7oWYuEz4lOSujeWyliPai4gfVDiFIcwBg3aGCPnmSGfzOKTK3ccPn0CKv3DBw==", + "requires": { + "execa": "^0.10.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", + "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + } + } }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-is-promise": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=" + }, + "p-limit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", + "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==" }, "package-json": { "version": "4.0.1", @@ -2960,33 +4630,36 @@ } }, "pako": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==" + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.7.tgz", + "integrity": "sha512-3HNK5tW4x8o5mO8RuHZp3Ydw9icZXx0RANAOMzlMzx7LVXhMJ4mo3MOBpzyd7r/+RUu8BmndP47LXT+vzjtWcQ==" }, "parse-filepath": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.1.tgz", - "integrity": "sha1-FZ1hVdQ5BNFsEO9piRHaHpGWm3M=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", "dev": true, "requires": { - "is-absolute": "^0.2.3", + "is-absolute": "^1.0.0", "map-cache": "^0.2.0", "path-root": "^0.1.1" } }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" + "error-ex": "^1.2.0" } }, + "parse-node-version": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.0.tgz", + "integrity": "sha512-02GTVHD1u0nWc20n2G7WX/PgdhNFG04j5fi1OkaJzPWLTcf6vh6229Lta1wTmXG/7Dg42tCssgkccVt7qvd8Kg==", + "dev": true + }, "parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", @@ -2999,6 +4672,23 @@ "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", "dev": true }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -3007,13 +4697,13 @@ "path-is-inside": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" }, "path-parse": { "version": "1.0.5", @@ -3042,9 +4732,20 @@ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", "dev": true }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, "pause-stream": { "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "resolved": "http://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", "dev": true, "requires": { @@ -3059,17 +4760,20 @@ "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true }, "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true }, "pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, "requires": { "pinkie": "^2.0.0" } @@ -3080,18 +4784,30 @@ "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=", "dev": true }, + "plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, "prepend-http": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", "dev": true }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", - "dev": true - }, "pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", @@ -3119,62 +4835,42 @@ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - }, - "q": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=" + "psl": { + "version": "1.1.29", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", + "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" }, - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } }, - "randomatic": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", - "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", "dev": true, "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" } }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, "range-parser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", @@ -3193,22 +4889,56 @@ } }, "rc": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.2.tgz", - "integrity": "sha1-2M6ctX6NZNnHut2YdsfDTL48cHc=", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "requires": { - "deep-extend": "~0.4.0", + "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" }, "dependencies": { - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } } } }, @@ -3225,6 +4955,17 @@ "util-deprecate": "~1.0.1" } }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, "rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", @@ -3234,19 +4975,20 @@ "resolve": "^1.1.6" } }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, "requires": { - "is-equal-shallow": "^0.1.3" + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" } }, "registry-auth-token": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.1.tgz", - "integrity": "sha1-+w0yie4Nmtosu1KvXf5mywcNMAY=", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", + "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", "dev": true, "requires": { "rc": "^1.1.6", @@ -3262,6 +5004,27 @@ "rc": "^1.0.1" } }, + "remove-bom-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", + "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5", + "is-utf8": "^0.2.1" + } + }, + "remove-bom-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", + "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", + "dev": true, + "requires": { + "remove-bom-buffer": "^3.0.0", + "safe-buffer": "^5.1.0", + "through2": "^2.0.3" + } + }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -3269,9 +5032,9 @@ "dev": true }, "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", "dev": true }, "repeat-string": { @@ -3286,35 +5049,74 @@ "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", "dev": true }, + "replace-homedir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", + "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1", + "is-absolute": "^1.0.0", + "remove-trailing-separator": "^1.1.0" + } + }, "request": { - "version": "2.83.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", - "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "requires": { "aws-sign2": "~0.7.0", - "aws4": "^1.6.0", + "aws4": "^1.8.0", "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.1", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", "forever-agent": "~0.6.1", - "form-data": "~2.3.1", - "har-validator": "~5.0.3", - "hawk": "~6.0.2", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.17", - "oauth-sign": "~0.8.2", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", "performance-now": "^2.1.0", - "qs": "~6.5.1", - "safe-buffer": "^5.1.1", - "stringstream": "~0.0.5", - "tough-cookie": "~2.3.3", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", "tunnel-agent": "^0.6.0", - "uuid": "^3.1.0" + "uuid": "^3.3.2" + }, + "dependencies": { + "mime-db": { + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" + }, + "mime-types": { + "version": "2.1.21", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", + "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "requires": { + "mime-db": "~1.37.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } } }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, "resolve": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", @@ -3325,39 +5127,63 @@ } }, "resolve-dir": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-0.1.1.tgz", - "integrity": "sha1-shklmlYC+sXFxJatiUpujMQwJh4=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, + "resolve-options": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", + "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", "dev": true, "requires": { - "expand-tilde": "^1.2.2", - "global-modules": "^0.2.3" + "value-or-function": "^3.0.0" } }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, "rimraf": { "version": "2.5.4", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", - "dev": true, "requires": { "glob": "^7.0.5" } }, - "run-sequence": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/run-sequence/-/run-sequence-1.2.2.tgz", - "integrity": "sha1-UJWgvr6YczsBQL0I3YDsAw3azes=", - "dev": true, - "requires": { - "chalk": "*", - "gulp-util": "*" - } - }, "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, + "safe-regex": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "saucelabs": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", @@ -3371,25 +5197,15 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, - "selenium-webdriver": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", - "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", - "requires": { - "jszip": "^3.1.3", - "rimraf": "^2.5.4", - "tmp": "0.0.30", - "xml2js": "^0.4.17" - }, - "dependencies": { - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "requires": { - "glob": "^7.0.5" - } - } + "selenium-webdriver": { + "version": "4.0.0-alpha.1", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.0.0-alpha.1.tgz", + "integrity": "sha512-z88rdjHAv3jmTZ7KSGUkTvo4rGzcDGMq0oXWHNIDK96Gs31JKVdu9+FMtT4KBrVoibg8dUicJDok6GnqqttO5Q==", + "requires": { + "jszip": "^3.1.3", + "rimraf": "^2.5.4", + "tmp": "0.0.30", + "xml2js": "^0.4.17" } }, "semver": { @@ -3406,6 +5222,24 @@ "semver": "^5.0.3" } }, + "semver-greatest-satisfied-range": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", + "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", + "dev": true, + "requires": { + "sver-compat": "^1.5.0" + } + }, + "semver-greatest-satisfied-range": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", + "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", + "dev": true, + "requires": { + "sver-compat": "^1.5.0" + } + }, "send": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/send/-/send-0.14.2.tgz", @@ -3458,12 +5292,6 @@ } } }, - "sequencify": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/sequencify/-/sequencify-0.0.7.tgz", - "integrity": "sha1-kM/xnQLgcCf9dn9erT57ldHnOAw=", - "dev": true - }, "serve-static": { "version": "1.11.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.11.2.tgz", @@ -3476,6 +5304,34 @@ "send": "0.14.2" } }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, "setprototypeof": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.2.tgz", @@ -3486,7 +5342,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, "requires": { "shebang-regex": "^1.0.0" } @@ -3494,14 +5349,7 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "shelljs": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", - "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", - "dev": true + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, "sigmund": { "version": "1.0.1", @@ -3512,15 +5360,122 @@ "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, - "sntp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", - "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, "requires": { - "hoek": "4.x.x" + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "source-map": { @@ -3528,6 +5483,19 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, "source-map-support": { "version": "0.4.18", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", @@ -3536,25 +5504,78 @@ "source-map": "^0.5.6" } }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, "sparkles": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.0.tgz", "integrity": "sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM=", "dev": true }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.2.tgz", + "integrity": "sha512-qky9CVt0lVIECkEsYbNILVnPvycuEBkXoMFLRWsREkomQLevYhtRKC+R91a5TOAQ3bCMjikRwhyaRqj1VYatYg==", + "dev": true + }, "split": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", "dev": true, "requires": { "through": "2" } }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, "sshpk": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", - "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.15.2.tgz", + "integrity": "sha512-Ra/OXQtuh0/enyl4ETZAfTaeksa6BXks5ZcjpSUNrjBr0DvrJKX+1fsKDPpT9TBXgHAFsa4510aNVgI8g/+SzA==", "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -3563,9 +5584,37 @@ "ecc-jsbn": "~0.1.1", "getpass": "^0.1.1", "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", "tweetnacl": "~0.14.0" } }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, "statuses": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", @@ -3573,12 +5622,13 @@ "dev": true }, "stream-combiner": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "version": "0.2.2", + "resolved": "http://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", + "integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=", "dev": true, "requires": { - "duplexer": "~0.1.1" + "duplexer": "~0.1.1", + "through": "~2.3.4" } }, "stream-combiner2": { @@ -3602,23 +5652,28 @@ } } }, - "stream-consume": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.0.tgz", - "integrity": "sha1-pB6tGm1ggc63n2WwYZAbbY89HQ8=", - "dev": true - }, "stream-equal": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/stream-equal/-/stream-equal-0.1.6.tgz", "integrity": "sha1-zFIvqzhRYBLk1O5HUTsUe3I1kBk=", "dev": true }, + "stream-exhaust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", + "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", + "dev": true + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" @@ -3627,14 +5682,12 @@ "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, "requires": { "ansi-regex": "^3.0.0" } @@ -3646,11 +5699,6 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" - }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -3660,25 +5708,23 @@ } }, "strip-bom": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-1.0.0.tgz", - "integrity": "sha1-hbiGLzhEtabV7IRnqTWYFzo295Q=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "first-chunk-stream": "^1.0.0", "is-utf8": "^0.2.0" } }, "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" }, "strip-json-comments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", - "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, "supports-color": { @@ -3686,6 +5732,42 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" }, + "sver-compat": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", + "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", + "dev": true, + "requires": { + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, + "tar": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + } + } + }, "term-size": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", @@ -3737,13 +5819,14 @@ } } }, - "tildify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tildify/-/tildify-1.2.0.tgz", - "integrity": "sha1-3OwD9V3Km3qj5bBPIYF+tW5jWIo=", + "through2-filter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", + "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=", "dev": true, "requires": { - "os-homedir": "^1.0.0" + "through2": "~2.0.0", + "xtend": "~4.0.0" } }, "time-stamp": { @@ -3759,13 +5842,26 @@ "dev": true }, "timers-ext": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.2.tgz", - "integrity": "sha1-YcxHp2wavTGV8UUn+XjViulMUgQ=", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", + "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", "dev": true, "requires": { - "es5-ext": "~0.10.14", + "es5-ext": "~0.10.46", "next-tick": "1" + }, + "dependencies": { + "es5-ext": { + "version": "0.10.46", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz", + "integrity": "sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw==", + "dev": true, + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "1" + } + } } }, "tmp": { @@ -3776,84 +5872,195 @@ "os-tmpdir": "~1.0.1" } }, + "to-absolute-glob": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", + "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", + "dev": true, + "requires": { + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" + } + }, "to-iso-string": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/to-iso-string/-/to-iso-string-0.0.2.tgz", "integrity": "sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE=", "dev": true }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "to-through": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", + "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", + "dev": true, + "requires": { + "through2": "^2.0.3" + } + }, "tough-cookie": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", - "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "requires": { + "psl": "^1.1.24", "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "dev": true + }, + "tslint": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.11.0.tgz", + "integrity": "sha1-mPMMAurjzecAYgHkwzywi0hYHu0=", + "dev": true, + "requires": { + "babel-code-frame": "^6.22.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^3.2.0", + "glob": "^7.1.1", + "js-yaml": "^3.7.0", + "minimatch": "^3.0.4", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.27.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, - "tslint": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-4.5.1.tgz", - "integrity": "sha1-BTVocb7yOkNJBnNABvwYgza6gks=", + "tslint-eslint-rules": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/tslint-eslint-rules/-/tslint-eslint-rules-5.4.0.tgz", + "integrity": "sha512-WlSXE+J2vY/VPgIcqQuijMQiel+UtmXS+4nvK4ZzlDiqBfXse8FAvkNnTcYhnQyOTW5KFM+uRRGXxYhFpuBc6w==", "dev": true, "requires": { - "babel-code-frame": "^6.20.0", - "colors": "^1.1.2", - "diff": "^3.0.1", - "findup-sync": "~0.3.0", - "glob": "^7.1.1", - "optimist": "~0.6.0", - "resolve": "^1.1.7", - "tsutils": "^1.1.0", - "update-notifier": "^2.0.0" + "doctrine": "0.7.2", + "tslib": "1.9.0", + "tsutils": "^3.0.0" }, "dependencies": { - "diff": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.4.0.tgz", - "integrity": "sha512-QpVuMTEoJMF7cKzi6bvWhRulU1fZqZnvyVQgNhPaxxuTYwyjn/j1v9falseQ/uXWwPnO56RBfwtg4h/EQXmucA==", + "tslib": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", + "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==", "dev": true }, - "findup-sync": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", - "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=", + "tsutils": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.5.2.tgz", + "integrity": "sha512-qIlklNuI/1Dzfm+G+kJV5gg3gimZIX5haYtIVQe7qGyKd7eu8T1t1DY6pz4Sc2CGXAj9s1izycctm9Zfl9sRuQ==", "dev": true, "requires": { - "glob": "~5.0.0" - }, - "dependencies": { - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dev": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } + "tslib": "^1.8.1" } } } }, - "tslint-eslint-rules": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/tslint-eslint-rules/-/tslint-eslint-rules-3.5.1.tgz", - "integrity": "sha1-5D79zddg1ihWAAMXIPlyyS9KBYo=", + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", "dev": true, "requires": { - "doctrine": "^0.7.2" + "tslib": "^1.8.1" } }, - "tsutils": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-1.9.1.tgz", - "integrity": "sha1-ufmrROVa+WgYMdXyjQrur1x1DLA=", - "dev": true - }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -3865,8 +6072,7 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "optional": true + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, "type-detect": { "version": "1.0.0", @@ -3884,10 +6090,16 @@ "mime-types": "~2.1.15" } }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, "typescript": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.6.2.tgz", - "integrity": "sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q=", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.2.tgz", + "integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==", "dev": true }, "unc-path-regex": { @@ -3896,12 +6108,74 @@ "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", "dev": true }, - "unique-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-1.0.0.tgz", - "integrity": "sha1-1ZpKdUJ0R9mqbJHnAmP40mpLEEs=", + "undertaker": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.0.tgz", + "integrity": "sha1-M52kZGJS0ILcN45wgGcpl1DhG0k=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "bach": "^1.0.0", + "collection-map": "^1.0.0", + "es6-weak-map": "^2.0.1", + "last-run": "^1.1.0", + "object.defaults": "^1.0.0", + "object.reduce": "^1.0.0", + "undertaker-registry": "^1.0.0" + } + }, + "undertaker-registry": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", + "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=", "dev": true }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "unique-stream": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz", + "integrity": "sha1-WqADz76Uxf+GbE59ZouxxNuts2k=", + "dev": true, + "requires": { + "json-stable-stringify": "^1.0.0", + "through2-filter": "^2.0.0" + } + }, "unique-string": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", @@ -3917,22 +6191,69 @@ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", "dev": true }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, "unzip-response": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", "dev": true }, + "upath": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", + "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", + "dev": true + }, "update-notifier": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.3.0.tgz", - "integrity": "sha1-TognpruRUUCrCTVZ1wFOPruDdFE=", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", + "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", "dev": true, "requires": { "boxen": "^1.2.1", "chalk": "^2.0.1", "configstore": "^3.0.0", "import-lazy": "^2.1.0", + "is-ci": "^1.0.10", "is-installed-globally": "^0.1.0", "is-npm": "^1.0.0", "latest-version": "^3.0.0", @@ -3941,36 +6262,50 @@ }, "dependencies": { "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" } }, "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^3.1.0", + "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", - "supports-color": "^4.0.0" + "supports-color": "^5.3.0" } }, "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "has-flag": "^2.0.0" + "has-flag": "^3.0.0" } } } }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, "url-parse-lax": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", @@ -3980,10 +6315,10 @@ "prepend-http": "^1.0.1" } }, - "user-home": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", - "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=", + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, "util-deprecate": { @@ -3998,19 +6333,35 @@ "dev": true }, "uuid": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" }, "v8flags": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz", - "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.1.tgz", + "integrity": "sha512-iw/1ViSEaff8NJ3HLyEjawk/8hjJib3E7pvG4pddVXfUg1983s3VGsiClDjhK64MQVDGqc1Q8r18S4VKQZS9EQ==", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, "requires": { - "user-home": "^1.1.1" + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, + "value-or-function": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", + "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=", + "dev": true + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -4039,63 +6390,139 @@ } }, "vinyl-fs": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-0.3.14.tgz", - "integrity": "sha1-mmhRzhysHBzqX+hsCTHWIMLPqeY=", - "dev": true, - "requires": { - "defaults": "^1.0.0", - "glob-stream": "^3.1.5", - "glob-watcher": "^0.0.6", - "graceful-fs": "^3.0.0", - "mkdirp": "^0.5.0", - "strip-bom": "^1.0.0", - "through2": "^0.6.1", - "vinyl": "^0.4.0" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", + "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", + "dev": true, + "requires": { + "fs-mkdirp-stream": "^1.0.0", + "glob-stream": "^6.1.0", + "graceful-fs": "^4.0.0", + "is-valid-glob": "^1.0.0", + "lazystream": "^1.0.0", + "lead": "^1.0.0", + "object.assign": "^4.0.4", + "pumpify": "^1.3.5", + "readable-stream": "^2.3.3", + "remove-bom-buffer": "^3.0.0", + "remove-bom-stream": "^1.2.0", + "resolve-options": "^1.1.0", + "through2": "^2.0.0", + "to-through": "^2.0.0", + "value-or-function": "^3.0.0", + "vinyl": "^2.0.0", + "vinyl-sourcemap": "^1.1.0" }, "dependencies": { "clone": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", - "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", "dev": true }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true }, "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "through2": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", - "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" + "safe-buffer": "~5.1.0" + } + }, + "vinyl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "dev": true, + "requires": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" } + } + } + }, + "vinyl-sourcemap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", + "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", + "dev": true, + "requires": { + "append-buffer": "^1.0.2", + "convert-source-map": "^1.5.0", + "graceful-fs": "^4.1.6", + "normalize-path": "^2.1.1", + "now-and-later": "^2.0.0", + "remove-bom-buffer": "^3.0.0", + "vinyl": "^2.0.0" + }, + "dependencies": { + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true }, "vinyl": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", - "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", "dev": true, "requires": { - "clone": "^0.2.0", - "clone-stats": "^0.0.1" + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" } } } @@ -4107,6 +6534,61 @@ "dev": true, "requires": { "tslint": "^4.1.1" + }, + "dependencies": { + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "findup-sync": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", + "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=", + "dev": true, + "requires": { + "glob": "~5.0.0" + }, + "dependencies": { + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "tslint": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-4.5.1.tgz", + "integrity": "sha1-BTVocb7yOkNJBnNABvwYgza6gks=", + "dev": true, + "requires": { + "babel-code-frame": "^6.20.0", + "colors": "^1.1.2", + "diff": "^3.0.1", + "findup-sync": "~0.3.0", + "glob": "^7.1.1", + "optimist": "~0.6.0", + "resolve": "^1.1.7", + "tsutils": "^1.1.0", + "update-notifier": "^2.0.0" + } + }, + "tsutils": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-1.9.1.tgz", + "integrity": "sha1-ufmrROVa+WgYMdXyjQrur1x1DLA=", + "dev": true + } } }, "webdriver-js-extender": { @@ -4116,38 +6598,39 @@ "requires": { "@types/selenium-webdriver": "^3.0.0", "selenium-webdriver": "^3.0.1" + }, + "dependencies": { + "selenium-webdriver": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", + "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", + "requires": { + "jszip": "^3.1.3", + "rimraf": "^2.5.4", + "tmp": "0.0.30", + "xml2js": "^0.4.17" + } + } } }, "webdriver-manager": { - "version": "12.0.6", - "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.0.6.tgz", - "integrity": "sha1-PfGkgZdwELTL+MnYXHpXeCjA5ws=", - "requires": { - "adm-zip": "^0.4.7", - "chalk": "^1.1.1", - "del": "^2.2.0", - "glob": "^7.0.3", - "ini": "^1.3.4", - "minimist": "^1.2.0", - "q": "^1.4.1", - "request": "^2.78.0", - "rimraf": "^2.5.2", - "semver": "^5.3.0", - "xml2js": "^0.4.17" + "version": "13.0.0-beta", + "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-13.0.0-beta.tgz", + "integrity": "sha512-sFEC0UDlcw/tP7WPt8uqirg2d5Sn0p6LgXELRscmnXQXWmCmWIrMkTIKHTS6fK07o4UAFQ6GL6Wuc1JHLjwYAw==", + "requires": { + "adm-zip": "^0.4.13", + "loglevel": "^1.6.1", + "request": "^2.88.0", + "semver": "^5.6.0", + "tar": "^4.4.8", + "xml2js": "^0.4.19", + "yargs": "^12.0.5" }, "dependencies": { - "adm-zip": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.7.tgz", - "integrity": "sha1-hgbCy/HEJs6MjsABdER/1Jtur8E=" - }, - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "requires": { - "glob": "^7.0.5" - } + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" } } }, @@ -4155,15 +6638,19 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", - "dev": true, "requires": { "isexe": "^2.0.0" } }, - "widest-line": { + "which-module": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.0.tgz", - "integrity": "sha1-AUKk6KJD+IgsAjOqDgKBqnYVInM=", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "widest-line": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", + "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", "dev": true, "requires": { "string-width": "^2.1.1" @@ -4174,6 +6661,35 @@ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -4188,14 +6704,6 @@ "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", "signal-exit": "^3.0.2" - }, - "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - } } }, "xdg-basedir": { @@ -4224,11 +6732,51 @@ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", "dev": true }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + }, "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "dependencies": { + "camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==" + } + } } } } diff --git a/package.json b/package.json index 2126d67f6..587aec7ac 100644 --- a/package.json +++ b/package.json @@ -12,50 +12,45 @@ ], "author": "Julie Ralph ", "dependencies": { - "@types/q": "^0.0.32", - "@types/selenium-webdriver": "^3.0.0", "blocking-proxy": "^1.0.0", "browserstack": "^1.5.1", "chalk": "^1.1.3", "glob": "^7.0.3", - "jasmine": "2.8.0", - "jasminewd2": "^2.1.0", + "jasmine": "^3.3.1", "optimist": "~0.6.0", - "q": "1.4.1", "saucelabs": "^1.5.0", - "selenium-webdriver": "3.6.0", + "selenium-webdriver": "^4.0.0-alpha.1", "source-map-support": "~0.4.0", "webdriver-js-extender": "2.1.0", - "webdriver-manager": "^12.0.6" + "webdriver-manager": "13.0.0-beta" }, "devDependencies": { "@types/node": "^6.0.46", "@types/chalk": "^0.4.28", "@types/glob": "^5.0.29", - "@types/jasmine": "^2.5.47", - "@types/jasminewd2": "^2.0.0", + "@types/jasmine": "^3.3.0", + "@types/loglevel": "^1.5.3", "@types/minimatch": "^2.0.28", "@types/minimist": "^1.1.28", "@types/optimist": "^0.0.29", + "@types/yargs": "^12.0.1", "body-parser": "~1.15.2", "chai": "~3.5.0", "chai-as-promised": "~5.3.0", "clang-format": "1.0.49", "expect.js": "~0.3.1", "express": "~4.14.0", - "gulp": "^3.9.1", + "gulp": "^4.0.0", "gulp-clang-format": "1.0.23", - "gulp-tslint": "^7.0.1", - "jshint": "^2.9.2", + "gulp-tslint": "^8.1.3", "lodash": "^4.5.1", "marked": "^0.3.3", "mocha": "2.5.3", "rimraf": "~2.5.3", - "run-sequence": "^1.1.5", "semver": "^5.3.0", - "tslint": "^4.1.1", - "tslint-eslint-rules": "^3.1.0", - "typescript": "^2.1.5", + "tslint": "^5.11.0", + "tslint-eslint-rules": "^5.4.0", + "typescript": "^3.2.2", "vrsource-tslint-rules": "^4.0.1" }, "repository": { @@ -75,12 +70,13 @@ "pretest": "gulp pretest", "start": "cd testapp && npm start", "test": "node scripts/test.js", + "tsc": "tsc", "website": "cd website && npm start", "compile_to_es5": "gulp compile_to_es5" }, "license": "MIT", "engines": { - "node": ">=6.9.x" + "node": ">=8.8.x" }, - "version": "5.4.1" + "version": "6.0.0-beta" } diff --git a/scripts/compile_to_es5.sh b/scripts/compile_to_es5.sh deleted file mode 100755 index 93d9a12b3..000000000 --- a/scripts/compile_to_es5.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -cd "$( dirname "${BASH_SOURCE[0]}" )/.." - -# Check number of parameters -if [ "$#" -gt 0 ]; then - echo "Usage: ./scripts/compile_to_es5.sh" - exit 1 -fi - -echo "Compiling down to es5..." -node node_modules/typescript/bin/tsc --target es5 --lib DOM,ES5,ScriptHost,ES6 -if [ $? -ne 0 ]; then - echo -e "\033[0;31m" 1>&2 # Red - echo "Couldn't compile for es5." - echo -e "\033[0m" 1>&2 # Normal Color - exit 1 -fi - -echo -e "\033[0;32m" # Green -echo "Compiled to es5" -echo -e "\033[0m" 1>&2 # Normal Color diff --git a/scripts/driverProviderAttachSession.js b/scripts/driverProviderAttachSession.js index 8612f9a44..b4d5501be 100644 --- a/scripts/driverProviderAttachSession.js +++ b/scripts/driverProviderAttachSession.js @@ -2,105 +2,139 @@ 'use strict'; -var http = require('http'), - spawn = require('child_process').spawnSync; +const http = require('http'); +const child_process = require('child_process'); -var sessionId = ''; - -// 1. Create a new selenium session. -var postData = JSON.stringify( - {'desiredCapabilities': {'browserName': 'firefox'}}); -var createOptions = { - hostname: 'localhost', - port: 4444, - path: '/wd/hub/session', - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': Buffer.byteLength(postData) - } -}; -var req = http.request(createOptions, function(res) { - res.on('data', setBody); - res.on('end', checkSession); -}); -req.write(postData); -req.end(); - -// 2. After making the request to create a selenium session, read the selenium -// session id. -var setBody = function(chunk) { - var body = chunk.toString(); - sessionId = JSON.parse(body).sessionId; +// Delete session method to be used at the end of the test as well as +// when the tests fail. +const deleteSession = (sessionId, err) => { + return new Promise(resolve => { + const deleteOptions = { + hostname: 'localhost', + port: 4444, + path: '/wd/hub/session/' + sessionId, + method: 'DELETE' + }; + const req = http.request(deleteOptions, res => { + res.on('end', () => { + if (err) { + throw err; + } + resolve(); + }); + }); + req.end(); + }); }; -// 3. After getting the session id, verify that the selenium session exists. -// If the session exists, run the protractor test. -var checkSession = function() { - var checkOptions = { - hostname: 'localhost', - port: 4444, - path: '/wd/hub/session/' + sessionId, - method: 'GET' - }; - var state = ''; - var req = http.request(checkOptions, function(res) { - res.on('data', function(chunk) { - state = JSON.parse(chunk.toString()).state; - }); - res.on('end', function() { - if (state === 'success') { - var runProtractor = spawn('node', - ['bin/protractor', 'spec/driverProviderAttachSessionConf.js', - '--seleniumSessionId=' + sessionId]); - console.log(runProtractor.stdout.toString()); - if (runProtractor.status !== 0) { - throw new Error('Protractor did not run properly.'); - } - } - else { - throw new Error('The selenium session was not created.'); +const run = async () => { + // 1. Create a new selenium session. + const sessionId = await new Promise(resolve => { + const postData = JSON.stringify( + {'desiredCapabilities': {'browserName': 'chrome'}}); + const createOptions = { + hostname: '127.0.0.1', + port: 4444, + path: '/wd/hub/session', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': Buffer.byteLength(postData) } - checkStoppedSession(); + }; + let body = ''; + const req = http.request(createOptions, (res) => { + res.on('data', (data) => { + body = JSON.parse(data.toString()); + }); + res.on('end', () => { + resolve(body.sessionId); + }); }); + req.write(postData); + req.end(); }); - req.end(); -}; -// 4. After the protractor test completes, check to see that the session still -// exists. If we can find the session, delete it. -var checkStoppedSession = function() { - var checkOptions = { - hostname: 'localhost', - port: 4444, - path: '/wd/hub/session/' + sessionId, - method: 'GET' - }; - var state = ''; - var req = http.request(checkOptions, function(res) { - res.on('data', function(chunk) { - state = JSON.parse(chunk.toString()).state; + await new Promise(resolve => { + // 2. After getting the session id, verify that the selenium session exists. + // If the session exists, run the protractor test. + const checkOptions = { + hostname: '127.0.0.1', + port: 4444, + path: '/wd/hub/sessions', + method: 'GET' + }; + + let values = []; + const req = http.request(checkOptions, (res) => { + res.on('data', (chunk) => { + values = JSON.parse(chunk.toString())['value']; + }); + res.on('end', () => { + let found = false; + for (let value of values) { + if (value['id'] === sessionId) { + found = true; + } + } + if (found) { + resolve(); + } else { + throw new Error('The selenium session was not created.'); + } + }); + res.on('error', (err) => { + console.log(err); + process.exit(1); + }); }); - res.on('end', function() { - if (state === 'success') { - deleteSession(); - } - else { - throw new Error('The selenium session should still exist.'); - } + req.end(); + }); + + // 3. Run Protractor and attach to the session. + const runProtractor = child_process.spawnSync('node', + ['bin/protractor', 'spec/driverProviderAttachSessionConf.js', + '--seleniumSessionId=' + sessionId]); + console.log(runProtractor.stdout.toString()); + if (runProtractor.status !== 0) { + const e = new Error('Protractor did not run properly.'); + await deleteSession(sessionId, e); + process.exit(1); + } + + // 4. After the protractor test completes, check to see that the session still + // exists. If we can find the session, delete it. + await new Promise(resolve => { + const checkOptions = { + hostname: '127.0.0.1', + port: 4444, + path: '/wd/hub/session/' + sessionId, + method: 'GET' + }; + const req = http.request(checkOptions, (res) => { + let state = ''; + res.on('data', (chunk) => { + state = JSON.parse(chunk.toString()).state; + }); + res.on('end', () => { + if (state === 'success') { + resolve(); + } + else { + const e = new Error('The selenium session should still exist.'); + deleteSession(sessionId, e); + } + }); + res.on('error', (err) => { + console.log(err); + process.exit(1); + }); }); + req.end(); }); - req.end(); -}; -// 5. Delete the selenium session. -var deleteSession = function() { - var deleteOptions = { - hostname: 'localhost', - port: 4444, - path: '/wd/hub/session/' + sessionId, - method: 'DELETE' - }; - var req = http.request(deleteOptions); - req.end(); -}; + // 5. Delete the selenium session. + await deleteSession(sessionId); +} + +run().then(); diff --git a/scripts/errorTest.js b/scripts/errorTest.js index 67b72a498..050d4d2d6 100644 --- a/scripts/errorTest.js +++ b/scripts/errorTest.js @@ -31,7 +31,7 @@ checkLogs(output, messages); runProtractor = spawn('node', ['bin/protractor', 'spec/errorTest/browserStackAuthentication.js']); output = runProtractor.stdout.toString(); -messages = ['WebDriverError: Invalid username or password', +messages = ['Invalid username or password', 'Process exited with error code ' + exitCodes.BrowserError.CODE]; checkLogs(output, messages); diff --git a/scripts/interactive_tests/interactive_test.js b/scripts/interactive_tests/interactive_test.js deleted file mode 100644 index a4342f764..000000000 --- a/scripts/interactive_tests/interactive_test.js +++ /dev/null @@ -1,49 +0,0 @@ -var env = require('../../spec/environment.js'); -var InteractiveTest = require('./interactive_test_util').InteractiveTest; -var port = env.interactiveTestPort; -var test = new InteractiveTest('node built/cli.js --elementExplorer true', port); - -// Check state persists. -test.addCommandExpectation('var x = 3'); -test.addCommandExpectation('x', '3'); - -// Check can return functions. -test.addCommandExpectation('var y = function(param) {return param;}'); -test.addCommandExpectation('y', 'function (param) {return param;}'); - -// Check promises complete. -test.addCommandExpectation('browser.driver.getCurrentUrl()', 'data:,'); -test.addCommandExpectation('browser.get("http://localhost:' + env.webServerDefaultPort + '/ng1")'); -test.addCommandExpectation('browser.getCurrentUrl()', - 'http://localhost:' + env.webServerDefaultPort + '/ng1/#/form'); - -// Check promises are resolved before being returned. -test.addCommandExpectation('var greetings = element(by.binding("greeting"))'); -test.addCommandExpectation('greetings.getText()', 'Hiya'); - -// Check require is injected. -test.addCommandExpectation('var q = require("q")'); - -// Check errors are handled gracefully -test.addCommandExpectation('element(by.binding("nonexistent"))'); -test.addCommandExpectation('element(by.binding("nonexistent")).getText()', - 'ERROR: NoSuchElementError: No element found using locator: ' + - 'by.binding("nonexistent")'); - -// Check global `list` works. -test.addCommandExpectation('list(by.binding("greeting"))', '[ \'Hiya\' ]'); -test.addCommandExpectation('list(by.binding("nonexistent"))', '[]'); - -// Check complete calls -test.addCommandExpectation('\t', - '[["element(by.id(\'\'))","element(by.css(\'\'))",' + - '"element(by.name(\'\'))","element(by.binding(\'\'))",' + - '"element(by.xpath(\'\'))","element(by.tagName(\'\'))",' + - '"element(by.className(\'\'))"],""]'); -test.addCommandExpectation('ele\t', '[["element"],"ele"]'); -test.addCommandExpectation('br\t', '[["break","","browser"],"br"]'); -// Make sure the global 'list' we added shows up. -test.addCommandExpectation('li\t', '[["list"],"li"]'); - -test.run(); - diff --git a/scripts/interactive_tests/interactive_test_util.js b/scripts/interactive_tests/interactive_test_util.js deleted file mode 100644 index 8e58bf87f..000000000 --- a/scripts/interactive_tests/interactive_test_util.js +++ /dev/null @@ -1,150 +0,0 @@ -var child_process = require('child_process'), - q = require('q'), - net = require('net'); - -var TIMEOUT = 10000; - -// An instance of a protractor debugger server. -var Server = function(serverStartCmd, port) { - // Start protractor and its debugger server as a child process. - this.start = function() { - var deferred = q.defer(); - var received = ''; - - serverStartCmd += ' --debuggerServerPort ' + port; - serverStartCmd = serverStartCmd.split(/\s/); - var test_process = child_process.spawn(serverStartCmd[0], - serverStartCmd.slice(1)); - - var timeoutObj = setTimeout(function() { - var errMsg = 'Did not start interactive server in ' + TIMEOUT/1000 + 's.'; - if (received) { - errMsg += ' Server startup output: ' + received; - } - deferred.reject(errMsg); - }, TIMEOUT); - - test_process.stdout.on('data', function(data) { - received += data; - if (received.indexOf('Server listening on 127.0.0.1:' + port) >= 0) { - clearTimeout(timeoutObj); - // Add a small time for browser to get ready - setTimeout(deferred.resolve, 2000); - } - }); - - test_process.stderr.on('data', function(data) { - received += data; - }); - - return deferred.promise; - }; -}; - -// A client to attach to Protractor's debugger server and exchange data. -var Client = function(port) { - var socket; - - // Connect to the server. - this.connect = function() { - var deferred = q.defer(); - socket = net.connect({port: port}, function() { - deferred.resolve(); - }); - return deferred.promise; - }; - - // Disconnect from the server. - this.disconnect = function() { - socket.end(); - }; - - // Send a command to the server and wait for a response. Return response as a - // promise. - this.sendCommand = function(cmd) { - var deferred = q.defer(); - var received = ''; - var timeoutObj = setTimeout(function() { - var errMsg = 'Command <' + JSON.stringify(cmd) + - '> did not receive a response in ' + TIMEOUT/1000 + 's.'; - if (received) { - errMsg += ' Received messages so far: ' + received; - } - deferred.reject(errMsg); - }, TIMEOUT); - - var ondata = function(data) { - received += data.toString(); - var i = received.indexOf('\r\n'); - if (i >= 0) { - clearTimeout(timeoutObj); - var response = received.substring(0, i).trim(); - deferred.resolve(response); - } - }; - socket.on('data', ondata); - - var onerror = function(data) { - deferred.reject('Received error: ' + data); - }; - socket.on('error', onerror); - - socket.write(cmd + '\r\n'); - return deferred.promise.fin(function() { - clearTimeout(timeoutObj); - socket.removeListener('data', ondata); - socket.removeListener('error', onerror); - }); - }; -}; - -/** - * Util for running an interactive Protractor test. - */ -exports.InteractiveTest = function(interactiveServerStartCmd, port) { - var expectations = []; - - // Adds a command to send as well as the response to verify against. - // If opt_expectedResult is undefined, the test will still wait for the server - // to respond after sending the command, but will not verify against it. - // Note, this does not actually interact with the server, but simply adds the - // command to a queue. - this.addCommandExpectation = function(command, opt_expectedResult) { - expectations.push({ - command: command, - expectedResult: opt_expectedResult - }); - }; - - // Execute the interactive test. This will first start Protractor and its - // debugger server. Then it will connect to the server. Finally, it will - // send the queue of commands against the server sequentially and verify the - // response from the server if needed. - this.run = function() { - var server = new Server(interactiveServerStartCmd, port); - return server.start().then(function() { - var client = new Client(port); - return client.connect().then(function() { - var verifyAll = function(i) { - if (i < expectations.length) { - var expectedResult = expectations[i].expectedResult; - var command = expectations[i].command; - return client.sendCommand(command).then(function(response) { - if (expectedResult !== undefined && expectedResult !== response) { - throw ('Command <' + JSON.stringify(command) + '> received: ' + - response + ', but expects: ' + expectedResult); - } else { - return verifyAll(i + 1); - } - }); - } - }; - return verifyAll(0); - }).fin(function() { - // '^]' This is the term signal. - client.sendCommand(String.fromCharCode(0x1D)); - client.disconnect(); - }); - }).done(); - }; -}; diff --git a/scripts/interactive_tests/with_base_url.js b/scripts/interactive_tests/with_base_url.js deleted file mode 100644 index cd82f7b62..000000000 --- a/scripts/interactive_tests/with_base_url.js +++ /dev/null @@ -1,12 +0,0 @@ -var env = require('../../spec/environment.js'); -var InteractiveTest = require('./interactive_test_util').InteractiveTest; -var port = env.interactiveTestPort; -var test = new InteractiveTest( - 'node built/cli.js --baseUrl http://localhost:' + env.webServerDefaultPort + - '/ng1 --elementExplorer true', port); - -// Check we automatically go to to baseUrl. -test.addCommandExpectation( - 'browser.driver.getCurrentUrl()', - 'http://localhost:' + env.webServerDefaultPort + '/ng1/#/form'); -test.run(); diff --git a/scripts/test.js b/scripts/test.js index 012defec1..bc1a9979e 100755 --- a/scripts/test.js +++ b/scripts/test.js @@ -1,9 +1,9 @@ #!/usr/bin/env node -var path = require('path'); +const path = require('path'); -var Executor = require('./test/test_util').Executor; +const Executor = require('./test/test_util').Executor; -var passingTests = [ +const passingTests = [ 'node built/cli.js spec/basicConf.js', 'node built/cli.js spec/basicConf.js --useBlockingProxy', 'node built/cli.js spec/multiConf.js', @@ -31,6 +31,7 @@ var passingTests = [ 'node built/cli.js spec/interactionConf.js', 'node built/cli.js spec/directConnectConf.js', 'node built/cli.js spec/restartBrowserBetweenTestsConf.js', + 'node spec/driverProviderTest.js', 'node built/cli.js spec/driverProviderLocalConf.js', 'node built/cli.js spec/driverProviderLocalConf.js --useBlockingProxy', 'node built/cli.js spec/getCapabilitiesConf.js', @@ -42,24 +43,20 @@ var passingTests = [ 'node built/cli.js spec/built/noCFBasicConf.js', 'node built/cli.js spec/built/noCFBasicConf.js --useBlockingProxy', 'node built/cli.js spec/built/noCFPluginConf.js', - //'node scripts/driverProviderAttachSession.js', - 'node built/cli.js spec/driverProviderUseExistingWebDriver.js', - 'node built/cli.js spec/driverProviderUseExistingWebDriver.js --useBlockingProxy', + 'node scripts/driverProviderAttachSession.js', 'node scripts/errorTest.js', - // Interactive Element Explorer tasks - 'node scripts/interactive_tests/interactive_test.js', - 'node scripts/interactive_tests/with_base_url.js', // Unit tests 'node node_modules/jasmine/bin/jasmine.js JASMINE_CONFIG_PATH=scripts/unit_test.json', // Dependency tests 'node node_modules/jasmine/bin/jasmine.js JASMINE_CONFIG_PATH=scripts/dependency_test.json', // Typings tests - 'node spec/install/test.js' + // TODO(selenium4): consider rewriting this test. + // 'node spec/install/test.js' ]; -var executor = new Executor(); +const executor = new Executor(); -passingTests.forEach(function(passing_test) { +passingTests.forEach((passing_test) => { executor.addCommandlineTest(passing_test) .assertExitCodeOnly(); }); @@ -72,7 +69,7 @@ passingTests.forEach(function(passing_test) { executor.addCommandlineTest('node built/cli.js spec/errorTest/singleFailureConf.js') .expectExitCode(1) .expectErrors({ - stackTrace: 'single_failure_spec1.js:5:32' + stackTrace: 'single_failure_spec1.js:5:38' }); // assert timeout works @@ -94,63 +91,63 @@ executor.addCommandlineTest('node built/cli.js spec/errorTest/multiFailureConf.j .expectExitCode(1) .expectErrors([{ message: 'Expected \'Hiya\' to equal \'INTENTIONALLY INCORRECT\'.', - stacktrace: 'single_failure_spec1.js:5:32' + stacktrace: 'single_failure_spec1.js:5:38' }, { message: 'Expected \'Hiya\' to equal \'INTENTIONALLY INCORRECT\'.', - stacktrace: 'single_failure_spec2.js:5:32' + stacktrace: 'single_failure_spec2.js:5:38' }]); executor.addCommandlineTest('node built/cli.js spec/errorTest/shardedFailureConf.js') .expectExitCode(1) .expectErrors([{ message: 'Expected \'Hiya\' to equal \'INTENTIONALLY INCORRECT\'.', - stacktrace: 'single_failure_spec1.js:5:32' + stacktrace: 'single_failure_spec1.js:5:38' }, { message: 'Expected \'Hiya\' to equal \'INTENTIONALLY INCORRECT\'.', - stacktrace: 'single_failure_spec2.js:5:32' + stacktrace: 'single_failure_spec2.js:5:38' }]); executor.addCommandlineTest('node built/cli.js spec/errorTest/mochaFailureConf.js') .expectExitCode(1) .expectErrors([{ message: 'expected \'My AngularJS App\' to equal \'INTENTIONALLY INCORRECT\'', - stacktrace: 'mocha_failure_spec.js:11:20' + stacktrace: 'mocha_failure_spec.js:11:41' }]); -// executor.addCommandlineTest('node built/cli.js spec/errorTest/pluginsFailingConf.js') -// .expectExitCode(1) -// .expectErrors([ -// {message: 'Expected true to be false'}, -// {message: 'from setup'}, -// {message: 'from postTest passing'}, -// {message: 'from postTest failing'}, -// {message: 'from teardown'} -// ]); - -// executor.addCommandlineTest('node built/cli.js spec/errorTest/slowHttpAndTimeoutConf.js') -// .expectExitCode(1) -// .expectErrors([ -// {message: 'The following tasks were pending[\\s\\S]*\\$http: slowcall'}, -// {message: 'The following tasks were pending:[\\s\\S]*' + -// '- \\$timeout: function\\(\\) {[\\s\\S]*' + -// '\\$scope\\.slowAngularTimeoutStatus = \'done\';[\\s\\S]' + -// '*}'} -// ]); - -// executor.addCommandlineTest('node built/cli.js spec/errorTest/slowHttpAndTimeoutConf.js ' + -// '--untrackOutstandingTimeouts true') -// .expectExitCode(1) -// .expectErrors([ -// {message: 'The following tasks were pending[\\s\\S]*\\$http: slowcall'}, -// {message: 'While waiting for element with locator - ' + -// 'Locator: by.binding\\(\\"slowAngularTimeoutStatus\\"\\)$'} -// ]); - -// executor.addCommandlineTest('node built/cli.js spec/angular2TimeoutConf.js') -// .expectExitCode(1) -// .expectErrors([ -// {message: 'Timed out waiting for asynchronous Angular tasks to finish'}, -// ]); +executor.addCommandlineTest('node built/cli.js spec/errorTest/pluginsFailingConf.js') + .expectExitCode(1) + .expectErrors([ + {message: 'Expected true to be false'}, + {message: 'from setup'}, + {message: 'from postTest passing'}, + {message: 'from postTest failing'}, + {message: 'from teardown'} + ]); + +executor.addCommandlineTest('node built/cli.js spec/errorTest/slowHttpAndTimeoutConf.js') + .expectExitCode(1) + .expectErrors([ + {message: 'The following tasks were pending[\\s\\S]*\\$http: slowcall'}, + {message: 'The following tasks were pending:[\\s\\S]*' + + '- \\$timeout: function\\(\\) {[\\s\\S]*' + + '\\$scope\\.slowAngularTimeoutStatus = \'done\';[\\s\\S]' + + '*}'} + ]); + +executor.addCommandlineTest('node built/cli.js spec/errorTest/slowHttpAndTimeoutConf.js ' + + '--untrackOutstandingTimeouts true') + .expectExitCode(1) + .expectErrors([ + {message: 'The following tasks were pending[\\s\\S]*\\$http: slowcall'}, + {message: 'While waiting for element with locator - ' + + 'Locator: by.binding\\(\\"slowAngularTimeoutStatus\\"\\)$'} + ]); + +executor.addCommandlineTest('node built/cli.js spec/angular2TimeoutConf.js') + .expectExitCode(1) + .expectErrors([ + {message: 'Timed out waiting for asynchronous Angular tasks to finish'}, + ]); // If we're running on CircleCI, save stdout and stderr from the test run to a log file. if (process.env['CIRCLE_ARTIFACTS']) { diff --git a/scripts/test/test_util.js b/scripts/test/test_util.js index a608c1e5f..1bbada24f 100644 --- a/scripts/test/test_util.js +++ b/scripts/test/test_util.js @@ -1,41 +1,44 @@ #!/usr/bin/env node -var child_process = require('child_process'), - q = require('q'), - fs = require('fs'); - -var CommandlineTest = function(command) { - var self = this; - this.command_ = command; - this.expectedExitCode_ = 0; - this.expectedErrors_ = []; - this.assertExitCodeOnly_ = false; - this.testLogStream = undefined; +const child_process = require('child_process'); +const fs = require('fs'); + +class CommandlineTest { + constructor(command) { + this.command = command; + this.expectedExitCode = 0; + this.expectedErrors = []; + this.isExitCode = false; + this.testLogStream = undefined; + this.expectedMinTestDuration = undefined; + this.expectedMaxTestDuration = undefined; + } + // Only assert the exit code and not failures. // This must be true if the command you're running does not support // the flag '--resultJsonOutputFile'. - this.assertExitCodeOnly = function() { - self.assertExitCodeOnly_ = true; - return self; - }; + assertExitCodeOnly() { + this.isExitCode = true; + return this; + } - this.setTestLogFile = function(filename) { - self.testLogStream = fs.createWriteStream(filename, {flags: 'a'}); - }; + setTestLogFile(filename) { + this.testLogStream = fs.createWriteStream(filename, {flags: 'a'}); + } // Set the expected exit code for the test command. - this.expectExitCode = function(exitCode) { - self.expectedExitCode_ = exitCode; - return self; - }; + expectExitCode(exitCode) { + this.expectedExitCode = exitCode; + return this; + } // Set the expected total test duration in milliseconds. - this.expectTestDuration = function(min, max) { - self.expectedMinTestDuration_ = min; - self.expectedMaxTestDuration_ = max; - return self; - }; + expectTestDuration(min, max) { + this.expectedMinTestDuration = min; + this.expectedMaxTestDuration = max; + return this; + } /** * Add expected error(s) for the test command. @@ -45,75 +48,75 @@ var CommandlineTest = function(command) { * stackTrace: string, //optional regex * } */ - this.expectErrors = function(expectedErrors) { + expectErrors(expectedErrors) { if (expectedErrors instanceof Array) { - self.expectedErrors_ = self.expectedErrors_.concat(expectedErrors); + this.expectedErrors = this.expectedErrors.concat(expectedErrors); } else { - self.expectedErrors_.push(expectedErrors); + this.expectedErrors.push(expectedErrors); } - return self; - }; + return this; + } - this.run = function() { - var start = new Date().getTime(); - var testOutputPath = 'test_output_' + start + '.tmp'; - var output = ''; + async run() { + const start = new Date().getTime(); + const testOutputPath = `test_output_${start}.tmp`; + let output = ''; - var flushAndFail = function(errorMsg) { + const flushAndFail = (errorMsg) => { process.stdout.write(output); throw new Error(errorMsg); }; - return q.promise(function(resolve, reject) { - if (!self.assertExitCodeOnly_) { - self.command_ = self.command_ + ' --resultJsonOutputFile ' + testOutputPath; - } - var args = self.command_.split(/\s/); - - var test_process; + try { - test_process = child_process.spawn(args[0], args.slice(1)); - - var processData = function(data) { - process.stdout.write('.'); - output += data; - if (self.testLogStream) { - self.testLogStream.write(data); + let exitCode = await new Promise((resolve, reject) => { + if (!this.assertExitCodeOnly) { + this.command = `${this.command} --resultJsonOutputFile ${testOutputPath}`; } - }; + const args = this.command.split(/\s/); + const test_process = child_process.spawn(args[0], args.slice(1)); + + const processData = (data) => { + process.stdout.write('.'); + output += data; + if (this.testLogStream) { + this.testLogStream.write(data); + } + }; - test_process.stdout.on('data', processData); - test_process.stderr.on('data', processData); + test_process.stdout.on('data', processData); + test_process.stderr.on('data', processData); - test_process.on('error', function(err) { - reject(err); - }); + test_process.on('error', (err) => { + reject(err); + }); - test_process.on('exit', function(exitCode) { - resolve(exitCode); + test_process.on('exit', function(exitCode) { + resolve(exitCode); + }); }); - }).then(function(exitCode) { - if (self.expectedExitCode_ !== exitCode) { - flushAndFail('expecting exit code: ' + self.expectedExitCode_ + + + if (this.expectedExitCode !== exitCode) { + flushAndFail('expecting exit code: ' + this.expectedExitCode + ', actual: ' + exitCode); } - if (self.testLogStream) { - self.testLogStream.end(); + if (this.testLogStream) { + this.testLogStream.end(); } // Skip the rest if we are only verify exit code. // Note: we're expecting a file populated by '--resultJsonOutputFile' after // this point. - if (self.assertExitCodeOnly_) { + if (this.assertExitCodeOnly) { return; } - var raw_data = fs.readFileSync(testOutputPath); - var testOutput = JSON.parse(raw_data); + const raw_data = fs.readFileSync(testOutputPath); + const testOutput = JSON.parse(raw_data); - var actualErrors = []; - var duration = 0; + let actualErrors = []; + let duration = 0; testOutput.forEach(function(specResult) { duration += specResult.duration; specResult.assertions.forEach(function(assertion) { @@ -123,9 +126,9 @@ var CommandlineTest = function(command) { }); }); - self.expectedErrors_.forEach(function(expectedError) { - var found = false; - for (var i = 0; i < actualErrors.length; ++i) { + this.expectedErrors.forEach((expectedError) => { + let found = false; + for (let i = 0; i < actualErrors.length; ++i) { var actualError = actualErrors[i]; // if expected message is defined and messages don't match @@ -167,25 +170,25 @@ var CommandlineTest = function(command) { flushAndFail('failed with ' + actualErrors.length + ' unexpected failures'); } - if (self.expectedMinTestDuration_ - && duration < self.expectedMinTestDuration_) { + if (this.expectedMinTestDuration + && duration < this.expectedMinTestDuration) { flushAndFail('expecting test min duration: ' + - self.expectedMinTestDuration_ + ', actual: ' + duration); + this.expectedMinTestDuration + ', actual: ' + duration); } - if (self.expectedMaxTestDuration_ - && duration > self.expectedMaxTestDuration_) { + if (this.expectedMaxTestDuration + && duration > this.expectedMaxTestDuration) { flushAndFail('expecting test max duration: ' + - self.expectedMaxTestDuration_ + ', actual: ' + duration); + this.expectedMaxTestDuration + ', actual: ' + duration); } - }).fin(function() { + } finally { try { fs.unlinkSync(testOutputPath); } catch (err) { // don't do anything } - }); - }; -}; + } + } +} /** * Util for running tests and testing functionalities including: @@ -195,34 +198,36 @@ var CommandlineTest = function(command) { * For now, this means protractor tests (jasmine/mocha). */ exports.Executor = function() { - var tests = []; - this.addCommandlineTest = function(command) { - var test = new CommandlineTest(command); + let tests = []; + this.addCommandlineTest = (command) => { + let test = new CommandlineTest(command); tests.push(test); return test; }; - this.execute = function(logFile) { - var failed = false; - - (function runTests(i) { - if (i < tests.length) { - console.log('running: ' + tests[i].command_); + this.runTests = async function(i, logFile, failed) { + if (i < tests.length) { + try { + console.log('running: ' + tests[i].command); if (logFile) { tests[i].setTestLogFile(logFile); } - tests[i].run().then(function() { - console.log('\n>>> \033[1;32mpass\033[0m'); - }, function(err) { - failed = true; - console.log('\n>>> \033[1;31mfail: ' + err.toString() + '\033[0m'); - }).fin(function() { - runTests(i + 1); - }).done(); - } else { - console.log('Summary: ' + (failed ? 'fail' : 'pass')); - process.exit(failed ? 1 : 0); + await tests[i].run(); + console.log('\n>>> \033[1;32mpass\033[0m'); + } catch (err) { + failed = true; + console.log('\n>>> \033[1;31mfail: ' + err.toString() + '\033[0m'); + } finally { + this.runTests(i + 1, logFile, failed); } - }(0)); + } else { + console.log('Summary: ' + (failed ? 'fail' : 'pass')); + process.exit(failed ? 1 : 0); + } + }; + + this.execute = (logFile) => { + let failed = false; + this.runTests(0, logFile, failed); }; }; diff --git a/scripts/test_on_travis.sh b/scripts/test_on_travis.sh index 67f3c71ea..c8e43c4eb 100755 --- a/scripts/test_on_travis.sh +++ b/scripts/test_on_travis.sh @@ -5,11 +5,11 @@ if [ $JOB = "smoke" ]; then node bin/protractor spec/ciSmokeConf.js elif [ $JOB = "full" ]; then node bin/protractor spec/ciFullConf.js - if [ $? = "0" ]; then - node bin/protractor spec/ciNg2Conf.js - else - exit 1 - fi + # if [ $? = "0" ]; then + # node bin/protractor spec/ciNg2Conf.js + # else + # exit 1 + # fi elif [ $JOB = "bstack" ]; then node bin/protractor spec/ciBStackConf.js else diff --git a/spec/.jshintrc b/spec/.jshintrc index dea6d2210..b8b730f77 100644 --- a/spec/.jshintrc +++ b/spec/.jshintrc @@ -1,4 +1,6 @@ { + "strict": false, + "esversion": 6, "predef": [ "protractor", "browser", diff --git a/spec/altRoot/findelements_spec.js b/spec/altRoot/findelements_spec.js index e6d3d7934..8af2697de 100644 --- a/spec/altRoot/findelements_spec.js +++ b/spec/altRoot/findelements_spec.js @@ -1,19 +1,19 @@ -describe('finding elements when ng-app is nested', function() { - beforeEach(function() { - browser.get('alt_root_index.html#/form'); +describe('finding elements when ng-app is nested', () => { + beforeEach(async() => { + await browser.get('alt_root_index.html#/form'); }); - it('should find an element by binding', function() { - var greeting = element(by.binding('{{greeting}}')); + it('should find an element by binding', async() => { + const greeting = element(by.binding('{{greeting}}')); - expect(greeting.getText()).toEqual('Hiya'); + expect(await greeting.getText()).toEqual('Hiya'); }); - it('should find elements outside of angular', function() { - var outside = element(by.id('outside-ng')); - var inside = element(by.id('inside-ng')); + it('should find elements outside of angular', async() => { + const outside = element(by.id('outside-ng')); + const inside = element(by.id('inside-ng')); - expect(outside.getText()).toEqual('{{1 + 2}}'); - expect(inside.getText()).toEqual('3'); + expect(await outside.getText()).toEqual('{{1 + 2}}'); + expect(await inside.getText()).toEqual('3'); }); }); diff --git a/spec/altRootConf.js b/spec/altRootConf.js index 080e4245d..bc7669030 100644 --- a/spec/altRootConf.js +++ b/spec/altRootConf.js @@ -3,6 +3,7 @@ var env = require('./environment.js'); // Tests for an Angular app where ng-app is not on the body. exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/angular2Conf.js b/spec/angular2Conf.js index d91d85eec..d02c9c420 100644 --- a/spec/angular2Conf.js +++ b/spec/angular2Conf.js @@ -3,6 +3,7 @@ var env = require('./environment'); // This is the configuration for a smoke test for an Angular TypeScript application. exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/basic/elements_spec.js b/spec/basic/elements_spec.js index ed2a3121f..2b0c1c935 100644 --- a/spec/basic/elements_spec.js +++ b/spec/basic/elements_spec.js @@ -1,159 +1,160 @@ -describe('ElementFinder', function() { - beforeEach(function() { +const {WebElement} = require('selenium-webdriver'); + +describe('ElementFinder', () => { + beforeEach(async() => { // Clear everything between each test. - browser.driver.get('about:blank'); + await browser.driver.get('about:blank'); }); - it('should return the same result as browser.findElement', function() { - browser.get('index.html#/form'); - var nameByElement = element(by.binding('username')); + it('should return the same result as browser.findElement', async() => { + await browser.get('index.html#/form'); + const nameByElement = element(by.binding('username')); - expect(nameByElement.getText()).toEqual( - browser.findElement(by.binding('username')).getText()); + expect(await nameByElement.getText()).toEqual( + await browser.findElement(by.binding('username')).getText()); }); - it('should wait to grab the WebElement until a method is called', function() { + it('should wait to grab the WebElement until a method is called', async() => { // These should throw no error before a page is loaded. - var usernameInput = element(by.model('username')); - var name = element(by.binding('username')); + const usernameInput = element(by.model('username')); + const name = element(by.binding('username')); - browser.get('index.html#/form'); + await browser.get('index.html#/form'); - expect(name.getText()).toEqual('Anon'); + expect(await name.getText()).toEqual('Anon'); - usernameInput.clear(); - usernameInput.sendKeys('Jane'); - expect(name.getText()).toEqual('Jane'); + await usernameInput.clear(); + await usernameInput.sendKeys('Jane'); + expect(await name.getText()).toEqual('Jane'); }); - it('should chain element actions', function() { - browser.get('index.html#/form'); + it('should chain element actions', async() => { + await browser.get('index.html#/form'); - var usernameInput = element(by.model('username')); - var name = element(by.binding('username')); + const usernameInput = element(by.model('username')); + const name = element(by.binding('username')); - expect(name.getText()).toEqual('Anon'); + expect(await name.getText()).toEqual('Anon'); - usernameInput.clear().sendKeys('Jane'); - expect(name.getText()).toEqual('Jane'); + await usernameInput.clear().sendKeys('Jane'); + expect(await name.getText()).toEqual('Jane'); }); it('chained call should wait to grab the WebElement until a method is called', - function() { + async() => { // These should throw no error before a page is loaded. - var reused = element(by.id('baz')). - element(by.binding('item.reusedBinding')); + const reused = element(by.id('baz')) + .element(by.binding('item.reusedBinding')); - browser.get('index.html#/conflict'); + await browser.get('index.html#/conflict'); - expect(reused.getText()).toEqual('Inner: inner'); - expect(reused.isPresent()).toBe(true); + expect(await reused.getText()).toEqual('Inner: inner'); + expect(await reused.isPresent()).toBe(true); }); it('should differentiate elements with the same binding by chaining', - function() { - browser.get('index.html#/conflict'); + async() => { + await browser.get('index.html#/conflict'); - var outerReused = element(by.binding('item.reusedBinding')); - var innerReused = - element(by.id('baz')).element(by.binding('item.reusedBinding')); + const outerReused = element(by.binding('item.reusedBinding')); + const innerReused = element(by.id('baz')) + .element(by.binding('item.reusedBinding')); - expect(outerReused.getText()).toEqual('Outer: outer'); - expect(innerReused.getText()).toEqual('Inner: inner'); - }); + expect(await outerReused.getText()).toEqual('Outer: outer'); + expect(await innerReused.getText()).toEqual('Inner: inner'); + }); - it('should chain deeper than 2', function() { + it('should chain deeper than 2', async() => { // These should throw no error before a page is loaded. - var reused = element(by.css('body')).element(by.id('baz')). - element(by.binding('item.reusedBinding')); + const reused = element(by.css('body')).element(by.id('baz')) + .element(by.binding('item.reusedBinding')); - browser.get('index.html#/conflict'); + await browser.get('index.html#/conflict'); - expect(reused.getText()).toEqual('Inner: inner'); + expect(await reused.getText()).toEqual('Inner: inner'); }); - it('should determine element presence properly with chaining', function() { - browser.get('index.html#/conflict'); - expect(element(by.id('baz')). - isElementPresent(by.binding('item.reusedBinding'))). - toBe(true); + it('should determine element presence properly with chaining', async() => { + await browser.get('index.html#/conflict'); + expect(await element(by.id('baz')) + .isElementPresent(by.binding('item.reusedBinding'))) + .toBe(true); - expect(element(by.id('baz')). - isElementPresent(by.binding('nopenopenope'))). - toBe(false); + expect(await element(by.id('baz')) + .isElementPresent(by.binding('nopenopenope'))) + .toBe(false); }); - it('should export an isPresent helper', function() { - browser.get('index.html#/form'); + it('should export an isPresent helper', async() => { + await browser.get('index.html#/form'); - expect(element(by.binding('greet')).isPresent()).toBe(true); - expect(element(by.binding('nopenopenope')).isPresent()).toBe(false); + expect(await element(by.binding('greet')).isPresent()).toBe(true); + expect(await element(by.binding('nopenopenope')).isPresent()).toBe(false); }); - it('should allow handling errors', function() { - browser.get('index.html#/form'); - $('.nopenopenope').getText().then(function(/* string */) { - // This should throw an error. Fail. + it('should allow handling errors', async() => { + await browser.get('index.html#/form'); + try { + await $('.nopenopenope').getText(); expect(true).toEqual(false); - }, function(/* error */) { + } catch (err) { expect(true).toEqual(true); - }); + } }); - it('should allow handling chained errors', function() { - browser.get('index.html#/form'); - $('.nopenopenope').$('furthernope').getText().then( - function(/* string */) { - // This should throw an error. Fail. - expect(true).toEqual(false); - }, function(/* error */) { - expect(true).toEqual(true); - }); + it('should allow handling chained errors', async() => { + await browser.get('index.html#/form'); + try { + await await $('.nopenopenope').$('furthernope').getText(); + expect(true).toEqual(false); + } catch (err) { + expect(true).toEqual(true); + } }); - it('isPresent() should be friendly with out of bounds error', function () { - browser.get('index.html#/form'); - var elementsNotPresent = element.all(by.id('notPresentElementID')); - expect(elementsNotPresent.first().isPresent()).toBe(false); - expect(elementsNotPresent.last().isPresent()).toBe(false); + it('isPresent() should be friendly with out of bounds error', async() => { + await browser.get('index.html#/form'); + const elementsNotPresent = element.all(by.id('notPresentElementID')); + expect(await elementsNotPresent.first().isPresent()).toBe(false); + expect(await elementsNotPresent.last().isPresent()).toBe(false); }); - it('isPresent() should not raise error on chained finders', function() { - browser.get('index.html#/form'); - var elmFinder = $('.nopenopenope').element(by.binding('greet')); + it('isPresent() should not raise error on chained finders', async() => { + await browser.get('index.html#/form'); + const elmFinder = $('.nopenopenope').element(by.binding('greet')); - expect(elmFinder.isPresent()).toBe(false); + expect(await elmFinder.isPresent()).toBe(false); }); - it('should export an allowAnimations helper', function() { - browser.get('index.html#/animation'); - var animationTop = element(by.id('animationTop')); - var toggledNode = element(by.id('toggledNode')); + it('should export an allowAnimations helper', async() => { + await browser.get('index.html#/animation'); + const animationTop = element(by.id('animationTop')); + const toggledNode = element(by.id('toggledNode')); - expect(animationTop.allowAnimations()).toBe(true); - animationTop.allowAnimations(false); - expect(animationTop.allowAnimations()).toBe(false); + expect(await animationTop.allowAnimations()).toBe(true); + await animationTop.allowAnimations(false); + expect(await animationTop.allowAnimations()).toBe(false); - expect(toggledNode.isPresent()).toBe(true); - element(by.id('checkbox')).click(); - expect(toggledNode.isPresent()).toBe(false); + expect(await toggledNode.isPresent()).toBe(true); + await element(by.id('checkbox')).click(); + expect(await toggledNode.isPresent()).toBe(false); }); - it('should keep a reference to the original locator', function() { - browser.get('index.html#/form'); + it('should keep a reference to the original locator', async() => { + await browser.get('index.html#/form'); - var byCss = by.css('body'); - var byBinding = by.binding('greet'); + const byCss = by.css('body'); + const byBinding = by.binding('greet'); - expect(element(byCss).locator()).toEqual(byCss); - expect(element(byBinding).locator()).toEqual(byBinding); + expect(await element(byCss).locator()).toEqual(byCss); + expect(await element(byBinding).locator()).toEqual(byBinding); }); - it('should propagate exceptions', function() { - browser.get('index.html#/form'); + it('should propagate exceptions', async() => { + await browser.get('index.html#/form'); - var invalidElement = element(by.binding('INVALID')); - var successful = invalidElement.getText().then(function() { + const invalidElement = element(by.binding('INVALID')); + const successful = await invalidElement.getText().then(() => { return true; }, function() { return false; @@ -161,306 +162,306 @@ describe('ElementFinder', function() { expect(successful).toEqual(false); }); - it('should be returned from a helper without infinite loops', function() { - browser.get('index.html#/form'); - var helperPromise = protractor.promise.when(true).then(function() { + it('should be returned from a helper without infinite loops', async() => { + await browser.get('index.html#/form'); + const helperPromise = Promise.resolve(true).then(() => { return element(by.binding('greeting')); }); - helperPromise.then(function(finalResult) { - expect(finalResult.getText()).toEqual('Hiya'); + await helperPromise.then(async(finalResult) => { + expect(await finalResult.getText()).toEqual('Hiya'); }); }); - it('should be usable in WebDriver functions', function() { - browser.get('index.html#/form'); - var greeting = element(by.binding('greeting')); - browser.executeScript('arguments[0].scrollIntoView', greeting); + it('should be usable in WebDriver functions', async() => { + await browser.get('index.html#/form'); + const greeting = element(by.binding('greeting')); + await browser.executeScript('arguments[0].scrollIntoView', greeting); }); - it('should allow null as success handler', function() { - browser.get('index.html#/form'); + it('should allow null as success handler', async() => { + await browser.get('index.html#/form'); - var name = element(by.binding('username')); + const name = element(by.binding('username')); - expect(name.getText()).toEqual('Anon'); + expect(await name.getText()).toEqual('Anon'); expect( - name.getText().then(null, function() {}) + await name.getText().then(null, function() {}) ).toEqual('Anon'); }); - it('should check equality correctly', function() { - browser.get('index.html#/form'); + it('should check equality correctly', async() => { + await browser.get('index.html#/form'); - var usernameInput = element(by.model('username')); - var name = element(by.binding('username')); + const usernameInput = element(by.model('username')); + const name = element(by.binding('username')); - expect(usernameInput.equals(usernameInput)).toEqual(true); - expect(usernameInput.equals(name)).toEqual(false); + expect(await usernameInput.equals(usernameInput)).toEqual(true); + expect(await usernameInput.equals(name)).toEqual(false); }); }); -describe('ElementArrayFinder', function() { +describe('ElementArrayFinder', () => { - it('action should act on all elements', function() { - browser.get('index.html#/conflict'); + it('action should act on all elements', async() => { + await browser.get('index.html#/conflict'); - var multiElement = element.all(by.binding('item.reusedBinding')); - expect(multiElement.getText()).toEqual(['Outer: outer', 'Inner: inner']); + const multiElement = element.all(by.binding('item.reusedBinding')); + expect(await multiElement.getText()) + .toEqual(['Outer: outer', 'Inner: inner']); }); - it('click action should act on all elements', function() { - var checkboxesElms = $$('#checkboxes input'); - browser.get('index.html'); + it('click action should act on all elements', async() => { + const checkboxesElms = $$('#checkboxes input'); + await browser.get('index.html'); - expect(checkboxesElms.isSelected()).toEqual([true, false, false, false]); - checkboxesElms.click(); - expect(checkboxesElms.isSelected()).toEqual([false, true, true, true]); + expect(await checkboxesElms.isSelected()) + .toEqual([true, false, false, false]); + await checkboxesElms.click(); + expect(await checkboxesElms.isSelected()) + .toEqual([false, true, true, true]); }); - it('action should act on all elements selected by filter', function() { - browser.get('index.html'); + it('action should act on all elements selected by filter', async() => { + await browser.get('index.html'); - var multiElement = $$('#checkboxes input').filter(function(elem, index) { + const multiElement = $$('#checkboxes input').filter((_, index) => { return index == 2 || index == 3; }); - multiElement.click(); - expect($('#letterlist').getText()).toEqual('wx'); + await multiElement.click(); + expect(await $('#letterlist').getText()).toEqual('wx'); }); - it('filter should chain with index correctly', function() { - browser.get('index.html'); + it('filter should chain with index correctly', async() => { + await browser.get('index.html'); - var elem = $$('#checkboxes input').filter(function(elem, index) { + const elem = $$('#checkboxes input').filter((_, index) => { return index == 2 || index == 3; }).last(); - elem.click(); - expect($('#letterlist').getText()).toEqual('x'); + await elem.click(); + expect(await $('#letterlist').getText()).toEqual('x'); }); - it('filter should work in page object', function() { - var elements = element.all(by.css('#animals ul li')).filter(function(elem) { - return elem.getText().then(function(text) { - return text === 'big dog'; - }); + it('filter should work in page object', async() => { + const elements = element.all(by.css('#animals ul li')) + .filter(async(elem) => { + let text = await elem.getText(); + return text === 'big dog'; }); - browser.get('index.html#/form'); - expect(elements.count()).toEqual(1); + await browser.get('index.html#/form'); + expect(await elements.count()).toEqual(1); }); - it('should be able to get ElementFinder from filtered ElementArrayFinder', function() { - var isDog = function(elem) { - return elem.getText().then(function(text) { - return text.indexOf('dog') > -1; - }); + it('should be able to get ElementFinder from filtered ElementArrayFinder', + async() => { + const isDog = async(elem) => { + const text = await elem.getText(); + return text.indexOf('dog') > -1; }; - var elements = element.all(by.css('#animals ul li')).filter(isDog); + const elements = element.all(by.css('#animals ul li')).filter(isDog); - browser.get('index.html#/form'); - expect(elements.count()).toEqual(3); - expect(elements.get(2).getText()).toEqual('other dog'); + await browser.get('index.html#/form'); + expect(await elements.count()).toEqual(3); + expect(await elements.get(2).getText()).toEqual('other dog'); }); - it('filter should be compoundable', function() { - var isDog = function(elem) { - return elem.getText().then(function(text) { - return text.indexOf('dog') > -1; - }); + it('filter should be compoundable', async() => { + const isDog = async(elem) => { + const text = await elem.getText(); + return text.indexOf('dog') > -1; }; - var isBig = function(elem) { - return elem.getText().then(function(text) { - return text.indexOf('big') > -1; - }); + const isBig = async(elem) => { + const text = await elem.getText(); + return text.indexOf('big') > -1; }; - var elements = element.all(by.css('#animals ul li')).filter(isDog).filter(isBig); + const elements = element.all(by.css('#animals ul li')) + .filter(isDog).filter(isBig); - browser.get('index.html#/form'); - expect(elements.count()).toEqual(1); - elements.then(function(arr) { - expect(arr[0].getText()).toEqual('big dog'); - }); + await browser.get('index.html#/form'); + expect(await elements.count()).toEqual(1); + const arr = await elements; + expect(await arr[0].getText()).toEqual('big dog'); }); - it('filter should work with reduce', function() { - var isDog = function(elem) { - return elem.getText().then(function(text) { - return text.indexOf('dog') > -1; - }); + it('filter should work with reduce', async() => { + const isDog = async(elem) => { + const text = await elem.getText(); + return text.indexOf('dog') > -1; }; - browser.get('index.html#/form'); - var value = element.all(by.css('#animals ul li')).filter(isDog). - reduce(function(currentValue, elem, index, elemArr) { - return elem.getText().then(function(text) { - return currentValue + index + '/' + elemArr.length + ': ' + text + '\n'; - }); + await browser.get('index.html#/form'); + const value = element.all(by.css('#animals ul li')).filter(await isDog). + reduce(async(currentValue, elem, index, elemArr) => { + const text = await elem.getText(); + return currentValue + index + '/' + elemArr.length + ': ' + + text + '\n'; }, ''); - expect(value).toEqual('0/3: big dog\n' + - '1/3: small dog\n' + - '2/3: other dog\n'); + expect(await value).toEqual( + '0/3: big dog\n' + + '1/3: small dog\n' + + '2/3: other dog\n'); }); - it('should find multiple elements scoped properly with chaining', function() { - browser.get('index.html#/conflict'); + it('should find multiple elements scoped properly with chaining', async() => { + await browser.get('index.html#/conflict'); - element.all(by.binding('item')).then(function(elems) { - expect(elems.length).toEqual(4); - }); + let elems = await element.all(by.binding('item')); + expect(elems.length).toEqual(4); - element(by.id('baz')).all(by.binding('item')).then(function(elems) { - expect(elems.length).toEqual(2); - }); + elems = await element(by.id('baz')).all(by.binding('item')); + expect(elems.length).toEqual(2); }); - it('should wait to grab multiple chained elements', function() { + it('should wait to grab multiple chained elements', async() => { // These should throw no error before a page is loaded. - var reused = element(by.id('baz')).all(by.binding('item')); + const reused = element(by.id('baz')).all(by.binding('item')); - browser.get('index.html#/conflict'); + await browser.get('index.html#/conflict'); - expect(reused.count()).toEqual(2); - expect(reused.get(0).getText()).toEqual('Inner: inner'); - expect(reused.last().getText()).toEqual('Inner other: innerbarbaz'); + expect(await reused.count()).toEqual(2); + expect(await reused.get(0).getText()).toEqual('Inner: inner'); + expect(await reused.last().getText()).toEqual('Inner other: innerbarbaz'); }); - it('should wait to grab elements chained by index', function() { + it('should wait to grab elements chained by index', async() => { // These should throw no error before a page is loaded. - var reused = element(by.id('baz')).all(by.binding('item')); - var first = reused.first(); - var second = reused.get(1); - var last = reused.last(); + const reused = element(by.id('baz')).all(by.binding('item')); + const first = reused.first(); + const second = reused.get(1); + const last = reused.last(); - browser.get('index.html#/conflict'); + await browser.get('index.html#/conflict'); - expect(reused.count()).toEqual(2); - expect(first.getText()).toEqual('Inner: inner'); - expect(second.getText()).toEqual('Inner other: innerbarbaz'); - expect(last.getText()).toEqual('Inner other: innerbarbaz'); + expect(await reused.count()).toEqual(2); + expect(await first.getText()).toEqual('Inner: inner'); + expect(await second.getText()).toEqual('Inner other: innerbarbaz'); + expect(await last.getText()).toEqual('Inner other: innerbarbaz'); }); - it('should count all elements', function() { - browser.get('index.html#/form'); + it('should count all elements', async() => { + await browser.get('index.html#/form'); - element.all(by.model('color')).count().then(function(num) { + await element.all(by.model('color')).count().then((num) => { expect(num).toEqual(3); }); // Should also work with promise expect unwrapping - expect(element.all(by.model('color')).count()).toEqual(3); + expect(await element.all(by.model('color')).count()).toEqual(3); }); - it('should return 0 when counting no elements', function() { - browser.get('index.html#/form'); + it('should return 0 when counting no elements', async() => { + await browser.get('index.html#/form'); - expect(element.all(by.binding('doesnotexist')).count()).toEqual(0); + expect(await element.all(by.binding('doesnotexist')).count()).toEqual(0); }); - it('supports isPresent()', function() { - browser.get('index.html#/form'); + it('supports isPresent()', async() => { + await browser.get('index.html#/form'); - expect(element.all(by.model('color')).isPresent()).toBeTruthy(); - expect(element.all(by.binding('doesnotexist')).isPresent()).toBeFalsy(); + expect(await element.all(by.model('color')).isPresent()).toBeTruthy(); + expect(await element.all(by.binding('doesnotexist')).isPresent()) + .toBeFalsy(); }); it('should return not present when an element disappears within an array', - function() { - browser.get('index.html#/form'); - element.all(by.model('color')).then(function(elements) { - var disappearingElem = elements[0]; - expect(disappearingElem.isPresent()).toBeTruthy(); - browser.get('index.html#/bindings'); - expect(disappearingElem.isPresent()).toBeFalsy(); - }); + async() => { + await browser.get('index.html#/form'); + const elements = await element.all(by.model('color')) + const disappearingElem = elements[0]; + expect(await disappearingElem.isPresent()).toBeTruthy(); + await browser.get('index.html#/bindings'); + expect(await disappearingElem.isPresent()).toBeFalsy(); }); - it('should get an element from an array', function() { - var colorList = element.all(by.model('color')); + it('should get an element from an array', async () => { + const colorList = element.all(by.model('color')); - browser.get('index.html#/form'); + await browser.get('index.html#/form'); - expect(colorList.get(0).getAttribute('value')).toEqual('blue'); - expect(colorList.get(1).getAttribute('value')).toEqual('green'); - expect(colorList.get(2).getAttribute('value')).toEqual('red'); + expect(await colorList.get(0).getAttribute('value')).toEqual('blue'); + expect(await colorList.get(1).getAttribute('value')).toEqual('green'); + expect(await colorList.get(2).getAttribute('value')).toEqual('red'); }); - it('should get an element from an array by promise index', function() { - var colorList = element.all(by.model('color')); - var index = protractor.promise.fulfilled(1); + it('should get an element from an array by promise index', async() => { + const colorList = element.all(by.model('color')); + const index = Promise.resolve(1); - browser.get('index.html#/form'); + await browser.get('index.html#/form'); - expect(colorList.get(index).getAttribute('value')).toEqual('green'); + expect(await colorList.get(await index).getAttribute('value')).toEqual('green'); }); - it('should get an element from an array using negative indices', function() { - var colorList = element.all(by.model('color')); + it('should get an element from an array using negative indices', async() => { + const colorList = element.all(by.model('color')); - browser.get('index.html#/form'); + await browser.get('index.html#/form'); - expect(colorList.get(-3).getAttribute('value')).toEqual('blue'); - expect(colorList.get(-2).getAttribute('value')).toEqual('green'); - expect(colorList.get(-1).getAttribute('value')).toEqual('red'); + expect(await colorList.get(-3).getAttribute('value')).toEqual('blue'); + expect(await colorList.get(-2).getAttribute('value')).toEqual('green'); + expect(await colorList.get(-1).getAttribute('value')).toEqual('red'); }); - it('should get the first element from an array', function() { - var colorList = element.all(by.model('color')); - browser.get('index.html#/form'); + it('should get the first element from an array', async() => { + const colorList = element.all(by.model('color')); + await browser.get('index.html#/form'); - expect(colorList.first().getAttribute('value')).toEqual('blue'); + expect(await colorList.first().getAttribute('value')).toEqual('blue'); }); - it('should get the last element from an array', function() { - var colorList = element.all(by.model('color')); - browser.get('index.html#/form'); + it('should get the last element from an array', async() => { + const colorList = element.all(by.model('color')); + await browser.get('index.html#/form'); - expect(colorList.last().getAttribute('value')).toEqual('red'); + expect(await colorList.last().getAttribute('value')).toEqual('red'); }); - it('should perform an action on each element in an array', function() { - var colorList = element.all(by.model('color')); - browser.get('index.html#/form'); + it('should perform an action on each element in an array', async() => { + const colorList = element.all(by.model('color')); + await browser.get('index.html#/form'); - colorList.each(function(colorElement) { - expect(colorElement.getText()).not.toEqual('purple'); + await colorList.each(async(colorElement) => { + expect(await colorElement.getText()).not.toEqual('purple'); }); }); - it('should allow accessing subelements from within each', function() { - browser.get('index.html#/form'); - var rows = element.all(by.css('.rowlike')); + it('should allow accessing subelements from within each', async() => { + await browser.get('index.html#/form'); + const rows = element.all(by.css('.rowlike')); - rows.each(function(row) { - var input = row.element(by.css('.input')); - expect(input.getAttribute('value')).toEqual('10'); + await rows.each(async(row) => { + const input = row.element(by.css('.input')); + expect(await input.getAttribute('value')).toEqual('10'); }); - rows.each(function(row) { - var input = row.element(by.css('input')); - expect(input.getAttribute('value')).toEqual('10'); + await rows.each(async(row) => { + const input = row.element(by.css('input')); + expect(await input.getAttribute('value')).toEqual('10'); }); }); - it('should keep a reference to the array original locator', function() { - var byCss = by.css('#animals ul li'); - var byModel = by.model('color'); - browser.get('index.html#/form'); + it('should keep a reference to the array original locator', async() => { + const byCss = by.css('#animals ul li'); + const byModel = by.model('color'); + await browser.get('index.html#/form'); - expect(element.all(byCss).locator()).toEqual(byCss); - expect(element.all(byModel).locator()).toEqual(byModel); + expect(await element.all(byCss).locator()).toEqual(byCss); + expect(await element.all(byModel).locator()).toEqual(byModel); }); - it('should map each element on array and with promises', function() { - browser.get('index.html#/form'); - var labels = element.all(by.css('#animals ul li')).map(function(elm, index) { + it('should map each element on array and with promises', async() => { + await browser.get('index.html#/form'); + const labels = element.all(by.css('#animals ul li')) + .map(async(elm, index) => { return { index: index, - text: elm.getText() + text: await elm.getText() }; }); - expect(labels).toEqual([ + expect(await labels).toEqual([ {index: 0, text: 'big dog'}, {index: 1, text: 'small dog'}, {index: 2, text: 'other dog'}, @@ -469,23 +470,24 @@ describe('ElementArrayFinder', function() { ]); }); - it('should map and resolve multiple promises', function() { - browser.get('index.html#/form'); - var labels = element.all(by.css('#animals ul li')).map(function(elm) { + it('should map and resolve multiple promises', async() => { + await browser.get('index.html#/form'); + const labels = element.all(by.css('#animals ul li')) + .map(async (elm) => { return { - text: elm.getText(), - tagName: elm.getTagName() + text: await elm.getText(), + tagName: await elm.getTagName() }; }); - var newExpected = function(expectedLabel) { + const newExpected = (expectedLabel) => { return { text: expectedLabel, tagName: 'li' }; }; - expect(labels).toEqual([ + expect(await labels).toEqual([ newExpected('big dog'), newExpected('small dog'), newExpected('other dog'), @@ -494,101 +496,99 @@ describe('ElementArrayFinder', function() { ]); }); - it('should map each element from a literal and promise array', function() { - browser.get('index.html#/form'); - var i = 1; - var labels = element.all(by.css('#animals ul li')) - .map(function(/* element */) { + it('should map each element from a literal and promise array', async() => { + await browser.get('index.html#/form'); + let i = 1; + const labels = await element.all(by.css('#animals ul li')) + .map(() => { return i++; }); - expect(labels).toEqual([1, 2, 3, 4, 5]); }); - it('should filter elements', function() { - browser.get('index.html#/form'); - var count = element.all(by.css('#animals ul li')).filter(function(elem) { - return elem.getText().then(function(text) { - return text === 'big dog'; - }); - }).then(function(filteredElements) { - return filteredElements.length; - }); + it('should filter elements', async() => { + await browser.get('index.html#/form'); - expect(count).toEqual(1); + const filteredElements = await element.all(by.css('#animals ul li')) + .filter(async(elem) => { + const text = await elem.getText(); + return text === 'big dog'; + }); + const count = filteredElements.length; + expect(await count).toEqual(1); }); - it('should reduce elements', function() { - browser.get('index.html#/form'); - var value = element.all(by.css('#animals ul li')). - reduce(function(currentValue, elem, index, elemArr) { - return elem.getText().then(function(text) { - return currentValue + index + '/' + elemArr.length + ': ' + text + '\n'; - }); + it('should reduce elements', async () => { + await browser.get('index.html#/form'); + const value = element.all(by.css('#animals ul li')). + reduce(async(currentValue, elem, index, elemArr) => { + const text = await elem.getText(); + return currentValue + index + '/' + elemArr.length + ': ' + + text + '\n'; }, ''); - expect(value).toEqual('0/5: big dog\n' + - '1/5: small dog\n' + - '2/5: other dog\n' + - '3/5: big cat\n' + - '4/5: small cat\n'); + expect(await value).toEqual( + '0/5: big dog\n' + + '1/5: small dog\n' + + '2/5: other dog\n' + + '3/5: big cat\n' + + '4/5: small cat\n'); }); - it('should allow using protractor locator within map', function() { - browser.get('index.html#/repeater'); + it('should allow using protractor locator within map', async() => { + await browser.get('index.html#/repeater'); - var expected = [ + const expected = [ { first: 'M', second: 'Monday' }, { first: 'T', second: 'Tuesday' }, { first: 'W', second: 'Wednesday' }, { first: 'Th', second: 'Thursday' }, { first: 'F', second: 'Friday' }]; - var result = element.all(by.repeater('allinfo in days')).map(function(el) { + const result = element.all(by.repeater('allinfo in days')) + .map(async(el) => { return { - first: el.element(by.binding('allinfo.initial')).getText(), - second: el.element(by.binding('allinfo.name')).getText() + first: await el.element(by.binding('allinfo.initial')).getText(), + second: await el.element(by.binding('allinfo.name')).getText() }; }); - expect(result).toEqual(expected); + expect(await result).toEqual(expected); }); }); -describe('evaluating statements', function() { - beforeEach(function() { - browser.get('index.html#/form'); +describe('evaluating statements', () => { + beforeEach(async() => { + await browser.get('index.html#/form'); }); - it('should evaluate statements in the context of an element', function() { - var checkboxElem = element(by.id('checkboxes')); + it('should evaluate statements in the context of an element', async() => { + const checkboxElem = element(by.id('checkboxes')); - checkboxElem.evaluate('show').then(function(output) { - expect(output).toBe(true); - }); + const output = await checkboxElem.evaluate('show'); + expect(output).toBe(true); // Make sure it works with a promise expectation. - expect(checkboxElem.evaluate('show')).toBe(true); + expect(await checkboxElem.evaluate('show')).toBe(true); }); }); -describe('shortcut css notation', function() { - beforeEach(function() { - browser.get('index.html#/bindings'); +describe('shortcut css notation', () => { + beforeEach(async() => { + await browser.get('index.html#/bindings'); }); - it('should grab by css', function() { - expect($('.planet-info').getText()). - toEqual(element(by.css('.planet-info')).getText()); - expect($$('option').count()).toEqual(element.all(by.css('option')).count()); + it('should grab by css', async() => { + expect(await $('.planet-info').getText()) + .toEqual(await element(by.css('.planet-info')).getText()); + expect(await $$('option').count()) + .toEqual(await element.all(by.css('option')).count()); }); - it('should chain $$ with $', function() { - var withoutShortcutCount = - element(by.css('select')).all(by.css('option')).then(function(options) { - return options.length; - }); - var withShortcutCount = $('select').$$('option').count(); + it('should chain $$ with $', async() => { + const options = await element(by.css('select')).all(by.css('option')); + const withoutShortcutCount = options.length; + const withShortcutCount = await $('select').$$('option').count(); expect(withoutShortcutCount).toEqual(withShortcutCount); }); diff --git a/spec/basic/expected_conditions_spec.js b/spec/basic/expected_conditions_spec.js index e91155070..365d81a76 100644 --- a/spec/basic/expected_conditions_spec.js +++ b/spec/basic/expected_conditions_spec.js @@ -1,255 +1,251 @@ -var EC = protractor.ExpectedConditions; +describe('expected conditions', () => { + let EC = null; -describe('expected conditions', function() { - beforeEach(function() { - browser.get('index.html#/form'); + beforeEach(async () => { + await browser.get('index.html#/form'); + EC = protractor.ExpectedConditions; }); - it('should have alertIsPresent', function() { - var alertIsPresent = EC.alertIsPresent(); - expect(alertIsPresent.call()).toBe(false); + it('should have alertIsPresent', async () => { + const alertIsPresent = EC.alertIsPresent(); + expect(await alertIsPresent.call()).toBe(false); - var alertButton = $('#alertbutton'); - alertButton.click(); - browser.wait(protractor.ExpectedConditions.alertIsPresent(), 5000); - browser.switchTo().alert().accept(); + const alertButton = $('#alertbutton'); + await alertButton.click(); + await browser.wait(protractor.ExpectedConditions.alertIsPresent(), 5000); + await browser.switchTo().alert().accept(); }); - it('should have presenceOf', function() { - var presenceOfInvalid = EC.presenceOf($('#INVALID')); - var presenceOfHideable = EC.presenceOf($('#shower')); + it('should have presenceOf', async () => { + const presenceOfInvalid = EC.presenceOf($('#INVALID')); + const presenceOfHideable = EC.presenceOf($('#shower')); - expect(presenceOfInvalid.call()).toBe(false); - expect(presenceOfHideable.call()).toBe(true); - element(by.model('show')).click(); - expect(presenceOfHideable.call()).toBe(true); // Should be able to reuse. + expect(await presenceOfInvalid.call()).toBe(false); + expect(await presenceOfHideable.call()).toBe(true); + await element(by.model('show')).click(); + expect(await presenceOfHideable.call()).toBe(true); // Should be able to reuse. }); - it('should have stalenessOf', function() { - var stalenessOfInvalid = EC.stalenessOf($('#INVALID')); - var stalenessOfHideable = EC.stalenessOf($('#shower')); + it('should have stalenessOf', async () => { + const stalenessOfInvalid = EC.stalenessOf($('#INVALID')); + const stalenessOfHideable = EC.stalenessOf($('#shower')); - expect(stalenessOfInvalid.call()).toBe(true); - expect(stalenessOfHideable.call()).toBe(false); - element(by.model('show')).click(); - expect(stalenessOfHideable.call()).toBe(false); + expect(await stalenessOfInvalid.call()).toBe(true); + expect(await stalenessOfHideable.call()).toBe(false); + await element(by.model('show')).click(); + expect(await stalenessOfHideable.call()).toBe(false); }); - it('should have visibilityOf', function() { - var visibilityOfInvalid = EC.visibilityOf($('#INVALID')); - var visibilityOfHideable = EC.visibilityOf($('#shower')); + it('should have visibilityOf', async () => { + const visibilityOfInvalid = EC.visibilityOf($('#INVALID')); + const visibilityOfHideable = EC.visibilityOf($('#shower')); - expect(visibilityOfInvalid.call()).toBe(false); - expect(visibilityOfHideable.call()).toBe(true); - element(by.model('show')).click(); - expect(visibilityOfHideable.call()).toBe(false); + expect(await visibilityOfInvalid.call()).toBe(false); + expect(await visibilityOfHideable.call()).toBe(true); + await element(by.model('show')).click(); + expect(await visibilityOfHideable.call()).toBe(false); }); - it('should have invisibilityOf', function() { - var invisibilityOfInvalid = EC.invisibilityOf($('#INVALID')); - var invisibilityOfHideable = EC.invisibilityOf($('#shower')); + it('should have invisibilityOf', async () => { + const invisibilityOfInvalid = EC.invisibilityOf($('#INVALID')); + const invisibilityOfHideable = EC.invisibilityOf($('#shower')); - expect(invisibilityOfInvalid.call()).toBe(true); - expect(invisibilityOfHideable.call()).toBe(false); - element(by.model('show')).click(); - expect(invisibilityOfHideable.call()).toBe(true); + expect(await invisibilityOfInvalid.call()).toBe(true); + expect(await invisibilityOfHideable.call()).toBe(false); + await element(by.model('show')).click(); + expect(await invisibilityOfHideable.call()).toBe(true); }); - it('should have titleContains', function() { - expect(EC.titleContains('My Angular').call()).toBe(true); - expect(EC.titleContains('My AngularJS App').call()).toBe(true); + it('should have titleContains', async () => { + expect(await EC.titleContains('My Angular').call()).toBe(true); + expect(await EC.titleContains('My AngularJS App').call()).toBe(true); }); - it('should have titleIs', function() { - expect(EC.titleIs('My Angular').call()).toBe(false); - expect(EC.titleIs('My AngularJS App').call()).toBe(true); + it('should have titleIs', async () => { + expect(await EC.titleIs('My Angular').call()).toBe(false); + expect(await EC.titleIs('My AngularJS App').call()).toBe(true); }); - it('should have urlContains', function() { - var baseUrlFromSpec = browser.baseUrl; - expect(EC.urlContains('/form').call()).toBe(true); - expect(EC.urlContains(baseUrlFromSpec+ 'index.html#/form').call()).toBe(true); + it('should have urlContains', async () => { + const baseUrlFromSpec = browser.baseUrl; + expect(await EC.urlContains('/form').call()).toBe(true); + expect(await EC.urlContains(baseUrlFromSpec+ 'index.html#/form').call()).toBe(true); }); - it('should have urlIs', function() { - var baseUrlFromSpec = browser.baseUrl; - expect(EC.urlIs(browser.baseUrl).call()).toBe(false); - expect(EC.urlIs(baseUrlFromSpec+'index.html#/form').call()).toBe(true); + it('should have urlIs', async () => { + const baseUrlFromSpec = browser.baseUrl; + expect(await EC.urlIs(browser.baseUrl).call()).toBe(false); + expect(await EC.urlIs(baseUrlFromSpec+'index.html#/form').call()).toBe(true); }); - it('should have elementToBeClickable', function() { - var invalidIsClickable = EC.elementToBeClickable($('#INVALID')); - var buttonIsClickable = EC.elementToBeClickable($('#disabledButton')); + it('should have elementToBeClickable', async () => { + const invalidIsClickable = EC.elementToBeClickable($('#INVALID')); + const buttonIsClickable = EC.elementToBeClickable($('#disabledButton')); - expect(invalidIsClickable.call()).toBe(false); - expect(buttonIsClickable.call()).toBe(true); - element(by.model('disabled')).click(); - expect(buttonIsClickable.call()).toBe(false); + expect(await invalidIsClickable.call()).toBe(false); + expect(await buttonIsClickable.call()).toBe(true); + await element(by.model('disabled')).click(); + expect(await buttonIsClickable.call()).toBe(false); }); - it('should have textToBePresentInElement', function() { - var invalidHasText = EC.textToBePresentInElement($('#INVALID'), 'shouldnt throw'); - var hideableHasText = EC.textToBePresentInElement($('#shower'), 'Shown'); + it('should have textToBePresentInElement', async () => { + const invalidHasText = EC.textToBePresentInElement($('#INVALID'), 'shouldnt throw'); + const hideableHasText = EC.textToBePresentInElement($('#shower'), 'Shown'); - expect(invalidHasText.call()).toBe(false); - expect(hideableHasText.call()).toBe(true); - element(by.model('show')).click(); - expect(hideableHasText.call()).toBe(false); + expect(await invalidHasText.call()).toBe(false); + expect(await hideableHasText.call()).toBe(true); + await element(by.model('show')).click(); + expect(await hideableHasText.call()).toBe(false); }); - it('should have textToBePresentInElementValue', function() { - var invalid = $('#INVALID'); - var about = element(by.model('aboutbox')); + it('should have textToBePresentInElementValue', async () => { + const invalid = $('#INVALID'); + const about = element(by.model('aboutbox')); - expect(EC.textToBePresentInElementValue(invalid, 'shouldnt throw').call()).toBe(false); - expect(EC.textToBePresentInElementValue(about, 'text box').call()).toBe(true); + expect(await EC.textToBePresentInElementValue(invalid, 'shouldnt throw').call()).toBe(false); + expect(await EC.textToBePresentInElementValue(about, 'text box').call()).toBe(true); }); - it('should have elementToBeSelected', function() { - var checkbox = element(by.model('show')); + it('should have elementToBeSelected', async () => { + const checkbox = element(by.model('show')); - expect(EC.elementToBeSelected(checkbox).call()).toBe(true); - checkbox.click(); - expect(EC.elementToBeSelected(checkbox).call()).toBe(false); + expect(await EC.elementToBeSelected(checkbox).call()).toBe(true); + await checkbox.click(); + expect(await EC.elementToBeSelected(checkbox).call()).toBe(false); }); - it('should have not', function() { - var presenceOfValidElement = EC.presenceOf($('#shower')); - expect(presenceOfValidElement.call()).toBe(true); - expect(EC.not(presenceOfValidElement).call()).toBe(false); + it('should have not', async () => { + const presenceOfValidElement = EC.presenceOf($('#shower')); + expect(await presenceOfValidElement.call()).toBe(true); + expect(await EC.not(presenceOfValidElement).call()).toBe(false); }); - it('should have and', function() { - var presenceOfValidElement = EC.presenceOf($('#shower')); - var presenceOfInvalidElement = EC.presenceOf($('#INVALID')); - var validityOfTitle = EC.titleIs('My AngularJS App'); + it('should have and', async () => { + const presenceOfValidElement = EC.presenceOf($('#shower')); + const presenceOfInvalidElement = EC.presenceOf($('#INVALID')); + const validityOfTitle = EC.titleIs('My AngularJS App'); - expect(EC.and(presenceOfValidElement, validityOfTitle).call()).toBe(true); + expect(await EC.and(presenceOfValidElement, validityOfTitle).call()).toBe(true); // support multiple conditions - expect(EC.and(presenceOfValidElement, - validityOfTitle, presenceOfInvalidElement).call()).toBe(false); + expect(await EC.and(presenceOfValidElement, validityOfTitle, presenceOfInvalidElement).call()).toBe(false); }); - it('and should shortcircuit', function() { - var invalidElem = $('#INVALID'); + it('and should shortcircuit', async () => { + const invalidElem = $('#INVALID'); - var presenceOfInvalidElement = EC.presenceOf(invalidElem); - var isDisplayed = invalidElem.isDisplayed.bind(invalidElem); + const presenceOfInvalidElement = EC.presenceOf(invalidElem); + const isDisplayed = invalidElem.isDisplayed.bind(invalidElem); // check isDisplayed on invalid element - var condition = EC.and(presenceOfInvalidElement, isDisplayed); + const condition = EC.and(presenceOfInvalidElement, isDisplayed); // Should short circuit after the first condition is false, and not throw error - expect(condition.call()).toBe(false); + expect(await condition.call()).toBe(false); }); - it('should have or', function() { - var presenceOfValidElement = EC.presenceOf($('#shower')); - var presenceOfInvalidElement = EC.presenceOf($('#INVALID')); - var presenceOfInvalidElement2 = EC.presenceOf($('#INVALID2')); + it('should have or', async () => { + const presenceOfValidElement = EC.presenceOf($('#shower')); + const presenceOfInvalidElement = EC.presenceOf($('#INVALID')); + const presenceOfInvalidElement2 = EC.presenceOf($('#INVALID2')); - expect(EC.or(presenceOfInvalidElement, presenceOfInvalidElement2).call()).toBe(false); + expect(await EC.or(presenceOfInvalidElement, presenceOfInvalidElement2).call()).toBe(false); // support multiple conditions - expect(EC.or(presenceOfInvalidElement, - presenceOfInvalidElement2, presenceOfValidElement).call()).toBe(true); + expect(await EC.or(presenceOfInvalidElement, presenceOfInvalidElement2, presenceOfValidElement).call()).toBe(true); }); - it('or should shortcircuit', function() { - var validElem = $('#shower'); - var invalidElem = $('#INVALID'); + it('or should shortcircuit', async () => { + const validElem = $('#shower'); + const invalidElem = $('#INVALID'); - var presenceOfValidElement = EC.presenceOf(validElem); - var isDisplayed = invalidElem.isDisplayed.bind(invalidElem); + const presenceOfValidElement = EC.presenceOf(validElem); + const isDisplayed = invalidElem.isDisplayed.bind(invalidElem); // check isDisplayed on invalid element - var condition = EC.or(presenceOfValidElement, isDisplayed); + const condition = EC.or(presenceOfValidElement, isDisplayed); // Should short circuit after the first condition is true, and not throw error - expect(condition.call()).toBe(true); + expect(await condition.call()).toBe(true); }); - it('should be able to mix conditions', function() { - var valid = EC.presenceOf($('#shower')); - var invalid = EC.presenceOf($('#INVALID')); + it('should be able to mix conditions', async () => { + const valid = EC.presenceOf($('#shower')); + const invalid = EC.presenceOf($('#INVALID')); - expect(EC.or(valid, EC.and(valid, invalid)).call()).toBe(true); - expect(EC.or(EC.not(valid), EC.and(valid, invalid)).call()).toBe(false); + expect(await EC.or(valid, await EC.and(valid, invalid)).call()).toBe(true); + expect(await EC.or(EC.not(valid), EC.and(valid, invalid)).call()).toBe(false); }); - describe('for forked browsers', function() { + describe('for forked browsers', () => { // ensure that we can run EC on forked browser instances - it('should have alertIsPresent', function() { - var browser2 = browser.forkNewDriverInstance(); - browser2.get('index.html#/form'); - var EC2 = browser2.ExpectedConditions; - var alertIsPresent = EC2.alertIsPresent(); - expect(alertIsPresent.call()).toBe(false); - - var alertButton = browser2.$('#alertbutton'); - alertButton.click(); - browser2.wait(EC2.alertIsPresent(), 1000); - - // TODO: Remove sleep when this is fixed: - // https://bugs.chromium.org/p/chromedriver/issues/detail?id=1500 - browser2.sleep(250); - browser2.switchTo().alert().accept(); + it('should have alertIsPresent', async () => { + const browser2 = await browser.forkNewDriverInstance(); + await browser2.get('index.html#/form'); + const EC2 = browser2.ExpectedConditions; + const alertIsPresent = EC2.alertIsPresent(); + expect(await alertIsPresent.call()).toBe(false); + + const alertButton = browser2.$('#alertbutton'); + await alertButton.click(); + await browser2.wait(EC2.alertIsPresent(), 1000); + + await browser2.switchTo().alert().accept(); }); }); - describe('race condition handling', function () { + describe('race condition handling', () => { - var disabledButton; + let disabledButton; - beforeEach(function () { + beforeEach(() => { disabledButton = $('#disabledButton[disabled="disabled"]'); }); - function enableButtonBeforeCallToUnmatchSelector(testElement, fnName) { - var originalFn = testElement[fnName]; + const enableButtonBeforeCallToUnmatchSelector = async (testElement, fnName) => { + const originalFn = testElement[fnName]; - testElement[fnName] = function () { - element(by.model('disabled')).click(); + testElement[fnName] = async () => { + await element(by.model('disabled')).click(); return originalFn.apply(this, arguments); }; // save original fn with _ prefix - testElement['_' + fnName] = originalFn; - } + testElement[`_${fnName}`] = originalFn; + }; - it('can deal with missing elements in visibilityOf', function() { - enableButtonBeforeCallToUnmatchSelector(disabledButton, 'isDisplayed'); + it('can deal with missing elements in visibilityOf', async () => { + await enableButtonBeforeCallToUnmatchSelector(disabledButton, 'isDisplayed'); - element(by.model('disabled')).click(); + await element(by.model('disabled')).click(); - expect(disabledButton._isDisplayed()).toBe(true); - expect(EC.visibilityOf(disabledButton).call()).toBe(false); + expect(await disabledButton._isDisplayed()).toBe(true); + expect(await EC.visibilityOf(disabledButton).call()).toBe(false); }); - it('can deal with missing elements in textToBePresentInElement', function() { - enableButtonBeforeCallToUnmatchSelector(disabledButton, 'getText'); + it('can deal with missing elements in textToBePresentInElement', async () => { + await enableButtonBeforeCallToUnmatchSelector(disabledButton, 'getText'); - element(by.model('disabled')).click(); + await element(by.model('disabled')).click(); - expect(disabledButton._getText()).toBe('Dummy'); - expect(EC.textToBePresentInElement(disabledButton, 'Dummy').call()).toBe(false); + expect(await disabledButton._getText()).toBe('Dummy'); + expect(await EC.textToBePresentInElement(disabledButton, 'Dummy').call()).toBe(false); }); - it('can deal with missing elements in textToBePresentInValue', function() { - enableButtonBeforeCallToUnmatchSelector(disabledButton, 'getAttribute'); + it('can deal with missing elements in textToBePresentInValue', async () => { + await enableButtonBeforeCallToUnmatchSelector(disabledButton, 'getAttribute'); - element(by.model('disabled')).click(); + await element(by.model('disabled')).click(); - expect(disabledButton._getAttribute('value')).toBe(''); - expect(EC.textToBePresentInElementValue(disabledButton, '').call()).toBe(false); + expect(await disabledButton._getAttribute('value')).toBe(''); + expect(await EC.textToBePresentInElementValue(disabledButton, '').call()).toBe(false); }); - it('can deal with missing elements in elementToBeClickable', function() { - enableButtonBeforeCallToUnmatchSelector(disabledButton, 'isEnabled'); + it('can deal with missing elements in elementToBeClickable', async () => { + await enableButtonBeforeCallToUnmatchSelector(disabledButton, 'isEnabled'); - element(by.model('disabled')).click(); + await element(by.model('disabled')).click(); - expect(disabledButton._isEnabled()).toBe(false); - expect(EC.elementToBeClickable(disabledButton).call()).toBe(false); + expect(await disabledButton._isEnabled()).toBe(false); + expect(await EC.elementToBeClickable(disabledButton).call()).toBe(false); }); }); }); diff --git a/spec/basic/handling_spec.js b/spec/basic/handling_spec.js index 60c359138..d2d0e8340 100644 --- a/spec/basic/handling_spec.js +++ b/spec/basic/handling_spec.js @@ -1,11 +1,10 @@ - -describe('handling timeout errors', function() { - - it('should call error handler on a timeout', function() { - browser.get('http://dummyUrl', 1).then(function() { +describe('handling timeout errors', () => { + it('should call error handler on a timeout', async () => { + try { + await browser.get('http://dummyUrl', 1); throw 'did not handle error'; - }, function(err) { + } catch (err) { expect(err instanceof Error).toBeTruthy(); - }); + } }); }); diff --git a/spec/basic/lib_spec.js b/spec/basic/lib_spec.js index 219e9b985..7af2df494 100644 --- a/spec/basic/lib_spec.js +++ b/spec/basic/lib_spec.js @@ -1,11 +1,11 @@ -describe('no protractor at all', function() { - it('should still do normal tests', function() { +describe('no protractor at all', () => { + it('should still do normal tests', () => { expect(true).toBe(true); }); }); -describe('protractor library', function() { - it('should expose the correct global variables', function() { +describe('protractor library', () => { + it('should expose the correct global variables', () => { expect(protractor).toBeDefined(); expect(browser).toBeDefined(); expect(by).toBeDefined(); @@ -13,45 +13,40 @@ describe('protractor library', function() { expect(element).toBeDefined(); expect($).toBeDefined(); expect(DartObject).toBeDefined(); - var obj = {}; - var dartProxy = new DartObject(obj); + const obj = {}; + const dartProxy = new DartObject(obj); expect(dartProxy.o === obj).toBe(true); }); it('should export other webdriver classes onto the global protractor', - function() { - expect(protractor.ActionSequence).toBeDefined(); + () => { + // TODO(selenium4): Actions API missing from typings. + // expect(protractor.Actions).toBeDefined(); expect(protractor.Key.RETURN).toEqual('\uE006'); }); - it('should export custom parameters to the protractor instance', function() { + it('should export custom parameters to the protractor instance', () => { expect(browser.params.login).toBeDefined(); expect(browser.params.login.user).toEqual('Jane'); expect(browser.params.login.password).toEqual('1234'); }); it('should allow a mix of using protractor and using the driver directly', - function() { - browser.get('index.html'); - expect(browser.getCurrentUrl()).toMatch('#/form'); + async() => { + await browser.get('index.html'); + expect(await browser.getCurrentUrl()).toMatch('#/form'); - browser.driver.findElement(protractor.By.linkText('repeater')).click(); - expect(browser.driver.getCurrentUrl()).toMatch('#/repeater'); + await browser.driver.findElement(protractor.By.linkText('repeater')).click(); + expect(await browser.driver.getCurrentUrl()).toMatch('#/repeater'); - browser.navigate().back(); - expect(browser.driver.getCurrentUrl()).toMatch('#/form'); - }); - - it('should unwrap WebElements', function() { - browser.get('index.html'); - var ptorEl = element(by.binding('greet')); - browser.executeScript('', ptorEl); // Will crash if element isn't unwrapped + await browser.navigate().back(); + expect(await browser.driver.getCurrentUrl()).toMatch('#/form'); }); - it('should have access to the processed config block', function() { - function containsMatching(arr, string) { - var contains = false; - for (var i = 0; i < arr.length; ++i) { + it('should have access to the processed config block', async() => { + let containsMatching = (arr, string) => { + let contains = false; + for (let i = 0; i < arr.length; ++i) { if (arr[i].indexOf(string) !== -1) { contains = true; } @@ -59,20 +54,19 @@ describe('protractor library', function() { return contains; } - browser.getProcessedConfig().then(function(config) { - expect(config.params.login).toBeDefined(); - expect(config.params.login.user).toEqual('Jane'); - expect(config.params.login.password).toEqual('1234'); - expect(containsMatching(config.specs, 'lib_spec.js')).toBe(true); - expect(config.capabilities).toBeDefined(); - }); + const config = await browser.getProcessedConfig(); + expect(config.params.login).toBeDefined(); + expect(config.params.login.user).toEqual('Jane'); + expect(config.params.login.password).toEqual('1234'); + expect(containsMatching(config.specs, 'lib_spec.js')).toBe(true); + expect(config.capabilities).toBeDefined(); }); - it('should allow adding custom locators', function() { - var findMenuItem = function() { - var itemName = arguments[0]; - var menu = document.querySelectorAll('.menu li'); - for (var i = 0; i < menu.length; ++i) { + it('should allow adding custom locators', async() => { + let findMenuItem = () => { + const itemName = arguments[0]; + const menu = document.querySelectorAll('.menu li'); + for (const i = 0; i < menu.length; ++i) { if (menu[i].textContent == itemName) { return [menu[i]]; } @@ -83,17 +77,17 @@ describe('protractor library', function() { expect(by.menuItem).toBeDefined(); - browser.get('index.html'); - expect(element(by.menuItem('repeater')).isPresent()); - expect(element(by.menuItem('repeater')).getText()).toEqual('repeater'); + await browser.get('index.html'); + expect(await element(by.menuItem('repeater')).isPresent()); + expect(await element(by.menuItem('repeater')).getText()).toEqual('repeater'); }); - it('should allow adding custom varargs locators', function() { - var findMenuItemWithName = function() { - var css = arguments[0]; - var itemName = arguments[1]; - var menu = document.querySelectorAll(css); - for (var i = 0; i < menu.length; ++i) { + it('should allow adding custom varargs locators', async() => { + let findMenuItemWithName = function() { + const css = arguments[0]; + const itemName = arguments[1]; + const menu = document.querySelectorAll(css); + for (const i = 0; i < menu.length; ++i) { if (menu[i].textContent == itemName) { return [menu[i]]; } @@ -104,30 +98,27 @@ describe('protractor library', function() { expect(by.menuItemWithName).toBeDefined(); - browser.get('index.html'); - expect(element(by.menuItemWithName('.menu li', 'repeater')).isPresent()); - expect(element(by.menuItemWithName('.menu li', 'repeater')).getText()). - toEqual('repeater'); + await browser.get('index.html'); + expect(await element(by.menuItemWithName('.menu li', 'repeater')).isPresent()); + expect(await element(by.menuItemWithName('.menu li', 'repeater')).getText()) + .toEqual('repeater'); }); - describe('helper functions', function() { - it('should get the absolute URL', function() { - browser.get('index.html'); - expect(browser.getLocationAbsUrl()). - toMatch('/form'); + describe('helper functions', () => { + it('should get the absolute URL', async() => { + await browser.get('index.html'); + expect(await browser.getLocationAbsUrl()).toMatch('/form'); - element(by.linkText('repeater')).click(); - expect(browser.getLocationAbsUrl()). - toMatch('/repeater'); + await element(by.linkText('repeater')).click(); + expect(await browser.getLocationAbsUrl()).toMatch('/repeater'); }); - it('should navigate to another url with setLocation', function() { - browser.get('index.html'); + it('should navigate to another url with setLocation', async() => { + await browser.get('index.html'); - browser.setLocation('/repeater'); + await browser.setLocation('/repeater'); - expect(browser.getLocationAbsUrl()). - toMatch('/repeater'); + expect(await browser.getLocationAbsUrl()).toMatch('/repeater'); }); }); }); diff --git a/spec/basic/locators_spec.js b/spec/basic/locators_spec.js index 0f370b00b..ba51cac86 100644 --- a/spec/basic/locators_spec.js +++ b/spec/basic/locators_spec.js @@ -1,427 +1,382 @@ -describe('locators', function() { - beforeEach(function() { - browser.get('index.html#/form'); +describe('locators', () => { + beforeEach(async() => { + await browser.get('index.html#/form'); }); - describe('by binding', function() { - it('should find an element by binding', function() { - var greeting = element(by.binding('greeting')); - - expect(greeting.getText()).toEqual('Hiya'); - }); - - it('should allow custom expectations to expect an element', function() { - jasmine.addMatchers({ - toHaveText: function() { - return { - compare: function(actual, expected) { - return { - pass: actual.getText().then(function(actual) { - return actual === expected; - }) - }; - } - }; - } - }); + describe('by binding', () => { + it('should find an element by binding', async() => { + const greeting = element(by.binding('greeting')); - expect(element(by.binding('greeting'))).toHaveText('Hiya'); + expect(await greeting.getText()).toEqual('Hiya'); }); - it('should find a binding by partial match', function() { - var greeting = element(by.binding('greet')); + it('should find a binding by partial match', async() => { + const greeting = element(by.binding('greet')); - expect(greeting.getText()).toEqual('Hiya'); + expect(await greeting.getText()).toEqual('Hiya'); }); - it('should find exact match by exactBinding', function() { - var greeting = element(by.exactBinding('greeting')); + it('should find exact match by exactBinding', async() => { + const greeting = element(by.exactBinding('greeting')); - expect(greeting.getText()).toEqual('Hiya'); + expect(await greeting.getText()).toEqual('Hiya'); }); - it('should not find partial match by exactBinding', function() { - var greeting = element(by.exactBinding('greet')); + it('should not find partial match by exactBinding', async() => { + const greeting = element(by.exactBinding('greet')); - expect(greeting.isPresent()).toBe(false); + expect(await greeting.isPresent()).toBe(false); }); it('should find an element by binding with ng-bind attribute', - function() { - var name = element(by.binding('username')); + async() => { + const name = element(by.binding('username')); - expect(name.getText()).toEqual('Anon'); + expect(await name.getText()).toEqual('Anon'); }); it('should find an element by binding with ng-bind-template attribute', - function() { - var name = element(by.binding('nickname|uppercase')); + async() => { + const name = element(by.binding('nickname|uppercase')); - expect(name.getText()).toEqual('(ANNIE)'); + expect(await name.getText()).toEqual('(ANNIE)'); }); }); - describe('by model', function() { - it('should find an element by text input model', function() { - var username = element(by.model('username')); - var name = element(by.binding('username')); + describe('by model', () => { + it('should find an element by text input model', async() => { + const username = element(by.model('username')); + const name = element(by.binding('username')); - username.clear(); - expect(name.getText()).toEqual(''); + await username.clear(); + expect(await name.getText()).toEqual(''); - username.sendKeys('Jane Doe'); - expect(name.getText()).toEqual('Jane Doe'); + await username.sendKeys('Jane Doe'); + expect(await name.getText()).toEqual('Jane Doe'); }); - it('should find an element by checkbox input model', function() { - expect(element(by.id('shower')).isDisplayed()). - toBe(true); + it('should find an element by checkbox input model', async() => { + expect(await element(by.id('shower')).isDisplayed()).toBe(true); - element(by.model('show')).click(); // colors + await element(by.model('show')).click(); // colors - expect(element(by.id('shower')).isDisplayed()). - toBe(false); + expect(await element(by.id('shower')).isDisplayed()).toBe(false); }); - it('should find a textarea by model', function() { - var about = element(by.model('aboutbox')); - expect(about.getAttribute('value')).toEqual('This is a text box'); + it('should find a textarea by model', async() => { + const about = element(by.model('aboutbox')); + expect(await about.getAttribute('value')).toEqual('This is a text box'); - about.clear(); - about.sendKeys('Something else to write about'); + await about.clear(); + await about.sendKeys('Something else to write about'); - expect(about.getAttribute('value')). - toEqual('Something else to write about'); + expect(await about.getAttribute('value')) + .toEqual('Something else to write about'); }); - it('should find multiple selects by model', function() { - var selects = element.all(by.model('dayColor.color')); - expect(selects.count()).toEqual(3); + it('should find multiple selects by model', async() => { + const selects = element.all(by.model('dayColor.color')); + expect(await selects.count()).toEqual(3); }); - it('should find the selected option', function() { - var select = element(by.model('fruit')); - var selectedOption = select.element(by.css('option:checked')); - expect(selectedOption.getText()).toEqual('apple'); + it('should find the selected option', async() => { + const select = element(by.model('fruit')); + const selectedOption = select.element(by.css('option:checked')); + expect(await selectedOption.getText()).toEqual('apple'); }); - it('should find inputs with alternate attribute forms', function() { - var letterList = element(by.id('letterlist')); - expect(letterList.getText()).toBe(''); + it('should find inputs with alternate attribute forms', async() => { + const letterList = element(by.id('letterlist')); + expect(await letterList.getText()).toBe(''); - element(by.model('check.w')).click(); - expect(letterList.getText()).toBe('w'); + await element(by.model('check.w')).click(); + expect(await letterList.getText()).toBe('w'); - element(by.model('check.x')).click(); - expect(letterList.getText()).toBe('wx'); + await element(by.model('check.x')).click(); + expect(await letterList.getText()).toBe('wx'); }); - it('should find multiple inputs', function() { - element.all(by.model('color')).then(function(arr) { - expect(arr.length).toEqual(3); - }); + it('should find multiple inputs', async() => { + const arr = await element.all(by.model('color')); + expect(arr.length).toEqual(3); }); - it('should clear text from an input model', function() { - var username = element(by.model('username')); - var name = element(by.binding('username')); + it('should clear text from an input model', async() => { + const username = element(by.model('username')); + const name = element(by.binding('username')); - username.clear(); - expect(name.getText()).toEqual(''); + await username.clear(); + expect(await name.getText()).toEqual(''); - username.sendKeys('Jane Doe'); - expect(name.getText()).toEqual('Jane Doe'); + await username.sendKeys('Jane Doe'); + expect(await name.getText()).toEqual('Jane Doe'); - username.clear(); - expect(name.getText()).toEqual(''); + await username.clear(); + expect(await name.getText()).toEqual(''); }); }); - describe('by partial button text', function() { - it('should find multiple buttons containing "text"', function() { - element.all(by.partialButtonText('text')).then(function(arr) { - expect(arr.length).toEqual(4); - expect(arr[0].getAttribute('id')).toBe('exacttext'); - expect(arr[1].getAttribute('id')).toBe('otherbutton'); - expect(arr[2].getAttribute('id')).toBe('submitbutton'); - expect(arr[3].getAttribute('id')).toBe('inputbutton'); - }); + describe('by partial button text', () => { + it('should find multiple buttons containing "text"', async() => { + const arr = await element.all(by.partialButtonText('text')); + expect(arr.length).toEqual(4); + expect(await arr[0].getAttribute('id')).toBe('exacttext'); + expect(await arr[1].getAttribute('id')).toBe('otherbutton'); + expect(await arr[2].getAttribute('id')).toBe('submitbutton'); + expect(await arr[3].getAttribute('id')).toBe('inputbutton'); }); }); - describe('by button text', function() { - it('should find two button containing "Exact text"', function() { - element.all(by.buttonText('Exact text')).then(function(arr) { - expect(arr.length).toEqual(2); - expect(arr[0].getAttribute('id')).toBe('exacttext'); - expect(arr[1].getAttribute('id')).toBe('submitbutton'); - }); + describe('by button text', () => { + it('should find two button containing "Exact text"', async() => { + const arr = await element.all(by.buttonText('Exact text')); + expect(arr.length).toEqual(2); + expect(await arr[0].getAttribute('id')).toBe('exacttext'); + expect(await arr[1].getAttribute('id')).toBe('submitbutton'); }); - it('should not find any buttons containing "text"', function() { - element.all(by.buttonText('text')).then(function(arr) { - expect(arr.length).toEqual(0); - }); + it('should not find any buttons containing "text"', async() => { + const arr = await element.all(by.buttonText('text')); + expect(arr.length).toEqual(0); }); }); - describe('by repeater', function() { - beforeEach(function() { - browser.get('index.html#/repeater'); + describe('by repeater', () => { + beforeEach(async() => { + await browser.get('index.html#/repeater'); }); - it('should find by partial match', function() { - var fullMatch = element( - by.repeater('baz in days | filter:\'T\''). - row(0).column('baz.initial')); - expect(fullMatch.getText()).toEqual('T'); + it('should find by partial match', async() => { + const fullMatch = element(by.repeater('baz in days | filter:\'T\'') + .row(0).column('baz.initial')); + expect(await fullMatch.getText()).toEqual('T'); - var partialMatch = element( - by.repeater('baz in days').row(0).column('b')); - expect(partialMatch.getText()).toEqual('T'); + const partialMatch = element(by.repeater('baz in days') + .row(0).column('b')); + expect(await partialMatch.getText()).toEqual('T'); - var partialRowMatch = element( - by.repeater('baz in days').row(0)); - expect(partialRowMatch.getText()).toEqual('T'); + const partialRowMatch = element(by.repeater('baz in days').row(0)); + expect(await partialRowMatch.getText()).toEqual('T'); }); - it('should return all rows when unmodified', function() { - var all = - element.all(by.repeater('allinfo in days')); - all.then(function(arr) { - expect(arr.length).toEqual(5); - expect(arr[0].getText()).toEqual('M Monday'); - expect(arr[1].getText()).toEqual('T Tuesday'); - expect(arr[2].getText()).toEqual('W Wednesday'); - }); + it('should return all rows when unmodified', async() => { + const arr = await element.all(by.repeater('allinfo in days')); + expect(arr.length).toEqual(5); + expect(await arr[0].getText()).toEqual('M Monday'); + expect(await arr[1].getText()).toEqual('T Tuesday'); + expect(await arr[2].getText()).toEqual('W Wednesday'); }); - it('should return a single column', function() { - var initials = element.all( + it('should return a single column', async() => { + const initial = await element.all( by.repeater('allinfo in days').column('initial')); - initials.then(function(arr) { - expect(arr.length).toEqual(5); - expect(arr[0].getText()).toEqual('M'); - expect(arr[1].getText()).toEqual('T'); - expect(arr[2].getText()).toEqual('W'); - }); + + expect(initial.length).toEqual(5); + expect(await initial[0].getText()).toEqual('M'); + expect(await initial[1].getText()).toEqual('T'); + expect(await initial[2].getText()).toEqual('W'); - var names = element.all( + const names = await element.all( by.repeater('allinfo in days').column('name')); - names.then(function(arr) { - expect(arr.length).toEqual(5); - expect(arr[0].getText()).toEqual('Monday'); - expect(arr[1].getText()).toEqual('Tuesday'); - expect(arr[2].getText()).toEqual('Wednesday'); - }); + + expect(names.length).toEqual(5); + expect(await names[0].getText()).toEqual('Monday'); + expect(await names[1].getText()).toEqual('Tuesday'); + expect(await names[2].getText()).toEqual('Wednesday'); }); - it('should allow chaining while returning a single column', function() { - var secondName = element(by.css('.allinfo')).element( + it('should allow chaining while returning a single column', async() => { + const secondName = element(by.css('.allinfo')).element( by.repeater('allinfo in days').column('name').row(2)); - expect(secondName.getText()).toEqual('Wednesday'); + expect(await secondName.getText()).toEqual('Wednesday'); }); - it('should return a single row', function() { - var secondRow = element( - by.repeater('allinfo in days').row(1)); - expect(secondRow.getText()).toEqual('T Tuesday'); + it('should return a single row', async() => { + const secondRow = element(by.repeater('allinfo in days').row(1)); + expect(await secondRow.getText()).toEqual('T Tuesday'); }); - it('should return an individual cell', function() { - var secondNameByRowFirst = element( - by.repeater('allinfo in days'). - row(1). - column('name')); + it('should return an individual cell', async() => { + const secondNameByRowFirst = element(by.repeater('allinfo in days') + .row(1).column('name')); - var secondNameByColumnFirst = element( - by.repeater('allinfo in days'). - column('name'). - row(1)); + const secondNameByColumnFirst = element(by.repeater('allinfo in days') + .column('name').row(1)); - expect(secondNameByRowFirst.getText()).toEqual('Tuesday'); - expect(secondNameByColumnFirst.getText()).toEqual('Tuesday'); + expect(await secondNameByRowFirst.getText()).toEqual('Tuesday'); + expect(await secondNameByColumnFirst.getText()).toEqual('Tuesday'); }); - it('should find a using data-ng-repeat', function() { - var byRow = - element(by.repeater('day in days').row(2)); - expect(byRow.getText()).toEqual('W'); + it('should find a using data-ng-repeat', async() => { + const byRow = element(by.repeater('day in days').row(2)); + expect(await byRow.getText()).toEqual('W'); - var byCol = - element(by.repeater('day in days').row(2). - column('day')); - expect(byCol.getText()).toEqual('W'); + const byCol = element(by.repeater('day in days').row(2).column('day')); + expect(await byCol.getText()).toEqual('W'); }); - it('should find using ng:repeat', function() { - var byRow = - element(by.repeater('bar in days').row(2)); - expect(byRow.getText()).toEqual('W'); + it('should find using ng:repeat', async() => { + const byRow = element(by.repeater('bar in days').row(2)); + expect(await byRow.getText()).toEqual('W'); - var byCol = - element(by.repeater('bar in days').row(2). - column('bar')); - expect(byCol.getText()).toEqual('W'); + const byCol = element(by.repeater('bar in days').row(2).column('bar')); + expect(await byCol.getText()).toEqual('W'); }); - it('should determine if repeater elements are present', function() { - expect(element(by.repeater('allinfo in days').row(3)).isPresent()). - toBe(true); + it('should determine if repeater elements are present', async() => { + expect(await element(by.repeater('allinfo in days').row(3)).isPresent()) + .toBe(true); // There are only 5 rows, so the 6th row is not present. - expect(element(by.repeater('allinfo in days').row(5)).isPresent()). - toBe(false); + expect(await element(by.repeater('allinfo in days').row(5)).isPresent()) + .toBe(false); }); - it('should have by.exactRepeater working', function() { - expect(element(by.exactRepeater('day in d')).isPresent()).toBe(false); - expect(element(by.exactRepeater('day in days')).isPresent()).toBe(true); + it('should have by.exactRepeater working', async() => { + expect(await element(by.exactRepeater('day in d')).isPresent()) + .toBe(false); + expect(await element(by.exactRepeater('day in days')).isPresent()) + .toBe(true); // Full ng-repeat: baz in tDays = (days | filter:'T') - var repeaterWithEqualSign = element( - by.exactRepeater('baz in tDays').row(0)); - expect(repeaterWithEqualSign.getText()).toEqual('T'); + const repeaterWithEqualSign = element(by.exactRepeater('baz in tDays') + .row(0)); + expect(await repeaterWithEqualSign.getText()).toEqual('T'); // Full ng-repeat: baz in days | filter:'T' - var repeaterWithPipe = element( - by.exactRepeater('baz in days').row(0)); - expect(repeaterWithPipe.getText()).toEqual('T'); - }); - - describe('repeaters using ng-repeat-start and ng-repeat-end', function() { - it('should return all elements when unmodified', function() { - var all = - element.all(by.repeater('bloop in days')); - - all.then(function(arr) { - expect(arr.length).toEqual(3 * 5); - expect(arr[0].getText()).toEqual('M'); - expect(arr[1].getText()).toEqual('-'); - expect(arr[2].getText()).toEqual('Monday'); - expect(arr[3].getText()).toEqual('T'); - expect(arr[4].getText()).toEqual('-'); - expect(arr[5].getText()).toEqual('Tuesday'); - }); + const repeaterWithPipe = element(by.exactRepeater('baz in days').row(0)); + expect(await repeaterWithPipe.getText()).toEqual('T'); + }); + + describe('repeaters using ng-repeat-start and ng-repeat-end', () => { + it('should return all elements when unmodified', async() => { + const all = await element.all(by.repeater('bloop in days')); + + expect(all.length).toEqual(3 * 5); + expect(await all[0].getText()).toEqual('M'); + expect(await all[1].getText()).toEqual('-'); + expect(await all[2].getText()).toEqual('Monday'); + expect(await all[3].getText()).toEqual('T'); + expect(await all[4].getText()).toEqual('-'); + expect(await all[5].getText()).toEqual('Tuesday'); }); - it('should return a group of elements for a row', function() { - var firstRow = element.all(by.repeater('bloop in days').row(0)); + it('should return a group of elements for a row', async() => { + const firstRow = await element.all(by.repeater('bloop in days').row(0)); - firstRow.then(function(arr) { - expect(arr.length).toEqual(3); - expect(arr[0].getText()).toEqual('M'); - expect(arr[1].getText()).toEqual('-'); - expect(arr[2].getText()).toEqual('Monday'); - }); + expect(firstRow.length).toEqual(3); + expect(await firstRow[0].getText()).toEqual('M'); + expect(await firstRow[1].getText()).toEqual('-'); + expect(await firstRow[2].getText()).toEqual('Monday'); }); - it('should return a group of elements for a column', function() { - var nameColumn = element.all( + it('should return a group of elements for a column', async() => { + const nameColumn = await element.all( by.repeater('bloop in days').column('name')); - nameColumn.then(function(arr) { - expect(arr.length).toEqual(5); - expect(arr[0].getText()).toEqual('Monday'); - expect(arr[1].getText()).toEqual('Tuesday'); - }); + expect(nameColumn.length).toEqual(5); + expect(await nameColumn[0].getText()).toEqual('Monday'); + expect(await nameColumn[1].getText()).toEqual('Tuesday'); }); - it('should find an individual element', function() { - var firstInitial = element( + it('should find an individual element', async() => { + const firstInitial = element( by.repeater('bloop in days').row(0).column('bloop.initial')); - expect(firstInitial.getText()).toEqual('M'); + expect(await firstInitial.getText()).toEqual('M'); }); }); }); - describe('by css containing text', function() { - it('should find elements by css and partial text', function() { - element.all(by.cssContainingText('#animals ul .pet', 'dog')).then(function(arr) { - expect(arr.length).toEqual(2); - expect(arr[0].getAttribute('id')).toBe('bigdog'); - expect(arr[1].getAttribute('id')).toBe('smalldog'); - }); + describe('by css containing text', () => { + it('should find elements by css and partial text', async() => { + const arr = await element.all( + by.cssContainingText('#animals ul .pet', 'dog')) + expect(arr.length).toEqual(2); + expect(await arr[0].getAttribute('id')).toBe('bigdog'); + expect(await arr[1].getAttribute('id')).toBe('smalldog'); }); - it('should find elements with text-transform style', function() { - expect(element(by.cssContainingText('#transformedtext div', 'Uppercase')) - .getAttribute('id')).toBe('textuppercase'); - expect(element(by.cssContainingText('#transformedtext div', 'Lowercase')) - .getAttribute('id')).toBe('textlowercase'); - expect(element(by.cssContainingText('#transformedtext div', 'capitalize')) - .getAttribute('id')).toBe('textcapitalize'); + it('should find elements with text-transform style', async() => { + expect(await element(by.cssContainingText('#transformedtext div', + 'Uppercase')).getAttribute('id')).toBe('textuppercase'); + expect(await element(by.cssContainingText('#transformedtext div', + 'Lowercase')).getAttribute('id')).toBe('textlowercase'); + expect(await element(by.cssContainingText('#transformedtext div', + 'capitalize')).getAttribute('id')).toBe('textcapitalize'); }); - it('should find elements with a regex', function() { - element.all(by.cssContainingText('#transformedtext div', /(upper|lower)case/i)) - .then(function(found) { - expect(found.length).toEqual(2); - expect(found[0].getText()).toBe('UPPERCASE'); - expect(found[1].getText()).toBe('lowercase'); - }); + it('should find elements with a regex', async() => { + const found = await element.all(by.cssContainingText( + '#transformedtext div', /(upper|lower)case/i)); + + expect(found.length).toEqual(2); + expect(await found[0].getText()).toBe('UPPERCASE'); + expect(await found[1].getText()).toBe('lowercase'); }); - it('should find elements with a regex with no flags', function() { + it('should find elements with a regex with no flags', async() => { // this test matches the non-transformed text. // the text is actually transformed with css, // so you can't match the Node innerText or textContent. - element.all(by.cssContainingText('#transformedtext div', /Uppercase/)) - .then(function(found) { - expect(found.length).toEqual(1); - expect(found[0].getText()).toBe('UPPERCASE'); - }); + const found = await element.all(by.cssContainingText( + '#transformedtext div', /Uppercase/)); + + expect(found.length).toEqual(1); + expect(await found[0].getText()).toBe('UPPERCASE'); }); }); - describe('by options', function() { - it('should find elements by options', function() { - var allOptions = element.all(by.options('fruit for fruit in fruits')); - expect(allOptions.count()).toEqual(4); + describe('by options', () => { + it('should find elements by options', async() => { + const allOptions = element.all(by.options('fruit for fruit in fruits')); + expect(await allOptions.count()).toEqual(4); - var firstOption = allOptions.first(); - expect(firstOption.getText()).toEqual('apple'); + const firstOption = allOptions.first(); + expect(await firstOption.getText()).toEqual('apple'); }); }); - describe('by deep css', function() { - beforeEach(function() { - browser.get('index.html#/shadow'); + describe('by deep css', () => { + beforeEach(async() => { + await browser.get('index.html#/shadow'); }); // Shadow DOM is not currently supported outside of Chrome. - browser.getCapabilities().then(function(capabilities) { + browser.getCapabilities().then((capabilities) => { if (capabilities.get('browserName') == 'chrome') { - it('should find items inside the shadow DOM', function() { - var parentHeading = element(by.deepCss('.parentshadowheading')); - var olderChildHeading = element(by.deepCss('.oldershadowheading')); - var youngerChildHeading = element(by.deepCss('.youngershadowheading')); + it('should find items inside the shadow DOM', async() => { + const parentHeading = element(by.deepCss('.parentshadowheading')); + const olderChildHeading = element(by.deepCss('.oldershadowheading')); + const youngerChildHeading = element( + by.deepCss('.youngershadowheading')); - expect(parentHeading.isPresent()).toBe(true); - expect(olderChildHeading.isPresent()).toBe(true); - expect(youngerChildHeading.isPresent()).toBe(true); + expect(await parentHeading.isPresent()).toBe(true); + expect(await olderChildHeading.isPresent()).toBe(true); + expect(await youngerChildHeading.isPresent()).toBe(true); - expect(parentHeading.getText()).toEqual('Parent'); - expect(olderChildHeading.getText()).toEqual('Older Child'); - expect(youngerChildHeading.getText()).toEqual('Younger Child'); + expect(await parentHeading.getText()).toEqual('Parent'); + expect(await olderChildHeading.getText()).toEqual('Older Child'); + expect(await youngerChildHeading.getText()).toEqual('Younger Child'); - expect(element(by.deepCss('.originalcontent')).getText()) + expect(await element(by.deepCss('.originalcontent')).getText()) .toEqual('original content'); }); } }); }); - it('should determine if an element is present', function() { - expect(browser.isElementPresent(by.binding('greet'))).toBe(true); - expect(browser.isElementPresent(by.binding('nopenopenope'))).toBe(false); + it('should determine if an element is present', async() => { + expect(await browser.isElementPresent(by.binding('greet'))).toBe(true); + expect(await browser.isElementPresent(by.binding('nopenopenope'))) + .toBe(false); }); - it('should determine if an ElementFinder is present', function() { - expect(browser.isElementPresent(element(by.binding('greet')))).toBe(true); - expect(browser.isElementPresent(element(by.binding('nopenopenope')))) + it('should determine if an ElementFinder is present', async() => { + expect(await browser.isElementPresent(element(by.binding('greet')))) + .toBe(true); + expect(await browser.isElementPresent(element(by.binding('nopenopenope')))) .toBe(false); }); }); diff --git a/spec/basic/mockmodule_spec.js b/spec/basic/mockmodule_spec.js index b9381dd18..546e4fc1f 100644 --- a/spec/basic/mockmodule_spec.js +++ b/spec/basic/mockmodule_spec.js @@ -1,99 +1,102 @@ -describe('mock modules', function() { +describe('mock modules', () => { // A module to override the 'version' service. This function will be // executed in the context of the application under test, so it may // not refer to any local variables. - var mockModuleA = function() { - var newModule = angular.module('moduleA', []); + const mockModuleA = () => { + let newModule = angular.module('moduleA', []); newModule.value('version', '2'); }; // A second module overriding the 'version' service. // This module shows the use of a string for the load // function. - var mockModuleB = `angular.module('moduleB', []).value('version', '3');`; + const mockModuleB = `angular.module('moduleB', []).value('version', '3');`; // A third module overriding the 'version' service. This function // references the additional arguments provided through addMockModule(). - var mockModuleC = function() { + const mockModuleC = () => { var newModule = angular.module('moduleC', []); newModule.value('version', arguments[0] + arguments[1]); }; - afterEach(function() { + afterEach(() => { browser.clearMockModules(); }); - it('should override services via mock modules', function() { + it('should override services via mock modules', async() => { browser.addMockModule('moduleA', mockModuleA); - browser.get('index.html'); + await browser.get('index.html'); - expect(element(by.css('[app-version]')).getText()).toEqual('2'); + expect(await element(by.css('[app-version]')).getText()).toEqual('2'); }); - it('should have the version of the last loaded module', function() { + it('should have the version of the last loaded module', async() => { browser.addMockModule('moduleA', mockModuleA); browser.addMockModule('moduleB', mockModuleB); - browser.get('index.html'); + await browser.get('index.html'); - expect(element(by.css('[app-version]')).getText()).toEqual('3'); + expect(await element(by.css('[app-version]')).getText()).toEqual('3'); }); - it('should use the latest module if two are added with the same name', function() { + it('should use the latest module if two are added with the same name', + async() => { browser.addMockModule('moduleA', mockModuleA); - var mockModuleA2 = function() { - var newModule = angular.module('moduleA', []); + let mockModuleA2 = () => { + let newModule = angular.module('moduleA', []); newModule.value('version', '3'); }; browser.addMockModule('moduleA', mockModuleA2); - browser.get('index.html'); + await browser.get('index.html'); - expect(element(by.css('[app-version]')).getText()).toEqual('3'); + expect(await element(by.css('[app-version]')).getText()).toEqual('3'); }); - it('should have the version of the module A after deleting module B', function() { + it('should have the version of the module A after deleting module B', + async() => { browser.addMockModule('moduleA', mockModuleA); browser.addMockModule('moduleB', mockModuleB); browser.removeMockModule('moduleB'); - browser.get('index.html'); + await browser.get('index.html'); - expect(element(by.css('[app-version]')).getText()).toEqual('2'); + expect(await element(by.css('[app-version]')).getText()).toEqual('2'); }); - it('should remove duplicate mock modules', function () { + it('should remove duplicate mock modules', async() => { browser.addMockModule('moduleA', mockModuleA); browser.addMockModule('moduleA', mockModuleA); browser.removeMockModule('moduleA'); - browser.get('index.html'); + await browser.get('index.html'); - expect(element(by.css('[app-version]')).getText()).toEqual('0.1'); + expect(await element(by.css('[app-version]')).getText()).toEqual('0.1'); }); - it('should be a noop to remove a module which does not exist', function() { + it('should be a noop to remove a module which does not exist', async() => { browser.addMockModule('moduleA', mockModuleA); browser.removeMockModule('moduleB'); - browser.get('index.html'); + await browser.get('index.html'); - expect(element(by.css('[app-version]')).getText()).toEqual('2'); + expect(await element(by.css('[app-version]')).getText()).toEqual('2'); }); - it('should have the version provided from parameters through Module C', function() { + it('should have the version provided from parameters through Module C', + async() => { browser.addMockModule('moduleC', mockModuleC, '42', 'beta'); - browser.get('index.html'); + await browser.get('index.html'); - expect(element(by.css('[app-version]')).getText()).toEqual('42beta'); + expect(await element(by.css('[app-version]')).getText()).toEqual('42beta'); }); - it('should retrieve a list of current mock modules', function() { + it('should retrieve a list of current mock modules', () => { browser.addMockModule('moduleA', mockModuleA); browser.addMockModule('moduleC', mockModuleC, '2', 'B'); @@ -103,20 +106,20 @@ describe('mock modules', function() { expect(browser.getRegisteredMockModules()[2]).toEqual(mockModuleC); }); - it('should load mock modules after refresh', function() { + it('should load mock modules after refresh', async() => { browser.addMockModule('moduleA', mockModuleA); - browser.get('index.html'); - expect(element(by.css('[app-version]')).getText()).toEqual('2'); + await browser.get('index.html'); + expect(await element(by.css('[app-version]')).getText()).toEqual('2'); - browser.navigate().refresh(); - expect(element(by.css('[app-version]')).getText()).toEqual('2'); + await browser.navigate().refresh(); + expect(await element(by.css('[app-version]')).getText()).toEqual('2'); }); // Back and forward do NOT work at the moment because of an issue // bootstrapping with Angular /* - it('should load mock modules after navigating back and forward', function() { + it('should load mock modules after navigating back and forward', () => { browser.addMockModule('moduleA', mockModuleA); browser.get('index.html'); @@ -133,26 +136,26 @@ describe('mock modules', function() { }); */ - it('should load mock modules after navigating back and forward from link', function() { - browser.getCapabilities().then(function(caps) { - if (caps.get('browserName') === 'safari') { - // Safari can't handle navigation. Ignore this test. - return; - } else { - browser.addMockModule('moduleA', mockModuleA); + it('should load mock modules after navigating back and forward from link', + async() => { + const caps = await browser.getCapabilities(); + if (caps.get('browserName') === 'safari') { + // Safari can't handle navigation. Ignore this test. + return; + } else { + browser.addMockModule('moduleA', mockModuleA); - browser.get('index.html'); - expect(element(by.css('[app-version]')).getText()).toEqual('2'); + await browser.get('index.html'); + expect(await element(by.css('[app-version]')).getText()).toEqual('2'); - element(by.linkText('repeater')).click(); - expect(element(by.css('[app-version]')).getText()).toEqual('2'); + await element(by.linkText('repeater')).click(); + expect(await element(by.css('[app-version]')).getText()).toEqual('2'); - browser.navigate().back(); - expect(element(by.css('[app-version]')).getText()).toEqual('2'); + await browser.navigate().back(); + expect(await element(by.css('[app-version]')).getText()).toEqual('2'); - browser.navigate().forward(); - expect(element(by.css('[app-version]')).getText()).toEqual('2'); - } - }); + await browser.navigate().forward(); + expect(await element(by.css('[app-version]')).getText()).toEqual('2'); + } }); }); diff --git a/spec/basic/navigation_spec.js b/spec/basic/navigation_spec.js index 98e5231e1..c400fbd43 100644 --- a/spec/basic/navigation_spec.js +++ b/spec/basic/navigation_spec.js @@ -1,58 +1,50 @@ -describe('navigation', function() { - beforeEach(function() { - browser.get('index.html#/form'); +const env = require('./../environment.js'); + +describe('navigation', () => { + beforeEach(async () => { + await browser.get('index.html#/form'); }); - it('should deal with alerts', function() { - var alertButton = $('#alertbutton'); - alertButton.click(); - var alertDialog = browser.switchTo().alert(); + it('should deal with alerts', async () => { + const alertButton = $('#alertbutton'); + await alertButton.click(); + const alertDialog = await browser.switchTo().alert(); - expect(alertDialog.getText()).toEqual('Hello'); + expect(await alertDialog.getText()).toEqual('Hello'); - alertDialog.accept(); + await alertDialog.accept(); }); - it('should refresh properly', function() { - var username = element(by.model('username')); - var name = element(by.binding('username')); - username.clear(); - expect(name.getText()).toEqual(''); - - browser.navigate().refresh(); + it('should refresh properly', async () => { + const username = element(by.model('username')); + const name = element(by.binding('username')); + await username.clear(); + expect(await name.getText()).toEqual(''); - expect(name.getText()).toEqual('Anon'); - }); + await browser.navigate().refresh(); - // Back and forward do NOT work at the moment because of an issue - // bootstrapping with Angular - /* - it('should navigate back and forward properly', function() { - browser.get('index.html#/repeater'); - expect(browser.getCurrentUrl()). - toEqual(env.baseUrl+'/ng1/index.html#/repeater'); - - browser.navigate().back(); - expect(browser.getCurrentUrl()). - toEqual(env.baseUrl+'/ng1/index.html#/form'); - - browser.navigate().forward(); - expect(browser.getCurrentUrl()). - toEqual(env.baseUrl+'/ng1/index.html#/repeater'); + expect(await name.getText()).toEqual('Anon'); }); - */ + + it('should navigate back and forward properly', async () => { + await browser.get('index.html#/repeater'); + expect(await browser.getCurrentUrl()).toEqual(`${env.baseUrl}/ng1/index.html#/repeater`); - it('should navigate back and forward properly from link', function() { - element(by.linkText('repeater')).click(); - expect(browser.getCurrentUrl()). - toEqual(browser.baseUrl + 'index.html#/repeater'); + await browser.navigate().back(); + expect(await browser.getCurrentUrl()).toEqual(`${env.baseUrl}/ng1/index.html#/form`); - browser.navigate().back(); - expect(browser.getCurrentUrl()). - toEqual(browser.baseUrl + 'index.html#/form'); + await browser.navigate().forward(); + expect(await browser.getCurrentUrl()).toEqual(`${env.baseUrl}/ng1/index.html#/repeater`); + }); - browser.navigate().forward(); - expect(browser.getCurrentUrl()). - toEqual(browser.baseUrl + 'index.html#/repeater'); + it('should navigate back and forward properly from link', async () => { + await element(by.linkText('repeater')).click(); + expect(await browser.getCurrentUrl()).toEqual(`${browser.baseUrl}index.html#/repeater`); + + await browser.navigate().back(); + expect(await browser.getCurrentUrl()).toEqual(`${browser.baseUrl}index.html#/form`); + + await browser.navigate().forward(); + expect(await browser.getCurrentUrl()).toEqual(`${browser.baseUrl}index.html#/repeater`); }); }); diff --git a/spec/basic/polling_spec.js b/spec/basic/polling_spec.js index 80e05e253..efb475b0e 100644 --- a/spec/basic/polling_spec.js +++ b/spec/basic/polling_spec.js @@ -3,59 +3,55 @@ * when using applications which poll with $http or $timeout. * A better solution is to switch to the angular $interval service if possible. */ -describe('synchronizing with pages that poll', function() { - beforeEach(function() { - browser.get('index.html#/polling'); +describe('synchronizing with pages that poll', () => { + beforeEach(async () => { + await browser.get('index.html#/polling'); }); - it('avoids timeouts using ignoreSynchronization', function() { - var startButton = element(by.id('pollstarter')); + it('avoids timeouts using waitForAngularEnabled set to false', async () => { + const startButton = element(by.id('pollstarter')); + + const count = element(by.binding('count')); + expect(await count.getText()).toEqual('0'); - var count = element(by.binding('count')); - expect(count.getText()).toEqual('0'); - - startButton.click(); + await startButton.click(); // Turn this on to see timeouts. - browser.ignoreSynchronization = true; + await browser.waitForAngularEnabled(false); - count.getText().then(function(text) { - expect(text).toBeGreaterThan(-1); - }); + const textBefore = await count.getText(); + expect(textBefore).toBeGreaterThan(-1); - browser.sleep(2000); + await browser.sleep(2000); - count.getText().then(function(text) { - expect(text).toBeGreaterThan(1); - }); + const textAfter = await count.getText(); + expect(textAfter).toBeGreaterThan(1); }); - it('avoids timeouts using waitForAngularEnabled', function() { - var startButton = element(by.id('pollstarter')); + it('avoids timeouts using waitForAngularEnabled', async () => { + const startButton = element(by.id('pollstarter')); - var count = element(by.binding('count')); - expect(count.getText()).toEqual('0'); + const count = element(by.binding('count')); + expect(await count.getText()).toEqual('0'); - startButton.click(); + await startButton.click(); // Turn this off to see timeouts. - browser.waitForAngularEnabled(false); + await browser.waitForAngularEnabled(false); - expect(browser.waitForAngularEnabled()).toBeFalsy(); + expect(await browser.waitForAngularEnabled()).toBeFalsy(); - count.getText().then(function(text) { - expect(text).toBeGreaterThan(-1); - }); + const textBefore = await count.getText(); + expect(textBefore).toBeGreaterThan(-1); - browser.sleep(2000); + await browser.sleep(2000); - count.getText().then(function(text) { - expect(text).toBeGreaterThan(1); - }); + const textAfter = await count.getText(); + expect(textAfter).toBeGreaterThan(1); }); - afterEach(function() { + afterEach(async () => { // Remember to turn it back on when you're done! - browser.waitForAngularEnabled(true); + await browser.waitForAngularEnabled(true); }); }); diff --git a/spec/basic/restart_spec.js b/spec/basic/restart_spec.js index e8aefde62..9b103e52b 100644 --- a/spec/basic/restart_spec.js +++ b/spec/basic/restart_spec.js @@ -1,14 +1,15 @@ -describe('browser.restart', function() { - it('doesn\'t break ignoreSynchronization', function() { - browser.get('index.html#/polling'); - browser.restart(); +describe('browser.restart', () => { + it(`doesn't break waitForAngularEnabled set to false`, async () => { + await browser.get('index.html#/polling'); + await browser.restart(); - browser.ignoreSynchronization = true; - // Get a non-angular page. It shouldn't fail if ignoreSynchronization is on. - browser.get('https://google.com/'); + await browser.waitForAngularEnabled(false); + // Get a non-angular page. It shouldn't fail if waitForAngularEnabled + // is turned off. + await browser.get('https://google.com/'); }); - afterAll(function() { - browser.ignoreSynchronization = false; + afterAll(async () => { + await browser.waitForAngularEnabled(true); }); }); diff --git a/spec/basic/synchronize_spec.js b/spec/basic/synchronize_spec.js index becd65e43..f8c56fd8a 100644 --- a/spec/basic/synchronize_spec.js +++ b/spec/basic/synchronize_spec.js @@ -1,95 +1,77 @@ -describe('synchronizing with slow pages', function() { - beforeEach(function() { - browser.get('index.html#/async'); +describe('synchronizing with slow pages', () => { + beforeEach(async () => { + await browser.get('index.html#/async'); }); - it('waits for http calls', function() { - var status = element(by.binding('slowHttpStatus')); - var button = element(by.css('[ng-click="slowHttp()"]')); + it('waits for http calls', async () => { + const status = element(by.binding('slowHttpStatus')); + const button = element(by.css('[ng-click="slowHttp()"]')); + expect(await status.getText()).toEqual('not started'); - expect(status.getText()).toEqual('not started'); - - button.click(); - - expect(status.getText()).toEqual('done'); + await button.click(); + expect(await status.getText()).toEqual('done'); }); - it('waits for long javascript execution', function() { - var status = element(by.binding('slowFunctionStatus')); - var button = element(by.css('[ng-click="slowFunction()"]')); - - expect(status.getText()).toEqual('not started'); - - button.click(); + it('waits for long javascript execution', async () => { + const status = element(by.binding('slowFunctionStatus')); + const button = element(by.css('[ng-click="slowFunction()"]')); + expect(await status.getText()).toEqual('not started'); - expect(status.getText()).toEqual('done'); + await button.click(); + expect(await status.getText()).toEqual('done'); }); - it('DOES NOT wait for timeout', function() { - var status = element(by.binding('slowTimeoutStatus')); - var button = element(by.css('[ng-click="slowTimeout()"]')); + it('DOES NOT wait for timeout', async () => { + const status = element(by.binding('slowTimeoutStatus')); + const button = element(by.css('[ng-click="slowTimeout()"]')); + expect(await status.getText()).toEqual('not started'); - expect(status.getText()).toEqual('not started'); - - button.click(); - - expect(status.getText()).toEqual('pending...'); + await button.click(); + expect(await status.getText()).toEqual('pending...'); }); - it('waits for $timeout', function() { - var status = element(by.binding('slowAngularTimeoutStatus')); - var button = element(by.css('[ng-click="slowAngularTimeout()"]')); - - expect(status.getText()).toEqual('not started'); - - button.click(); + it('waits for $timeout', async () => { + const status = element(by.binding('slowAngularTimeoutStatus')); + const button = element(by.css('[ng-click="slowAngularTimeout()"]')); + expect(await status.getText()).toEqual('not started'); - expect(status.getText()).toEqual('done'); + await button.click(); + expect(await status.getText()).toEqual('done'); }); - it('waits for $timeout then a promise', function() { - var status = element(by.binding( - 'slowAngularTimeoutPromiseStatus')); - var button = element(by.css( - '[ng-click="slowAngularTimeoutPromise()"]')); + it('waits for $timeout then a promise', async () => { + const status = element(by.binding('slowAngularTimeoutPromiseStatus')); + const button = element(by.css('[ng-click="slowAngularTimeoutPromise()"]')); + expect(await status.getText()).toEqual('not started'); - expect(status.getText()).toEqual('not started'); - - button.click(); - - expect(status.getText()).toEqual('done'); + await button.click(); + expect(await status.getText()).toEqual('done'); }); - it('waits for long http call then a promise', function() { - var status = element(by.binding('slowHttpPromiseStatus')); - var button = element(by.css('[ng-click="slowHttpPromise()"]')); - - expect(status.getText()).toEqual('not started'); - - button.click(); + it('waits for long http call then a promise', async () => { + const status = element(by.binding('slowHttpPromiseStatus')); + const button = element(by.css('[ng-click="slowHttpPromise()"]')); + expect(await status.getText()).toEqual('not started'); - expect(status.getText()).toEqual('done'); + await button.click(); + expect(await status.getText()).toEqual('done'); }); - it('waits for slow routing changes', function() { - var status = element(by.binding('routingChangeStatus')); - var button = element(by.css('[ng-click="routingChange()"]')); + it('waits for slow routing changes', async () => { + const status = element(by.binding('routingChangeStatus')); + const button = element(by.css('[ng-click="routingChange()"]')); + expect(await status.getText()).toEqual('not started'); - expect(status.getText()).toEqual('not started'); - - button.click(); - - expect(browser.getPageSource()).toMatch('polling mechanism'); + await button.click(); + expect(await browser.getPageSource()).toMatch('polling mechanism'); }); - it('waits for slow ng-include templates to load', function() { - var status = element(by.css('.included')); - var button = element(by.css('[ng-click="changeTemplateUrl()"]')); - - expect(status.getText()).toEqual('fast template contents'); - - button.click(); + it('waits for slow ng-include templates to load', async () => { + const status = element(by.css('.included')); + const button = element(by.css('[ng-click="changeTemplateUrl()"]')); + expect(await status.getText()).toEqual('fast template contents'); - expect(status.getText()).toEqual('slow template contents'); + await button.click(); + expect(await status.getText()).toEqual('slow template contents'); }); }); diff --git a/spec/basicConf.js b/spec/basicConf.js index 46b451a85..9b378fd52 100644 --- a/spec/basicConf.js +++ b/spec/basicConf.js @@ -3,6 +3,7 @@ var env = require('./environment.js'); // The main suite of Protractor tests. exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/ciBStackConf.js b/spec/ciBStackConf.js index 5ffc8c187..75d4c9e11 100644 --- a/spec/ciBStackConf.js +++ b/spec/ciBStackConf.js @@ -4,12 +4,15 @@ var env = require('./environment.js'); exports.config = { browserstackUser: process.env.BROWSER_STACK_USERNAME, browserstackKey: process.env.BROWSER_STACK_ACCESS_KEY, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', // Spec patterns are relative to this directory. specs: [ - 'basic/*_spec.js' + // TODO(selenium4): revert back to 'basic/*_spec.js', for now just use lib_spec + // 'basic/*_spec.js' + 'basic/lib_spec.js' ], // Exclude patterns are relative to this directory. diff --git a/spec/ciFullConf.js b/spec/ciFullConf.js index 38ec8a2d6..a1ac1ea7e 100644 --- a/spec/ciFullConf.js +++ b/spec/ciFullConf.js @@ -4,12 +4,13 @@ var env = require('./environment.js'); exports.config = { sauceUser: process.env.SAUCE_USERNAME, sauceKey: process.env.SAUCE_ACCESS_KEY, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', // Spec patterns are relative to this directory. specs: [ - 'basic/*_spec.js' + 'basic/*_spec.js', ], // Exclude patterns are relative to this directory. @@ -22,16 +23,13 @@ exports.config = { 'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER, 'build': process.env.TRAVIS_BUILD_NUMBER, 'name': 'Protractor suite tests', - 'version': '54', - 'selenium-version': '2.53.1', - 'chromedriver-version': '2.26', - 'platform': 'OS X 10.11' + 'version': '70' }, { 'browserName': 'firefox', 'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER, 'build': process.env.TRAVIS_BUILD_NUMBER, 'name': 'Protractor suite tests', - 'version': '47', + 'version': '60', }], baseUrl: env.baseUrl + '/ng1/', diff --git a/spec/ciNg2Conf.js b/spec/ciNg2Conf.js index 9b6dac66f..4dc249482 100644 --- a/spec/ciNg2Conf.js +++ b/spec/ciNg2Conf.js @@ -1,21 +1,3 @@ -exports.config = require('./angular2Conf.js').config; - -exports.config.sauceUser = process.env.SAUCE_USERNAME; -exports.config.sauceKey = process.env.SAUCE_ACCESS_KEY; -exports.config.seleniumAddress = undefined; - -// TODO: add in firefox when issue #2784 is fixed -exports.config.multiCapabilities = [{ - 'browserName': 'chrome', - 'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER, - 'build': process.env.TRAVIS_BUILD_NUMBER, - 'name': 'Protractor suite tests', - 'version': '54', - 'selenium-version': '2.53.1', - 'chromedriver-version': '2.26', - 'platform': 'OS X 10.11' - }]; -exports.config.capabilities = undefined; -exports.config.allScriptsTimeout = 120000; -exports.config.getPageTimeout = 120000; -exports.config.jasmineNodeOpts.defaultTimeoutInterval = 120000; +exports.config = require('./ciFullConf.js').config; +exports.config.specs = require('./angular2Conf.js').config.specs; +exports.config.exclude = undefined; \ No newline at end of file diff --git a/spec/ciSmokeConf.js b/spec/ciSmokeConf.js index b49ff61eb..30630c418 100644 --- a/spec/ciSmokeConf.js +++ b/spec/ciSmokeConf.js @@ -5,13 +5,16 @@ var env = require('./environment.js'); exports.config = { sauceUser: process.env.SAUCE_USERNAME, sauceKey: process.env.SAUCE_ACCESS_KEY, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', specs: [ - 'basic/locators_spec.js', - 'basic/mockmodule_spec.js', - 'basic/synchronize_spec.js' + // TODO(selenium4): revert back. For now just put on lib_spec.js + // 'basic/locators_spec.js', + // 'basic/mockmodule_spec.js', + // 'basic/synchronize_spec.js' + 'basic/lib_spec.js' ], // Two latest versions of IE, and Safari. diff --git a/spec/control/spec.js b/spec/control/spec.js index bab554221..89d8872cd 100644 --- a/spec/control/spec.js +++ b/spec/control/spec.js @@ -1,6 +1,6 @@ -describe('protractor control flow', function() { - it('should not deadlock', function() { - browser.driver.wait(function() { +describe('protractor control flow', () => { + it('should not deadlock', async() => { + await browser.driver.wait(() => { return true; }, 1000); expect(true).toEqual(true); diff --git a/spec/controlLockConf.js b/spec/controlLockConf.js index 06d2a6066..215a13d0c 100644 --- a/spec/controlLockConf.js +++ b/spec/controlLockConf.js @@ -1,10 +1,11 @@ -var env = require('./environment.js'); -var webdriver = require('selenium-webdriver'); +const env = require('./environment.js'); +const webdriver = require('selenium-webdriver'); // Tests for cases that have caused WebDriver promise locks in // the past. exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', @@ -16,15 +17,15 @@ exports.config = { baseUrl: env.baseUrl + '/ng1/', - onPrepare: function() { - + onPrepare: async function() { // This is a reasonable use case - do some promise that takes some time, // and then do a wait until something is set up correctly. - return webdriver.promise.delayed(100).then(function() { - // This could also be replaced by an 'execute' to see the same behavior. - return browser.driver.wait(function() { - return true; - }, 10000, 'onPrepare wait'); + await new Promise(resolve => { + setTimeout(resolve, 100); }); + // This could also be replaced by an 'execute' to see the same behavior. + return await browser.driver.wait(function() { + return true; + }, 10000, 'onPrepare wait'); } }; diff --git a/spec/custom/smoke_spec.js b/spec/custom/smoke_spec.js index b34800c7c..9005fba22 100644 --- a/spec/custom/smoke_spec.js +++ b/spec/custom/smoke_spec.js @@ -1,5 +1,5 @@ -describe('smoke jasmine tests', function() { - it('should do some dummy test', function() { +describe('smoke jasmine tests', () => { + it('should do some dummy test', () => { expect(1).toBe(1); }); }); diff --git a/spec/customFramework.js b/spec/customFramework.js index fc0badbde..f0c5f1fe0 100644 --- a/spec/customFramework.js +++ b/spec/customFramework.js @@ -1,7 +1,8 @@ -var env = require('./environment.js'); +const env = require('./environment.js'); exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'custom', frameworkPath: './custom/framework.js', diff --git a/spec/dependencyTest/protractor_spec.js b/spec/dependencyTest/protractor_spec.js index 7dcc1ec4c..cf6f520da 100644 --- a/spec/dependencyTest/protractor_spec.js +++ b/spec/dependencyTest/protractor_spec.js @@ -13,9 +13,10 @@ describe('require(\'protractor\')', () => { }); it('should have selenium-webdriver functions defined', () => { - var seleniumFunctions = ['ActionSequence', 'Builder', - 'Capabilities', 'Command', 'EventEmitter', 'FileDetector', - 'Session', 'WebDriver', 'WebElement', 'WebElementPromise']; + // TODO(selenium4): Update Actions when it is in typings. ActionSequence and EventEmitter is deprecated. + var seleniumFunctions = [/*'Actions', */'Builder', + 'Capabilities', 'Command', 'FileDetector', 'Session', 'WebDriver', + 'WebElement', 'WebElementPromise']; for (var pos in seleniumFunctions) { var propertyObj = seleniumFunctions[pos]; expect(typeof protractor[propertyObj]).toEqual('function'); @@ -31,10 +32,6 @@ describe('require(\'protractor\')', () => { }); - it('should have selenium-webdriver promise.Promise', function() { - expect(typeof protractor['promise']['Promise']).toEqual('function'); - }); - describe('browser class', () => { it('should have static variables defined', () => { var staticVariables = ['By']; @@ -48,8 +45,7 @@ describe('require(\'protractor\')', () => { describe('promise namespace', () => { it('should have functions defined (spot check)', () => { - var promiseFunctions = ['Promise', 'defer', 'delayed', 'createFlow', - 'controlFlow', 'all', 'fulfilled', 'filter', 'when' ] + var promiseFunctions = ['delayed', 'filter']; for (var pos in promiseFunctions) { var property = promiseFunctions[pos]; expect(typeof protractor.promise[property]).toEqual('function'); diff --git a/spec/dependencyTest/seleniumWebdriver_spec.js b/spec/dependencyTest/seleniumWebdriver_spec.js index 9b8c00ce3..d07b40ed9 100644 --- a/spec/dependencyTest/seleniumWebdriver_spec.js +++ b/spec/dependencyTest/seleniumWebdriver_spec.js @@ -5,21 +5,20 @@ var Chrome = require('selenium-webdriver/chrome'); var Firefox = require('selenium-webdriver/firefox'); var SeleniumError = require('selenium-webdriver').error; var Remote = require('selenium-webdriver/remote'); -var Testing = require('selenium-webdriver/testing'); var WEBDRIVER = { - staticFunctions: ['attachToSession', 'createSession'], + staticFunctions: ['createSession'], instanceFunctions: ['actions', 'wait', 'sleep', 'getCurrentUrl', 'getTitle', - 'takeScreenshot', 'getSession', 'getCapabilities', 'quit', 'touchActions', - 'executeAsyncScript', 'call', 'wait', 'getWindowHandle', + 'takeScreenshot', 'getSession', 'getCapabilities', 'quit', + 'executeAsyncScript', 'wait', 'getWindowHandle', 'getAllWindowHandles', 'getPageSource', 'close', 'get', 'findElement', 'findElements', 'manage', 'navigate', 'switchTo'] }; var WEBELEMENT = { instanceFunctions: ['getDriver', 'getId', 'findElement', 'click', 'sendKeys', 'getTagName', - 'getCssValue', 'getAttribute', 'getText', 'getSize', 'getLocation', 'isEnabled', 'isSelected', + 'getCssValue', 'getAttribute', 'getText', 'getRect', 'isEnabled', 'isSelected', 'submit', 'clear', 'isDisplayed', 'takeScreenshot'] }; var BY = { @@ -35,9 +34,6 @@ var CHROME = { var FIREFOX = { staticFunction: 'Driver' }; -var TESTING = { - instanceFunctions: ['after', 'afterEach', 'before', 'beforeEach', 'it', 'xit'] -}; describe('selenium-webdriver dependency', function() { describe('require("selenium-webdriver").WebDriver', function() { @@ -116,14 +112,4 @@ describe('selenium-webdriver dependency', function() { expect(typeof Remote['SeleniumServer'] == 'function').toBe(true); }); }); - describe('require("selenium-webdriver/testing")', function() { - - it('should have functions', function() { - for (var pos in TESTING.instanceFunctions) { - var func = TESTING.instanceFunctions[pos]; - expect(typeof Testing[func] == 'function').toBe(true); - } - }); - - }); }); diff --git a/spec/directConnect/directconnect_spec.js b/spec/directConnect/directconnect_spec.js index 9ea93849b..a34083f53 100644 --- a/spec/directConnect/directconnect_spec.js +++ b/spec/directConnect/directconnect_spec.js @@ -1,14 +1,14 @@ -describe('direct connect', function() { - it('should instantiate and run', function() { - var usernameInput = element(by.model('username')); - var name = element(by.binding('username')); +describe('direct connect', () => { + it('should instantiate and run', async() => { + const usernameInput = element(by.model('username')); + const name = element(by.binding('username')); - browser.get('index.html#/form'); + await browser.get('index.html#/form'); - expect(name.getText()).toEqual('Anon'); + expect(await name.getText()).toEqual('Anon'); - usernameInput.clear(); - usernameInput.sendKeys('Jane'); - expect(name.getText()).toEqual('Jane'); + await usernameInput.clear(); + await usernameInput.sendKeys('Jane'); + expect(await name.getText()).toEqual('Jane'); }); }); diff --git a/spec/directConnectConf.js b/spec/directConnectConf.js index d344fa0fb..bc0312292 100644 --- a/spec/directConnectConf.js +++ b/spec/directConnectConf.js @@ -3,6 +3,7 @@ var env = require('./environment.js'); // A configuration file running a simple direct connect spec exports.config = { directConnect: true, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/driverProviderAttachSessionConf.js b/spec/driverProviderAttachSessionConf.js index e75bcb6c4..3cdef4c46 100644 --- a/spec/driverProviderAttachSessionConf.js +++ b/spec/driverProviderAttachSessionConf.js @@ -2,6 +2,7 @@ var env = require('./environment'); exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/driverProviderLocalConf.js b/spec/driverProviderLocalConf.js index 9dfb2d827..f0f062bc0 100644 --- a/spec/driverProviderLocalConf.js +++ b/spec/driverProviderLocalConf.js @@ -3,6 +3,7 @@ var env = require('./environment'); exports.config = { framework: 'jasmine', + SELENIUM_PROMISE_MANAGER: false, specs: [ 'driverProviders/local/*_spec.js' diff --git a/spec/driverProviderTest.js b/spec/driverProviderTest.js new file mode 100644 index 000000000..a10237869 --- /dev/null +++ b/spec/driverProviderTest.js @@ -0,0 +1,145 @@ +/** + * Sanity integration tests for Driver Providers. + * + * Assumed setup: + * - selenium server running locally at http://localhost:4444 + * - selenium jar and chromedriver in protractor/selenium, where + * webdriver-manager stores them. + * - if you want to test saucelabs, test with --sauceUser and --sauceKey + * - if you want to test browserstack driverProvider, test with + --browserstackUser and --browserstackKey + * You should verify that there are no lingering processes when these tests + * complete. + */ + +const argv = require('optimist').argv; +const env = require('./environment'); + +const Direct = require('../built/driverProviders/direct').Direct; +const Hosted = require('../built/driverProviders/hosted').Hosted; +const Local = require('../built/driverProviders/local').Local; +const Sauce = require('../built/driverProviders/sauce').Sauce; +const BrowserStack = require('../built/driverProviders/browserStack').BrowserStack; + +const testDriverProvider = async (driverProvider) => { + await driverProvider.setupEnv(); + const driver = driverProvider.getNewDriver(); + await driver.get('about:blank'); + const url = await driver.getCurrentUrl(); + if (url != 'about:blank') { + throw new Error(`url was not about:blank, instead found ${url}`); + } + + if (driverProvider.updateJob) { + await driverProvider.updateJob({'passed': true}); + await driverProvider.teardownEnv(); + } else { + await driverProvider.teardownEnv(); + } +}; + +const chromeConfig = { + capabilities: { + browserName: 'chrome' + } +}; + +testDriverProvider(new Direct(chromeConfig)). + then(() => { + console.log('direct.dp with chrome working!'); + }, (err) => { + console.error('direct.dp with chrome failed with', err); + throw err; + }); + +const firefoxConfig = { + capabilities: { + browserName: 'firefox' + } +}; +testDriverProvider(new Direct(firefoxConfig)). + then(() => { + console.log('direct.dp with firefox working!'); + }, (err) => { + console.error('direct.dp with firefox failed with', err); + throw err; + }); + +const hostedConfig = { + seleniumAddress: env.seleniumAddress, + capabilities: { + browserName: 'firefox' + } +}; +testDriverProvider(new Hosted(hostedConfig)). + then(() => { + console.log('hosted.dp working!'); + }, (err) => { + console.error('hosted.dp failed with', err); + throw err; + }); + +const hostedPromisedConfig = { + seleniumAddress: Promise.resolve(env.seleniumAddress), + capabilities: { + browserName: 'firefox' + } +}; +testDriverProvider(new Hosted(hostedPromisedConfig)). + then(() => { + console.log('hosted.dp with promises working!'); + }, (err) => { + console.error('hosted.dp with promises failed with', err); + throw err; + }); + +const localConfig = { + seleniumArgs: [], + capabilities: { + browserName: 'chrome' + } +}; +testDriverProvider(new Local(localConfig)). + then(() => { + console.log('local.dp working!'); + }, (err) => { + console.error('local.dp failed with', err); + throw err; + }); + +if (argv.sauceUser && argv.sauceKey) { + const sauceConfig = { + sauceUser: argv.sauceUser, + sauceKey: argv.sauceKey, + sauceBuild: argv.sauceBuild, + capabilities: { + browserName: 'chrome' + } + }; + testDriverProvider(new Sauce(sauceConfig)). + then(() => { + console.log('sauce.dp working!'); + }, (err) => { + console.error('sauce.dp failed with', err); + throw err; + }); +} + +if (argv.browserstackUser && argv.browserstackKey) { + const browserStackConfig = { + browserstackUser: argv.browserstackUser, + browserstackKey: argv.browserstackKey, + capabilities: { + 'build': 'protractor-browserstack-spec', + 'name': 'protractor-browserstack-spec', + 'browserName': 'chrome', + } + }; + testDriverProvider(new BrowserStack(browserStackConfig)). + then(() => { + console.log('browserstack.dp working!'); + }, (err) => { + console.error('browserstack.dp failed with', err); + throw err; + }); +} diff --git a/spec/driverProviderUseExistingWebDriver.js b/spec/driverProviderUseExistingWebDriver.js deleted file mode 100644 index 6bf045579..000000000 --- a/spec/driverProviderUseExistingWebDriver.js +++ /dev/null @@ -1,22 +0,0 @@ -var env = require('./environment'); -var webdriver = require('selenium-webdriver'); - -var existingDriver = new webdriver.Builder() - .usingServer(env.seleniumAddress) - .withCapabilities(env.capabilities) - .build(); - -exports.config = { - - framework: 'jasmine', - - specs: [ - 'driverProviders/useExistingWebDriver/*_spec.js' - ], - - capabilities: env.capabilities, - - baseUrl: env.baseUrl, - - seleniumWebDriver: existingDriver, -}; diff --git a/spec/driverProviders/attachSession/attachSession_spec.js b/spec/driverProviders/attachSession/attachSession_spec.js index e69bc614c..9d2df2c48 100644 --- a/spec/driverProviders/attachSession/attachSession_spec.js +++ b/spec/driverProviders/attachSession/attachSession_spec.js @@ -1,11 +1,14 @@ -describe('selenium session id', function() { - var URL = '/ng2/#/async'; +describe('selenium session id', () => { + const URL = '/ng2/#/async'; - beforeEach(function() { - browser.get(URL); + beforeEach(async () => { + await browser.get(URL); }); - it('should be able to use an existing session', function() { - var increment = $('#increment'); - expect(increment).toBeDefined(); + + it('should be able to use an existing session', async () => { + const incrementButton = element.all(by.css('button.action')).get(0); + const incrementValue = element.all(by.css('span.val')).get(0); + await incrementButton.click(); + expect(await incrementValue.getText()).toBe('1'); }); }); diff --git a/spec/driverProviders/local/local_spec.js b/spec/driverProviders/local/local_spec.js index 2924d2d02..0c95b473e 100644 --- a/spec/driverProviders/local/local_spec.js +++ b/spec/driverProviders/local/local_spec.js @@ -1,17 +1,17 @@ -describe('local driver provider', function() { - var URL = '/ng2/#/async'; +describe('local driver provider', () => { + const URL = '/ng2/#/async'; - it('should get a page and find an element', function() { - browser.get(URL); - var increment = $('#increment'); - expect(increment).toBeDefined(); + it('should get a page and find an element', async() => { + await browser.get(URL); + const increment = $('#increment'); + expect(await increment.isPresent()).toBeDefined(); }); - it('should get a forked instance, and find an element', function() { - browser.get(URL); - var browser2 = browser.forkNewDriverInstance(); - browser2.get(URL); - var increment = browser2.$('#increment'); - expect(increment).toBeDefined(); + it('should get a forked instance, and find an element', async() => { + await browser.get(URL); + const browser2 = await browser.forkNewDriverInstance(); + await browser2.get(URL); + const increment = browser2.$('#increment'); + expect(await increment.isPresent()).toBeDefined(); }); }); diff --git a/spec/driverProviders/useExistingWebDriver/useExistingDriver_spec.js b/spec/driverProviders/useExistingWebDriver/useExistingDriver_spec.js deleted file mode 100644 index a69bf939b..000000000 --- a/spec/driverProviders/useExistingWebDriver/useExistingDriver_spec.js +++ /dev/null @@ -1,16 +0,0 @@ -describe('uses existing webdriver', function() { - var URL = '/ng2/#/async'; - - beforeEach(function() { - browser.get(URL); - }); - it('should be able to use an existing session', function() { - var increment = $('#increment'); - expect(increment).toBeDefined(); - }); - // the driverProvider is set up to ignore the quitDriver() call; - // so we call quit() ourselves to tidy up when testing is done. - afterEach(function() { - browser.quit(); - }); -}); diff --git a/spec/driverprovider_test.js b/spec/driverprovider_test.js deleted file mode 100644 index e67d8b85f..000000000 --- a/spec/driverprovider_test.js +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Sanity integration tests for Driver Providers. - * - * Assumed setup: - * - selenium server running locally at http://localhost:4444 - * - selenium jar and chromedriver in protractor/selenium, where - * webdriver-manager stores them. - * - if you want to test saucelabs, test with --sauceUser and --sauceKey - * - if you want to test browserstack driverProvider, test with - --browserstackUser and --browserstackKey - * You should verify that there are no lingering processes when these tests - * complete. - */ - -var argv = require('optimist').argv; -var q = require('q'); -var env = require('./environment'); - -var Direct = require('../built/driverProviders/direct').Direct; -var Hosted = require('../built/driverProviders/hosted').Hosted; -var Local = require('../built/driverProviders/local').Local; -var Sauce = require('../built/driverProviders/sauce').Sauce; -var BrowserStack = require('../built/driverProviders/browserStack').BrowserStack; - -var testDriverProvider = function(driverProvider) { - return driverProvider.setupEnv().then(function() { - var driver = driverProvider.getNewDriver(); - var deferred = q.defer(); - driver.get('about:blank'); - driver.getCurrentUrl().then(function(url) { - if (url != 'about:blank') { - throw new Error('url was not about:blank, instead found ' + url); - } - deferred.resolve(); - }); - return deferred.promise; - }).then(function() { - if (driverProvider.updateJob) { - return driverProvider.updateJob({ - 'passed': true - }).then(function() { - return driverProvider.teardownEnv(); - }); - } else { - return driverProvider.teardownEnv(); - } - }); -}; - -var chromeConfig = { - capabilities: { - browserName: 'chrome' - } -}; -testDriverProvider(new Direct(chromeConfig)). - then(function() { - console.log('direct.dp with chrome working!'); - }, function(err) { - console.log('direct.dp with chrome failed with ' + err.stack); - }); - -var firefoxConfig = { - capabilities: { - browserName: 'firefox' - } -}; -testDriverProvider(new Direct(firefoxConfig)). - then(function() { - console.log('direct.dp with firefox working!'); - }, function(err) { - console.log('direct.dp with firefox failed with ' + err.stack); - }); - -var hostedConfig = { - seleniumAddress: env.seleniumAddress, - capabilities: { - browserName: 'firefox' - } -}; -testDriverProvider(new Hosted(hostedConfig)). - then(function() { - console.log('hosted.dp working!'); - }, function(err) { - console.log('hosted.dp failed with ' + err); - }); - -var hostedPromisedConfig = { - seleniumAddress: q.when(env.seleniumAddress), - capabilities: { - browserName: 'firefox' - } -}; -testDriverProvider(new Hosted(hostedPromisedConfig)). - then(function() { - console.log('hosted.dp with promises working!'); - }, function(err) { - console.log('hosted.dp with promises failed with ' + err); - }); - -var localConfig = { - seleniumArgs: [], - capabilities: { - browserName: 'chrome' - } -}; -testDriverProvider(new Local(localConfig)). - then(function() { - console.log('local.dp working!'); - }, function(err) { - console.log('local.dp failed with ' + err); - }); - -if (argv.sauceUser && argv.sauceKey) { - var sauceConfig = { - sauceUser: argv.sauceUser, - sauceKey: argv.sauceKey, - sauceBuild: argv.sauceBuild, - capabilities: { - browserName: 'chrome' - } - }; - testDriverProvider(new Sauce(sauceConfig)). - then(function() { - console.log('sauce.dp working!'); - }, function(err) { - console.log('sauce.dp failed with ' + err); - }); -} - -if (argv.browserstackUser && argv.browserstackKey) { - var browserStackConfig = { - browserstackUser: argv.browserstackUser, - browserstackKey: argv.browserstackKey, - capabilities: { - 'build': 'protractor-browserstack-spec', - 'name': 'protractor-browserstack-spec', - 'browserName': 'chrome', - } - }; - testDriverProvider(new BrowserStack(browserStackConfig)). - then(function() { - console.log('browserstack.dp working!'); - }, function(err) { - console.log('browserstack.dp failed with ' + err); - }); -} diff --git a/spec/errorTest/afterLaunchChangesExitCodeConf.js b/spec/errorTest/afterLaunchChangesExitCodeConf.js index 049140fc7..1d8b41b8d 100644 --- a/spec/errorTest/afterLaunchChangesExitCodeConf.js +++ b/spec/errorTest/afterLaunchChangesExitCodeConf.js @@ -2,6 +2,7 @@ var env = require('../environment.js'); exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/errorTest/baseCase/error_spec.js b/spec/errorTest/baseCase/error_spec.js index 2b39ccaab..129f2a176 100644 --- a/spec/errorTest/baseCase/error_spec.js +++ b/spec/errorTest/baseCase/error_spec.js @@ -1,6 +1,6 @@ -describe('finding an element that does not exist', function() { - it('should throw an error', function() { - browser.get('index.html'); +describe('finding an element that does not exist', () => { + it('should throw an error', async () => { + await browser.get('index.html'); element(by.binding('INVALID')); // greeting }); }); diff --git a/spec/errorTest/baseCase/mocha_failure_spec.js b/spec/errorTest/baseCase/mocha_failure_spec.js index 6dcfe5586..023968acb 100644 --- a/spec/errorTest/baseCase/mocha_failure_spec.js +++ b/spec/errorTest/baseCase/mocha_failure_spec.js @@ -1,13 +1,13 @@ // Use the external Chai As Promised to deal with resolving promises in // expectations. -var chai = require('chai'); -var chaiAsPromised = require('chai-as-promised'); +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); chai.use(chaiAsPromised); -var expect = chai.expect; +const expect = chai.expect; -describe('protractor library', function() { - it('should fail', function() { - browser.get('index.html'); - expect(browser.getTitle()).to.eventually.equal('INTENTIONALLY INCORRECT'); +describe('protractor library', () => { + it('should fail', async () => { + await browser.get('index.html'); + expect(await browser.getTitle()).to.equal('INTENTIONALLY INCORRECT'); }); }); diff --git a/spec/errorTest/baseCase/single_failure_spec1.js b/spec/errorTest/baseCase/single_failure_spec1.js index c94068415..f2fb4cd15 100644 --- a/spec/errorTest/baseCase/single_failure_spec1.js +++ b/spec/errorTest/baseCase/single_failure_spec1.js @@ -1,7 +1,7 @@ -describe('single failure spec1', function() { - it('should fail expectation', function() { - browser.get('index.html'); - var greeting = element(by.binding('greeting')); - expect(greeting.getText()).toEqual('INTENTIONALLY INCORRECT'); +describe('single failure spec1', () => { + it('should fail expectation', async () => { + await browser.get('index.html'); + const greeting = element(by.binding('greeting')); + expect(await greeting.getText()).toEqual('INTENTIONALLY INCORRECT'); }); }); diff --git a/spec/errorTest/baseCase/single_failure_spec2.js b/spec/errorTest/baseCase/single_failure_spec2.js index c39404110..d3110349e 100644 --- a/spec/errorTest/baseCase/single_failure_spec2.js +++ b/spec/errorTest/baseCase/single_failure_spec2.js @@ -1,7 +1,7 @@ -describe('single failure spec2', function() { - it('should fail expectation', function() { - browser.get('index.html'); - var greeting = element(by.binding('greeting')); - expect(greeting.getText()).toEqual('INTENTIONALLY INCORRECT'); +describe('single failure spec2', () => { + it('should fail expectation', async () => { + await browser.get('index.html'); + const greeting = element(by.binding('greeting')); + expect(await greeting.getText()).toEqual('INTENTIONALLY INCORRECT'); }); }); diff --git a/spec/errorTest/baseCase/slow_http_and_timeout_spec.js b/spec/errorTest/baseCase/slow_http_and_timeout_spec.js index 8efcb9bc4..07d93642d 100644 --- a/spec/errorTest/baseCase/slow_http_and_timeout_spec.js +++ b/spec/errorTest/baseCase/slow_http_and_timeout_spec.js @@ -1,27 +1,27 @@ -describe('slow asynchronous events', function() { - beforeEach(function() { - browser.get('index.html#/async'); +describe('slow asynchronous events', () => { + beforeEach(async () => { + await browser.get('index.html#/async'); }); - it('waits for http calls', function() { - var status = element(by.binding('slowHttpStatus')); - var button = element(by.css('[ng-click="slowHttp()"]')); + it('waits for http calls', async () => { + const status = element(by.binding('slowHttpStatus')); + const button = element(by.css('[ng-click="slowHttp()"]')); - expect(status.getText()).toEqual('not started'); + expect(await status.getText()).toEqual('not started'); - button.click(); + await button.click(); - expect(status.getText()).toEqual('done'); + expect(await status.getText()).toEqual('done'); }); - it('waits for $timeout', function() { - var status = element(by.binding('slowAngularTimeoutStatus')); - var button = element(by.css('[ng-click="slowAngularTimeout()"]')); + it('waits for $timeout', async () => { + const status = element(by.binding('slowAngularTimeoutStatus')); + const button = element(by.css('[ng-click="slowAngularTimeout()"]')); - expect(status.getText()).toEqual('not started'); + expect(await status.getText()).toEqual('not started'); - button.click(); + await button.click(); - expect(status.getText()).toEqual('done'); + expect(await status.getText()).toEqual('done'); }); }); diff --git a/spec/errorTest/baseCase/success_spec.js b/spec/errorTest/baseCase/success_spec.js index e39caa3d6..0a9609b2e 100644 --- a/spec/errorTest/baseCase/success_spec.js +++ b/spec/errorTest/baseCase/success_spec.js @@ -1,7 +1,7 @@ -describe('success spec', function() { - it('should pass', function() { - browser.get('index.html'); - var greeting = element(by.binding('greeting')); - expect(greeting.getText()).toEqual('Hiya'); +describe('success spec', () => { + it('should pass', async () => { + await browser.get('index.html'); + const greeting = element(by.binding('greeting')); + expect(await greeting.getText()).toEqual('Hiya'); }); }); diff --git a/spec/errorTest/baseCase/timeout_spec.js b/spec/errorTest/baseCase/timeout_spec.js index 5387a6a5c..a489b9675 100644 --- a/spec/errorTest/baseCase/timeout_spec.js +++ b/spec/errorTest/baseCase/timeout_spec.js @@ -1,5 +1,5 @@ -describe('timeout spec', function() { - it('should timeout due to jasmine spec limit', function() { - browser.get('index.html#/form'); +describe('timeout spec', () => { + it('should timeout due to jasmine spec limit', async () => { + await browser.get('index.html#/form'); }, 1); }); diff --git a/spec/errorTest/browserStackAuthentication.js b/spec/errorTest/browserStackAuthentication.js index 6e954ca56..2ef4b9242 100644 --- a/spec/errorTest/browserStackAuthentication.js +++ b/spec/errorTest/browserStackAuthentication.js @@ -1,8 +1,9 @@ -var env = require('../environment.js'); +const env = require('../environment.js'); exports.config = { browserstackUser: 'foobar', browserstackKey: 'foobar', + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/errorTest/debugMultiCapabilities.js b/spec/errorTest/debugMultiCapabilities.js index 188f1e39d..76fc3c780 100644 --- a/spec/errorTest/debugMultiCapabilities.js +++ b/spec/errorTest/debugMultiCapabilities.js @@ -1,7 +1,8 @@ -var env = require('../environment.js'); +const env = require('../environment.js'); exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', debug: true, specs: [ diff --git a/spec/errorTest/getMultiCapabilitiesConf.js b/spec/errorTest/getMultiCapabilitiesConf.js index 587a0041b..b3c53b8b8 100644 --- a/spec/errorTest/getMultiCapabilitiesConf.js +++ b/spec/errorTest/getMultiCapabilitiesConf.js @@ -1,7 +1,8 @@ -var env = require('../environment.js'); +const env = require('../environment.js'); exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, // Spec patterns are relative to this directory. specs: [ diff --git a/spec/errorTest/mochaFailureConf.js b/spec/errorTest/mochaFailureConf.js index 5e726330f..5e5248b75 100644 --- a/spec/errorTest/mochaFailureConf.js +++ b/spec/errorTest/mochaFailureConf.js @@ -1,7 +1,8 @@ -var env = require('../environment.js'); +const env = require('../environment.js'); exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, specs: [ 'baseCase/mocha_failure_spec.js' diff --git a/spec/errorTest/multiFailureConf.js b/spec/errorTest/multiFailureConf.js index ba06b506b..9b6dbb75d 100644 --- a/spec/errorTest/multiFailureConf.js +++ b/spec/errorTest/multiFailureConf.js @@ -1,7 +1,8 @@ -var env = require('../environment.js'); +const env = require('../environment.js'); exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/errorTest/pluginsFailingConf.js b/spec/errorTest/pluginsFailingConf.js index ecb6c88e6..441d1de14 100644 --- a/spec/errorTest/pluginsFailingConf.js +++ b/spec/errorTest/pluginsFailingConf.js @@ -1,9 +1,10 @@ -var env = require('../environment.js'); +const env = require('../environment.js'); // A small suite to make sure the full functionality of plugins work exports.config = { // seleniumAddress: env.seleniumAddress, mockSelenium: true, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/errorTest/sauceLabsAuthentication.js b/spec/errorTest/sauceLabsAuthentication.js index 35f2180f8..7fe6e5d47 100644 --- a/spec/errorTest/sauceLabsAuthentication.js +++ b/spec/errorTest/sauceLabsAuthentication.js @@ -1,8 +1,9 @@ -var env = require('../environment.js'); +const env = require('../environment.js'); exports.config = { sauceUser: 'foobar', sauceKey: 'foobar', + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/errorTest/shardedFailureConf.js b/spec/errorTest/shardedFailureConf.js index a36d509dc..9e35edad1 100644 --- a/spec/errorTest/shardedFailureConf.js +++ b/spec/errorTest/shardedFailureConf.js @@ -1,7 +1,8 @@ -var env = require('../environment.js'); +const env = require('../environment.js'); exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/errorTest/singleFailureConf.js b/spec/errorTest/singleFailureConf.js index 5c8a930ab..3aa60b2f1 100644 --- a/spec/errorTest/singleFailureConf.js +++ b/spec/errorTest/singleFailureConf.js @@ -1,7 +1,8 @@ -var env = require('../environment.js'); +const env = require('../environment.js'); exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/errorTest/slowHttpAndTimeoutConf.js b/spec/errorTest/slowHttpAndTimeoutConf.js index 7fd280059..df8d955af 100644 --- a/spec/errorTest/slowHttpAndTimeoutConf.js +++ b/spec/errorTest/slowHttpAndTimeoutConf.js @@ -1,7 +1,8 @@ -var env = require('../environment.js'); +const env = require('../environment.js'); exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/errorTest/timeoutConf.js b/spec/errorTest/timeoutConf.js index 213432cba..6d616c667 100644 --- a/spec/errorTest/timeoutConf.js +++ b/spec/errorTest/timeoutConf.js @@ -1,7 +1,8 @@ -var env = require('../environment.js'); +const env = require('../environment.js'); exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/getCapabilitiesConf.js b/spec/getCapabilitiesConf.js index 50035871f..beaa6b5f7 100644 --- a/spec/getCapabilitiesConf.js +++ b/spec/getCapabilitiesConf.js @@ -1,8 +1,8 @@ -var env = require('./environment.js'); -var q = require('q'); +const env = require('./environment.js'); exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, // Spec patterns are relative to this directory. specs: [ @@ -10,15 +10,15 @@ exports.config = { ], framework: 'debugprint', - getMultiCapabilities: function() { - var deferred = q.defer(); + getMultiCapabilities: async function() { // Wait for a server to be ready or get capabilities asynchronously. - setTimeout(function() { - deferred.resolve([{ - 'browserName': 'firefox' - }]); - }, 1000); - return deferred.promise; + return await new Promise(resolve => { + setTimeout(() => { + resolve([{ + 'browserName': 'firefox' + }]); + }, 1000); + }); }, baseUrl: env.baseUrl + '/ng1/' diff --git a/spec/hybrid/async_spec.js b/spec/hybrid/async_spec.js index 6a09bceb4..8b2235104 100644 --- a/spec/hybrid/async_spec.js +++ b/spec/hybrid/async_spec.js @@ -1,71 +1,72 @@ -describe('async angular1/2 hybrid using ngUpgrade application', function() { - describe('@angular/upgrade/static', function() { - it('should be able to click buttons and wait for $timeout', function() { - browser.get('/upgrade'); +describe('async angular1/2 hybrid using ngUpgrade application', () => { + describe('@angular/upgrade/static', () => { + it('should be able to click buttons and wait for $timeout', async () => { + await browser.get('/upgrade'); - var rootBtn = $$('my-app button').first(); - expect(rootBtn.getText()).toEqual('Click Count: 0'); - rootBtn.click(); - expect(rootBtn.getText()).toEqual('Click Count: 1'); + const rootBtn = $$('my-app button').first(); + expect(await rootBtn.getText()).toEqual('Click Count: 0'); + await rootBtn.click(); + expect(await rootBtn.getText()).toEqual('Click Count: 1'); - var ng2Btn = $$('ng2 button').first(); - expect(ng2Btn.getText()).toEqual('Click Count: 0'); - ng2Btn.click(); - expect(ng2Btn.getText()).toEqual('Click Count: 1'); + const ng2Btn = $$('ng2 button').first(); + expect(await ng2Btn.getText()).toEqual('Click Count: 0'); + await ng2Btn.click(); + expect(await ng2Btn.getText()).toEqual('Click Count: 1'); - var ng1Btn = $('ng1 button'); - expect(ng1Btn.getText()).toEqual('Click Count: 0'); - ng1Btn.click(); - expect(ng1Btn.getText()).toEqual('Click Count: 1'); + const ng1Btn = $('ng1 button'); + expect(await ng1Btn.getText()).toEqual('Click Count: 0'); + await ng1Btn.click(); + expect(await ng1Btn.getText()).toEqual('Click Count: 1'); }); - it('should be able to automatically infer ng1/ng2/ngUpgrade', function() { - browser.get('/upgrade'); - expect($('h1').getText()).toBe('My App'); - browser.get('/ng1'); - expect($$('h4').first().getText()).toBe('Bindings'); - browser.get('/upgrade'); - expect($('h1').getText()).toBe('My App'); - browser.get('/ng2'); - expect($('h1').getText()).toBe('Test App for Angular 2'); - browser.get('/upgrade'); - expect($('h1').getText()).toBe('My App'); + it('should be able to automatically infer ng1/ng2/ngUpgrade', async () => { + await browser.get('/upgrade'); + expect(await $('h1').getText()).toBe('My App'); + await browser.get('/ng1'); + expect(await $$('h4').first().getText()).toBe('Bindings'); + await browser.get('/upgrade'); + expect(await $('h1').getText()).toBe('My App'); + await browser.get('/ng2'); + expect(await $('h1').getText()).toBe('Test App for Angular 2'); + await browser.get('/upgrade'); + expect(await $('h1').getText()).toBe('My App'); }); }); - describe('@angular/upgrade (not static)', function() { - it('should be able to click buttons and wait for $timeout', function() { - browser.get('/upgrade?no_static'); + describe('@angular/upgrade (not static)', () => { + it('should be able to click buttons and wait for $timeout', async () => { + await browser.get('/upgrade?no_static'); - var rootBtn = $$('my-app button').first(); - expect(rootBtn.getText()).toEqual('Click Count: 0'); - rootBtn.click(); - expect(rootBtn.getText()).toEqual('Click Count: 1'); + const rootBtn = $$('my-app button').first(); + expect(await rootBtn.getText()).toEqual('Click Count: 0'); + await rootBtn.click(); + expect(await rootBtn.getText()).toEqual('Click Count: 1'); - var ng2Btn = $$('ng2 button').first(); - expect(ng2Btn.getText()).toEqual('Click Count: 0'); - ng2Btn.click(); - expect(ng2Btn.getText()).toEqual('Click Count: 1'); + const ng2Btn = $$('ng2 button').first(); + expect(await ng2Btn.getText()).toEqual('Click Count: 0'); + await ng2Btn.click(); + expect(await ng2Btn.getText()).toEqual('Click Count: 1'); - var ng1Btn = $('ng1 button'); - expect(ng1Btn.getText()).toEqual('Click Count: 0'); - ng1Btn.click(); - expect(ng1Btn.getText()).toEqual('Click Count: 1'); + const ng1Btn = $('ng1 button'); + expect(await ng1Btn.getText()).toEqual('Click Count: 0'); + await ng1Btn.click(); + expect(await ng1Btn.getText()).toEqual('Click Count: 1'); }); }); }); -describe('async angular1/2 hybrid using downgrade application', function() { - it('should be able to click buttons and wait for $timeout', function() { - browser.get('/upgrade?downgrade'); - var rootBtn = $$('my-app button').first(); - expect(rootBtn.getText()).toEqual('Click Count: 0'); - rootBtn.click(); - expect(rootBtn.getText()).toEqual('Click Count: 1'); +describe('async angular1/2 hybrid using downgrade application', () => { + it('should be able to click buttons and wait for $timeout', async () => { + await browser.get('/upgrade?downgrade'); - var ng2Btn = $$('ng2 button').first(); - expect(ng2Btn.getText()).toEqual('Click Count: 0'); - ng2Btn.click(); - expect(ng2Btn.getText()).toEqual('Click Count: 1'); + const rootBtn = $$('my-app button').first(); + expect(await rootBtn.getText()).toEqual('Click Count: 0'); + await rootBtn.click(); + expect(await rootBtn.getText()).toEqual('Click Count: 1'); + + const ng2Btn = $$('ng2 button').first(); + expect(await ng2Btn.getText()).toEqual('Click Count: 0'); + await ng2Btn.click(); + expect(await ng2Btn.getText()).toEqual('Click Count: 1'); }); }); diff --git a/spec/hybridConf.js b/spec/hybridConf.js index 114c4d1aa..3bf95e24d 100644 --- a/spec/hybridConf.js +++ b/spec/hybridConf.js @@ -1,8 +1,9 @@ -var env = require('./environment'); +const env = require('./environment'); // This is the configuration for a smoke test for a hybrid ng1/ng2 application. exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/inferRootConf.js b/spec/inferRootConf.js index 939d07871..a4332dfc3 100644 --- a/spec/inferRootConf.js +++ b/spec/inferRootConf.js @@ -3,6 +3,7 @@ var env = require('./environment.js'); // Tests for an Angular app where ng-app is not on the body. exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/install/.gitignore b/spec/install/.gitignore index 8add3f42f..0c29ea66d 100644 --- a/spec/install/.gitignore +++ b/spec/install/.gitignore @@ -1,3 +1,4 @@ node_modules npm-debug.log tmp/ +package-lock.json \ No newline at end of file diff --git a/spec/install/browserjs_spec.js b/spec/install/browserjs_spec.js index f3fdb0c99..bd73afd5e 100644 --- a/spec/install/browserjs_spec.js +++ b/spec/install/browserjs_spec.js @@ -2,29 +2,22 @@ describe('browser', () => { let session1; let session2; - afterEach(() => { - browser.restart(); + afterEach(async () => { + await browser.restart(); }); - it('should load a browser session', (done) => { - browser.get('http://angularjs.org'); - browser.getSession().then(session => { - session1 = session.getId(); - expect(session1).not.toBeUndefined(); - }).catch(err => { - done.fail('session should be defined'); - }); - done(); + it('should load a browser session', async () => { + await browser.get('http://angularjs.org'); + const session = await browser.getSession(); + session1 = session.getId(); + expect(session1).not.toBeUndefined(); }); - it('should have a new browser session', (done) => { - browser.get('http://angularjs.org'); - browser.getSession().then(session => { - session2 = session.getId(); - expect(session2).not.toBeUndefined(); - expect(session1).not.toEqual(session2); - }).catch(err => { - done.fail('session should be defined'); - }); - done(); + + it('should have a new browser session', async () => { + await browser.get('http://angularjs.org'); + const session = await browser.getSession(); + session2 = session.getId(); + expect(session2).not.toBeUndefined(); + expect(session1).not.toEqual(session2); }); }); diff --git a/spec/install/browserts_spec.ts b/spec/install/browserts_spec.ts index b3a48ef76..3ba62c7cf 100644 --- a/spec/install/browserts_spec.ts +++ b/spec/install/browserts_spec.ts @@ -5,29 +5,22 @@ describe('browser', () => { let session1: string; let session2: string; - afterEach(() => { - browser.restart(); + afterEach(async () => { + await browser.restart(); }); - it('should load a browser session', (done) => { - browser.get('http://angularjs.org'); - browser.getSession().then(session => { - session1 = session.getId(); - expect(session1).not.toBeUndefined(); - }).catch(err => { - done.fail('session should be defined'); - }); - done(); + it('should load a browser session', async () => { + await browser.get('http://angularjs.org'); + const session = await browser.getSession(); + session1 = session.getId(); + expect(session1).not.toBeUndefined(); }); - it('should have a new browser session', (done) => { - browser.get('http://angularjs.org'); - browser.getSession().then(session => { - session2 = session.getId(); - expect(session2).not.toBeUndefined(); - expect(session1).not.toEqual(session2); - }).catch(err => { - done.fail('session should be defined'); - }); - done(); + + it('should have a new browser session', async () => { + await browser.get('http://angularjs.org'); + const session = await browser.getSession(); + session2 = session.getId(); + expect(session2).not.toBeUndefined(); + expect(session1).not.toEqual(session2); }); }); diff --git a/spec/install/conf.ts b/spec/install/conf.ts index cdb036b93..ee38a9e36 100644 --- a/spec/install/conf.ts +++ b/spec/install/conf.ts @@ -4,6 +4,7 @@ var env = require('../../environment'); export let config: Config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, capabilities: env.capabilities, specs: [ 'browserts_spec.js', diff --git a/spec/install/javascript_spec.js b/spec/install/javascript_spec.js index cbcfcdc66..caeabf8f6 100644 --- a/spec/install/javascript_spec.js +++ b/spec/install/javascript_spec.js @@ -8,15 +8,15 @@ describe('javascript', function () { expect(protractor.ExpectedConditions === ExpectedConditions).toBeTruthy(); }); it('should have selenium-webdriver components for the protractor namespace', function () { - expect(typeof protractor.promise.all).toEqual('function'); - expect(typeof protractor.promise.defer).toEqual('function'); - expect(typeof protractor.promise.Promise).toEqual('function'); - expect(typeof protractor.ActionSequence).toEqual('function'); + // expect(typeof protractor.promise.all).toEqual('function'); + // expect(typeof protractor.promise.defer).toEqual('function'); + // expect(typeof protractor.promise.Promise).toEqual('function'); + // expect(typeof protractor.ActionSequence).toEqual('function'); expect(typeof protractor.Browser).toEqual('object'); expect(typeof protractor.Builder).toEqual('function'); expect(typeof protractor.Capabilities).toEqual('function'); expect(typeof protractor.Capability).toEqual('object'); - expect(typeof protractor.EventEmitter).toEqual('function'); + // expect(typeof protractor.EventEmitter).toEqual('function'); expect(typeof protractor.FileDetector).toEqual('function'); expect(typeof protractor.Key).toEqual('object'); expect(typeof protractor.Session).toEqual('function'); diff --git a/spec/install/package.json b/spec/install/package.json index 7c4921858..c03569997 100644 --- a/spec/install/package.json +++ b/spec/install/package.json @@ -10,17 +10,13 @@ "author": "", "license": "MIT", "dependencies": { - "@types/jasmine": "^2.5.38", - "@types/selenium-webdriver": "^2.53.39", - "protractor": "file:../../", - "q": "^1.4.1", - "rimraf": "^2.5.4", - "selenium-webdriver": "^3.0.1", - "typescript": "^2.1.3" + "rimraf": "^2.5.4" }, "devDependencies": { "@types/jasmine": "^2.5.47", - "@types/jasminewd2": "^2.0.0", - "@types/q": "0.0.32" + "jasmine": "^2.8.0", + "protractor": "file:../../", + "selenium-webdriver": "^4.0.0-alpha.1", + "typescript": "^3.2.2" } } diff --git a/spec/install/test.js b/spec/install/test.js index 8e182e79a..c8088bcb9 100644 --- a/spec/install/test.js +++ b/spec/install/test.js @@ -11,23 +11,13 @@ class TestUtils { }; }; -function install() { - rimraf.sync(path.resolve(__dirname, 'node_modules')); - var options = {cwd: __dirname}; - var output = TestUtils.runCommand('npm', ['install'], options); - if (output && output[1]) { - console.log(output[1].toString()); - } else { - throw new Error('Something went wrong in function install.') - } -} - function tsc() { + rimraf.sync(path.resolve(__dirname, 'tmp')); var options = {cwd: __dirname}; var output = TestUtils.runCommand('npm', ['run', 'tsc'], options); if (output && output[1]) { var options = {cwd: path.resolve('.')}; - console.log(output[1].toString()); + console.log(output[2].toString()); if (output[1].toString().indexOf('error') >= 0) { throw new Error('tsc failed.'); } @@ -50,6 +40,8 @@ function test(file) { var failures = line.split(' specs, ')[1].charAt(0); if (failures !== '0') { throw new Error('Failed with ' + failures + ' failure(s).'); + } else { + console.log('must have passed?'); } } } @@ -58,7 +50,6 @@ function test(file) { } } -install(); tsc(); -test('tmp/conf.js'); -test('tmp/typescript_conf.js'); +// test('tmp/conf.js'); +// test('tmp/typescript_conf.js'); diff --git a/spec/install/tsconfig.json b/spec/install/tsconfig.json index 06128a465..73a453eaf 100644 --- a/spec/install/tsconfig.json +++ b/spec/install/tsconfig.json @@ -3,14 +3,14 @@ "target": "es6", "module": "commonjs", "moduleResolution": "node", - "sourceMap": false, - "declaration": false, - "noImplicitAny": false, + "sourceMap": true, + "declaration": true, + "removeComments": false, + "noImplicitAny": true, "types": ["node", "jasmine"], - "outDir": "tmp" + "outDir": "tmp/" }, "exclude": [ - "node_modules", - "typings/globals" + "node_modules" ] } diff --git a/spec/install/typescript_conf.ts b/spec/install/typescript_conf.ts index da229aabc..c3950a8eb 100644 --- a/spec/install/typescript_conf.ts +++ b/spec/install/typescript_conf.ts @@ -2,6 +2,7 @@ import {Config} from 'protractor'; export let config: Config = { mockSelenium: true, + SELENIUM_PROMISE_MANAGER: false, specs: ['typescript_spec.js'], framework: 'jasmine' } diff --git a/spec/install/typescript_spec.ts b/spec/install/typescript_spec.ts index f1cca8214..cdc140f8b 100644 --- a/spec/install/typescript_spec.ts +++ b/spec/install/typescript_spec.ts @@ -10,15 +10,15 @@ describe('typescript imports', () => { expect(protractor.ExpectedConditions === ExpectedConditions).toBeTruthy(); }); it('should have selenium-webdriver components for the protractor namespace', () => { - expect(typeof protractor.promise.all).toEqual('function'); - expect(typeof protractor.promise.defer).toEqual('function'); - expect(typeof protractor.promise.Promise).toEqual('function'); - expect(typeof protractor.ActionSequence).toEqual('function'); + // expect(typeof protractor.promise.all).toEqual('function'); + // expect(typeof protractor.promise.defer).toEqual('function'); + // expect(typeof protractor.promise.Promise).toEqual('function'); + // expect(typeof protractor.ActionSequence).toEqual('function'); expect(typeof protractor.Browser).toEqual('object'); expect(typeof protractor.Builder).toEqual('function'); expect(typeof protractor.Capabilities).toEqual('function'); expect(typeof protractor.Capability).toEqual('object'); - expect(typeof protractor.EventEmitter).toEqual('function'); + // expect(typeof protractor.EventEmitter).toEqual('function'); expect(typeof protractor.FileDetector).toEqual('function'); expect(typeof protractor.Key).toEqual('object'); expect(typeof protractor.Session).toEqual('function'); diff --git a/spec/interaction/interaction_spec.js b/spec/interaction/interaction_spec.js index ae68ddb86..3de1c7005 100644 --- a/spec/interaction/interaction_spec.js +++ b/spec/interaction/interaction_spec.js @@ -1,134 +1,134 @@ -describe('Browser', function() { +class Person { - var newBrowser; + constructor(name, browser) { + this.name = name; + this.browser = browser; + this.$ = browser.$; + this.element = browser.element; + } - afterEach(function(done) { + async openApp() { + await this.browser.get('index.html#/interaction'); + }; + + async login() { + await this.element(by.model('userInput')).sendKeys(this.name); + await this.$('#sendUser').click(); + }; + + async clearMessages() { + await this.$('#clearMessages').click(); + }; + + async sendMessage(msg) { + await this.element(by.model('message')).sendKeys(msg); + await this.$('#sendMessage').click(); + }; + + getMessages() { + return this.element.all(by.repeater('msg in messages track by $index')); + }; +}; + +describe('Browser', () => { + + let newBrowser; + + afterEach(async() => { // Calling quit will remove the browser. // You can choose to not quit the browser, and protractor will quit all of // them for you when it exits (i.e. if you need a static number of browsers // throughout all of your tests). However, I'm forking browsers in my tests // and don't want to pile up my browser count. if (newBrowser) { - newBrowser.quit().then(() => { - done(); - }); - } else { - done(); + await newBrowser.quit(); } }); - it('should be able to fork', function() { - browser.get('index.html'); - newBrowser = browser.forkNewDriverInstance(); + it('should be able to fork', async() => { + await browser.get('index.html'); + newBrowser = await browser.forkNewDriverInstance(); expect(newBrowser).not.toEqual(browser); expect(newBrowser.driver).not.toEqual(browser.driver); - expect(newBrowser.driver.getCurrentUrl()).toEqual('data:,'); + expect(await newBrowser.driver.getCurrentUrl()).toEqual('data:,'); }); - it('should be able to navigate to same url on fork', function() { - browser.get('index.html'); - newBrowser = browser.forkNewDriverInstance(true); - expect(newBrowser.driver.getCurrentUrl()). - toMatch('index.html#/form'); + it('should be able to navigate to same url on fork', async() => { + await browser.get('index.html'); + newBrowser = await browser.forkNewDriverInstance(true); + expect(await newBrowser.driver.getCurrentUrl()).toMatch('index.html#/form'); }); - it('should be able to copy mock modules on fork', function() { - var mockModule = function() { - var newModule = angular.module('mockModule', []); + it('should be able to copy mock modules on fork', async() => { + const mockModule = () => { + const newModule = angular.module('mockModule', []); newModule.value('version', '2'); }; browser.addMockModule('mockModule', mockModule); - browser.get('index.html'); + await browser.get('index.html'); - newBrowser = browser.forkNewDriverInstance(true, true); - expect(newBrowser.element(by.css('[app-version]')).getText()).toEqual('2'); + newBrowser = await browser.forkNewDriverInstance(true, true); + expect(await newBrowser.element(by.css('[app-version]')).getText()) + .toEqual('2'); }); - describe('Multiple browsers', function() { - - var Person = function(name, browser) { - var $ = browser.$; - var element = browser.element; - - this.openApp = function() { - browser.get('index.html#/interaction'); - }; - - this.login = function() { - element(by.model('userInput')).sendKeys(name); - $('#sendUser').click(); - }; - - this.clearMessages = function() { - $('#clearMessages').click(); - }; - - this.sendMessage = function(msg) { - element(by.model('message')).sendKeys(msg); - $('#sendMessage').click(); - }; - - this.getMessages = function() { - return element.all(by.repeater('msg in messages track by $index')); - }; - }; - - var p0, p1; + describe('Multiple browsers', () => { + let p0, p1; - beforeEach(function() { + beforeEach(async() => { // default browser. p0 = new Person('p0', browser); - p0.openApp(); - p0.login(); - p0.clearMessages(); + await p0.openApp(); + await p0.login(); + await p0.clearMessages(); // Any additional browsers can be instantiated via browser.forkNewDriverInstance(). - newBrowser = browser.forkNewDriverInstance(true); + newBrowser = await browser.forkNewDriverInstance(true); p1 = new Person('p1', newBrowser); - p1.openApp(); - p1.login(); + await p1.openApp(); + await p1.login(); }); - it('should be able to interact', function() { - expect(p0.getMessages().count()).toEqual(0); + it('should be able to interact', async() => { + expect(await p0.getMessages().count()).toEqual(0); - p0.sendMessage('p0'); - browser.sleep(100); // The app polls every 100ms for updates. - expect(p0.getMessages().count()).toEqual(1); - expect(p1.getMessages().count()).toEqual(1); + await p0.sendMessage('p0'); + await browser.sleep(100); // The app polls every 100ms for updates. + expect(await p0.getMessages().count()).toEqual(1); + expect(await p1.getMessages().count()).toEqual(1); - p1.sendMessage('p1'); - browser.sleep(100); // The app polls every 100ms for updates. - expect(p0.getMessages().count()).toEqual(2); - expect(p1.getMessages().count()).toEqual(2); + await p1.sendMessage('p1'); + await browser.sleep(100); // The app polls every 100ms for updates. + expect(await p0.getMessages().count()).toEqual(2); + expect(await p1.getMessages().count()).toEqual(2); }); - it('should perform actions in sync', function() { - var ACTIONS = 10; - expect(p0.getMessages().count()).toEqual(0); + it('should perform actions in sync', async() => { + const ACTIONS = 10; + expect(await p0.getMessages().count()).toEqual(0); - var expectedMessages = []; - var i; + let expectedMessages = []; + let i; for (i = 0; i < ACTIONS; ++i) { - p0.sendMessage(i); + await p0.sendMessage(i); expectedMessages.push('p0: ' + i); } for (i = 0; i < ACTIONS; ++i) { - p1.sendMessage(i); + await p1.sendMessage(i); expectedMessages.push('p1: ' + i); } for (i = 0; i < ACTIONS; ++i) { - p0.sendMessage(i); - p1.sendMessage(i); + await p0.sendMessage(i); + await p1.sendMessage(i); expectedMessages.push('p0: ' + i); expectedMessages.push('p1: ' + i); } - browser.sleep(100); // The app polls every 100ms for updates. - expect(p0.getMessages().getText()).toEqual(expectedMessages); - expect(p1.getMessages().getText()).toEqual(expectedMessages); + await browser.sleep(100); // The app polls every 100ms for updates. + expect(await p0.getMessages().getText()).toEqual(expectedMessages); + expect(await p1.getMessages().getText()).toEqual(expectedMessages); }); }); }); diff --git a/spec/interactionConf.js b/spec/interactionConf.js index daa9f76bd..4579bedf7 100644 --- a/spec/interactionConf.js +++ b/spec/interactionConf.js @@ -3,6 +3,7 @@ var env = require('./environment.js'); // Test having two browsers interacting with each other. exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/login/login_spec.js b/spec/login/login_spec.js index f39fd428b..8671477b2 100644 --- a/spec/login/login_spec.js +++ b/spec/login/login_spec.js @@ -1,15 +1,14 @@ -var env = require('../environment.js'); +const env = require('../environment.js'); -describe('pages with login', function() { - it('should log in with a non-Angular page', function() { - browser.get(env.baseUrl + '/ng1/index.html'); +describe('pages with login', () => { + it('should log in with a non-Angular page', async() => { + await browser.get(env.baseUrl + '/ng1/index.html'); - var angularElement = element(by.model('username')); - expect(angularElement.getAttribute('value')).toEqual('Anon'); + const angularElement = element(by.model('username')); + expect(await angularElement.getAttribute('value')).toEqual('Anon'); // Make sure the cookie is still set. - browser.manage().getCookie('testcookie').then(function(cookie) { - expect(cookie.value).toEqual('Jane-1234'); - }); + const cookie = await browser.manage().getCookie('testcookie'); + expect(cookie.value).toEqual('Jane-1234'); }); }); diff --git a/spec/mocha/lib_spec.js b/spec/mocha/lib_spec.js index 9b2f0d056..60da3c8b0 100644 --- a/spec/mocha/lib_spec.js +++ b/spec/mocha/lib_spec.js @@ -1,26 +1,26 @@ // Use the external Chai As Promised to deal with resolving promises in // expectations. -var chai = require('chai'); -var chaiAsPromised = require('chai-as-promised'); +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); chai.use(chaiAsPromised); -var expect = chai.expect; +const expect = chai.expect; // Chai's expect().to.exist style makes default jshint unhappy. // jshint expr:true -describe('no protractor at all', function() { - it('should still do normal tests', function() { +describe('no protractor at all', () => { + it('should still do normal tests', () => { expect(true).to.equal(true); }); }); -describe('protractor library', function() { - it.skip('should be able to skip tests', function() { +describe('protractor library', () => { + it.skip('should be able to skip tests', () => { expect(true).to.equal(false); }); - it('should expose the correct global variables', function() { + it('should expose the correct global variables', () => { expect(protractor).to.exist; expect(browser).to.exist; expect(by).to.exist; @@ -28,22 +28,26 @@ describe('protractor library', function() { expect($).to.exist; }); - it('should wrap webdriver', function() { + it('should wrap webdriver', async function() { // Mocha will report the spec as slow if it goes over this time in ms. this.slow(6000); - browser.get('index.html'); - expect(browser.getTitle()).to.eventually.equal('My AngularJS App'); + + await browser.get('index.html'); + expect(await browser.getTitle()).to.equal('My AngularJS App'); }); - describe('with async tests', function() { - var finished = false; + describe('with async tests', () => { + let finished = false; - it('should wait for async operations to finish', function() { - browser.get('index.html').then(function() { finished = true; }); + it('should wait for async operations to finish', async() => { + await browser.get('index.html'); + finished = true; }); - after('verify mocha waited', function() { - if(!finished) { throw new Error('Mocha did not wait for async!'); } + after('verify mocha waited', () => { + if(!finished) { + throw new Error('Mocha did not wait for async!'); + } }); }); }); diff --git a/spec/mochaConf.js b/spec/mochaConf.js index 2bd6c74f7..0327e2a77 100644 --- a/spec/mochaConf.js +++ b/spec/mochaConf.js @@ -3,6 +3,7 @@ var env = require('./environment.js'); // A small suite to make sure the mocha framework works. exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'mocha', diff --git a/spec/ng2/async_spec.js b/spec/ng2/async_spec.js index 351a6361a..316d8935f 100644 --- a/spec/ng2/async_spec.js +++ b/spec/ng2/async_spec.js @@ -1,82 +1,84 @@ -describe('async angular2 application', function() { - var URL = '/ng2/#/async'; +describe('async angular2 application', () => { + const URL = '/ng2/#/async'; - beforeEach(function() { - browser.get(URL); + beforeEach(async() => { + await browser.get(URL); }); - it('should work with synchronous actions', function() { - var increment = $('#increment'); - increment.$('.action').click(); + it('should work with synchronous actions', async() => { + const increment = $('#increment'); + await increment.$('.action').click(); - expect(increment.$('.val').getText()).toEqual('1'); + expect(await increment.$('.val').getText()).toEqual('1'); }); - it('should wait for asynchronous actions', function() { - var timeout = $('#delayedIncrement'); + it('should wait for asynchronous actions', async() => { + const timeout = $('#delayedIncrement'); // At this point, the async action is still pending, so the count should // still be 0. - expect(timeout.$('.val').getText()).toEqual('0'); + expect(await timeout.$('.val').getText()).toEqual('0'); - timeout.$('.action').click(); + await timeout.$('.action').click(); - expect(timeout.$('.val').getText()).toEqual('1'); + expect(await timeout.$('.val').getText()).toEqual('1'); }); - it('should turn off when ignoreSynchronization is true', function() { - var timeout = $('#delayedIncrement'); + it('should turn off when ignoreSynchronization is true', async() => { + // const timeout = $('#delayedIncrement'); // At this point, the async action is still pending, so the count should // still be 0. - expect(timeout.$('.val').getText()).toEqual('0'); + expect(await $('#delayedIncrement').$('.val').getText()).toEqual('0'); - browser.waitForAngularEnabled(false); + await browser.waitForAngularEnabled(false); - timeout.$('.action').click(); - timeout.$('.cancel').click(); + await $('#delayedIncrement').$('.action').click(); + await $('#delayedIncrement').$('.cancel').click(); - browser.waitForAngularEnabled(true); + await browser.waitForAngularEnabled(true); // whenStable should be called since the async action is cancelled. The // count should still be 0; - expect(timeout.$('.val').getText()).toEqual('0'); + expect(await $('#delayedIncrement').$('.val').getText()).toEqual('0'); }); - it('should wait for a series of asynchronous actions', function() { - var timeout = $('#chainedDelayedIncrements'); + it('should wait for a series of asynchronous actions', async() => { + const timeout = $('#chainedDelayedIncrements'); // At this point, the async action is still pending, so the count should // still be 0. - expect(timeout.$('.val').getText()).toEqual('0'); + expect(await timeout.$('.val').getText()).toEqual('0'); - timeout.$('.action').click(); + await timeout.$('.action').click(); - expect(timeout.$('.val').getText()).toEqual('10'); + expect(await timeout.$('.val').getText()).toEqual('10'); }); - describe('long async spec', function() { - var originalTimeout; - beforeEach(function() { + describe('long async spec', () => { + let originalTimeout; + beforeEach(() => { originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000; }); - it('should wait for a series of periodic increments', function() { - var timeout = $('#periodicIncrement_unzoned'); + it('should wait for a series of periodic increments', async() => { + const timeout = $('#periodicIncrement_unzoned'); // Waits for the val to count 2. - var EC = protractor.ExpectedConditions; - timeout.$('.action').click(); - browser.wait(EC.textToBePresentInElement(timeout.$('.val'), '1'), 4000); - timeout.$('.cancel').click(); - - var text = timeout.$('.val').getText(); - browser.driver.sleep(3000); - expect(timeout.$('.val').getText()).toEqual(text); + const EC = protractor.ExpectedConditions; + await timeout.$('.action').click(); + // Increase waiting time from 4s to 7s due to slow connection during SauceLabs tests + await browser.wait(EC.textToBePresentInElement(timeout.$('.val'), '1'), + 7000); + await timeout.$('.cancel').click(); + + const text = await timeout.$('.val').getText(); + await browser.driver.sleep(3000); + expect(await timeout.$('.val').getText()).toEqual(text); }); - afterEach(function() { + afterEach(() => { jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; }); }); diff --git a/spec/ng2/timeout_spec.js b/spec/ng2/timeout_spec.js index a023b67b2..fae6a7153 100644 --- a/spec/ng2/timeout_spec.js +++ b/spec/ng2/timeout_spec.js @@ -1,10 +1,10 @@ -describe('async angular2 application timeout', function() { - var URL = '/ng2/#/async'; +describe('async angular2 application timeout', () => { + const URL = '/ng2/#/async'; - it('should timeout if intervals are used in the NgZone', function() { - browser.get(URL); - var timeout = $('#periodicIncrement'); - timeout.$('.action').click(); - timeout.$('.cancel').click(); + it('should timeout if intervals are used in the NgZone', async () => { + await browser.get(URL); + const timeout = $('#periodicIncrement'); + await timeout.$('.action').click(); + await timeout.$('.cancel').click(); }); }); diff --git a/spec/noGlobals/noGlobals_spec.js b/spec/noGlobals/noGlobals_spec.js index 56051b99a..adde7ebdd 100644 --- a/spec/noGlobals/noGlobals_spec.js +++ b/spec/noGlobals/noGlobals_spec.js @@ -1,7 +1,7 @@ -describe('configuration with no globals', function() { - var URL = '/ng2/#/async'; +describe('configuration with no globals', () => { + const URL = '/ng2/#/async'; - it('should have objects belonging to protractor namespace', function() { + it('should have objects belonging to protractor namespace', () => { expect(typeof protractor).toEqual('object'); expect(typeof protractor.browser).toEqual('object'); expect(typeof protractor.$).toEqual('function'); @@ -11,7 +11,7 @@ describe('configuration with no globals', function() { expect(typeof protractor.By).toEqual('object'); }); - it('should not have other globals', function() { + it('should not have other globals', () => { expect(typeof browser).toEqual('undefined'); expect(typeof $).toEqual('undefined'); expect(typeof $$).toEqual('undefined'); @@ -20,11 +20,11 @@ describe('configuration with no globals', function() { expect(typeof By).toEqual('undefined'); }); - it('should be able to use methods under the protractor namespace', function() { - protractor.browser.get(URL); - var increment = protractor.$('#increment'); + it('should be able to use methods under the protractor namespace', async () => { + await protractor.browser.get(URL); + const increment = protractor.$('#increment'); expect(typeof increment).toEqual('object'); - increment.$('.action').click(); - expect(increment.$('.val').getText()).toEqual('1'); + await increment.$('.action').click(); + expect(await increment.$('.val').getText()).toEqual('1'); }); }); diff --git a/spec/noGlobalsConf.js b/spec/noGlobalsConf.js index 6b00ddac8..e93bd8cbc 100644 --- a/spec/noGlobalsConf.js +++ b/spec/noGlobalsConf.js @@ -1,9 +1,9 @@ -var env = require('./environment'); +const env = require('./environment'); // This is the configuration for a smoke test for an Angular2 application. exports.config = { - seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/onCleanUp/onCleanUp_spec.js b/spec/onCleanUp/onCleanUp_spec.js index 8522aaa80..7b6fe089b 100644 --- a/spec/onCleanUp/onCleanUp_spec.js +++ b/spec/onCleanUp/onCleanUp_spec.js @@ -1,5 +1,5 @@ -describe('onCleanUp function in the config', function() { - it('should not be affected by tests', function() { +describe('onCleanUp function in the config', () => { + it('should not be affected by tests', () => { expect(true).toBe(true); }); }); diff --git a/spec/onCleanUpAsyncReturnValueConf.js b/spec/onCleanUpAsyncReturnValueConf.js index 7c0a45f0f..56b3d0e85 100644 --- a/spec/onCleanUpAsyncReturnValueConf.js +++ b/spec/onCleanUpAsyncReturnValueConf.js @@ -1,9 +1,9 @@ -var env = require('./environment.js'); -var q = require('q'); +const env = require('./environment.js'); // Test that onCleanUp actions are performed. exports.config = { mockSelenium: true, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', @@ -15,11 +15,9 @@ exports.config = { baseUrl: env.baseUrl + '/ng1/', - onCleanUp: function(exitCode) { - var deferred = q.defer(); - setTimeout(function() { - deferred.resolve(exitCode); - }, 500); - return deferred.promise; + onCleanUp: async(exitCode) => { + return await new Promise(resolve => { + setTimeout(resolve(exitCode), 500); + }); } }; diff --git a/spec/onCleanUpNoReturnValueConf.js b/spec/onCleanUpNoReturnValueConf.js index 50353b3c4..b2bfdae1a 100644 --- a/spec/onCleanUpNoReturnValueConf.js +++ b/spec/onCleanUpNoReturnValueConf.js @@ -1,8 +1,9 @@ -var env = require('./environment.js'); +const env = require('./environment.js'); // Test that onCleanUp actions are performed. exports.config = { mockSelenium: true, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', @@ -14,7 +15,7 @@ exports.config = { baseUrl: env.baseUrl + '/ng1/', - onCleanUp: function(/* exitCode */) { + onCleanUp: (/* exitCode */) => { // no return } }; diff --git a/spec/onCleanUpSyncReturnValueConf.js b/spec/onCleanUpSyncReturnValueConf.js index 460df1558..2085db6d1 100644 --- a/spec/onCleanUpSyncReturnValueConf.js +++ b/spec/onCleanUpSyncReturnValueConf.js @@ -1,8 +1,9 @@ -var env = require('./environment.js'); +const env = require('./environment.js'); // Test that onCleanUp actions are performed. exports.config = { mockSelenium: true, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', @@ -14,7 +15,7 @@ exports.config = { baseUrl: env.baseUrl + '/ng1/', - onCleanUp: function(exitCode) { + onCleanUp: (exitCode) => { return exitCode; } }; diff --git a/spec/onPrepare/asyncstartup.js b/spec/onPrepare/asyncstartup.js index 5d5e0e593..9b0e9416e 100644 --- a/spec/onPrepare/asyncstartup.js +++ b/spec/onPrepare/asyncstartup.js @@ -1,5 +1,6 @@ -var q = require('q'); - -module.exports = q.fcall(function() { +module.exports = async() => { browser.params.password = '12345'; -}).delay(1000); + return await new Promise((resolve, _) => { + setTimeout(resolve, 1000); + }); +} diff --git a/spec/onPrepare/onPrepare_spec.js b/spec/onPrepare/onPrepare_spec.js index 9766ebccf..6c43e91de 100644 --- a/spec/onPrepare/onPrepare_spec.js +++ b/spec/onPrepare/onPrepare_spec.js @@ -1,5 +1,5 @@ -describe('onPrepare function in the config', function() { - it('should have a special variable set in onPrepare', function() { +describe('onPrepare function in the config', () => { + it('should have a special variable set in onPrepare', () => { expect(browser.params.password).toEqual('12345'); }); }); diff --git a/spec/onPrepareConf.js b/spec/onPrepareConf.js index 25f6d93b2..3359ff5d5 100644 --- a/spec/onPrepareConf.js +++ b/spec/onPrepareConf.js @@ -1,10 +1,11 @@ // Configuration using a function in onPrepare to set a parameter before // testing. -var env = require('./environment.js'); +const env = require('./environment.js'); // The main suite of Protractor tests. exports.config = { mockSelenium: true, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', @@ -16,7 +17,7 @@ exports.config = { baseUrl: env.baseUrl + '/ng1/', - onPrepare: function() { + onPrepare: () => { browser.params.password = '12345'; } }; diff --git a/spec/onPrepareFileConf.js b/spec/onPrepareFileConf.js index bd67998c1..32f0f0f57 100644 --- a/spec/onPrepareFileConf.js +++ b/spec/onPrepareFileConf.js @@ -1,9 +1,10 @@ -var env = require('./environment.js'); +const env = require('./environment.js'); // Configuration using a string in onPrepare to load a file with code to // execute once before tests. exports.config = { mockSelenium: true, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/onPreparePromiseConf.js b/spec/onPreparePromiseConf.js index 8ec3cb9c3..9906e1fef 100644 --- a/spec/onPreparePromiseConf.js +++ b/spec/onPreparePromiseConf.js @@ -1,11 +1,11 @@ // Configuration using a function in onPrepare to set a parameter before // testing. -var env = require('./environment.js'); -var q = require('q'); +const env = require('./environment.js'); // The main suite of Protractor tests. exports.config = { mockSelenium: true, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', @@ -17,9 +17,10 @@ exports.config = { baseUrl: env.baseUrl + '/ng1/', - onPrepare: function() { - return q.fcall(function() { - browser.params.password = '12345'; - }).delay(1000); + onPrepare: async() => { + browser.params.password = '12345'; + return await new Promise(resolve => { + setTimeout(resolve, 1000); + }); } }; diff --git a/spec/plugins/browserGetSyncedConf.js b/spec/plugins/browserGetSyncedConf.js index 87ef0e162..49957b8e9 100644 --- a/spec/plugins/browserGetSyncedConf.js +++ b/spec/plugins/browserGetSyncedConf.js @@ -1,9 +1,9 @@ -var env = require('../environment.js'), - q = require('q'); +const env = require('../environment.js'); // Make sure that borwser-related plugin hooks work with browser sync on exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', @@ -19,20 +19,26 @@ exports.config = { // Plugin patterns are relative to this directory. plugins: [{ inline: { - onPageLoad: function() { - return q.delay(5000).then(function() { - protractor.ON_PAGE_LOAD = true; + onPageLoad: async function() { + return await new Promise(resolve => { + setTimeout(() => { + protractor.ON_PAGE_LOAD = true; + resolve(); + }, 5000); }); }, - onPageStable: function() { + onPageStable: async function() { if (protractor.ON_PAGE_LOAD) { this.addSuccess(); } else { this.addFailure( 'onPageLoad did not finish before onPageStable began'); } - return q.delay(5000).then(function() { - protractor.ON_PAGE_SYNC = true; + return await new Promise(resolve => { + setTimeout(() => { + protractor.ON_PAGE_SYNC = true; + resolve(); + }, 5000); }); }, teardown: function() { diff --git a/spec/plugins/browserGetUnsyncedConf.js b/spec/plugins/browserGetUnsyncedConf.js index 7031d3950..87492a570 100644 --- a/spec/plugins/browserGetUnsyncedConf.js +++ b/spec/plugins/browserGetUnsyncedConf.js @@ -1,9 +1,9 @@ -var env = require('../environment.js'), - q = require('q'); +const env = require('../environment.js'); // Make sure that borwser-related plugin hooks work with browser sync off exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', @@ -19,17 +19,20 @@ exports.config = { // Plugin patterns are relative to this directory. plugins: [{ inline: { - setup: function() { - browser.ignoreSynchronization = true; + setup: async function() { + await browser.waitForAngularEnabled(false); }, - onPageLoad: function() { - return q.delay(5000).then(function() { - protractor.ON_PAGE_LOAD = true; + onPageLoad: async function() { + return await new Promise(resolve => { + setTimeout(() => { + protractor.ON_PAGE_LOAD = true; + resolve(); + }, 5000); }); }, onPageStable: function() { this.addFailure('onPageStable should not have ran when ' + - 'browser.ignoreSynchronization is true.'); + 'await browser.waitForAngularEnabled() is false.'); }, teardown: function() { if (protractor.ON_PAGE_LOAD) { diff --git a/spec/plugins/multiPluginConf.js b/spec/plugins/multiPluginConf.js index 44c799564..27986e5a3 100644 --- a/spec/plugins/multiPluginConf.js +++ b/spec/plugins/multiPluginConf.js @@ -3,6 +3,7 @@ var env = require('../environment.js'); // A small suite to make sure the full functionality of plugins work exports.config = { mockSelenium: true, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/plugins/plugins/async_plugin.js b/spec/plugins/plugins/async_plugin.js index 2530ed0c2..d34f69a00 100644 --- a/spec/plugins/plugins/async_plugin.js +++ b/spec/plugins/plugins/async_plugin.js @@ -1,29 +1,27 @@ -var q = require('q'); - module.exports = { - setup: function() { - var self = this; - return q.delay(100).then(function() { - self.addSuccess(); + setup: async function() { + await new Promise(resolve => { + setTimeout(resolve, 100); }); + this.addSuccess(); }, - teardown: function() { - var self = this; - return q.delay(100).then(function() { - self.addSuccess(); + teardown: async function() { + await new Promise(resolve => { + setTimeout(resolve, 100); }); + this.addSuccess(); }, postResults: function() { // This function should cause no failures. }, - postTest: function() { - var self = this; - return q.delay(100).then(function() { - self.addSuccess(); + postTest: async function() { + await new Promise(resolve => { + setTimeout(resolve, 100); }); + this.addSuccess(); }, name: 'some plugin name' diff --git a/spec/plugins/plugins/failing_plugin.js b/spec/plugins/plugins/failing_plugin.js index ad436faaf..ec78ef060 100644 --- a/spec/plugins/plugins/failing_plugin.js +++ b/spec/plugins/plugins/failing_plugin.js @@ -1,28 +1,26 @@ -var q = require('q'); - module.exports = { - setup: function() { - var self = this; - return q.delay(100).then(function() { - self.addFailure('from setup'); + setup: async function() { + await new Promise(resolve => { + setTimeout(resolve, 100); }); + self.addFailure('from setup'); }, - teardown: function() { - var self = this; - return q.delay(100).then(function() { - self.addFailure('from teardown'); + teardown: async function() { + await new Promise(resolve => { + setTimeout(resolve, 100); }); + self.addFailure('from teardown'); }, postResults: function() { // This function should cause no failures. }, - postTest: function(passed) { - var self = this; - return q.delay(100).then(function() { - self.addFailure('from postTest ' + (passed ? 'passing' : 'failing')); + postTest: async function(passed) { + await new Promise(resolve => { + setTimeout(resolve, 100); }); + self.addFailure('from postTest ' + (passed ? 'passing' : 'failing')); } }; diff --git a/spec/plugins/postTestConfTemplate.js b/spec/plugins/postTestConfTemplate.js index 185cfcbb4..41d68d564 100644 --- a/spec/plugins/postTestConfTemplate.js +++ b/spec/plugins/postTestConfTemplate.js @@ -1,8 +1,9 @@ var env = require('../environment.js'); -module.exports = function(framework) { +module.exports = (framework) => { return { mockSelenium: true, + SELENIUM_PROMISE_MANAGER: false, framework: framework, diff --git a/spec/plugins/skipStabilityConf.js b/spec/plugins/skipStabilityConf.js index 2b0f1c627..c5107876f 100644 --- a/spec/plugins/skipStabilityConf.js +++ b/spec/plugins/skipStabilityConf.js @@ -3,6 +3,7 @@ var env = require('../environment.js'); // Verifies that plugins can change skipAngularStability on the fly. exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/plugins/smokeConf.js b/spec/plugins/smokeConf.js index 44cbc5ef0..a1d2dd21d 100644 --- a/spec/plugins/smokeConf.js +++ b/spec/plugins/smokeConf.js @@ -4,6 +4,7 @@ var env = require('../environment.js'); // Tests the (potential) edge case of exactly one plugin being used exports.config = { mockSelenium: true, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/plugins/specs/bigger_spec.js b/spec/plugins/specs/bigger_spec.js index e803fe255..d17e13d7f 100644 --- a/spec/plugins/specs/bigger_spec.js +++ b/spec/plugins/specs/bigger_spec.js @@ -1,9 +1,9 @@ -describe('check if plugin setup ran', function() { - it('should have set protractor.__BASIC_PLUGIN_RAN', function() { +describe('check if plugin setup ran', () => { + it('should have set protractor.__BASIC_PLUGIN_RAN', () => { expect(protractor.__BASIC_PLUGIN_RAN_SETUP).toBe(true); }); - it('should have set protractor.__INLINE_PLUGIN_RAN', function() { + it('should have set protractor.__INLINE_PLUGIN_RAN', () => { expect(protractor.__INLINE_PLUGIN_RAN).toBe(true); }); }); diff --git a/spec/plugins/specs/browser_get_wait_spec.js b/spec/plugins/specs/browser_get_wait_spec.js index 70a7a9d4c..26130f54d 100644 --- a/spec/plugins/specs/browser_get_wait_spec.js +++ b/spec/plugins/specs/browser_get_wait_spec.js @@ -1,6 +1,6 @@ -describe('category', function() { - it('name', function() { - browser.get('index.html'); - browser.waitForAngular(); +describe('category', () => { + it('name', async() => { + await browser.get('index.html'); + await browser.waitForAngular(); }); }); diff --git a/spec/plugins/specs/fail_spec.js b/spec/plugins/specs/fail_spec.js index 955fcc500..a24463c0e 100644 --- a/spec/plugins/specs/fail_spec.js +++ b/spec/plugins/specs/fail_spec.js @@ -1,9 +1,9 @@ -describe('check if plugin setup ran', function() { - it('should have set protractor.__BASIC_PLUGIN_RAN', function() { +describe('check if plugin setup ran', () => { + it('should have set protractor.__BASIC_PLUGIN_RAN', () => { expect(protractor.__BASIC_PLUGIN_RAN_SETUP).toBe(true); }); - it('should run multiple tests which fail', function() { + it('should run multiple tests which fail', () => { expect(true).toBe(false); }); }); diff --git a/spec/plugins/specs/simple_spec.js b/spec/plugins/specs/simple_spec.js index ab92cf579..d34218bfb 100644 --- a/spec/plugins/specs/simple_spec.js +++ b/spec/plugins/specs/simple_spec.js @@ -1,4 +1,4 @@ -describe('category', function() { - it('name', function() { +describe('category', () => { + it('name', () => { }); }); diff --git a/spec/plugins/specs/smoke_spec.js b/spec/plugins/specs/smoke_spec.js index bc7f67f1b..2dafffaf0 100644 --- a/spec/plugins/specs/smoke_spec.js +++ b/spec/plugins/specs/smoke_spec.js @@ -1,5 +1,5 @@ -describe('check if plugin setup ran', function() { - it('should have set protractor.__BASIC_PLUGIN_RAN_*', function() { +describe('check if plugin setup ran', () => { + it('should have set protractor.__BASIC_PLUGIN_RAN_*', () => { expect(protractor.__BASIC_PLUGIN_RAN_SETUP).toBe(true); expect(protractor.__BASIC_PLUGIN_RAN_ON_PREPARE).toBe(true); }); diff --git a/spec/plugins/waitForAngularConf.js b/spec/plugins/waitForAngularConf.js index 1e29fbe56..82177161f 100644 --- a/spec/plugins/waitForAngularConf.js +++ b/spec/plugins/waitForAngularConf.js @@ -1,9 +1,9 @@ -var env = require('../environment.js'), - q = require('q'); +var env = require('../environment.js'); // A small suite to make sure that the plugin hooks for waitForAngular work exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', @@ -19,9 +19,12 @@ exports.config = { // Plugin patterns are relative to this directory. plugins: [{ inline: { - waitForPromise: function(/* oldURL */) { - return q.delay(5000).then(function() { - protractor.WAIT_FOR_PROMISE = true; + waitForPromise: async function() { + return await new Promise(resolve => { + setTimeout(() => { + protractor.WAIT_FOR_PROMISE = true; + resolve(); + }, 5000); }); }, waitForCondition: function() { diff --git a/spec/restartBrowserBetweenTests/setCookies_spec.js b/spec/restartBrowserBetweenTests/setCookies_spec.js index c461db8f4..6d21c5fa4 100644 --- a/spec/restartBrowserBetweenTests/setCookies_spec.js +++ b/spec/restartBrowserBetweenTests/setCookies_spec.js @@ -1,23 +1,21 @@ -var env = require('../environment.js'); +const env = require('../environment.js'); -describe('pages with login', function() { - it('should set a cookie', function() { - browser.get(env.baseUrl + '/ng1/index.html'); +describe('pages with login', () => { + it('should set a cookie', async() => { + await browser.get(env.baseUrl + '/ng1/index.html'); - browser.manage().addCookie({name:'testcookie', value: 'Jane-1234'}); + await browser.manage().addCookie({name:'testcookie', value: 'Jane-1234'}); // Make sure the cookie is set. - browser.manage().getCookie('testcookie').then(function(cookie) { - expect(cookie.value).toEqual('Jane-1234'); - }); + const cookie = await browser.manage().getCookie('testcookie'); + expect(cookie.value).toEqual('Jane-1234'); }); - it('should check the cookie is gone', function() { - browser.get(env.baseUrl + '/ng1/index.html'); + it('should check the cookie is gone', async() => { + await browser.get(env.baseUrl + '/ng1/index.html'); // Make sure the cookie is gone. - browser.manage().getCookie('testcookie').then(function(cookie) { - expect(cookie).toEqual(null); - }); + const cookie = await browser.manage().getCookie('testcookie'); + expect(cookie).toEqual(null); }); }); diff --git a/spec/restartBrowserBetweenTestsConf.js b/spec/restartBrowserBetweenTestsConf.js index d74cf0bef..86277d8ab 100644 --- a/spec/restartBrowserBetweenTestsConf.js +++ b/spec/restartBrowserBetweenTestsConf.js @@ -1,8 +1,9 @@ -var env = require('./environment.js'); +const env = require('./environment.js'); // The main suite of Protractor tests. exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/suites/always_fail_spec.js b/spec/suites/always_fail_spec.js index 493beb95b..b35492385 100644 --- a/spec/suites/always_fail_spec.js +++ b/spec/suites/always_fail_spec.js @@ -1,5 +1,5 @@ -describe('This test suite', function() { - it('should never be ran through the --suite option', function() { +describe('This test suite', () => { + it('should never be ran through the --suite option', () => { expect(true).toBe(false); }); }); diff --git a/spec/suites/ok_2_spec.js b/spec/suites/ok_2_spec.js index c39678bee..88cf2c860 100644 --- a/spec/suites/ok_2_spec.js +++ b/spec/suites/ok_2_spec.js @@ -1,5 +1,5 @@ -describe('This test suite', function() { - it('should be ran through the --suite option', function() { +describe('This test suite', () => { + it('should be ran through the --suite option', () => { expect(true).toBe(true); }); }); diff --git a/spec/suites/ok_spec.js b/spec/suites/ok_spec.js index c39678bee..88cf2c860 100644 --- a/spec/suites/ok_spec.js +++ b/spec/suites/ok_spec.js @@ -1,5 +1,5 @@ -describe('This test suite', function() { - it('should be ran through the --suite option', function() { +describe('This test suite', () => { + it('should be ran through the --suite option', () => { expect(true).toBe(true); }); }); diff --git a/spec/suitesConf.js b/spec/suitesConf.js index 2705378aa..3f6927638 100644 --- a/spec/suitesConf.js +++ b/spec/suitesConf.js @@ -2,6 +2,7 @@ var env = require('./environment.js'); exports.config = { mockSelenium: true, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', diff --git a/spec/ts/basic/element_spec.ts b/spec/ts/basic/element_spec.ts index 18ca6a7da..75716f73b 100644 --- a/spec/ts/basic/element_spec.ts +++ b/spec/ts/basic/element_spec.ts @@ -1,186 +1,189 @@ // Based off of spec/basic/elements_spec.js -import * as q from 'q'; +import {$, browser, by, element, ElementArrayFinder, ElementFinder, promise as ppromise, WebElement} from '../../..'; -import {$, $$, browser, by, By, element, ElementArrayFinder, ElementFinder, ExpectedConditions, promise as ppromise, WebElement} from '../../..'; - -describe('ElementFinder', function() { - it('should return the same result as browser.findElement', async function() { +describe('ElementFinder', () => { + it('should return the same result as browser.findElement', async() => { await browser.get('index.html#/form'); const nameByElement = element(by.binding('username')); - await expect(nameByElement.getText()) - .toEqual(browser.findElement(by.binding('username')).getText()); + expect(await nameByElement.getText()) + .toEqual(await browser.findElement(by.binding('username')).getText()); }); - it('should wait to grab the WebElement until a method is called', async function() { + it('should wait to grab the WebElement until a method is called', async() => { // These should throw no error before a page is loaded. const usernameInput = element(by.model('username')); const name = element(by.binding('username')); await browser.get('index.html#/form'); - await expect(name.getText()).toEqual('Anon'); + expect(await name.getText()).toEqual('Anon'); await usernameInput.clear(); await usernameInput.sendKeys('Jane'); - await expect(name.getText()).toEqual('Jane'); + expect(await name.getText()).toEqual('Jane'); }); - it('should chain element actions', async function() { + it('should chain element actions', async() => { await browser.get('index.html#/form'); const usernameInput = element(by.model('username')); const name = element(by.binding('username')); - await expect(name.getText()).toEqual('Anon'); + expect(await name.getText()).toEqual('Anon'); await((usernameInput.clear() as any) as ElementFinder).sendKeys('Jane'); - await expect(name.getText()).toEqual('Jane'); + expect(await name.getText()).toEqual('Jane'); }); - it('should run chained element actions in sequence', function(done: any) { + it('should run chained element actions in sequence', async(done: any) => { // Testing private methods is bad :( let els = new ElementArrayFinder(browser, () => { - return ppromise.when([null as WebElement]); + return Promise.resolve([null as WebElement]); }); let applyAction_: (actionFn: (value: WebElement, index: number, array: WebElement[]) => any) => ElementArrayFinder = (ElementArrayFinder as any).prototype.applyAction_; let order: string[] = []; - let deferredA = q.defer(); + let deferredA: Promise; els = applyAction_.call(els, () => { - return deferredA.promise.then(() => { + deferredA = new Promise(resolve => { order.push('a'); + resolve(); }); }); - let deferredB = q.defer(); + let deferredB: Promise; els = applyAction_.call(els, () => { - return deferredB.promise.then(() => { + deferredB = new Promise(resolve => { order.push('b'); + resolve(); }); }); - deferredB.resolve(); + await deferredB; setTimeout(async function() { - deferredA.resolve(); + await deferredA; await els; expect(order).toEqual(['a', 'b']); done(); }, 100); }); - it('chained call should wait to grab the WebElement until a method is called', async function() { + it('chained call should wait to grab the WebElement until a method is called', + async() => { // These should throw no error before a page is loaded. - const reused = element(by.id('baz')).element(by.binding('item.reusedBinding')); + const reused = element(by.id('baz')) + .element(by.binding('item.reusedBinding')); await browser.get('index.html#/conflict'); - await expect(reused.getText()).toEqual('Inner: inner'); - await expect(reused.isPresent()).toBe(true); + expect(await reused.getText()).toEqual('Inner: inner'); + expect(await reused.isPresent()).toBe(true); }); - it('should differentiate elements with the same binding by chaining', async function() { + it('should differentiate elements with the same binding by chaining', + async() => { await browser.get('index.html#/conflict'); const outerReused = element(by.binding('item.reusedBinding')); const innerReused = element(by.id('baz')).element(by.binding('item.reusedBinding')); - await expect(outerReused.getText()).toEqual('Outer: outer'); - await expect(innerReused.getText()).toEqual('Inner: inner'); + expect(await outerReused.getText()).toEqual('Outer: outer'); + expect(await innerReused.getText()).toEqual('Inner: inner'); }); - it('should chain deeper than 2', async function() { + it('should chain deeper than 2', async() => { // These should throw no error before a page is loaded. - const reused = - element(by.css('body')).element(by.id('baz')).element(by.binding('item.reusedBinding')); + const reused = element(by.css('body')).element(by.id('baz')) + .element(by.binding('item.reusedBinding')); await browser.get('index.html#/conflict'); - await expect(reused.getText()).toEqual('Inner: inner'); + expect(await reused.getText()).toEqual('Inner: inner'); }); - it('should allow handling errors', async function() { + it('should allow handling errors', async() => { await browser.get('index.html#/form'); try { await $('.nopenopenope').getText(); // The above line should have throw an error. Fail. - await expect(true).toEqual(false); + expect(true).toEqual(false); } catch (e) { - await expect(true).toEqual(true); + expect(true).toEqual(true); } }); - it('should allow handling chained errors', async function() { + it('should allow handling chained errors', async() => { await browser.get('index.html#/form'); try { await $('.nopenopenope').$('furthernope').getText(); // The above line should have throw an error. Fail. - await expect(true).toEqual(false); + expect(true).toEqual(false); } catch (e) { - await expect(true).toEqual(true); + expect(true).toEqual(true); } }); - it('should keep a reference to the original locator', async function() { + it('should keep a reference to the original locator', async() => { await browser.get('index.html#/form'); const byCss = by.css('body'); const byBinding = by.binding('greet'); - await expect(element(byCss).locator()).toEqual(byCss); - await expect(element(byBinding).locator()).toEqual(byBinding); + expect(await element(byCss).locator()).toEqual(byCss); + expect(await element(byBinding).locator()).toEqual(byBinding); }); - it('should propagate exceptions', async function() { + it('should propagate exceptions', async() => { await browser.get('index.html#/form'); const invalidElement = element(by.binding('INVALID')); const successful = invalidElement.getText().then( function() { return true; - } as any as (() => ppromise.Promise), + } as any as (() => Promise), function() { return false; - } as any as (() => ppromise.Promise)); - await expect(successful).toEqual(false); + } as any as (() => Promise)); + expect(await successful).toEqual(false); }); - it('should be returned from a helper without infinite loops', async function() { + it('should be returned from a helper without infinite loops', async() => { await browser.get('index.html#/form'); - const helperPromise = ppromise.when(true).then(function() { + const helperPromise = Promise.resolve(true).then(() => { return element(by.binding('greeting')); }); - await helperPromise.then(async function(finalResult: ElementFinder) { - await expect(finalResult.getText()).toEqual('Hiya'); - } as any as (() => ppromise.Promise)); + await helperPromise.then(async(finalResult: ElementFinder) => { + expect(await finalResult.getText()).toEqual('Hiya'); + }); }); - it('should be usable in WebDriver functions', async function() { + it('should be usable in WebDriver functions', async() => { await browser.get('index.html#/form'); const greeting = element(by.binding('greeting')); await browser.executeScript('arguments[0].scrollIntoView', greeting); }); - it('should allow null as success handler', async function() { + it('should allow null as success handler', async() => { await browser.get('index.html#/form'); const name = element(by.binding('username')); - await expect(name.getText()).toEqual('Anon'); - await expect(name.getText().then(null, function() {})).toEqual('Anon'); + expect(await name.getText()).toEqual('Anon'); + expect(await name.getText().then(null, function() {})).toEqual('Anon'); }); - it('should check equality correctly', async function() { + it('should check equality correctly', async() => { await browser.get('index.html#/form'); const usernameInput = element(by.model('username')); const name = element(by.binding('username')); - await expect(usernameInput.equals(usernameInput)).toEqual(true); - await expect(usernameInput.equals(name)).toEqual(false); + expect(await usernameInput.equals(usernameInput)).toEqual(true); + expect(await usernameInput.equals(name)).toEqual(false); }); }); diff --git a/spec/ts/basic/is_disabled_spec.ts b/spec/ts/basic/is_disabled_spec.ts index 0ccc20ff8..1c95383f8 100644 --- a/spec/ts/basic/is_disabled_spec.ts +++ b/spec/ts/basic/is_disabled_spec.ts @@ -1,16 +1,7 @@ -import {browser, promise as ppromise} from '../../..'; +import {promise as wdpromise} from '../../..'; -describe('verify control flow is off', function() { +describe('verify control flow is off', () => { it('should have set webdriver.promise.USE_PROMISE_MANAGER', () => { - expect((ppromise as any).USE_PROMISE_MANAGER).toBe(false); - }); - - it('should not wait on one command before starting another', async function() { - // Wait forever - browser.controlFlow().wait( - (browser.controlFlow() as any).promise((): void => undefined) as ppromise.Promise); - - // then return - await browser.controlFlow().execute(() => ppromise.when(null)); + expect((wdpromise as any).USE_PROMISE_MANAGER).toBe(false); }); }); diff --git a/spec/ts/noCFPluginConf.ts b/spec/ts/noCFPluginConf.ts index fac32bf7a..3f62f7319 100644 --- a/spec/ts/noCFPluginConf.ts +++ b/spec/ts/noCFPluginConf.ts @@ -1,6 +1,4 @@ -import * as q from 'q'; import {Config, protractor} from '../..'; -import {promise as wdpromise} from 'selenium-webdriver'; const env = require('../environment.js'); export let config: Config = { @@ -20,11 +18,11 @@ export let config: Config = { plugins: [{ inline: { - onPageLoad: function() { - //TODO: remove cast when @types/selenium-webdriver understands disabling the control flow - return (q.delay(100) as any as wdpromise.Promise).then(function() { - (protractor as any).ON_PAGE_LOAD = true; + onPageLoad: async function() { + await new Promise(resolve => { + setTimeout(resolve, 100); }); + (protractor as any).ON_PAGE_LOAD = true; } } }] diff --git a/spec/ts/plugin/plugin_spec.ts b/spec/ts/plugin/plugin_spec.ts index 2ee4cc2b5..bcbfdc84b 100644 --- a/spec/ts/plugin/plugin_spec.ts +++ b/spec/ts/plugin/plugin_spec.ts @@ -1,8 +1,8 @@ import {browser, protractor} from '../../..'; -describe('plugins', function() { - it('should have run the onPageLoad hook', async function() { +describe('plugins', () => { + it('should have run the onPageLoad hook', async() => { await browser.get('index.html'); - await expect((protractor as any).ON_PAGE_LOAD).toBe(true); + expect((protractor as any).ON_PAGE_LOAD).toBe(true); }); }); diff --git a/spec/unit/driverProviders/direct_test.js b/spec/unit/driverProviders/direct_test.js index c105608fa..be2e02f16 100644 --- a/spec/unit/driverProviders/direct_test.js +++ b/spec/unit/driverProviders/direct_test.js @@ -1,35 +1,35 @@ -var fs = require('fs'), +const fs = require('fs'), os = require('os'), path = require('path'); -var BrowserError = require('../../../built/exitCodes').BrowserError; -var ProtractorError = require('../../../built/exitCodes').ProtractorError; -var Logger = require('../../../built/logger').Logger; -var WriteTo = require('../../../built/logger').WriteTo; -var Direct = require('../../../built/driverProviders').Direct; -var webdriver, file; +const BrowserError = require('../../../built/exitCodes').BrowserError; +const ProtractorError = require('../../../built/exitCodes').ProtractorError; +const Logger = require('../../../built/logger').Logger; +const WriteTo = require('../../../built/logger').WriteTo; +const Direct = require('../../../built/driverProviders').Direct; +let webdriver, file; -describe('direct connect', function() { - beforeEach(function() { +describe('direct connect', () => { + beforeEach(() => { ProtractorError.SUPRESS_EXIT_CODE = true; Logger.setWrite(WriteTo.NONE); }); - afterEach(function() { + afterEach(() => { ProtractorError.SUPRESS_EXIT_CODE = false; Logger.setWrite(WriteTo.CONSOLE); }); - describe('without the chrome driver', function() { - it('should throw an error if no drivers are present', function() { - var config = { + describe('without the chrome driver', () => { + it('should throw an error if no drivers are present', async () => { + const config = { directConnect: true, capabilities: { browserName: 'chrome' }, chromeDriver: '/foo/bar/chromedriver' }; - var errorFound = false; + let errorFound = false; try { webdriver = new Direct(config); - webdriver.getNewDriver(); + await webdriver.getNewDriver(); } catch(e) { errorFound = true; expect(e.code).toBe(BrowserError.CODE); @@ -38,32 +38,32 @@ describe('direct connect', function() { }); }); - describe('with chromedriver drivers', function() { - var chromedriver = ''; - beforeEach(function() { + describe('with chromedriver drivers', () => { + let chromedriver = ''; + beforeEach(() => { // add files to selenium folder file = 'chromedriver'; chromedriver = path.resolve(os.tmpdir(), file); fs.openSync(chromedriver, 'w'); }); - afterEach(function() { + afterEach(() => { try { fs.unlinkSync(chromedriver); } catch(e) { } }); - it('should throw an error if the driver cannot be used', function() { - var config = { + it('should throw an error if the driver cannot be used', async () => { + const config = { directConnect: true, capabilities: { browserName: 'foobar explorer' }, chromeDriver: chromedriver }; - var errorFound = false; + let errorFound = false; try { webdriver = new Direct(config); - webdriver.getNewDriver(); + await webdriver.getNewDriver(); } catch(e) { errorFound = true; expect(e.code).toBe(BrowserError.CODE); @@ -73,16 +73,16 @@ describe('direct connect', function() { }); describe('binary does not exist', () => { - it('should throw an error if the update-config.json does not exist', () => { + it('should throw an error if the update-config.json does not exist', async () => { spyOn(fs, 'readFileSync').and.callFake(() => { return null; }); - var config = { + const config = { directConnect: true, capabilities: { browserName: 'chrome' }, }; - var errorFound = false; + let errorFound = false; try { webdriver = new Direct(config); - webdriver.getNewDriver(); + await webdriver.getNewDriver(); } catch(e) { errorFound = true; expect(e.code).toBe(BrowserError.CODE); diff --git a/spec/unit/driverProviders/local_test.js b/spec/unit/driverProviders/local_test.js index 052282551..80d2bdd24 100644 --- a/spec/unit/driverProviders/local_test.js +++ b/spec/unit/driverProviders/local_test.js @@ -1,34 +1,34 @@ -var fs = require('fs'), +const fs = require('fs'), os = require('os'), path = require('path'); -var BrowserError = require('../../../built/exitCodes').BrowserError; -var ProtractorError = require('../../../built/exitCodes').ProtractorError; -var Logger = require('../../../built/logger').Logger; -var WriteTo = require('../../../built/logger').WriteTo; -var Local = require('../../../built/driverProviders').Local; -var webdriver, file; +const BrowserError = require('../../../built/exitCodes').BrowserError; +const ProtractorError = require('../../../built/exitCodes').ProtractorError; +const Logger = require('../../../built/logger').Logger; +const WriteTo = require('../../../built/logger').WriteTo; +const Local = require('../../../built/driverProviders').Local; +let webdriver, file; -describe('local connect', function() { - beforeEach(function() { +describe('local connect', () => { + beforeEach(() => { ProtractorError.SUPRESS_EXIT_CODE = true; Logger.setWrite(WriteTo.NONE); }); - afterEach(function() { + afterEach(() => { ProtractorError.SUPRESS_EXIT_CODE = false; Logger.setWrite(WriteTo.CONSOLE); }); - describe('without the selenium standalone jar', function() { - it('should throw an error jar file is not present', function() { - var config = { + describe('without the selenium standalone jar', () => { + it('should throw an error jar file is not present', async () => { + const config = { capabilities: { browserName: 'chrome' }, seleniumServerJar: '/foo/bar/selenium.jar' }; - var errorFound = false; + let errorFound = false; try { webdriver = new Local(config); - webdriver.setupEnv(); + await webdriver.setupEnv(); } catch(e) { errorFound = true; expect(e.code).toBe(BrowserError.CODE); @@ -37,52 +37,36 @@ describe('local connect', function() { }); }); - describe('with the selenium standalone jar', function() { - it('should throw an error if the jar file does not work', function() { - var jarFile = ''; - beforeEach(function() { - // add files to selenium folder - file = 'selenium.jar'; - jarFile = path.resolve(os.tmpdir(), file); - fs.openSync(jarFile, 'w'); - }); - - afterEach(function() { - try { - fs.unlinkSync(jarFile); - } catch(e) { - } - }); - - it('should throw an error if the selenium sever jar cannot be used', function() { - var config = { - capabilities: { browserName: 'foobar explorer' }, - seleniumServerJar: jarFile - }; - var errorFound = false; - try { - webdriver = new Local(config); - webdriver.getNewDriver(); - } catch(e) { - errorFound = true; - expect(e.code).toBe(BrowserError.CODE); - } - expect(errorFound).toBe(true); - }); + describe('with the selenium standalone jar', () => { + it('should throw an error if the selenium sever jar cannot be used', async () => { + let jarFile = ''; + const config = { + capabilities: { browserName: 'foobar explorer' }, + seleniumServerJar: jarFile + }; + let errorFound = false; + try { + webdriver = new Local(config); + await webdriver.getNewDriver(); + } catch(e) { + errorFound = true; + expect(e.code).toBe(BrowserError.CODE); + } + expect(errorFound).toBe(true); }); }); describe('binary does not exist', () => { - it('should throw an error if the update-config.json does not exist', () => { + it('should throw an error if the update-config.json does not exist', async () => { spyOn(fs, 'readFileSync').and.callFake(() => { return null; }); - var config = { + const config = { capabilities: { browserName: 'chrome' }, openSync: fs.openSync }; - var errorFound = false; + let errorFound = false; try { webdriver = new Local(config); - webdriver.setupDriverEnv(); + await webdriver.setupDriverEnv(); } catch(e) { errorFound = true; expect(e.code).toBe(BrowserError.CODE); diff --git a/spec/unit/runner_test.js b/spec/unit/runner_test.js index 411c0aeb1..fa68be8cd 100644 --- a/spec/unit/runner_test.js +++ b/spec/unit/runner_test.js @@ -2,69 +2,71 @@ var Runner = require('../../built/runner').Runner; var Logger = require('../../built/logger').Logger, WriteTo = require('../../built/logger').WriteTo; -describe('the Protractor runner', function() { - beforeAll(function() { +describe('the Protractor runner', () => { + beforeAll(() => { Logger.writeTo = WriteTo.NONE; }); - afterAll(function() { + afterAll(() => { Logger.writeTo = WriteTo.CONSOLE; }); - it('should export its config', function() { - var config = { + it('should export its config', () => { + const config = { foo: 'bar' }; - var runner = new Runner(config); + const runner = new Runner(config); expect(runner.getConfig()).toEqual(config); }); - it('should run', function(done) { - var config = { + it('should run', async () => { + const config = { mockSelenium: true, specs: ['*.js'], framework: 'debugprint' }; - var exitCode; - var runner = new Runner(config); + let exitCode; + const runner = new Runner(config); runner.exit_ = function(exit) { exitCode = exit; }; - runner.run().then(function() { - expect(exitCode).toEqual(0); - done(); - }); + await runner.run() + expect(exitCode).toEqual(0); }); - it('should fail with no specs', function() { - var config = { + it('should fail with no specs', async () => { + const config = { mockSelenium: true, specs: [], framework: 'debugprint' }; - var exitCode; - Runner.prototype.exit_ = function(exit) { - exitCode = exit; - }; - var runner = new Runner(config); - expect(function() { - runner.run(); - }).toThrow(); + const runner = new Runner(config); + let errMessage = 'No error found'; + try { + await runner.run() + } catch (err) { + errMessage = err.message; + } + expect(errMessage).not.toBe('No error found'); }); - it('should fail when no custom framework is defined', function(done) { - var config = { + it('should fail when no custom framework is defined', async () => { + const config = { mockSelenium: true, specs: ['*.js'], framework: 'custom' }; - var runner = new Runner(config); - runner.run().then(function() { - done.fail('expected error when no custom framework is defined'); - }, done); + const runner = new Runner(config); + let errMessage = 'No error found'; + try { + await runner.run() + } catch (err) { + errMessage = err.message; + } + expect(errMessage).not.toBe('No error found'); }); }); diff --git a/spec/withLoginConf.js b/spec/withLoginConf.js index f51c3e5c7..de7902473 100644 --- a/spec/withLoginConf.js +++ b/spec/withLoginConf.js @@ -4,6 +4,7 @@ var env = require('./environment.js'); // handle log-in using the onPrepare field. exports.config = { seleniumAddress: env.seleniumAddress, + SELENIUM_PROMISE_MANAGER: false, framework: 'jasmine', @@ -15,20 +16,19 @@ exports.config = { baseUrl: env.baseUrl + '/ng1/', - onPrepare: function() { - browser.driver.get(env.baseUrl + '/ng1/login.html'); + onPrepare: async() => { + await browser.driver.get(env.baseUrl + '/ng1/login.html'); - browser.driver.findElement(by.id('username')).sendKeys('Jane'); - browser.driver.findElement(by.id('password')).sendKeys('1234'); - browser.driver.findElement(by.id('clickme')).click(); + await browser.driver.findElement(by.id('username')).sendKeys('Jane'); + await browser.driver.findElement(by.id('password')).sendKeys('1234'); + await browser.driver.findElement(by.id('clickme')).click(); // Login takes some time, so wait until it's done. // For the test app's login, we know it's done when it redirects to // index.html. - return browser.driver.wait(function() { - return browser.driver.getCurrentUrl().then(function(url) { - return /index/.test(url); - }); + return await browser.driver.wait(async() => { + const url = await browser.driver.getCurrentUrl(); + return /index/.test(url); }, 10000); } }; diff --git a/tsconfig.json b/tsconfig.json index 0ac13f1d3..e49b0ebf6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,6 +16,7 @@ "website", "scripts", "exampleTypescript", - "spec/**/*" + "spec/**/*", + "typings" ] } diff --git a/typings/chrome.d.ts b/typings/chrome.d.ts new file mode 100644 index 000000000..a5dbd2fd1 --- /dev/null +++ b/typings/chrome.d.ts @@ -0,0 +1,362 @@ +import * as webdriver from './index'; +import * as remote from './remote'; +import * as http from './http'; + +/** + * Creates a new WebDriver client for Chrome. + * + * @extends {webdriver.WebDriver} + */ +export class Driver extends webdriver.WebDriver { + /** + * Creates a new session with the ChromeDriver. + * + * @param {(Capabilities|Options)=} opt_config The configuration options. + * @param {(remote.DriverService|http.Executor)=} opt_serviceExecutor Either + * a DriverService to use for the remote end, or a preconfigured executor + * for an externally managed endpoint. If neither is provided, the + * {@linkplain ##getDefaultService default service} will be used by + * default. + * @param {promise.ControlFlow=} opt_flow The control flow to use, or `null` + * to use the currently active flow. + * @return {!Driver} A new driver instance. + */ + static createSession(opt_config?: Options | webdriver.CreateSessionCapabilities, opt_service?: remote.DriverService | http.Executor, opt_flow?: webdriver.promise.ControlFlow): Driver; +} + +export interface IOptionsValues { + args: string[]; + binary?: string; + detach: boolean; + extensions: string[]; + localState?: any; + logFile?: string; + prefs?: any; +} + +export interface IPerfLoggingPrefs { + enableNetwork: boolean; + enablePage: boolean; + enableTimeline: boolean; + tracingCategories: string; + bufferUsageReportingInterval: number; +} + +/** + * Class for managing ChromeDriver specific options. + */ +export class Options { + /** + * @constructor + */ + constructor(); + + /** + * Extracts the ChromeDriver specific options from the given capabilities + * object. + * @param {!webdriver.Capabilities} capabilities The capabilities object. + * @return {!Options} The ChromeDriver options. + */ + static fromCapabilities(capabilities: webdriver.Capabilities): Options; + + /** + * Add additional command line arguments to use when launching the Chrome + * browser. Each argument may be specified with or without the '--' prefix + * (e.g. '--foo' and 'foo'). Arguments with an associated value should be + * delimited by an '=': 'foo=bar'. + * @param {...(string|!Array.)} var_args The arguments to add. + * @return {!Options} A self reference. + */ + addArguments(...var_args: string[]): Options; + + /** + * Configures the chromedriver to start Chrome in headless mode. + * + * > __NOTE:__ Resizing the browser window in headless mode is only supported + * > in Chrome 60. Users are encouraged to set an initial window size with + * > the {@link #windowSize windowSize({width, height})} option. + * + * @return {!Options} A self reference. + */ + headless(): Options; + + /** + * List of Chrome command line switches to exclude that ChromeDriver by default + * passes when starting Chrome. Do not prefix switches with '--'. + * + * @param {...(string|!Array)} var_args The switches to exclude. + * @return {!Options} A self reference. + */ + excludeSwitches(...var_args: string[]): Options; + + /** + * Add additional extensions to install when launching Chrome. Each extension + * should be specified as the path to the packed CRX file, or a Buffer for an + * extension. + * @param {...(string|!Buffer|!Array.<(string|!Buffer)>)} var_args The + * extensions to add. + * @return {!Options} A self reference. + */ + addExtensions(...var_args: any[]): Options; + + /** + * Sets the path to the Chrome binary to use. On Mac OS X, this path should + * reference the actual Chrome executable, not just the application binary + * (e.g. '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'). + * + * The binary path be absolute or relative to the chromedriver server + * executable, but it must exist on the machine that will launch Chrome. + * + * @param {string} path The path to the Chrome binary to use. + * @return {!Options} A self reference. + */ + setChromeBinaryPath(path: string): Options; + + /** + * Sets whether to leave the started Chrome browser running if the controlling + * ChromeDriver service is killed before {@link webdriver.WebDriver#quit()} is + * called. + * @param {boolean} detach Whether to leave the browser running if the + * chromedriver service is killed before the session. + * @return {!Options} A self reference. + */ + detachDriver(detach: boolean): Options; + + /** + * Sets the user preferences for Chrome's user profile. See the 'Preferences' + * file in Chrome's user data directory for examples. + * @param {!Object} prefs Dictionary of user preferences to use. + * @return {!Options} A self reference. + */ + setUserPreferences(prefs: any): Options; + + /** + * Sets the logging preferences for the new session. + * @param {!webdriver.logging.Preferences} prefs The logging preferences. + * @return {!Options} A self reference. + */ + setLoggingPrefs(prefs: webdriver.logging.Preferences): Options; + + /** + * Sets the performance logging preferences. Options include: + * + * - `enableNetwork`: Whether or not to collect events from Network domain. + * - `enablePage`: Whether or not to collect events from Page domain. + * - `enableTimeline`: Whether or not to collect events from Timeline domain. + * Note: when tracing is enabled, Timeline domain is implicitly disabled, + * unless `enableTimeline` is explicitly set to true. + * - `tracingCategories`: A comma-separated string of Chrome tracing categories + * for which trace events should be collected. An unspecified or empty + * string disables tracing. + * - `bufferUsageReportingInterval`: The requested number of milliseconds + * between DevTools trace buffer usage events. For example, if 1000, then + * once per second, DevTools will report how full the trace buffer is. If a + * report indicates the buffer usage is 100%, a warning will be issued. + * + * @param {{enableNetwork: boolean, + * enablePage: boolean, + * enableTimeline: boolean, + * tracingCategories: string, + * bufferUsageReportingInterval: number}} prefs The performance + * logging preferences. + * @return {!Options} A self reference. + */ + setPerfLoggingPrefs(prefs: IPerfLoggingPrefs): Options; + + /** + * Sets preferences for the 'Local State' file in Chrome's user data + * directory. + * @param {!Object} state Dictionary of local state preferences. + * @return {!Options} A self reference. + */ + setLocalState(state: any): Options; + + /** + * Sets the name of the activity hosting a Chrome-based Android WebView. This + * option must be set to connect to an [Android WebView]( + * https://sites.google.com/a/chromium.org/chromedriver/getting-started/getting-started---android) + * + * @param {string} name The activity name. + * @return {!Options} A self reference. + */ + androidActivity(name: string): Options; + + /** + * Sets the device serial number to connect to via ADB. If not specified, the + * ChromeDriver will select an unused device at random. An error will be + * returned if all devices already have active sessions. + * + * @param {string} serial The device serial number to connect to. + * @return {!Options} A self reference. + */ + androidDeviceSerial(serial: string): Options; + + /** + * Configures the ChromeDriver to launch Chrome on Android via adb. This + * function is shorthand for + * {@link #androidPackage options.androidPackage('com.android.chrome')}. + * @return {!Options} A self reference. + */ + androidChrome(): Options; + + /** + * Sets the package name of the Chrome or WebView app. + * + * @param {?string} pkg The package to connect to, or `null` to disable Android + * and switch back to using desktop Chrome. + * @return {!Options} A self reference. + */ + androidPackage(pkg: string): Options; + + /** + * Sets the process name of the Activity hosting the WebView (as given by `ps`). + * If not specified, the process name is assumed to be the same as + * {@link #androidPackage}. + * + * @param {string} processName The main activity name. + * @return {!Options} A self reference. + */ + androidProcess(processName: string): Options; + + /** + * Sets whether to connect to an already-running instead of the specified + * {@linkplain #androidProcess app} instead of launching the app with a clean + * data directory. + * + * @param {boolean} useRunning Whether to connect to a running instance. + * @return {!Options} A self reference. + */ + androidUseRunningApp(useRunning: boolean): Options; + + /** + * Sets the path to Chrome's log file. This path should exist on the machine + * that will launch Chrome. + * @param {string} path Path to the log file to use. + * @return {!Options} A self reference. + */ + setChromeLogFile(path: string): Options; + + /** + * Sets the directory to store Chrome minidumps in. This option is only + * supported when ChromeDriver is running on Linux. + * @param {string} path The directory path. + * @return {!Options} A self reference. + */ + setChromeMinidumpPath(path: string): Options; + + /** + * Configures Chrome to emulate a mobile device. For more information, refer + * to the ChromeDriver project page on [mobile emulation][em]. Configuration + * options include: + * + * - `deviceName`: The name of a pre-configured [emulated device][devem] + * - `width`: screen width, in pixels + * - `height`: screen height, in pixels + * - `pixelRatio`: screen pixel ratio + * + * __Example 1: Using a Pre-configured Device__ + * + * let options = new chrome.Options().setMobileEmulation( + * {deviceName: 'Google Nexus 5'}); + * + * let driver = new chrome.Driver(options); + * + * __Example 2: Using Custom Screen Configuration__ + * + * let options = new chrome.Options().setMobileEmulation({ + * width: 360, + * height: 640, + * pixelRatio: 3.0 + * }); + * + * let driver = new chrome.Driver(options); + * + * + * [em]: https://sites.google.com/a/chromium.org/chromedriver/mobile-emulation + * [devem]: https://developer.chrome.com/devtools/docs/device-mode + * + * @param {?({deviceName: string}| + * {width: number, height: number, pixelRatio: number})} config The + * mobile emulation configuration, or `null` to disable emulation. + * @return {!Options} A self reference. + */ + setMobileEmulation(config: any): Options; + + /** + * Sets the proxy settings for the new session. + * @param {webdriver.ProxyConfig} proxy The proxy configuration to use. + * @return {!Options} A self reference. + */ + setProxy(proxy: webdriver.ProxyConfig): Options; + + /** + * Converts this options instance to a {@link webdriver.Capabilities} object. + * @param {webdriver.Capabilities=} opt_capabilities The capabilities to merge + * these options into, if any. + * @return {!webdriver.Capabilities} The capabilities. + */ + toCapabilities(opt_capabilities?: webdriver.Capabilities): webdriver.Capabilities; +} + +/** + * Creates {@link remote.DriverService} instances that manage a ChromeDriver + * server. + */ +export class ServiceBuilder extends remote.DriverService.Builder { + /** + * @param {string=} opt_exe Path to the server executable to use. If omitted, + * the builder will attempt to locate the chromedriver on the current + * PATH. + * @throws {Error} If provided executable does not exist, or the chromedriver + * cannot be found on the PATH. + * @constructor + */ + constructor(opt_exe?: string); + + /** + * Sets which port adb is listening to. _The ChromeDriver will connect to adb + * if an {@linkplain Options#androidPackage Android session} is requested, but + * adb **must** be started beforehand._ + * + * @param {number} port Which port adb is running on. + * @return {!ServiceBuilder} A self reference. + */ + setAdbPort(port: number): this; + + /** + * Sets the path of the log file the driver should log to. If a log file is + * not specified, the driver will log to stderr. + * @param {string} path Path of the log file to use. + * @return {!ServiceBuilder} A self reference. + */ + loggingTo(path: string): this; + + /** + * Enables verbose logging. + * @return {!ServiceBuilder} A self reference. + */ + enableVerboseLogging(): this; + + /** + * Sets the number of threads the driver should use to manage HTTP requests. + * By default, the driver will use 4 threads. + * @param {number} n The number of threads to use. + * @return {!ServiceBuilder} A self reference. + */ + setNumHttpThreads(n: number): this; +} + +/** + * Returns the default ChromeDriver service. If such a service has not been + * configured, one will be constructed using the default configuration for + * a ChromeDriver executable found on the system PATH. + * @return {!remote.DriverService} The default ChromeDriver service. + */ +export function getDefaultService(): remote.DriverService; + +/** + * Sets the default service to use for new ChromeDriver instances. + * @param {!remote.DriverService} service The service to use. + * @throws {Error} If the default service is currently running. + */ +export function setDefaultService(service: remote.DriverService): void; diff --git a/typings/edge.d.ts b/typings/edge.d.ts new file mode 100644 index 000000000..90b45fc92 --- /dev/null +++ b/typings/edge.d.ts @@ -0,0 +1,92 @@ +import * as webdriver from './index'; +import * as remote from './remote'; + +export class Driver extends webdriver.WebDriver { + /** + * Creates a new browser session for Microsoft's Edge browser. + * + * @param {(capabilities.Capabilities|Options)=} opt_config The configuration + * options. + * @param {remote.DriverService=} opt_service The session to use; will use + * the {@linkplain #getDefaultService default service} by default. + * @param {promise.ControlFlow=} opt_flow The control flow to use, or + * {@code null} to use the currently active flow. + * @return {!Driver} A new driver instance. + */ + static createSession(opt_config?: webdriver.CreateSessionCapabilities, opt_service?: remote.DriverService, opt_flow?: webdriver.promise.ControlFlow): Driver; + + /** + * This function is a no-op as file detectors are not supported by this + * implementation. + * @override + */ + setFileDetector(): void; +} + +/** + * Class for managing MicrosoftEdgeDriver specific options. + */ +export class Options { + /** + * Extracts the MicrosoftEdgeDriver specific options from the given + * capabilities object. + * @param {!capabilities.Capabilities} caps The capabilities object. + * @return {!Options} The MicrosoftEdgeDriver options. + */ + static fromCapabilities(cap: webdriver.Capabilities): Options; + + /** + * Sets the proxy settings for the new session. + * @param {capabilities.ProxyConfig} proxy The proxy configuration to use. + * @return {!Options} A self reference. + */ + setProxy(proxy: webdriver.ProxyConfig): Options; + + /** + * Sets the page load strategy for Edge. + * Supported values are 'normal', 'eager', and 'none'; + * + * @param {string} pageLoadStrategy The page load strategy to use. + * @return {!Options} A self reference. + */ + setPageLoadStrategy(pageLoadStrategy: string): Options; + + /** + * Converts this options instance to a {@link capabilities.Capabilities} + * object. + * @param {capabilities.Capabilities=} opt_capabilities The capabilities to + * merge these options into, if any. + * @return {!capabilities.Capabilities} The capabilities. + */ + toCapabilities(opt_capabilities?: webdriver.Capabilities): webdriver.Capabilities; +} + +/** + * Creates {@link remote.DriverService} instances that manage a + * MicrosoftEdgeDriver server in a child process. + */ +export class ServiceBuilder extends remote.DriverService.Builder { + /** + * @param {string=} opt_exe Path to the server executable to use. If omitted, + * the builder will attempt to locate the MicrosoftEdgeDriver on the current + * PATH. + * @throws {Error} If provided executable does not exist, or the + * MicrosoftEdgeDriver cannot be found on the PATH. + */ + constructor(opt_exe?: string); +} + +/** + * Returns the default MicrosoftEdgeDriver service. If such a service has + * not been configured, one will be constructed using the default configuration + * for an MicrosoftEdgeDriver executable found on the system PATH. + * @return {!remote.DriverService} The default MicrosoftEdgeDriver service. + */ +export function getDefaultService(): remote.DriverService; + +/** + * Sets the default service to use for new MicrosoftEdgeDriver instances. + * @param {!remote.DriverService} service The service to use. + * @throws {Error} If the default service is currently running. + */ +export function setDefaultService(service: remote.DriverService): void; diff --git a/typings/firefox.d.ts b/typings/firefox.d.ts new file mode 100644 index 000000000..0c2ecdbe9 --- /dev/null +++ b/typings/firefox.d.ts @@ -0,0 +1,292 @@ +import * as webdriver from './index'; +import * as remote from './remote'; +import * as http from './http'; + +/** + * Manages a Firefox subprocess configured for use with WebDriver. + */ +export class Binary { + /** + * @param {string=} opt_exe Path to the Firefox binary to use. If not + * specified, will attempt to locate Firefox on the current system. + * @constructor + */ + constructor(opt_exe?: string); + + /** + * Add arguments to the command line used to start Firefox. + * @param {...(string|!Array.)} var_args Either the arguments to add as + * varargs, or the arguments as an array. + */ + addArguments(...var_args: string[]): void; + + /** + * Launches Firefox and eturns a promise that will be fulfilled when the process + * terminates. + * @param {string} profile Path to the profile directory to use. + * @return {!promise.Promise.} A promise for the process result. + * @throws {Error} If this instance has already been started. + */ + launch(profile: string): webdriver.promise.Promise; + + /** + * Kills the managed Firefox process. + * @return {!promise.Promise} A promise for when the process has terminated. + */ + kill(): webdriver.promise.Promise; +} + +/** + * Models a Firefox proifle directory for use with the FirefoxDriver. The + * {@code Proifle} directory uses an in-memory model until {@link #writeToDisk} + * is called. + */ +export class Profile { + /** + * @param {string=} opt_dir Path to an existing Firefox profile directory to + * use a template for this profile. If not specified, a blank profile will + * be used. + * @constructor + */ + constructor(opt_dir?: string); + + /** + * Registers an extension to be included with this profile. + * @param {string} extension Path to the extension to include, as either an + * unpacked extension directory or the path to a xpi file. + */ + addExtension(extension: string): void; + + /** + * Sets a desired preference for this profile. + * @param {string} key The preference key. + * @param {(string|number|boolean)} value The preference value. + * @throws {Error} If attempting to set a frozen preference. + */ + setPreference(key: string, value: string): void; + setPreference(key: string, value: number): void; + setPreference(key: string, value: boolean): void; + + /** + * Returns the currently configured value of a profile preference. This does + * not include any defaults defined in the profile's template directory user.js + * file (if a template were specified on construction). + * @param {string} key The desired preference. + * @return {(string|number|boolean|undefined)} The current value of the + * requested preference. + */ + getPreference(key: string): any; + + /** + * @return {number} The port this profile is currently configured to use, or + * 0 if the port will be selected at random when the profile is written + * to disk. + */ + getPort(): number; + + /** + * Sets the port to use for the WebDriver extension loaded by this profile. + * @param {number} port The desired port, or 0 to use any free port. + */ + setPort(port: number): void; + + /** + * @return {boolean} Whether the FirefoxDriver is configured to automatically + * accept untrusted SSL certificates. + */ + acceptUntrustedCerts(): boolean; + + /** + * Sets whether the FirefoxDriver should automatically accept untrusted SSL + * certificates. + * @param {boolean} value . + */ + setAcceptUntrustedCerts(value: boolean): void; + + /** + * Sets whether to assume untrusted certificates come from untrusted issuers. + * @param {boolean} value . + */ + setAssumeUntrustedCertIssuer(value: boolean): void; + + /** + * @return {boolean} Whether to assume untrusted certs come from untrusted + * issuers. + */ + assumeUntrustedCertIssuer(): boolean; + + /** + * Sets whether to use native events with this profile. + * @param {boolean} enabled . + */ + setNativeEventsEnabled(enabled: boolean): void; + + /** + * Returns whether native events are enabled in this profile. + * @return {boolean} . + */ + nativeEventsEnabled(): boolean; + + /** + * Writes this profile to disk. + * @param {boolean=} opt_excludeWebDriverExt Whether to exclude the WebDriver + * extension from the generated profile. Used to reduce the size of an + * {@link #encode() encoded profile} since the server will always install + * the extension itself. + * @return {!promise.Promise.} A promise for the path to the new + * profile directory. + */ + writeToDisk(opt_excludeWebDriverExt?: boolean): webdriver.promise.Promise; + + /** + * Encodes this profile as a zipped, base64 encoded directory. + * @return {!promise.Promise.} A promise for the encoded profile. + */ + encode(): webdriver.promise.Promise; +} + +/** + * Configuration options for the FirefoxDriver. + */ +export class Options { + /** + * Sets the profile to use. The profile may be specified as a + * {@link Profile} object or as the path to an existing Firefox profile to use + * as a template. + * + * @param {(string|!Profile)} profile The profile to use. + * @return {!Options} A self reference. + */ + setProfile(profile: string | any): Options; + + /** + * Sets the binary to use. The binary may be specified as the path to a Firefox + * executable, or as a {@link Binary} object. + * + * @param {(string|!Binary)} binary The binary to use. + * @return {!Options} A self reference. + */ + setBinary(binary: string | any): Options; + + /** + * Sets the logging preferences for the new session. + * @param {logging.Preferences} prefs The logging preferences. + * @return {!Options} A self reference. + */ + setLoggingPreferences(prefs: webdriver.logging.Preferences): Options; + + /** + * Sets the proxy to use. + * + * @param {capabilities.ProxyConfig} proxy The proxy configuration to use. + * @return {!Options} A self reference. + */ + setProxy(proxy: webdriver.ProxyConfig): Options; + + /** + * Sets whether to use Mozilla's geckodriver to drive the browser. This option + * is enabled by default and required for Firefox 47+. + * + * @param {boolean} enable Whether to enable the geckodriver. + * @see https://github.com/mozilla/geckodriver + */ + useGeckoDriver(enable: boolean): Options; + + /** + * Converts these options to a {@link capabilities.Capabilities} instance. + * + * @return {!capabilities.Capabilities} A new capabilities object. + */ + toCapabilities(): webdriver.Capabilities; +} + +/** + * @return {string} . + * @throws {Error} + */ +export function findWires(): string; + +/** + * @param {(string|!Binary)} binary . + * @return {!remote.DriverService} . + */ +export function createWiresService(binary: string | any): remote.DriverService; + +/** + * @param {(Profile|string)} profile The profile to prepare. + * @param {number} port The port the FirefoxDriver should listen on. + * @return {!Promise} a promise for the path to the profile directory. + */ +export function prepareProfile(profile: string | any, port: number): any; + +/** + * A WebDriver client for Firefox. + */ +export class Driver extends webdriver.WebDriver { + /** + * Creates a new Firefox session. + * + * @param {(Options|capabilities.Capabilities|Object)=} opt_config The + * configuration options for this driver, specified as either an + * {@link Options} or {@link capabilities.Capabilities}, or as a raw hash + * object. + * @param {(http.Executor|remote.DriverService)=} opt_executor Either a + * pre-configured command executor to use for communicating with an + * externally managed remote end (which is assumed to already be running), + * or the `DriverService` to use to start the geckodriver in a child + * process. + * + * If an executor is provided, care should e taken not to use reuse it with + * other clients as its internal command mappings will be updated to support + * Firefox-specific commands. + * + * _This parameter may only be used with Mozilla's GeckoDriver._ + * + * @param {promise.ControlFlow=} opt_flow The flow to + * schedule commands through. Defaults to the active flow object. + * @throws {Error} If a custom command executor is provided and the driver is + * configured to use the legacy FirefoxDriver from the Selenium project. + * @return {!Driver} A new driver instance. + */ + static createSession(opt_config?: Options | webdriver.Capabilities, opt_executor?: http.Executor | remote.DriverService, opt_flow?: webdriver.promise.ControlFlow): Driver; + + /** + * This function is a no-op as file detectors are not supported by this + * implementation. + * @override + */ + setFileDetector(): void; +} + +/** + * Creates {@link selenium-webdriver/remote.DriverService} instances that manage + * a [geckodriver](https://github.com/mozilla/geckodriver) server in a child + * process. + */ +export class ServiceBuilder extends remote.DriverService.Builder { + /** + * @param {string=} opt_exe Path to the server executable to use. If omitted, + * the builder will attempt to locate the geckodriver on the system PATH. + */ + constructor(opt_exe?: string); + + /** + * Enables verbose logging. + * + * @param {boolean=} opt_trace Whether to enable trace-level logging. By + * default, only debug logging is enabled. + * @return {!ServiceBuilder} A self reference. + */ + enableVerboseLogging(opt_trace?: boolean): this; + + /** + * Sets the path to the executable Firefox binary that the geckodriver should + * use. If this method is not called, this builder will attempt to locate + * Firefox in the default installation location for the current platform. + * + * @param {(string|!Binary)} binary Path to the executable Firefox binary to use. + * @return {!ServiceBuilder} A self reference. + * @see Binary#locate() + */ + setFirefoxBinary(binary: string | Binary): this; +} diff --git a/typings/http.d.ts b/typings/http.d.ts new file mode 100644 index 000000000..72fa1b2c5 --- /dev/null +++ b/typings/http.d.ts @@ -0,0 +1,152 @@ +import * as webdriver from './index'; + +/** + * Converts a headers map to a HTTP header block string. + * @param {!Map} headers The map to convert. + * @return {string} The headers as a string. + */ +export function headersToString(headers: any): string; + +/** + * Represents a HTTP request message. This class is a 'partial' request and only + * defines the path on the server to send a request to. It is each client's + * responsibility to build the full URL for the final request. + * @final + */ +export class Request { + /** + * @param {string} method The HTTP method to use for the request. + * @param {string} path The path on the server to send the request to. + * @param {Object=} opt_data This request's non-serialized JSON payload data. + */ + constructor(method: string, path: string, opt_data?: Object); + + /** @override */ + toString(): string; +} + +/** + * Represents a HTTP response message. + * @final + */ +export class Response { + /** + * @param {number} status The response code. + * @param {!Object} headers The response headers. All header names + * will be converted to lowercase strings for consistent lookups. + * @param {string} body The response body. + */ + constructor(status: number, headers: Object, body: string); + + /** @override */ + toString(): string; +} + +export function post(path: string): any; +export function del(path: string): any; +export function get(path: string): any; +export function resource(method: string, path: string): any; + +/** + * A basic HTTP client used to send messages to a remote end. + */ +export class HttpClient { + /** + * @param {string} serverUrl URL for the WebDriver server to send commands to. + * @param {http.Agent=} opt_agent The agent to use for each request. + * Defaults to `http.globalAgent`. + * @param {?string=} opt_proxy The proxy to use for the connection to the + * server. Default is to use no proxy. + */ + constructor(serverUrl: string, opt_agent?: any, opt_proxy?: string); + + /** + * Sends a request to the server. The client will automatically follow any + * redirects returned by the server, fulfilling the returned promise with the + * final response. + * + * @param {!HttpRequest} httpRequest The request to send. + * @return {!promise.Promise} A promise that will be fulfilled + * with the server's response. + */ + send(httpRequest: Request): webdriver.promise.Promise; +} + +/** + * Sends a single HTTP request. + * @param {!Object} options The request options. + * @param {function(!HttpResponse)} onOk The function to call if the + * request succeeds. + * @param {function(!Error)} onError The function to call if the request fails. + * @param {?string=} opt_data The data to send with the request. + * @param {?string=} opt_proxy The proxy server to use for the request. + */ +export function sendRequest(options: Object, onOk: any, onError: any, opt_data?: string, opt_proxy?: string): any; + +/** + * A command executor that communicates with the server using HTTP + JSON. + * + * By default, each instance of this class will use the legacy wire protocol + * from [Selenium project][json]. The executor will automatically switch to the + * [W3C wire protocol][w3c] if the remote end returns a compliant response to + * a new session command. + * + * [json]: https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol + * [w3c]: https://w3c.github.io/webdriver/webdriver-spec.html + * + * @implements {cmd.Executor} + */ +export class Executor { + /** + * @param {!(HttpClient|IThenable)} client The client to use for sending + * requests to the server, or a promise-like object that will resolve to + * to the client. + */ + constructor(client: HttpClient | webdriver.promise.IThenable); + + /** + * Defines a new command for use with this executor. When a command is sent, + * the {@code path} will be preprocessed using the command's parameters; any + * path segments prefixed with ':' will be replaced by the parameter of the + * same name. For example, given '/person/:name' and the parameters + * '{name: 'Bob'}', the final command path will be '/person/Bob'. + * + * @param {string} name The command name. + * @param {string} method The HTTP method to use when sending this command. + * @param {string} path The path to send the command to, relative to + * the WebDriver server's command root and of the form + * '/path/:variable/segment'. + */ + defineCommand(name: string, method: string, path: string): void; + + /** @override */ + execute(command: any): any; +} + +/** + * @param {string} str . + * @return {?} . + */ +export function tryParse(str: string): any; + +/** + * Callback used to parse {@link HttpResponse} objects from a + * {@link HttpClient}. + * @param {!HttpResponse} httpResponse The HTTP response to parse. + * @param {boolean} w3c Whether the response should be processed using the + * W3C wire protocol. + * @return {{value: ?}} The parsed response. + * @throws {WebDriverError} If the HTTP response is an error. + */ +export function parseHttpResponse(httpResponse: Response, w3c: boolean): any; + +/** + * Builds a fully qualified path using the given set of command parameters. Each + * path segment prefixed with ':' will be replaced by the value of the + * corresponding parameter. All parameters spliced into the path will be + * removed from the parameter map. + * @param {string} path The original resource path. + * @param {!Object<*>} parameters The parameters object to splice into the path. + * @return {string} The modified path. + */ +export function buildPath(path: string, parameters: Object): string; diff --git a/typings/ie.d.ts b/typings/ie.d.ts new file mode 100644 index 000000000..bf3932450 --- /dev/null +++ b/typings/ie.d.ts @@ -0,0 +1,208 @@ +import * as webdriver from './index'; + +/** + * A WebDriver client for Microsoft's Internet Explorer. + */ +export class Driver extends webdriver.WebDriver { + /** + * Creates a new session for Microsoft's Internet Explorer. + * + * @param {(capabilities.Capabilities|Options)=} opt_config The configuration + * options. + * @param {promise.ControlFlow=} opt_flow The control flow to use, + * or {@code null} to use the currently active flow. + * @return {!Driver} A new driver instance. + */ + static createSession(opt_config?: webdriver.Capabilities | Options, opt_flow?: webdriver.promise.ControlFlow): Driver; + + /** + * This function is a no-op as file detectors are not supported by this + * implementation. + * @override + */ + setFileDetector(): void; +} + +/** + * Class for managing IEDriver specific options. + */ +export class Options { + constructor(); + + /** + * Extracts the IEDriver specific options from the given capabilities + * object. + * @param {!capabilities.Capabilities} caps The capabilities object. + * @return {!Options} The IEDriver options. + */ + static fromCapabilities(caps: webdriver.Capabilities): Options; + + /** + * Whether to disable the protected mode settings check when the session is + * created. Disbling this setting may lead to significant instability as the + * browser may become unresponsive/hang. Only 'best effort' support is provided + * when using this capability. + * + * For more information, refer to the IEDriver's + * [required system configuration](http://goo.gl/eH0Yi3). + * + * @param {boolean} ignoreSettings Whether to ignore protected mode settings. + * @return {!Options} A self reference. + */ + introduceFlakinessByIgnoringProtectedModeSettings(ignoreSettings: boolean): Options; + + /** + * Indicates whether to skip the check that the browser's zoom level is set to + * 100%. + * + * @param {boolean} ignore Whether to ignore the browser's zoom level settings. + * @return {!Options} A self reference. + */ + ignoreZoomSetting(ignore: boolean): Options; + + /** + * Sets the initial URL loaded when IE starts. This is intended to be used with + * {@link #ignoreProtectedModeSettings} to allow the user to initialize IE in + * the proper Protected Mode zone. Setting this option may cause browser + * instability or flaky and unresponsive code. Only 'best effort' support is + * provided when using this option. + * + * @param {string} url The initial browser URL. + * @return {!Options} A self reference. + */ + initialBrowserUrl(url: string): Options; + + /** + * Configures whether to enable persistent mouse hovering (true by default). + * Persistent hovering is achieved by continuously firing mouse over events at + * the last location the mouse cursor has been moved to. + * + * @param {boolean} enable Whether to enable persistent hovering. + * @return {!Options} A self reference. + */ + enablePersistentHover(enable: boolean): Options; + + /** + * Configures whether the driver should attempt to remove obsolete + * {@linkplain webdriver.WebElement WebElements} from its internal cache on + * page navigation (true by default). Disabling this option will cause the + * driver to run with a larger memory footprint. + * + * @param {boolean} enable Whether to enable element reference cleanup. + * @return {!Options} A self reference. + */ + enableElementCacheCleanup(enable: boolean): Options; + + /** + * Configures whether to require the IE window to have input focus before + * performing any user interactions (i.e. mouse or keyboard events). This + * option is disabled by default, but delivers much more accurate interaction + * events when enabled. + * + * @param {boolean} require Whether to require window focus. + * @return {!Options} A self reference. + */ + requireWindowFocus(require: boolean): Options; + + /** + * Configures the timeout, in milliseconds, that the driver will attempt to + * located and attach to a newly opened instance of Internet Explorer. The + * default is zero, which indicates waiting indefinitely. + * + * @param {number} timeout How long to wait for IE. + * @return {!Options} A self reference. + */ + browserAttachTimeout(timeout: number): Options; + + /** + * Configures whether to launch Internet Explorer using the CreateProcess API. + * If this option is not specified, IE is launched using IELaunchURL, if + * available. For IE 8 and above, this option requires the TabProcGrowth + * registry value to be set to 0. + * + * @param {boolean} force Whether to use the CreateProcess API. + * @return {!Options} A self reference. + */ + forceCreateProcessApi(force: boolean): Options; + + /** + * Specifies command-line switches to use when launching Internet Explorer. + * This is only valid when used with {@link #forceCreateProcessApi}. + * + * @param {...(string|!Array.)} var_args The arguments to add. + * @return {!Options} A self reference. + */ + addArguments(...var_args: string[]): Options; + + /** + * Configures whether proxies should be configured on a per-process basis. If + * not set, setting a {@linkplain #setProxy proxy} will configure the system + * proxy. The default behavior is to use the system proxy. + * + * @param {boolean} enable Whether to enable per-process proxy settings. + * @return {!Options} A self reference. + */ + usePerProcessProxy(enable: boolean): Options; + + /** + * Configures whether to clear the cache, cookies, history, and saved form data + * before starting the browser. _Using this capability will clear session data + * for all running instances of Internet Explorer, including those started + * manually._ + * + * @param {boolean} cleanSession Whether to clear all session data on startup. + * @return {!Options} A self reference. + */ + ensureCleanSession(cleanSession: boolean): Options; + + /** + * Sets the path to the log file the driver should log to. + * @param {string} file The log file path. + * @return {!Options} A self reference. + */ + setLogFile(file: string): Options; + + /** + * Sets the IEDriverServer's logging {@linkplain Level level}. + * @param {Level} level The logging level. + * @return {!Options} A self reference. + */ + setLogLevel(level: webdriver.logging.Level): Options; + + /** + * Sets the IP address of the driver's host adapter. + * @param {string} host The IP address to use. + * @return {!Options} A self reference. + */ + setHost(host: string): Options; + + /** + * Sets the path of the temporary data directory to use. + * @param {string} path The log file path. + * @return {!Options} A self reference. + */ + setExtractPath(path: string): Options; + + /** + * Sets whether the driver should start in silent mode. + * @param {boolean} silent Whether to run in silent mode. + * @return {!Options} A self reference. + */ + silent(silent: boolean): Options; + + /** + * Sets the proxy settings for the new session. + * @param {capabilities.ProxyConfig} proxy The proxy configuration to use. + * @return {!Options} A self reference. + */ + setProxy(proxy: webdriver.ProxyConfig): Options; + + /** + * Converts this options instance to a {@link capabilities.Capabilities} + * object. + * @param {capabilities.Capabilities=} opt_capabilities The capabilities to + * merge these options into, if any. + * @return {!capabilities.Capabilities} The capabilities. + */ + toCapabilities(opt_capabilities?: webdriver.Capabilities): webdriver.Capabilities; +} diff --git a/typings/index.d.ts b/typings/index.d.ts new file mode 100644 index 000000000..d2cf50da1 --- /dev/null +++ b/typings/index.d.ts @@ -0,0 +1,4802 @@ +// Type definitions for Selenium WebDriverJS 3.0 +// Project: https://github.com/SeleniumHQ/selenium/tree/master/javascript/node/selenium-webdriver +// Definitions by: Bill Armstrong , +// Yuki Kokubun , +// Craig Nishina , +// Simon Gellis , +// Ben Dixon +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.3 + +import * as chrome from './chrome'; +import * as edge from './edge'; +import * as firefox from './firefox'; +import * as ie from './ie'; +import * as opera from './opera'; +import * as safari from './safari'; + +// google3 local modification: +// Add namespace webdriver in the global namespace for backwards compatibility. +declare global { +namespace webdriver { +// end google3 local modification. + +export namespace error { + class IError extends Error { + constructor(opt_error?: string); + } + + /** + * The base WebDriver error type. This error type is only used directly when a + * more appropriate category is not defined for the offending error. + */ + class WebDriverError extends IError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * An attempt was made to select an element that cannot be selected. + */ + class ElementNotSelectableError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * An element command could not be completed because the element is not visible + * on the page. + */ + class ElementNotVisibleError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * The arguments passed to a command are either invalid or malformed. + */ + class InvalidArgumentError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * An illegal attempt was made to set a cookie under a different domain than + * the current page. + */ + class InvalidCookieDomainError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * The coordinates provided to an interactions operation are invalid. + */ + class InvalidElementCoordinatesError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * An element command could not be completed because the element is in an + * invalid state, e.g. attempting to click an element that is no longer attached + * to the document. + */ + class InvalidElementStateError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * Argument was an invalid selector. + */ + class InvalidSelectorError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * Occurs when a command is directed to a session that does not exist. + */ + class NoSuchSessionError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * An error occurred while executing JavaScript supplied by the user. + */ + class JavascriptError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * The target for mouse interaction is not in the browser’s viewport and cannot + * be brought into that viewport. + */ + class MoveTargetOutOfBoundsError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * An attempt was made to operate on a modal dialog when one was not open. + */ + class NoSuchAlertError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * An element could not be located on the page using the given search + * parameters. + */ + class NoSuchElementError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * A request to switch to a frame could not be satisfied because the frame + * could not be found. + */ + class NoSuchFrameError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * A request to switch to a window could not be satisfied because the window + * could not be found. + */ + class NoSuchWindowError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * A script did not complete before its timeout expired. + */ + class ScriptTimeoutError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * A new session could not be created. + */ + class SessionNotCreatedError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * An element command failed because the referenced element is no longer + * attached to the DOM. + */ + class StaleElementReferenceError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * An operation did not completErrorCodee before its timeout expired. + */ + class TimeoutError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * A request to set a cookie’s value could not be satisfied. + */ + class UnableToSetCookieError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * A screen capture operation was not possible. + */ + class UnableToCaptureScreenError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * A modal dialog was open, blocking this operation. + */ + class UnexpectedAlertOpenError extends WebDriverError { + /** + * @param {string=} opt_error the error message, if any. + * @param {string=} opt_text the text of the open dialog, if available. + */ + constructor(opt_error?: string, opt_text?: string); + + /** + * @return {(string|undefined)} The text displayed with the unhandled alert, + * if available. + */ + getAlertText(): string; + } + + /** + * A command could not be executed because the remote end is not aware of it. + */ + class UnknownCommandError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * The requested command matched a known URL but did not match an method for + * that URL. + */ + class UnknownMethodError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } + + /** + * Reports an unsupport operation. + */ + class UnsupportedOperationError extends WebDriverError { + /** @param {string=} opt_error the error message, if any. */ + constructor(opt_error?: string); + } +} + +export namespace logging { + /** + * A hash describing log preferences. + * @typedef {Object.} + */ + class Preferences { + setLevel(type: string, level: Level | string | number): void; + toJSON(): { [key: string]: string }; + } + + interface IType { + /** Logs originating from the browser. */ + BROWSER: string; + /** Logs from a WebDriver client. */ + CLIENT: string; + /** Logs from a WebDriver implementation. */ + DRIVER: string; + /** Logs related to performance. */ + PERFORMANCE: string; + /** Logs from the remote server. */ + SERVER: string; + } + + /** + * Common log types. + * @enum {string} + */ + const Type: IType; + + /** + * Defines a message level that may be used to control logging output. + * + * @final + */ + class Level { + name_: string; + value_: number; + /** + * @param {string} name the level's name. + * @param {number} level the level's numeric value. + */ + constructor(name: string, level: number); + + /** @override */ + toString(): string; + + /** This logger's name. */ + name: string; + + /** The numeric log level. */ + value: number; + + /** + * Indicates no log messages should be recorded. + * @const + */ + static OFF: Level; + /** + * Log messages with a level of `1000` or higher. + * @const + */ + static SEVERE: Level; + /** + * Log messages with a level of `900` or higher. + * @const + */ + static WARNING: Level; + /** + * Log messages with a level of `800` or higher. + * @const + */ + static INFO: Level; + /** + * Log messages with a level of `700` or higher. + * @const + */ + static DEBUG: Level; + /** + * Log messages with a level of `500` or higher. + * @const + */ + static FINE: Level; + /** + * Log messages with a level of `400` or higher. + * @const + */ + static FINER: Level; + /** + * Log messages with a level of `300` or higher. + * @const + */ + static FINEST: Level; + /** + * Indicates all log messages should be recorded. + * @const + */ + static ALL: Level; + } + + /** + * Converts a level name or value to a {@link logging.Level} value. + * If the name/value is not recognized, {@link logging.Level.ALL} + * will be returned. + * @param {(number|string)} nameOrValue The log level name, or value, to + * convert . + * @return {!logging.Level} The converted level. + */ + function getLevel(nameOrValue: string | number): Level; + + interface IEntryJSON { + level: string; + message: string; + timestamp: number; + type: string; + } + + /** + * A single log entry. + */ + class Entry { + /** + * @param {(!logging.Level|string)} level The entry level. + * @param {string} message The log message. + * @param {number=} opt_timestamp The time this entry was generated, in + * milliseconds since 0:00:00, January 1, 1970 UTC. If omitted, the + * current time will be used. + * @param {string=} opt_type The log type, if known. + * @constructor + */ + constructor(level: Level | string | number, message: string, opt_timestamp?: number, opt_type?: string | IType); + + /** @type {!logging.Level} */ + level: Level; + + /** @type {string} */ + message: string; + + /** @type {number} */ + timestamp: number; + + /** @type {string} */ + type: string; + + /** + * @return {{level: string, message: string, timestamp: number, + * type: string}} The JSON representation of this entry. + */ + toJSON(): IEntryJSON; + } + + /** + * An object used to log debugging messages. Loggers use a hierarchical, + * dot-separated naming scheme. For instance, 'foo' is considered the parent of + * the 'foo.bar' and an ancestor of 'foo.bar.baz'. + * + * Each logger may be assigned a {@linkplain #setLevel log level}, which + * controls which level of messages will be reported to the + * {@linkplain #addHandler handlers} attached to this instance. If a log level + * is not explicitly set on a logger, it will inherit its parent. + * + * This class should never be directly instantiated. Instead, users should + * obtain logger references using the {@linkplain ./logging.getLogger() + * getLogger()} function. + * + * @final + */ + class Logger { + /** + * @param {string} name the name of this logger. + * @param {Level=} opt_level the initial level for this logger. + */ + constructor(name: string, opt_level?: Level); + + /** @private {string} */ + name_: string; + /** @private {Level} */ + level_: Level; + /** @private {Logger} */ + parent_: Logger; + /** @private {Set} */ + handlers_: any; + + /** @return {string} the name of this logger. */ + getName(): string; + + /** + * @param {Level} level the new level for this logger, or `null` if the logger + * should inherit its level from its parent logger. + */ + setLevel(level: Level): void; + + /** @return {Level} the log level for this logger. */ + getLevel(): Level; + + /** + * @return {!Level} the effective level for this logger. + */ + getEffectiveLevel(): Level; + + /** + * @param {!Level} level the level to check. + * @return {boolean} whether messages recorded at the given level are loggable + * by this instance. + */ + isLoggable(level: Level): boolean; + + /** + * Adds a handler to this logger. The handler will be invoked for each message + * logged with this instance, or any of its descendants. + * + * @param {function(!Entry)} handler the handler to add. + */ + addHandler(handler: any): void; + + /** + * Removes a handler from this logger. + * + * @param {function(!Entry)} handler the handler to remove. + * @return {boolean} whether a handler was successfully removed. + */ + removeHandler(handler: any): void; + + /** + * Logs a message at the given level. The message may be defined as a string + * or as a function that will return the message. If a function is provided, + * it will only be invoked if this logger's + * {@linkplain #getEffectiveLevel() effective log level} includes the given + * `level`. + * + * @param {!Level} level the level at which to log the message. + * @param {(string|function(): string)} loggable the message to log, or a + * function that will return the message. + */ + log(level: Level, loggable: string | Function): void; + + /** + * Logs a message at the {@link Level.SEVERE} log level. + * @param {(string|function(): string)} loggable the message to log, or a + * function that will return the message. + */ + severe(loggable: string | Function): void; + + /** + * Logs a message at the {@link Level.WARNING} log level. + * @param {(string|function(): string)} loggable the message to log, or a + * function that will return the message. + */ + warning(loggable: string | Function): void; + + /** + * Logs a message at the {@link Level.INFO} log level. + * @param {(string|function(): string)} loggable the message to log, or a + * function that will return the message. + */ + info(loggable: string | Function): void; + + /** + * Logs a message at the {@link Level.DEBUG} log level. + * @param {(string|function(): string)} loggable the message to log, or a + * function that will return the message. + */ + debug(loggable: string | Function): void; + + /** + * Logs a message at the {@link Level.FINE} log level. + * @param {(string|function(): string)} loggable the message to log, or a + * function that will return the message. + */ + fine(loggable: string | Function): void; + + /** + * Logs a message at the {@link Level.FINER} log level. + * @param {(string|function(): string)} loggable the message to log, or a + * function that will return the message. + */ + finer(loggable: string | Function): void; + + /** + * Logs a message at the {@link Level.FINEST} log level. + * @param {(string|function(): string)} loggable the message to log, or a + * function that will return the message. + */ + finest(loggable: string | Function): void; + } + + /** + * Maintains a collection of loggers. + * + * @final + */ + class LogManager { + /** + * Retrieves a named logger, creating it in the process. This function will + * implicitly create the requested logger, and any of its parents, if they + * do not yet exist. + * + * @param {string} name the logger's name. + * @return {!Logger} the requested logger. + */ + getLogger(name: string): Logger; + + /** + * Creates a new logger. + * + * @param {string} name the logger's name. + * @param {!Logger} parent the logger's parent. + * @return {!Logger} the new logger. + * @private + */ + createLogger_(name: string, parent: Logger): Logger; + } +} + +export namespace promise { + // region Functions + + /** + * Set `USE_PROMISE_MANAGER` to `false` to disable the promise manager. + * This is useful, if you use async/await (see https://github.com/SeleniumHQ/selenium/issues/2969 + * and https://github.com/SeleniumHQ/selenium/issues/3037). + */ + let USE_PROMISE_MANAGER: boolean; + + /** + * Given an array of promises, will return a promise that will be fulfilled + * with the fulfillment values of the input array's values. If any of the + * input array's promises are rejected, the returned promise will be rejected + * with the same reason. + * + * @param {!Array<(T|!ManagedPromise)>} arr An array of + * promises to wait on. + * @return {!ManagedPromise} A promise that is + * fulfilled with an array containing the fulfilled values of the + * input array, or rejected with the same reason as the first + * rejected value. + * @template T + */ + function all(arr: Array>): Promise; + + /** + * Invokes the appropriate callback function as soon as a promised + * {@code value} is resolved. This function is similar to + * {@link promise.when}, except it does not return a new promise. + * @param {*} value The value to observe. + * @param {Function} callback The function to call when the value is + * resolved successfully. + * @param {Function=} opt_errback The function to call when the value is + * rejected. + */ + function asap(value: any, callback: Function, opt_errback?: Function): void; + + /** + * @return {!promise.ControlFlow} The currently active control flow. + */ + function controlFlow(): ControlFlow; + + /** + * Creates a new control flow. The provided callback will be invoked as the + * first task within the new flow, with the flow as its sole argument. Returns + * a promise that resolves to the callback result. + * @param {function(!ControlFlow)} callback The entry point + * to the newly created flow. + * @return {!ManagedPromise} A promise that resolves to the callback + * result. + */ + function createFlow(callback: (flow: ControlFlow) => R): Promise; + + /** + * Determines whether a {@code value} should be treated as a promise. + * Any object whose 'then' property is a function will be considered a promise. + * + * @param {*} value The value to test. + * @return {boolean} Whether the value is a promise. + */ + function isPromise(value: any): boolean; + + /** + * Tests is a function is a generator. + * @param {!Function} fn The function to test. + * @return {boolean} Whether the function is a generator. + */ + function isGenerator(fn: Function): boolean; + + /** + * Creates a promise that will be resolved at a set time in the future. + * @param {number} ms The amount of time, in milliseconds, to wait before + * resolving the promise. + * @return {!ManagedPromise} The promise. + */ + function delayed(ms: number): Promise; + + /** + * Calls a function for each element in an array, and if the function returns + * true adds the element to a new array. + * + * If the return value of the filter function is a promise, this function + * will wait for it to be fulfilled before determining whether to insert the + * element into the new array. + * + * If the filter function throws or returns a rejected promise, the promise + * returned by this function will be rejected with the same reason. Only the + * first failure will be reported; all subsequent errors will be silently + * ignored. + * + * @param {!(Array|ManagedPromise>)} arr The + * array to iterator over, or a promise that will resolve to said array. + * @param {function(this: SELF, TYPE, number, !Array): ( + * boolean|ManagedPromise)} fn The function + * to call for each element in the array. + * @param {SELF=} opt_self The object to be used as the value of 'this' within + * {@code fn}. + * @template TYPE, SELF + */ + function filter(arr: T[] | Promise, fn: (element: T, type: any, index: number, array: T[]) => any, opt_self?: any): Promise; + + /** + * Creates a new deferred object. + * @return {!promise.Deferred} The new deferred object. + */ + function defer(): Deferred; + + /** + * Creates a promise that has been resolved with the given value. + * @param {T=} opt_value The resolved value. + * @return {!Promise} The resolved promise. + * @deprecated Use {@link Promise#resolve Promise.resolve(value)}. + * @template T + */ + function fulfilled(opt_value?: T): Promise; + + /** + * Calls a function for each element in an array and inserts the result into a + * new array, which is used as the fulfillment value of the promise returned + * by this function. + * + * If the return value of the mapping function is a promise, this function + * will wait for it to be fulfilled before inserting it into the new array. + * + * If the mapping function throws or returns a rejected promise, the + * promise returned by this function will be rejected with the same reason. + * Only the first failure will be reported; all subsequent errors will be + * silently ignored. + * + * @param {!(Array|ManagedPromise>)} arr The + * array to iterator over, or a promise that will resolve to said array. + * @param {function(this: SELF, TYPE, number, !Array): ?} fn The + * function to call for each element in the array. This function should + * expect three arguments (the element, the index, and the array itself. + * @param {SELF=} opt_self The object to be used as the value of 'this' within + * {@code fn}. + * @template TYPE, SELF + */ + function map(arr: T[] | Promise, fn: (self: any, type: any, index: number, array: T[]) => any, opt_self?: any): Promise; + + /** + * Creates a promise that has been rejected with the given reason. + * @param {*=} opt_reason The rejection reason; may be any value, but is + * usually an Error or a string. + * @return {!Promise} The rejected promise. + * @deprecated Use {@link Promise#reject Promise.Promise(reason)}. + */ + function rejected(opt_reason?: any): Promise; + + /** + * Wraps a function that expects a node-style callback as its final + * argument. This callback expects two arguments: an error value (which will be + * null if the call succeeded), and the success value as the second argument. + * The callback will the resolve or reject the returned promise, based on its + * arguments. + * @param {!Function} fn The function to wrap. + * @param {...?} var_args The arguments to apply to the function, excluding the + * final callback. + * @return {!ManagedPromise} A promise that will be resolved with the + * result of the provided function's callback. + */ + function checkedNodeCall(fn: Function, ...var_args: any[]): Promise; + + /** + * Consumes a {@code GeneratorFunction}. Each time the generator yields a + * promise, this function will wait for it to be fulfilled before feeding the + * fulfilled value back into {@code next}. Likewise, if a yielded promise is + * rejected, the rejection error will be passed to {@code throw}. + * + * __Example 1:__ the Fibonacci Sequence. + * + * promise.consume(function* fibonacci() { + * var n1 = 1, n2 = 1; + * for (var i = 0; i < 4; ++i) { + * var tmp = yield n1 + n2; + * n1 = n2; + * n2 = tmp; + * } + * return n1 + n2; + * }).then(function(result) { + * console.log(result); // 13 + * }); + * + * __Example 2:__ a generator that throws. + * + * promise.consume(function* () { + * yield promise.delayed(250).then(function() { + * throw Error('boom'); + * }); + * }).catch(function(e) { + * console.log(e.toString()); // Error: boom + * }); + * + * @param {!Function} generatorFn The generator function to execute. + * @param {Object=} opt_self The object to use as 'this' when invoking the + * initial generator. + * @param {...*} var_args Any arguments to pass to the initial generator. + * @return {!ManagedPromise} A promise that will resolve to the + * generator's final result. + * @throws {TypeError} If the given function is not a generator. + */ + function consume(generatorFn: Function, opt_self?: any, ...var_args: any[]): Promise; + + /** + * Registers an observer on a promised {@code value}, returning a new promise + * that will be resolved when the value is. If {@code value} is not a promise, + * then the return promise will be immediately resolved. + * @param {*} value The value to observe. + * @param {Function=} opt_callback The function to call when the value is + * resolved successfully. + * @param {Function=} opt_errback The function to call when the value is + * rejected. + * @return {!ManagedPromise} A new promise. + */ + function when(value: T | Promise, opt_callback?: (value: T) => any, opt_errback?: (error: any) => any): Promise; + + /** + * Returns a promise that will be resolved with the input value in a + * fully-resolved state. If the value is an array, each element will be fully + * resolved. Likewise, if the value is an object, all keys will be fully + * resolved. In both cases, all nested arrays and objects will also be + * fully resolved. All fields are resolved in place; the returned promise will + * resolve on {@code value} and not a copy. + * + * Warning: This function makes no checks against objects that contain + * cyclical references: + * + * var value = {}; + * value['self'] = value; + * promise.fullyResolved(value); // Stack overflow. + * + * @param {*} value The value to fully resolve. + * @return {!ManagedPromise} A promise for a fully resolved version + * of the input value. + */ + function fullyResolved(value: any): Promise; + + /** + * Changes the default flow to use when no others are active. + * @param {!ControlFlow} flow The new default flow. + * @throws {Error} If the default flow is not currently active. + */ + function setDefaultFlow(flow: ControlFlow): void; + + // endregion + + /** + * Error used when the computation of a promise is cancelled. + */ + class CancellationError extends Error { + /** + * @param {string=} opt_msg The cancellation message. + */ + constructor(opt_msg?: string); + } + + interface IThenable extends PromiseLike { + /** + * Registers listeners for when this instance is resolved. + * + * @param onfulfilled + * The function to call if this promise is successfully resolved. The function + * should expect a single argument: the promise's resolved value. + * @param onrejected + * The function to call if this promise is rejected. The function should + * expect a single argument: the rejection reason. + * @return A new promise which will be resolved with the result + * of the invoked callback. + * @template R + */ + then( + onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, + onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): PromiseLike; + + /** + * Registers a listener for when this promise is rejected. This is synonymous + * with the {@code catch} clause in a synchronous API: + * + * // Synchronous API: + * try { + * doSynchronousWork(); + * } catch (ex) { + * console.error(ex); + * } + * + * // Asynchronous promise API: + * doAsynchronousWork().catch(function(ex) { + * console.error(ex); + * }); + * + * @param {function(*): (R|IThenable)} errback The + * function to call if this promise is rejected. The function should + * expect a single argument: the rejection reason. + * @return {!ManagedPromise} A new promise which will be + * resolved with the result of the invoked callback. + * @template R + */ + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise; + } + + /** + * Thenable is a promise-like object with a {@code then} method which may be + * used to schedule callbacks on a promised value. + * + * @interface + * @template T + */ + interface Thenable extends IThenable {} + class Thenable { + /** + * Registers a listener to invoke when this promise is resolved, regardless + * of whether the promise's value was successfully computed. This function + * is synonymous with the {@code finally} clause in a synchronous API: + * + * // Synchronous API: + * try { + * doSynchronousWork(); + * } finally { + * cleanUp(); + * } + * + * // Asynchronous promise API: + * doAsynchronousWork().finally(cleanUp); + * + * __Note:__ similar to the {@code finally} clause, if the registered + * callback returns a rejected promise or throws an error, it will silently + * replace the rejection error (if any) from this promise: + * + * try { + * throw Error('one'); + * } finally { + * throw Error('two'); // Hides Error: one + * } + * + * promise.rejected(Error('one')) + * .finally(function() { + * throw Error('two'); // Hides Error: one + * }); + * + * @param {function(): (R|IThenable)} callback The function to call when + * this promise is resolved. + * @return {!ManagedPromise} A promise that will be fulfilled + * with the callback result. + * @template R + */ + finally(callback: Function): Promise; + + /** + * Adds a property to a class prototype to allow runtime checks of whether + * instances of that class implement the Thenable interface. This function + * will also ensure the prototype's {@code then} function is exported from + * compiled code. + * @param {function(new: Thenable, ...?)} ctor The + * constructor whose prototype to modify. + */ + static addImplementation(ctor: Function): void; + + /** + * Checks if an object has been tagged for implementing the Thenable + * interface as defined by {@link Thenable.addImplementation}. + * @param {*} object The object to test. + * @return {boolean} Whether the object is an implementation of the Thenable + * interface. + */ + static isImplementation(object: any): boolean; + } + + interface IFulfilledCallback { + (value: T | IThenable | Thenable | undefined): void; + } + + interface IRejectedCallback { + (reason: any): void; + } + + /** + * Represents the eventual value of a completed operation. Each promise may be + * in one of three states: pending, fulfilled, or rejected. Each promise starts + * in the pending state and may make a single transition to either a + * fulfilled or rejected state, at which point the promise is considered + * resolved. + * + * @implements {promise.Thenable} + * @template T + * @see http://promises-aplus.github.io/promises-spec/ + */ + class Promise implements IThenable, PromiseLike { + /** + * @param {function( + * function((T|IThenable|Thenable)=), + * function(*=))} resolver + * Function that is invoked immediately to begin computation of this + * promise's value. The function should accept a pair of callback + * functions, one for fulfilling the promise and another for rejecting it. + * @param {ControlFlow=} opt_flow The control flow + * this instance was created under. Defaults to the currently active flow. + */ + constructor(resolver: (resolve: IFulfilledCallback, reject: IRejectedCallback) => void, opt_flow?: ControlFlow); + + /** + * Creates a promise that is immediately resolved with the given value. + * + * @param {T=} opt_value The value to resolve. + * @return {!ManagedPromise} A promise resolved with the given value. + * @template T + */ + static resolve(opt_value?: T): Promise; + + /** + * Creates a promise that is immediately rejected with the given reason. + * + * @param {*=} opt_reason The rejection reason. + * @return {!ManagedPromise} A new rejected promise. + */ + static reject(opt_reason?: any): Promise; + + /** + * Registers listeners for when this instance is resolved. + * + * @param onfulfilled + * The function to call if this promise is successfully resolved. The function + * should expect a single argument: the promise's resolved value. + * @param onrejected + * The function to call if this promise is rejected. The function should + * expect a single argument: the rejection reason. + * @return A new promise which will be resolved with the result + * of the invoked callback. + */ + then( + onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, + onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise; + + /** + * Registers a listener for when this promise is rejected. This is synonymous + * with the {@code catch} clause in a synchronous API: + * + * // Synchronous API: + * try { + * doSynchronousWork(); + * } catch (ex) { + * console.error(ex); + * } + * + * // Asynchronous promise API: + * doAsynchronousWork().catch(function(ex) { + * console.error(ex); + * }); + * + * @param onrejected + * The function to call if this promise is rejected. The function should + * expect a single argument: the rejection reason. + * @return A new promise which will be resolved with the result of the invoked callback. + */ + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise; + } + + /** + * Represents a value that will be resolved at some point in the future. This + * class represents the protected 'producer' half of a Promise - each Deferred + * has a {@code promise} property that may be returned to consumers for + * registering callbacks, reserving the ability to resolve the deferred to the + * producer. + * + *

    If this Deferred is rejected and there are no listeners registered before + * the next turn of the event loop, the rejection will be passed to the + * {@link promise.ControlFlow} as an unhandled failure. + * + */ + class Deferred { + // region Constructors + + /** + * + * @param {promise.ControlFlow=} opt_flow The control flow + * this instance was created under. This should only be provided during + * unit tests. + * @constructor + */ + constructor(opt_flow?: ControlFlow); + + // endregion + + static State_: { + BLOCKED: number; + PENDING: number; + REJECTED: number; + RESOLVED: number; + }; + + // region Properties + + /** + * The consumer promise for this instance. Provides protected access to the + * callback registering functions. + * @type {!promise.Promise} + */ + promise: Promise; + + // endregion + + // region Methods + + /** + * Rejects this promise. If the error is itself a promise, this instance will + * be chained to it and be rejected with the error's resolved value. + * @param {*=} opt_error The rejection reason, typically either a + * {@code Error} or a {@code string}. + */ + reject(opt_error?: any): void; + errback(opt_error?: any): void; + + /** + * Resolves this promise with the given value. If the value is itself a + * promise and not a reference to this deferred, this instance will wait for + * it before resolving. + * @param {*=} opt_value The resolved value. + */ + fulfill(opt_value?: T): void; + + /** + * Removes all of the listeners previously registered on this deferred. + * @throws {Error} If this deferred has already been resolved. + */ + removeAll(): void; + + // endregion + } + + interface IControlFlowTimer { + clearInterval(ms: number): void; + clearTimeout(ms: number): void; + setInterval(fn: Function, ms: number): number; + setTimeout(fn: Function, ms: number): number; + } + + interface IEventType { + /** Emitted when all tasks have been successfully executed. */ + IDLE: string; + + /** Emitted when a ControlFlow has been reset. */ + RESET: string; + + /** Emitted whenever a new task has been scheduled. */ + SCHEDULE_TASK: string; + + /** + * Emitted whenever a control flow aborts due to an unhandled promise + * rejection. This event will be emitted along with the offending rejection + * reason. Upon emitting this event, the control flow will empty its task + * queue and revert to its initial state. + */ + UNCAUGHT_EXCEPTION: string; + } + + /** + * Handles the execution of scheduled tasks, each of which may be an + * asynchronous operation. The control flow will ensure tasks are executed in + * the ordered scheduled, starting each task only once those before it have + * completed. + * + * Each task scheduled within this flow may return a + * {@link promise.Promise} to indicate it is an asynchronous + * operation. The ControlFlow will wait for such promises to be resolved before + * marking the task as completed. + * + * Tasks and each callback registered on a {@link promise.Promise} + * will be run in their own ControlFlow frame. Any tasks scheduled within a + * frame will take priority over previously scheduled tasks. Furthermore, if any + * of the tasks in the frame fail, the remainder of the tasks in that frame will + * be discarded and the failure will be propagated to the user through the + * callback/task's promised result. + * + * Each time a ControlFlow empties its task queue, it will fire an + * {@link promise.ControlFlow.EventType.IDLE IDLE} event. Conversely, + * whenever the flow terminates due to an unhandled error, it will remove all + * remaining tasks in its queue and fire an + * {@link promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION + * UNCAUGHT_EXCEPTION} event. If there are no listeners registered with the + * flow, the error will be rethrown to the global error handler. + * + * @extends {EventEmitter} + * @final + */ + class ControlFlow extends EventEmitter { + /** + * @constructor + */ + constructor(); + + /** + * Events that may be emitted by an {@link promise.ControlFlow}. + * @enum {string} + */ + static EventType: IEventType; + + /** + * Returns a string representation of this control flow, which is its current + * {@link #getSchedule() schedule}, sans task stack traces. + * @return {string} The string representation of this contorl flow. + * @override + */ + toString(): string; + + /** + * Resets this instance, clearing its queue and removing all event listeners. + */ + reset(): void; + + /** + * Generates an annotated string describing the internal state of this control + * flow, including the currently executing as well as pending tasks. If + * {@code opt_includeStackTraces === true}, the string will include the + * stack trace from when each task was scheduled. + * @param {string=} opt_includeStackTraces Whether to include the stack traces + * from when each task was scheduled. Defaults to false. + * @return {string} String representation of this flow's internal state. + */ + getSchedule(opt_includeStackTraces?: boolean): string; + + /** + * Schedules a task for execution. If there is nothing currently in the + * queue, the task will be executed in the next turn of the event loop. If + * the task function is a generator, the task will be executed using + * {@link promise.consume}. + * + * @param {function(): (T|promise.Promise)} fn The function to + * call to start the task. If the function returns a + * {@link promise.Promise}, this instance will wait for it to be + * resolved before starting the next task. + * @param {string=} opt_description A description of the task. + * @return {!promise.Promise} A promise that will be resolved + * with the result of the action. + * @template T + */ + execute(fn: () => (T | Promise), opt_description?: string): Promise; + + /** + * Inserts a {@code setTimeout} into the command queue. This is equivalent to + * a thread sleep in a synchronous programming language. + * + * @param {number} ms The timeout delay, in milliseconds. + * @param {string=} opt_description A description to accompany the timeout. + * @return {!promise.Promise} A promise that will be resolved with + * the result of the action. + */ + timeout(ms: number, opt_description?: string): Promise; + + /** + * Schedules a task that shall wait for a condition to hold. Each condition + * function may return any value, but it will always be evaluated as a boolean. + * + * Condition functions may schedule sub-tasks with this instance, however, + * their execution time will be factored into whether a wait has timed out. + * + * In the event a condition returns a Promise, the polling loop will wait for + * it to be resolved before evaluating whether the condition has been satisfied. + * The resolution time for a promise is factored into whether a wait has timed + * out. + * + * If the condition function throws, or returns a rejected promise, the + * wait task will fail. + * + * If the condition is defined as a promise, the flow will wait for it to + * settle. If the timeout expires before the promise settles, the promise + * returned by this function will be rejected. + * + * If this function is invoked with `timeout === 0`, or the timeout is omitted, + * the flow will wait indefinitely for the condition to be satisfied. + * + * @param {(!promise.Promise|function())} condition The condition to poll, + * or a promise to wait on. + * @param {number=} opt_timeout How long to wait, in milliseconds, for the + * condition to hold before timing out. If omitted, the flow will wait + * indefinitely. + * @param {string=} opt_message An optional error message to include if the + * wait times out; defaults to the empty string. + * @return {!promise.Promise} A promise that will be fulfilled + * when the condition has been satisified. The promise shall be rejected if + * the wait times out waiting for the condition. + * @throws {TypeError} If condition is not a function or promise or if timeout + * is not a number >= 0. + * @template T + */ + wait(condition: Promise | Function, opt_timeout?: number, opt_message?: string): Promise; + } +} + +/** + * Defines a condition for use with WebDriver's WebDriver#wait wait command. + */ +export class Condition { + /** + * @param {string} message A descriptive error message. Should complete the + * sentence 'Waiting [...]' + * @param {function(!WebDriver): OUT} fn The condition function to + * evaluate on each iteration of the wait loop. + * @constructor + */ + constructor(message: string, fn: (webdriver: WebDriver) => any); + + /** @return {string} A description of this condition. */ + description(): string; + + /** @type {function(!WebDriver): OUT} */ + fn(webdriver: WebDriver): any; +} + +/** + * Defines a condition that will result in a {@link WebElement}. + * + * @extends {Condition)>} + */ +export class WebElementCondition extends Condition { + // add an unused private member so the compiler treats this + // class distinct from other Conditions + private _nominal: undefined; +} + +export namespace until { + /** + * Creates a condition that will wait until the input driver is able to switch + * to the designated frame. The target frame may be specified as + * + * 1. a numeric index into + * [window.frames](https://developer.mozilla.org/en-US/docs/Web/API/Window.frames) + * for the currently selected frame. + * 2. a {@link ./WebElement}, which must reference a FRAME or IFRAME + * element on the current page. + * 3. a locator which may be used to first locate a FRAME or IFRAME on the + * current page before attempting to switch to it. + * + * Upon successful resolution of this condition, the driver will be left + * focused on the new frame. + * + * @param {!(number|./WebElement|By| + * function(!./WebDriver): !./WebElement)} frame + * The frame identifier. + * @return {!Condition} A new condition. + */ + function ableToSwitchToFrame(frame: number | WebElement | By | ((webdriver: WebDriver) => WebElement) | ByHash): Condition; + + /** + * Creates a condition that waits for an alert to be opened. Upon success, the + * returned promise will be fulfilled with the handle for the opened alert. + * + * @return {!Condition} The new condition. + */ + function alertIsPresent(): Condition; + + /** + * Creates a condition that will wait for the given element to be disabled. + * + * @param {!WebElement} element The element to test. + * @return {!WebElementCondition} The new condition. + * @see WebDriver#isEnabled + */ + function elementIsDisabled(element: WebElement): WebElementCondition; + + /** + * Creates a condition that will wait for the given element to be enabled. + * + * @param {!WebElement} element The element to test. + * @return {!WebElementCondition} The new condition. + * @see WebDriver#isEnabled + */ + function elementIsEnabled(element: WebElement): WebElementCondition; + + /** + * Creates a condition that will wait for the given element to be deselected. + * + * @param {!WebElement} element The element to test. + * @return {!WebElementCondition} The new condition. + * @see WebDriver#isSelected + */ + function elementIsNotSelected(element: WebElement): WebElementCondition; + + /** + * Creates a condition that will wait for the given element to be in the DOM, + * yet not visible to the user. + * + * @param {!WebElement} element The element to test. + * @return {!WebElementCondition} The new condition. + * @see WebDriver#isDisplayed + */ + function elementIsNotVisible(element: WebElement): WebElementCondition; + + /** + * Creates a condition that will wait for the given element to be selected. + * @param {!WebElement} element The element to test. + * @return {!WebElementCondition} The new condition. + * @see WebDriver#isSelected + */ + function elementIsSelected(element: WebElement): WebElementCondition; + + /** + * Creates a condition that will wait for the given element to become visible. + * + * @param {!WebElement} element The element to test. + * @return {!WebElementCondition} The new condition. + * @see WebDriver#isDisplayed + */ + function elementIsVisible(element: WebElement): WebElementCondition; + + /** + * Creates a condition that will loop until an element is + * {@link ./WebDriver#findElement found} with the given locator. + * + * @param {!(By|Function)} locator The locator to use. + * @return {!WebElementCondition} The new condition. + */ + function elementLocated(locator: Locator): WebElementCondition; + + /** + * Creates a condition that will wait for the given element's + * {@link WebDriver#getText visible text} to contain the given + * substring. + * + * @param {!WebElement} element The element to test. + * @param {string} substr The substring to search for. + * @return {!WebElementCondition} The new condition. + * @see WebDriver#getText + */ + function elementTextContains(element: WebElement, substr: string): WebElementCondition; + + /** + * Creates a condition that will wait for the given element's + * {@link WebDriver#getText visible text} to match the given + * {@code text} exactly. + * + * @param {!WebElement} element The element to test. + * @param {string} text The expected text. + * @return {!WebElementCondition} The new condition. + * @see WebDriver#getText + */ + function elementTextIs(element: WebElement, text: string): WebElementCondition; + + /** + * Creates a condition that will wait for the given element's + * {@link WebDriver#getText visible text} to match a regular + * expression. + * + * @param {!WebElement} element The element to test. + * @param {!RegExp} regex The regular expression to test against. + * @return {!WebElementCondition} The new condition. + * @see WebDriver#getText + */ + function elementTextMatches(element: WebElement, regex: RegExp): WebElementCondition; + + /** + * Creates a condition that will loop until at least one element is + * {@link WebDriver#findElement found} with the given locator. + * + * @param {!(Locator|By.Hash|Function)} locator The locator + * to use. + * @return {!Condition.>} The new + * condition. + */ + function elementsLocated(locator: Locator): Condition; + + /** + * Creates a condition that will wait for the given element to become stale. An + * element is considered stale once it is removed from the DOM, or a new page + * has loaded. + * + * @param {!WebElement} element The element that should become stale. + * @return {!Condition} The new condition. + */ + function stalenessOf(element: WebElement): Condition; + + /** + * Creates a condition that will wait for the current page's title to contain + * the given substring. + * + * @param {string} substr The substring that should be present in the page + * title. + * @return {!Condition.} The new condition. + */ + function titleContains(substr: string): Condition; + + /** + * Creates a condition that will wait for the current page's title to match the + * given value. + * + * @param {string} title The expected page title. + * @return {!Condition} The new condition. + */ + function titleIs(title: string): Condition; + + /** + * Creates a condition that will wait for the current page's title to match the + * given regular expression. + * + * @param {!RegExp} regex The regular expression to test against. + * @return {!Condition.} The new condition. + */ + function titleMatches(regex: RegExp): Condition; + + /** + * Creates a condition that will wait for the current page's url to contain + * the given substring. + * + * @param {string} substrUrl The substring that should be present in the current + * URL. + * @return {!Condition} The new condition. + */ + function urlContains(substrUrl: string): Condition; + + /** + * Creates a condition that will wait for the current page's url to match the + * given value. + * + * @param {string} url The expected page url. + * @return {!Condition} The new condition. + */ + function urlIs(url: string): Condition; + + /** + * Creates a condition that will wait for the current page's url to match the + * given regular expression. + * + * @param {!RegExp} regex The regular expression to test against. + * @return {!Condition} The new condition. + */ + function urlMatches(regex: RegExp): Condition; +} + +export interface ILocation { + x: number; + y: number; +} + +export interface ISize { + width: number; + height: number; +} + +export interface IRectangle { + x: number; + y: number; + width: number; + height: number; +} + +export interface IButton { + LEFT: string; + MIDDLE: string; + RIGHT: string; +} + +/** + * Representations of pressable keys that aren't text. These are stored in + * the Unicode PUA (Private Use Area) code points, 0xE000-0xF8FF. Refer to + * http://www.google.com.au/search?&q=unicode+pua&btnG=Search + * + * @enum {string} + */ +export const Button: IButton; + +export interface IKey { + NULL: string; + CANCEL: string; // ^break + HELP: string; + BACK_SPACE: string; + TAB: string; + CLEAR: string; + RETURN: string; + ENTER: string; + SHIFT: string; + CONTROL: string; + ALT: string; + PAUSE: string; + ESCAPE: string; + SPACE: string; + PAGE_UP: string; + PAGE_DOWN: string; + END: string; + HOME: string; + ARROW_LEFT: string; + LEFT: string; + ARROW_UP: string; + UP: string; + ARROW_RIGHT: string; + RIGHT: string; + ARROW_DOWN: string; + DOWN: string; + INSERT: string; + DELETE: string; + SEMICOLON: string; + EQUALS: string; + + NUMPAD0: string; // number pad keys + NUMPAD1: string; + NUMPAD2: string; + NUMPAD3: string; + NUMPAD4: string; + NUMPAD5: string; + NUMPAD6: string; + NUMPAD7: string; + NUMPAD8: string; + NUMPAD9: string; + MULTIPLY: string; + ADD: string; + SEPARATOR: string; + SUBTRACT: string; + DECIMAL: string; + DIVIDE: string; + + F1: string; // function keys + F2: string; + F3: string; + F4: string; + F5: string; + F6: string; + F7: string; + F8: string; + F9: string; + F10: string; + F11: string; + F12: string; + + COMMAND: string; // Apple command key + META: string; // alias for Windows key + + /** + * Simulate pressing many keys at once in a 'chord'. Takes a sequence of + * keys or strings, appends each of the values to a string, + * and adds the chord termination key ({@link Key.NULL}) and returns + * the resulting string. + * + * Note: when the low-level webdriver key handlers see Keys.NULL, active + * modifier keys (CTRL/ALT/SHIFT/etc) release via a keyup event. + * + * @param {...string} var_args The key sequence to concatenate. + * @return {string} The null-terminated key sequence. + */ + chord(...var_args: Array): string; +} + +/** + * Representations of pressable keys that aren't text. These are stored in + * the Unicode PUA (Private Use Area) code points, 0xE000-0xF8FF. Refer to + * http://www.google.com.au/search?&q=unicode+pua&btnG=Search + * + * @enum {string} + */ +export const Key: IKey; + +/** + * Class for defining sequences of complex user interactions. Each sequence + * will not be executed until {@link #perform} is called. + * + * Example: + * + * new ActionSequence(driver). + * keyDown(Key.SHIFT). + * click(element1). + * click(element2). + * dragAndDrop(element3, element4). + * keyUp(Key.SHIFT). + * perform(); + * + */ +export class ActionSequence { + // region Constructors + + /** + * @param {!WebDriver} driver The driver instance to use. + * @constructor + */ + constructor(driver: WebDriver); + + // endregion + + // region Methods + + /** + * Executes this action sequence. + * @return {!promise.Promise} A promise that will be resolved once + * this sequence has completed. + */ + perform(): promise.Promise; + + /** + * Moves the mouse. The location to move to may be specified in terms of the + * mouse's current location, an offset relative to the top-left corner of an + * element, or an element (in which case the middle of the element is used). + * + * @param {(!./WebElement|{x: number, y: number})} location The + * location to drag to, as either another WebElement or an offset in + * pixels. + * @param {{x: number, y: number}=} opt_offset If the target {@code location} + * is defined as a {@link ./WebElement}, this parameter defines + * an offset within that element. The offset should be specified in pixels + * relative to the top-left corner of the element's bounding box. If + * omitted, the element's center will be used as the target offset. + * @return {!ActionSequence} A self reference. + */ + mouseMove(location: WebElement | ILocation, opt_offset?: ILocation): ActionSequence; + + /** + * Presses a mouse button. The mouse button will not be released until + * {@link #mouseUp} is called, regardless of whether that call is made in this + * sequence or another. The behavior for out-of-order events (e.g. mouseDown, + * click) is undefined. + * + * If an element is provided, the mouse will first be moved to the center + * of that element. This is equivalent to: + * + * sequence.mouseMove(element).mouseDown() + * + * Warning: this method currently only supports the left mouse button. See + * [issue 4047](http://code.google.com/p/selenium/issues/detail?id=4047). + * + * @param {(./WebElement|input.Button)=} opt_elementOrButton Either + * the element to interact with or the button to click with. + * Defaults to {@link input.Button.LEFT} if neither an element nor + * button is specified. + * @param {input.Button=} opt_button The button to use. Defaults to + * {@link input.Button.LEFT}. Ignored if a button is provided as the + * first argument. + * @return {!ActionSequence} A self reference. + */ + mouseDown(opt_elementOrButton?: WebElement | string, opt_button?: string): ActionSequence; + + /** + * Releases a mouse button. Behavior is undefined for calling this function + * without a previous call to {@link #mouseDown}. + * + * If an element is provided, the mouse will first be moved to the center + * of that element. This is equivalent to: + * + * sequence.mouseMove(element).mouseUp() + * + * Warning: this method currently only supports the left mouse button. See + * [issue 4047](http://code.google.com/p/selenium/issues/detail?id=4047). + * + * @param {(./WebElement|input.Button)=} opt_elementOrButton Either + * the element to interact with or the button to click with. + * Defaults to {@link input.Button.LEFT} if neither an element nor + * button is specified. + * @param {input.Button=} opt_button The button to use. Defaults to + * {@link input.Button.LEFT}. Ignored if a button is provided as the + * first argument. + * @return {!ActionSequence} A self reference. + */ + mouseUp(opt_elementOrButton?: WebElement | string, opt_button?: string): ActionSequence; + + /** + * Convenience function for performing a 'drag and drop' manuever. The target + * element may be moved to the location of another element, or by an offset (in + * pixels). + * + * @param {!./WebElement} element The element to drag. + * @param {(!./WebElement|{x: number, y: number})} location The + * location to drag to, either as another WebElement or an offset in + * pixels. + * @return {!ActionSequence} A self reference. + */ + dragAndDrop(element: WebElement, location: WebElement | ILocation): ActionSequence; + + /** + * Clicks a mouse button. + * + * If an element is provided, the mouse will first be moved to the center + * of that element. This is equivalent to: + * + * sequence.mouseMove(element).click() + * + * @param {(./WebElement|input.Button)=} opt_elementOrButton Either + * the element to interact with or the button to click with. + * Defaults to {@link input.Button.LEFT} if neither an element nor + * button is specified. + * @param {input.Button=} opt_button The button to use. Defaults to + * {@link input.Button.LEFT}. Ignored if a button is provided as the + * first argument. + * @return {!ActionSequence} A self reference. + */ + click(opt_elementOrButton?: WebElement | string, opt_button?: string): ActionSequence; + + /** + * Double-clicks a mouse button. + * + * If an element is provided, the mouse will first be moved to the center of + * that element. This is equivalent to: + * + * sequence.mouseMove(element).doubleClick() + * + * Warning: this method currently only supports the left mouse button. See + * [issue 4047](http://code.google.com/p/selenium/issues/detail?id=4047). + * + * @param {(./WebElement|input.Button)=} opt_elementOrButton Either + * the element to interact with or the button to click with. + * Defaults to {@link input.Button.LEFT} if neither an element nor + * button is specified. + * @param {input.Button=} opt_button The button to use. Defaults to + * {@link input.Button.LEFT}. Ignored if a button is provided as the + * first argument. + * @return {!ActionSequence} A self reference. + */ + doubleClick(opt_elementOrButton?: WebElement | string, opt_button?: string): ActionSequence; + + /** + * Performs a modifier key press. The modifier key is not released + * until {@link #keyUp} or {@link #sendKeys} is called. The key press will be + * targetted at the currently focused element. + * @param {!Key} key The modifier key to push. Must be one of + * {ALT, CONTROL, SHIFT, COMMAND, META}. + * @return {!ActionSequence} A self reference. + * @throws {Error} If the key is not a valid modifier key. + */ + keyDown(key: string): ActionSequence; + + /** + * Performs a modifier key release. The release is targetted at the currently + * focused element. + * @param {!Key} key The modifier key to release. Must be one of + * {ALT, CONTROL, SHIFT, COMMAND, META}. + * @return {!ActionSequence} A self reference. + * @throws {Error} If the key is not a valid modifier key. + */ + keyUp(key: string): ActionSequence; + + /** + * Simulates typing multiple keys. Each modifier key encountered in the + * sequence will not be released until it is encountered again. All key events + * will be targeted at the currently focused element. + * + * @param {...(string|!input.Key|!Array<(string|!input.Key)>)} var_args + * The keys to type. + * @return {!ActionSequence} A self reference. + * @throws {Error} If the key is not a valid modifier key. + */ + sendKeys(...var_args: Array>): ActionSequence; + + // endregion +} + +/** + * Class for defining sequences of user touch interactions. Each sequence + * will not be executed until {@link #perform} is called. + * + * Example: + * + * new TouchSequence(driver). + * tapAndHold({x: 0, y: 0}). + * move({x: 3, y: 4}). + * release({x: 10, y: 10}). + * perform(); + */ +export class TouchSequence { + /* + * @param {!WebDriver} driver The driver instance to use. + * @constructor + */ + constructor(driver: WebDriver); + + /** + * Executes this action sequence. + * @return {!promise.Promise} A promise that will be resolved once + * this sequence has completed. + */ + perform(): promise.Promise; + + /** + * Taps an element. + * + * @param {!WebElement} elem The element to tap. + * @return {!TouchSequence} A self reference. + */ + tap(elem: WebElement): TouchSequence; + + /** + * Double taps an element. + * + * @param {!WebElement} elem The element to double tap. + * @return {!TouchSequence} A self reference. + */ + doubleTap(elem: WebElement): TouchSequence; + + /** + * Long press on an element. + * + * @param {!WebElement} elem The element to long press. + * @return {!TouchSequence} A self reference. + */ + longPress(elem: WebElement): TouchSequence; + + /** + * Touch down at the given location. + * + * @param {{ x: number, y: number }} location The location to touch down at. + * @return {!TouchSequence} A self reference. + */ + tapAndHold(location: ILocation): TouchSequence; + + /** + * Move a held {@linkplain #tapAndHold touch} to the specified location. + * + * @param {{x: number, y: number}} location The location to move to. + * @return {!TouchSequence} A self reference. + */ + move(location: ILocation): TouchSequence; + + /** + * Release a held {@linkplain #tapAndHold touch} at the specified location. + * + * @param {{x: number, y: number}} location The location to release at. + * @return {!TouchSequence} A self reference. + */ + release(location: ILocation): TouchSequence; + + /** + * Scrolls the touch screen by the given offset. + * + * @param {{x: number, y: number}} offset The offset to scroll to. + * @return {!TouchSequence} A self reference. + */ + scroll(offset: IOffset): TouchSequence; + + /** + * Scrolls the touch screen, starting on `elem` and moving by the specified + * offset. + * + * @param {!WebElement} elem The element where scroll starts. + * @param {{x: number, y: number}} offset The offset to scroll to. + * @return {!TouchSequence} A self reference. + */ + scrollFromElement(elem: WebElement, offset: IOffset): TouchSequence; + + /** + * Flick, starting anywhere on the screen, at speed xspeed and yspeed. + * + * @param {{xspeed: number, yspeed: number}} speed The speed to flick in each + direction, in pixels per second. + * @return {!TouchSequence} A self reference. + */ + flick(speed: ISpeed): TouchSequence; + + /** + * Flick starting at elem and moving by x and y at specified speed. + * + * @param {!WebElement} elem The element where flick starts. + * @param {{x: number, y: number}} offset The offset to flick to. + * @param {number} speed The speed to flick at in pixels per second. + * @return {!TouchSequence} A self reference. + */ + flickElement(elem: WebElement, offset: IOffset, speed: number): TouchSequence; +} + +export interface IOffset { + x: number; + y: number; +} + +export interface ISpeed { + xspeed: number; + yspeed: number; +} + +/** + * Represents a modal dialog such as {@code alert}, {@code confirm}, or + * {@code prompt}. Provides functions to retrieve the message displayed with + * the alert, accept or dismiss the alert, and set the response text (in the + * case of {@code prompt}). + */ +export class Alert { + /** + * @param {!WebDriver} driver The driver controlling the browser this alert + * is attached to. + * @param {string} text The message text displayed with this alert. + */ + constructor(driver: WebDriver, text: string); + + // region Methods + + /** + * Retrieves the message text displayed with this alert. For instance, if the + * alert were opened with alert('hello'), then this would return 'hello'. + * @return {!promise.Promise} A promise that will be resolved to the + * text displayed with this alert. + */ + getText(): promise.Promise; + + /** + * Sets the username and password in an alert prompting for credentials (such + * as a Basic HTTP Auth prompt). This method will implicitly + * {@linkplain #accept() submit} the dialog. + * + * @param {string} username The username to send. + * @param {string} password The password to send. + * @return {!promise.Promise} A promise that will be resolved when this + * command has completed. + */ + authenticateAs(username: string, password: string): promise.Promise; + + /** + * Accepts this alert. + * @return {!promise.Promise} A promise that will be resolved when + * this command has completed. + */ + accept(): promise.Promise; + + /** + * Dismisses this alert. + * @return {!promise.Promise} A promise that will be resolved when + * this command has completed. + */ + dismiss(): promise.Promise; + + /** + * Sets the response text on this alert. This command will return an error if + * the underlying alert does not support response text (e.g. window.alert and + * window.confirm). + * @param {string} text The text to set. + * @return {!promise.Promise} A promise that will be resolved when + * this command has completed. + */ + sendKeys(text: string): promise.Promise; + + // endregion +} + +/** + * AlertPromise is a promise that will be fulfilled with an Alert. This promise + * serves as a forward proxy on an Alert, allowing calls to be scheduled + * directly on this instance before the underlying Alert has been fulfilled. In + * other words, the following two statements are equivalent: + * + * driver.switchTo().alert().dismiss(); + * driver.switchTo().alert().then(function(alert) { + * return alert.dismiss(); + * }); + * + * @implements {promise.Thenable.} + * @final + */ +export interface AlertPromise extends promise.IThenable {} +export class AlertPromise extends Alert { + /** + * @param {!WebDriver} driver The driver controlling the browser this + * alert is attached to. + * @param {!promise.Thenable} alert A thenable + * that will be fulfilled with the promised alert. + */ + constructor(driver: WebDriver, alert: promise.Promise); +} + +/** + * Recognized browser names. + * @enum {string} + */ +export interface IBrowser { + ANDROID: string; + CHROME: string; + EDGE: string; + FIREFOX: string; + IE: string; + INTERNET_EXPLORER: string; + IPAD: string; + IPHONE: string; + OPERA: string; + PHANTOM_JS: string; + SAFARI: string; + HTMLUNIT: string; +} + +export const Browser: IBrowser; + +export interface ProxyConfig { + proxyType: string; + proxyAutoconfigUrl?: string; + ftpProxy?: string; + httpProxy?: string; + sslProxy?: string; + noProxy?: string; + socksProxy?: string; + socksUsername?: string; + socksPassword?: string; +} + +/** + * Creates new {@link WebDriver WebDriver} instances. The environment + * variables listed below may be used to override a builder's configuration, + * allowing quick runtime changes. + * + * - {@code SELENIUM_BROWSER}: defines the target browser in the form + * {@code browser[:version][:platform]}. + * + * - {@code SELENIUM_REMOTE_URL}: defines the remote URL for all builder + * instances. This environment variable should be set to a fully qualified + * URL for a WebDriver server (e.g. http://localhost:4444/wd/hub). This + * option always takes precedence over {@code SELENIUM_SERVER_JAR}. + * + * - {@code SELENIUM_SERVER_JAR}: defines the path to the + * + * standalone Selenium server jar to use. The server will be started the + * first time a WebDriver instance and be killed when the process exits. + * + * Suppose you had mytest.js that created WebDriver with + * + * var driver = new Builder() + * .forBrowser('chrome') + * .build(); + * + * This test could be made to use Firefox on the local machine by running with + * `SELENIUM_BROWSER=firefox node mytest.js`. Rather than change the code to + * target Google Chrome on a remote machine, you can simply set the + * `SELENIUM_BROWSER` and `SELENIUM_REMOTE_URL` environment variables: + * + * SELENIUM_BROWSER=chrome:36:LINUX \ + * SELENIUM_REMOTE_URL=http://www.example.com:4444/wd/hub \ + * node mytest.js + * + * You could also use a local copy of the standalone Selenium server: + * + * SELENIUM_BROWSER=chrome:36:LINUX \ + * SELENIUM_SERVER_JAR=/path/to/selenium-server-standalone.jar \ + * node mytest.js + */ +export class Builder { + // region Constructors + + /** + * @constructor + */ + constructor(); + + // endregion + + // region Methods + + /** + * Configures this builder to ignore any environment variable overrides and to + * only use the configuration specified through this instance's API. + * + * @return {!Builder} A self reference. + */ + disableEnvironmentOverrides(): Builder; + + /** + * Creates a new WebDriver client based on this builder's current + * configuration. + * + * This method will return a {@linkplain ThenableWebDriver} instance, allowing + * users to issue commands directly without calling `then()`. The returned + * thenable wraps a promise that will resolve to a concrete + * {@linkplain webdriver.WebDriver WebDriver} instance. The promise will be + * rejected if the remote end fails to create a new session. + * + * @return {!ThenableWebDriver} A new WebDriver instance. + * @throws {Error} If the current configuration is invalid. + */ + build(): ThenableWebDriver; + + /** + * Configures the target browser for clients created by this instance. + * Any calls to {@link #withCapabilities} after this function will + * overwrite these settings. + * + *

    You may also define the target browser using the {@code SELENIUM_BROWSER} + * environment variable. If set, this environment variable should be of the + * form {@code browser[:[version][:platform]]}. + * + * @param {(string|Browser)} name The name of the target browser; + * common defaults are available on the {@link Browser} enum. + * @param {string=} opt_version A desired version; may be omitted if any + * version should be used. + * @param {string=} opt_platform The desired platform; may be omitted if any + * version may be used. + * @return {!Builder} A self reference. + */ + forBrowser(name: string, opt_version?: string, opt_platform?: string): Builder; + + /** + * Returns the base set of capabilities this instance is currently configured + * to use. + * @return {!Capabilities} The current capabilities for this builder. + */ + getCapabilities(): Capabilities; + + /** + * @return {string} The URL of the WebDriver server this instance is configured + * to use. + */ + getServerUrl(): string; + + /** + * @return {?string} The URL of the proxy server to use for the WebDriver's + * HTTP connections, or `null` if not set. + */ + getWebDriverProxy(): string; + + /** + * Sets the default action to take with an unexpected alert before returning + * an error. + * @param {string} beahvior The desired behavior; should be 'accept', 'dismiss', + * or 'ignore'. Defaults to 'dismiss'. + * @return {!Builder} A self reference. + */ + setAlertBehavior(behavior: string): Builder; + + /** + * Sets Chrome-specific options for drivers created by this builder. Any + * logging or proxy settings defined on the given options will take precedence + * over those set through {@link #setLoggingPrefs} and {@link #setProxy}, + * respectively. + * + * @param {!chrome.Options} options The ChromeDriver options to use. + * @return {!Builder} A self reference. + */ + setChromeOptions(options: chrome.Options): Builder; + + /** + * Sets the control flow that created drivers should execute actions in. If + * the flow is never set, or is set to {@code null}, it will use the active + * flow at the time {@link #build()} is called. + * @param {promise.ControlFlow} flow The control flow to use, or + * {@code null} to + * @return {!Builder} A self reference. + */ + setControlFlow(flow: promise.ControlFlow): Builder; + + /** + * Set {@linkplain edge.Options options} specific to Microsoft's Edge browser + * for drivers created by this builder. Any proxy settings defined on the + * given options will take precedence over those set through + * {@link #setProxy}. + * + * @param {!edge.Options} options The MicrosoftEdgeDriver options to use. + * @return {!Builder} A self reference. + */ + setEdgeOptions(options: edge.Options): Builder; + + /** + * Sets whether native events should be used. + * @param {boolean} enabled Whether to enable native events. + * @return {!Builder} A self reference. + */ + setEnableNativeEvents(enabled: boolean): Builder; + + /** + * Sets Firefox-specific options for drivers created by this builder. Any + * logging or proxy settings defined on the given options will take precedence + * over those set through {@link #setLoggingPrefs} and {@link #setProxy}, + * respectively. + * + * @param {!firefox.Options} options The FirefoxDriver options to use. + * @return {!Builder} A self reference. + */ + setFirefoxOptions(options: firefox.Options): Builder; + + /** + * Set Internet Explorer specific {@linkplain ie.Options options} for drivers + * created by this builder. Any proxy settings defined on the given options + * will take precedence over those set through {@link #setProxy}. + * + * @param {!ie.Options} options The IEDriver options to use. + * @return {!Builder} A self reference. + */ + setIeOptions(options: ie.Options): Builder; + + /** + * Sets the logging preferences for the created session. Preferences may be + * changed by repeated calls, or by calling {@link #withCapabilities}. + * @param {!(logging.Preferences|Object.)} prefs The + * desired logging preferences. + * @return {!Builder} A self reference. + */ + setLoggingPrefs(prefs: logging.Preferences | Object): Builder; + + /** + * Sets Opera specific {@linkplain opera.Options options} for drivers created + * by this builder. Any logging or proxy settings defined on the given options + * will take precedence over those set through {@link #setLoggingPrefs} and + * {@link #setProxy}, respectively. + * + * @param {!opera.Options} options The OperaDriver options to use. + * @return {!Builder} A self reference. + */ + setOperaOptions(options: opera.Options): Builder; + + /** + * Sets the proxy configuration to use for WebDriver clients created by this + * builder. Any calls to {@link #withCapabilities} after this function will + * overwrite these settings. + * @param {!capabilities.ProxyConfig} config The configuration to use. + * @return {!Builder} A self reference. + */ + setProxy(config: ProxyConfig): Builder; + + /** + * Sets Safari specific {@linkplain safari.Options options} for drivers + * created by this builder. Any logging settings defined on the given options + * will take precedence over those set through {@link #setLoggingPrefs}. + * + * @param {!safari.Options} options The Safari options to use. + * @return {!Builder} A self reference. + */ + setSafari(options: safari.Options): Builder; + + /** + * Sets how elements should be scrolled into view for interaction. + * @param {number} behavior The desired scroll behavior: either 0 to align with + * the top of the viewport or 1 to align with the bottom. + * @return {!Builder} A self reference. + */ + setScrollBehavior(behavior: number): Builder; + + /** + * Sets the http agent to use for each request. + * If this method is not called, the Builder will use http.globalAgent by default. + * + * @param {http.Agent} agent The agent to use for each request. + * @return {!Builder} A self reference. + */ + usingHttpAgent(agent: any): Builder; + + /** + * Sets the URL of a remote WebDriver server to use. Once a remote URL has been + * specified, the builder direct all new clients to that server. If this method + * is never called, the Builder will attempt to create all clients locally. + * + *

    As an alternative to this method, you may also set the + * {@code SELENIUM_REMOTE_URL} environment variable. + * + * @param {string} url The URL of a remote server to use. + * @return {!Builder} A self reference. + */ + usingServer(url: string): Builder; + + /** + * Sets the URL of the proxy to use for the WebDriver's HTTP connections. + * If this method is never called, the Builder will create a connection + * without a proxy. + * + * @param {string} proxy The URL of a proxy to use. + * @return {!Builder} A self reference. + */ + usingWebDriverProxy(proxy: string): Builder; + + /** + * Sets the desired capabilities when requesting a new session. This will + * overwrite any previously set capabilities. + * @param {!(Object|Capabilities)} capabilities The desired + * capabilities for a new session. + * @return {!Builder} A self reference. + */ + withCapabilities(capabilities: Object | Capabilities): Builder; + + // endregion +} + +/** + * Describes a mechanism for locating an element on the page. + * @final + */ +export class By { + /** + * @param {string} using the name of the location strategy to use. + * @param {string} value the value to search for. + */ + constructor(using: string, value: string); + + /** + * Locates elements that have a specific class name. + * + * @param {string} name The class name to search for. + * @return {!By} The new locator. + * @see http://www.w3.org/TR/2011/WD-html5-20110525/elements.html#classes + * @see http://www.w3.org/TR/CSS2/selector.html#class-html + */ + static className(name: string): By; + + /** + * Locates elements using a CSS selector. + * + * @param {string} selector The CSS selector to use. + * @return {!By} The new locator. + * @see http://www.w3.org/TR/CSS2/selector.html + */ + static css(selector: string): By; + + /** + * Locates eleemnts by the ID attribute. This locator uses the CSS selector + * `*[id='$ID']`, _not_ `document.getElementById`. + * + * @param {string} id The ID to search for. + * @return {!By} The new locator. + */ + static id(id: string): By; + + /** + * Locates link elements whose + * {@linkplain WebElement#getText visible text} matches the given + * string. + * + * @param {string} text The link text to search for. + * @return {!By} The new locator. + */ + static linkText(text: string): By; + + /** + * Locates an elements by evaluating a + * {@linkplain WebDriver#executeScript JavaScript expression}. + * The result of this expression must be an element or list of elements. + * + * @param {!(string|Function)} script The script to execute. + * @param {...*} var_args The arguments to pass to the script. + * @return {function(!./WebDriver): !./promise.Promise} + * A new JavaScript-based locator function. + */ + static js(script: string | Function, ...var_args: any[]): (webdriver: WebDriver) => promise.Promise; + + /** + * Locates elements whose `name` attribute has the given value. + * + * @param {string} name The name attribute to search for. + * @return {!By} The new locator. + */ + static name(name: string): By; + + /** + * Locates link elements whose + * {@linkplain WebElement#getText visible text} contains the given + * substring. + * + * @param {string} text The substring to check for in a link's visible text. + * @return {!By} The new locator. + */ + static partialLinkText(text: string): By; + + /** + * Locates elements with a given tag name. + * + * @param {string} name The tag name to search for. + * @return {!By} The new locator. + * @deprecated Use {@link By.css() By.css(tagName)} instead. + */ + static tagName(name: string): By; + + /** + * Locates elements matching a XPath selector. Care should be taken when + * using an XPath selector with a {@link WebElement} as WebDriver + * will respect the context in the specified in the selector. For example, + * given the selector `//div`, WebDriver will search from the document root + * regardless of whether the locator was used with a WebElement. + * + * @param {string} xpath The XPath selector to use. + * @return {!By} The new locator. + * @see http://www.w3.org/TR/xpath/ + */ + static xpath(xpath: string): By; + + /** @override */ + toString(): string; +} + +/** + * Short-hand expressions for the primary element locator strategies. + * For example the following two statements are equivalent: + * + * var e1 = driver.findElement(By.id('foo')); + * var e2 = driver.findElement({id: 'foo'}); + * + * Care should be taken when using JavaScript minifiers (such as the + * Closure compiler), as locator hashes will always be parsed using + * the un-obfuscated properties listed. + * + * @typedef {( + * {className: string}| + * {css: string}| + * {id: string}| + * {js: string}| + * {linkText: string}| + * {name: string}| + * {partialLinkText: string}| + * {tagName: string}| + * {xpath: string})} + */ +export type ByHash = { className: string } | + { css: string } | + { id: string } | + { js: string } | + { linkText: string } | + { name: string } | + { partialLinkText: string } | + { tagName: string } | + { xpath: string }; + +export type Locator = By | Function | ByHash; + +/** + * Common webdriver capability keys. + * @enum {string} + */ +export interface ICapability { + /** + * Indicates whether a driver should accept all SSL certs by default. This + * capability only applies when requesting a new session. To query whether + * a driver can handle insecure SSL certs, see + * {@link Capability.SECURE_SSL}. + */ + ACCEPT_SSL_CERTS: string; + + /** + * The browser name. Common browser names are defined in the + * {@link Browser} enum. + */ + BROWSER_NAME: string; + + /** + * Defines how elements should be scrolled into the viewport for interaction. + * This capability will be set to zero (0) if elements are aligned with the + * top of the viewport, or one (1) if aligned with the bottom. The default + * behavior is to align with the top of the viewport. + */ + ELEMENT_SCROLL_BEHAVIOR: string; + + /** + * Whether the driver is capable of handling modal alerts (e.g. alert, + * confirm, prompt). To define how a driver should handle alerts, + * use {@link Capability.UNEXPECTED_ALERT_BEHAVIOR}. + */ + HANDLES_ALERTS: string; + + /** + * Key for the logging driver logging preferences. + */ + LOGGING_PREFS: string; + + /** + * Whether this session generates native events when simulating user input. + */ + NATIVE_EVENTS: string; + + /** + * Describes the platform the browser is running on. Will be one of + * ANDROID, IOS, LINUX, MAC, UNIX, or WINDOWS. When requesting a + * session, ANY may be used to indicate no platform preference (this is + * semantically equivalent to omitting the platform capability). + */ + PLATFORM: string; + + /** + * Describes the proxy configuration to use for a new WebDriver session. + */ + PROXY: string; + + /** Whether the driver supports changing the brower's orientation. */ + ROTATABLE: string; + + /** + * Whether a driver is only capable of handling secure SSL certs. To request + * that a driver accept insecure SSL certs by default, use + * {@link Capability.ACCEPT_SSL_CERTS}. + */ + SECURE_SSL: string; + + /** Whether the driver supports manipulating the app cache. */ + SUPPORTS_APPLICATION_CACHE: string; + + /** Whether the driver supports locating elements with CSS selectors. */ + SUPPORTS_CSS_SELECTORS: string; + + /** Whether the browser supports JavaScript. */ + SUPPORTS_JAVASCRIPT: string; + + /** Whether the driver supports controlling the browser's location info. */ + SUPPORTS_LOCATION_CONTEXT: string; + + /** Whether the driver supports taking screenshots. */ + TAKES_SCREENSHOT: string; + + /** + * Defines how the driver should handle unexpected alerts. The value should + * be one of 'accept', 'dismiss', or 'ignore. + */ + UNEXPECTED_ALERT_BEHAVIOR: string; + + /** Defines the browser version. */ + VERSION: string; +} + +export const Capability: ICapability; + +export class Capabilities { + // region Constructors + + /** + * @param {(Capabilities|Object)=} opt_other Another set of + * capabilities to merge into this instance. + * @constructor + */ + constructor(opt_other?: Capabilities | Object); + + // endregion + + // region Methods + + /** @return {!Object} The JSON representation of this instance. */ + toJSON(): any; + + /** + * Merges another set of capabilities into this instance. Any duplicates in + * the provided set will override those already set on this instance. + * @param {!(Capabilities|Object)} other The capabilities to + * merge into this instance. + * @return {!Capabilities} A self reference. + */ + merge(other: Capabilities | Object): Capabilities; + + /** + * @param {string} key The capability to set. + * @param {*} value The capability value. Capability values must be JSON + * serializable. Pass {@code null} to unset the capability. + * @return {!Capabilities} A self reference. + */ + set(key: string, value: any): Capabilities; + + /** + * Sets the logging preferences. Preferences may be specified as a + * {@link logging.Preferences} instance, or a as a map of log-type to + * log-level. + * @param {!(logging.Preferences|Object.)} prefs The + * logging preferences. + * @return {!Capabilities} A self reference. + */ + setLoggingPrefs(prefs: logging.Preferences | Object): Capabilities; + + /** + * Sets the proxy configuration for this instance. + * @param {ProxyConfig} proxy The desired proxy configuration. + * @return {!Capabilities} A self reference. + */ + setProxy(proxy: ProxyConfig): Capabilities; + + /** + * Sets whether native events should be used. + * @param {boolean} enabled Whether to enable native events. + * @return {!Capabilities} A self reference. + */ + setEnableNativeEvents(enabled: boolean): Capabilities; + + /** + * Sets how elements should be scrolled into view for interaction. + * @param {number} behavior The desired scroll behavior: either 0 to align with + * the top of the viewport or 1 to align with the bottom. + * @return {!Capabilities} A self reference. + */ + setScrollBehavior(behavior: number): Capabilities; + + /** + * Sets the default action to take with an unexpected alert before returning + * an error. + * @param {string} behavior The desired behavior; should be 'accept', 'dismiss', + * or 'ignore'. Defaults to 'dismiss'. + * @return {!Capabilities} A self reference. + */ + setAlertBehavior(behavior: string): Capabilities; + + /** + * @param {string} key The capability to return. + * @return {*} The capability with the given key, or {@code null} if it has + * not been set. + */ + get(key: string): any; + + /** + * @param {string} key The capability to check. + * @return {boolean} Whether the specified capability is set. + */ + has(key: string): boolean; + + // endregion + + // region Static Methods + + /** + * @return {!Capabilities} A basic set of capabilities for Android. + */ + static android(): Capabilities; + + /** + * @return {!Capabilities} A basic set of capabilities for Chrome. + */ + static chrome(): Capabilities; + + /** + * @return {!Capabilities} A basic set of capabilities for Microsoft Edge. + */ + static edge(): Capabilities; + + /** + * @return {!Capabilities} A basic set of capabilities for Firefox. + */ + static firefox(): Capabilities; + + /** + * @return {!Capabilities} A basic set of capabilities for + * Internet Explorer. + */ + static ie(): Capabilities; + + /** + * @return {!Capabilities} A basic set of capabilities for iPad. + */ + static ipad(): Capabilities; + + /** + * @return {!Capabilities} A basic set of capabilities for iPhone. + */ + static iphone(): Capabilities; + + /** + * @return {!Capabilities} A basic set of capabilities for Opera. + */ + static opera(): Capabilities; + + /** + * @return {!Capabilities} A basic set of capabilities for + * PhantomJS. + */ + static phantomjs(): Capabilities; + + /** + * @return {!Capabilities} A basic set of capabilities for Safari. + */ + static safari(): Capabilities; + + /** + * @return {!Capabilities} A basic set of capabilities for HTMLUnit. + */ + static htmlunit(): Capabilities; + + /** + * @return {!Capabilities} A basic set of capabilities for HTMLUnit + * with enabled Javascript. + */ + static htmlunitwithjs(): Capabilities; + + // endregion +} + +/** + * An enumeration of valid command string. + */ +export interface ICommandName { + GET_SERVER_STATUS: string; + + NEW_SESSION: string; + GET_SESSIONS: string; + DESCRIBE_SESSION: string; + + CLOSE: string; + QUIT: string; + + GET_CURRENT_URL: string; + GET: string; + GO_BACK: string; + GO_FORWARD: string; + REFRESH: string; + + ADD_COOKIE: string; + GET_COOKIE: string; + GET_ALL_COOKIES: string; + DELETE_COOKIE: string; + DELETE_ALL_COOKIES: string; + + GET_ACTIVE_ELEMENT: string; + FIND_ELEMENT: string; + FIND_ELEMENTS: string; + FIND_CHILD_ELEMENT: string; + FIND_CHILD_ELEMENTS: string; + + CLEAR_ELEMENT: string; + CLICK_ELEMENT: string; + SEND_KEYS_TO_ELEMENT: string; + SUBMIT_ELEMENT: string; + + GET_CURRENT_WINDOW_HANDLE: string; + GET_WINDOW_HANDLES: string; + GET_WINDOW_POSITION: string; + SET_WINDOW_POSITION: string; + GET_WINDOW_SIZE: string; + SET_WINDOW_SIZE: string; + MAXIMIZE_WINDOW: string; + + SWITCH_TO_WINDOW: string; + SWITCH_TO_FRAME: string; + GET_PAGE_SOURCE: string; + GET_TITLE: string; + + EXECUTE_SCRIPT: string; + EXECUTE_ASYNC_SCRIPT: string; + + GET_ELEMENT_TEXT: string; + GET_ELEMENT_TAG_NAME: string; + IS_ELEMENT_SELECTED: string; + IS_ELEMENT_ENABLED: string; + IS_ELEMENT_DISPLAYED: string; + GET_ELEMENT_LOCATION: string; + GET_ELEMENT_LOCATION_IN_VIEW: string; + GET_ELEMENT_SIZE: string; + GET_ELEMENT_ATTRIBUTE: string; + GET_ELEMENT_VALUE_OF_CSS_PROPERTY: string; + ELEMENT_EQUALS: string; + + SCREENSHOT: string; + IMPLICITLY_WAIT: string; + SET_SCRIPT_TIMEOUT: string; + SET_TIMEOUT: string; + + ACCEPT_ALERT: string; + DISMISS_ALERT: string; + GET_ALERT_TEXT: string; + SET_ALERT_TEXT: string; + + EXECUTE_SQL: string; + GET_LOCATION: string; + SET_LOCATION: string; + GET_APP_CACHE: string; + GET_APP_CACHE_STATUS: string; + CLEAR_APP_CACHE: string; + IS_BROWSER_ONLINE: string; + SET_BROWSER_ONLINE: string; + + GET_LOCAL_STORAGE_ITEM: string; + GET_LOCAL_STORAGE_KEYS: string; + SET_LOCAL_STORAGE_ITEM: string; + REMOVE_LOCAL_STORAGE_ITEM: string; + CLEAR_LOCAL_STORAGE: string; + GET_LOCAL_STORAGE_SIZE: string; + + GET_SESSION_STORAGE_ITEM: string; + GET_SESSION_STORAGE_KEYS: string; + SET_SESSION_STORAGE_ITEM: string; + REMOVE_SESSION_STORAGE_ITEM: string; + CLEAR_SESSION_STORAGE: string; + GET_SESSION_STORAGE_SIZE: string; + + SET_SCREEN_ORIENTATION: string; + GET_SCREEN_ORIENTATION: string; + + // These belong to the Advanced user interactions - an element is + // optional for these commands. + CLICK: string; + DOUBLE_CLICK: string; + MOUSE_DOWN: string; + MOUSE_UP: string; + MOVE_TO: string; + SEND_KEYS_TO_ACTIVE_ELEMENT: string; + + // These belong to the Advanced Touch API + TOUCH_SINGLE_TAP: string; + TOUCH_DOWN: string; + TOUCH_UP: string; + TOUCH_MOVE: string; + TOUCH_SCROLL: string; + TOUCH_DOUBLE_TAP: string; + TOUCH_LONG_PRESS: string; + TOUCH_FLICK: string; + + GET_AVAILABLE_LOG_TYPES: string; + GET_LOG: string; + GET_SESSION_LOGS: string; + + UPLOAD_FILE: string; +} + +export const CommandName: ICommandName; + +/** + * Describes a command to be executed by the WebDriverJS framework. + * @param {!CommandName} name The name of this command. + * @constructor + */ +export class Command { + // region Constructors + + /** + * @param {!CommandName} name The name of this command. + * @constructor + */ + constructor(name: string); + + // endregion + + // region Methods + + /** + * @return {!CommandName} This command's name. + */ + getName(): string; + + /** + * Sets a parameter to send with this command. + * @param {string} name The parameter name. + * @param {*} value The parameter value. + * @return {!Command} A self reference. + */ + setParameter(name: string, value: any): Command; + + /** + * Sets the parameters for this command. + * @param {!Object.<*>} parameters The command parameters. + * @return {!Command} A self reference. + */ + setParameters(parameters: any): Command; + + /** + * Returns a named command parameter. + * @param {string} key The parameter key to look up. + * @return {*} The parameter value, or undefined if it has not been set. + */ + getParameter(key: string): any; + + /** + * @return {!Object.<*>} The parameters to send with this command. + */ + getParameters(): any; + + // endregion +} + +/** + * Handles the execution of WebDriver {@link Command commands}. + * @interface + */ +export class Executor { + /** + * Executes the given {@code command}. If there is an error executing the + * command, the provided callback will be invoked with the offending error. + * Otherwise, the callback will be invoked with a null Error and non-null + * response object. + * + * @param {!Command} command The command to execute. + * @return {!promise.Promise} A promise that will be fulfilled with + * the command result. + */ + execute(command: Command): promise.Promise +} + +/** + * Describes an event listener registered on an {@linkplain EventEmitter}. + */ +export class Listener { + /** + * @param {!Function} fn The acutal listener function. + * @param {(Object|undefined)} scope The object in whose scope to invoke the + * listener. + * @param {boolean} oneshot Whether this listener should only be used once. + */ + constructor(fn: Function, scope: Object, oneshot: boolean); +} + +/** + * Object that can emit events for others to listen for. This is used instead + * of Closure's event system because it is much more light weight. The API is + * based on Node's EventEmitters. + */ +export class EventEmitter { + // region Constructors + + /** + * @constructor + */ + constructor(); + + // endregion + + // region Methods + + /** + * Fires an event and calls all listeners. + * @param {string} type The type of event to emit. + * @param {...*} var_args Any arguments to pass to each listener. + */ + emit(type: string, ...var_args: any[]): void; + + /** + * Returns a mutable list of listeners for a specific type of event. + * @param {string} type The type of event to retrieve the listeners for. + * @return {!Set} The registered listeners for the given event + * type. + */ + listeners(type: string): any; + + /** + * Registers a listener. + * @param {string} type The type of event to listen for. + * @param {!Function} fn The function to invoke when the event is fired. + * @param {Object=} opt_self The object in whose scope to invoke the listener. + * @param {boolean=} opt_oneshot Whether the listener should b (e removed after + * the first event is fired. + * @return {!EventEmitter} A self reference. + * @private + */ + addListener(type: string, fn: Function, opt_scope?: any, opt_oneshot?: boolean): EventEmitter; + + /** + * Registers a one-time listener which will be called only the first time an + * event is emitted, after which it will be removed. + * @param {string} type The type of event to listen for. + * @param {!Function} fn The function to invoke when the event is fired. + * @param {Object=} opt_scope The object in whose scope to invoke the listener. + * @return {!EventEmitter} A self reference. + */ + once(type: string, fn: any, opt_scope?: any): EventEmitter; + + /** + * An alias for {@code #addListener()}. + * @param {string} type The type of event to listen for. + * @param {!Function} fn The function to invoke when the event is fired. + * @param {Object=} opt_scope The object in whose scope to invoke the listener. + * @return {!EventEmitter} A self reference. + */ + on(type: string, fn: Function, opt_scope?: any): EventEmitter; + + /** + * Removes a previously registered event listener. + * @param {string} type The type of event to unregister. + * @param {!Function} listenerFn The handler function to remove. + * @return {!EventEmitter} A self reference. + */ + removeListener(type: string, listenerFn: Function): EventEmitter; + + /** + * Removes all listeners for a specific type of event. If no event is + * specified, all listeners across all types will be removed. + * @param {string=} opt_type The type of event to remove listeners from. + * @return {!EventEmitter} A self reference. + */ + removeAllListeners(opt_type?: string): EventEmitter; + + // endregion +} + +/** + * Interface for navigating back and forth in the browser history. + */ +export class Navigation { + // region Constructors + + /** + * Interface for navigating back and forth in the browser history. + * + * This class should never be instantiated directly. Insead, obtain an instance + * with + * + * navigate() + * + * @see WebDriver#navigate() + */ + constructor(driver: WebDriver); + + // endregion + + // region Methods + + /** + * Schedules a command to navigate to a new URL. + * @param {string} url The URL to navigate to. + * @return {!promise.Promise.} A promise that will be resolved + * when the URL has been loaded. + */ + to(url: string): promise.Promise; + + /** + * Schedules a command to move backwards in the browser history. + * @return {!promise.Promise.} A promise that will be resolved + * when the navigation event has completed. + */ + back(): promise.Promise; + + /** + * Schedules a command to move forwards in the browser history. + * @return {!promise.Promise.} A promise that will be resolved + * when the navigation event has completed. + */ + forward(): promise.Promise; + + /** + * Schedules a command to refresh the current page. + * @return {!promise.Promise.} A promise that will be resolved + * when the navigation event has completed. + */ + refresh(): promise.Promise; + + // endregion +} + +export interface IWebDriverOptionsCookie { + /** + * The name of the cookie. + */ + name: string; + + /** + * The cookie value. + */ + value: string; + + /** + * The cookie path. Defaults to "/" when adding a cookie. + */ + path?: string; + + /** + * The domain the cookie is visible to. Defaults to the current browsing + * context's document's URL when adding a cookie. + */ + domain?: string; + + /** + * Whether the cookie is a secure cookie. Defaults to false when adding a new + * cookie. + */ + secure?: boolean; + + /** + * Whether the cookie is an HTTP only cookie. Defaults to false when adding a + * new cookie. + */ + httpOnly?: boolean; + + /** + * When the cookie expires. + * + * When {@linkplain Options#addCookie() adding a cookie}, this may be specified + * in _seconds_ since Unix epoch (January 1, 1970). The expiry will default to + * 20 years in the future if omitted. + * + * The expiry is always returned in seconds since epoch when + * {@linkplain Options#getCookies() retrieving cookies} from the browser. + * + * @type {(!Date|number|undefined)} + */ + expiry?: number | Date; +} + +export interface IWebDriverCookie extends IWebDriverOptionsCookie { + /** + * When the cookie expires. + * + * The expiry is always returned in seconds since epoch when + * {@linkplain Options#getCookies() retrieving cookies} from the browser. + * + * @type {(!number|undefined)} + */ + expiry?: number; +} + +/** + * Provides methods for managing browser and driver state. + */ +export class Options { + // region Constructors + + /** + * @param {!WebDriver} driver The parent driver. + * @constructor + */ + constructor(driver: WebDriver); + + // endregion + + // region Methods + + /** + * Schedules a command to add a cookie. + * @param {IWebDriverOptionsCookie} spec Defines the cookie to add. + * @return {!promise.Promise} A promise that will be resolved + * when the cookie has been added to the page. + * @throws {error.InvalidArgumentError} if any of the cookie parameters are + * invalid. + * @throws {TypeError} if `spec` is not a cookie object. + */ + addCookie(spec: IWebDriverOptionsCookie): promise.Promise; + + /** + * Schedules a command to delete all cookies visible to the current page. + * @return {!promise.Promise} A promise that will be resolved when all + * cookies have been deleted. + */ + deleteAllCookies(): promise.Promise; + + /** + * Schedules a command to delete the cookie with the given name. This command is + * a no-op if there is no cookie with the given name visible to the current + * page. + * @param {string} name The name of the cookie to delete. + * @return {!promise.Promise} A promise that will be resolved when the + * cookie has been deleted. + */ + deleteCookie(name: string): promise.Promise; + + /** + * Schedules a command to retrieve all cookies visible to the current page. + * Each cookie will be returned as a JSON object as described by the WebDriver + * wire protocol. + * @return {!promise.Promise} A promise that will be resolved with the + * cookies visible to the current page. + * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object + */ + getCookies(): promise.Promise; + + /** + * Schedules a command to retrieve the cookie with the given name. Returns null + * if there is no such cookie. The cookie will be returned as a JSON object as + * described by the WebDriver wire protocol. + * @param {string} name The name of the cookie to retrieve. + * @return {!promise.Promise} A promise that will be resolved with the + * named cookie, or {@code null} if there is no such cookie. + * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object + */ + getCookie(name: string): promise.Promise; + + /** + * @return {!Logs} The interface for managing driver + * logs. + */ + logs(): Logs; + + /** + * @return {!Timeouts} The interface for managing driver + * timeouts. + */ + timeouts(): Timeouts; + + /** + * @return {!Window} The interface for managing the + * current window. + */ + window(): Window; + + // endregion +} + +/** + * An interface for managing timeout behavior for WebDriver instances. + */ +export class Timeouts { + // region Constructors + + /** + * @param {!WebDriver} driver The parent driver. + * @constructor + */ + constructor(driver: WebDriver); + + // endregion + + // region Methods + + /** + * Specifies the amount of time the driver should wait when searching for an + * element if it is not immediately present. + *

    + * When searching for a single element, the driver should poll the page + * until the element has been found, or this timeout expires before failing + * with a {@code bot.ErrorCode.NO_SUCH_ELEMENT} error. When searching + * for multiple elements, the driver should poll the page until at least one + * element has been found or this timeout has expired. + *

    + * Setting the wait timeout to 0 (its default value), disables implicit + * waiting. + *

    + * Increasing the implicit wait timeout should be used judiciously as it + * will have an adverse effect on test run time, especially when used with + * slower location strategies like XPath. + * + * @param {number} ms The amount of time to wait, in milliseconds. + * @return {!promise.Promise} A promise that will be resolved when the + * implicit wait timeout has been set. + */ + implicitlyWait(ms: number): promise.Promise; + + /** + * Sets the amount of time to wait, in milliseconds, for an asynchronous script + * to finish execution before returning an error. If the timeout is less than or + * equal to 0, the script will be allowed to run indefinitely. + * + * @param {number} ms The amount of time to wait, in milliseconds. + * @return {!promise.Promise} A promise that will be resolved when the + * script timeout has been set. + */ + setScriptTimeout(ms: number): promise.Promise; + + /** + * Sets the amount of time to wait for a page load to complete before returning + * an error. If the timeout is negative, page loads may be indefinite. + * @param {number} ms The amount of time to wait, in milliseconds. + * @return {!promise.Promise} A promise that will be resolved when + * the timeout has been set. + */ + pageLoadTimeout(ms: number): promise.Promise; + + // endregion +} + +/** + * An interface for managing the current window. + */ +export class Window { + // region Constructors + + /** + * @param {!WebDriver} driver The parent driver. + * @constructor + */ + constructor(driver: WebDriver); + + // endregion + + // region Methods + + /** + * Retrieves the window's current position, relative to the top left corner of + * the screen. + * @return {!promise.Promise} A promise that will be resolved with the + * window's position in the form of a {x:number, y:number} object literal. + */ + getPosition(): promise.Promise; + + /** + * Repositions the current window. + * @param {number} x The desired horizontal position, relative to the left side + * of the screen. + * @param {number} y The desired vertical position, relative to the top of the + * of the screen. + * @return {!promise.Promise} A promise that will be resolved when the + * command has completed. + */ + setPosition(x: number, y: number): promise.Promise; + + /** + * Retrieves the window's current size. + * @return {!promise.Promise} A promise that will be resolved with the + * window's size in the form of a {width:number, height:number} object + * literal. + */ + getSize(): promise.Promise; + + /** + * Resizes the current window. + * @param {number} width The desired window width. + * @param {number} height The desired window height. + * @return {!promise.Promise} A promise that will be resolved when the + * command has completed. + */ + setSize(width: number, height: number): promise.Promise; + + /** + * Google3 modification for v4 API backport. + * Returns the current top-level window's size and position. + */ + getRect(): Promise; + + /** + * Google3 modification for v4 API backport. + * Sets the current top-level window's size and position. You may update + * just the size by omitting `x` & `y`, or just the position by omitting + * `width` & `height` options. + */ + setRect({x, y, width, height}: Partial): Promise; + + /** + * Maximizes the current window. + * @return {!promise.Promise} A promise that will be resolved when the + * command has completed. + */ + maximize(): promise.Promise; + + // endregion +} + +/** + * Interface for managing WebDriver log records. + */ +export class Logs { + // region Constructors + + /** + * @param {!WebDriver} driver The parent driver. + * @constructor + */ + constructor(driver: WebDriver); + + // endregion + + // region + + /** + * Fetches available log entries for the given type. + * + *

    Note that log buffers are reset after each call, meaning that + * available log entries correspond to those entries not yet returned for a + * given log type. In practice, this means that this call will return the + * available log entries since the last call, or from the start of the + * session. + * + * @param {!logging.Type} type The desired log type. + * @return {!promise.Promise.>} A + * promise that will resolve to a list of log entries for the specified + * type. + */ + get(type: string): promise.Promise; + + /** + * Retrieves the log types available to this driver. + * @return {!promise.Promise.>} A + * promise that will resolve to a list of available log types. + */ + getAvailableLogTypes(): promise.Promise; + + // endregion +} + +/** + * An interface for changing the focus of the driver to another frame or window. + */ +export class TargetLocator { + // region Constructors + + /** + * @param {!WebDriver} driver The parent driver. + * @constructor + */ + constructor(driver: WebDriver); + + // endregion + + // region Methods + + /** + * Schedules a command retrieve the {@code document.activeElement} element on + * the current document, or {@code document.body} if activeElement is not + * available. + * @return {!WebElement} The active element. + */ + activeElement(): WebElementPromise; + + /** + * Schedules a command to switch focus of all future commands to the first frame + * on the page. + * @return {!promise.Promise} A promise that will be resolved when the + * driver has changed focus to the default content. + */ + defaultContent(): promise.Promise; + + /** + * Schedules a command to switch the focus of all future commands to another + * frame on the page. The target frame may be specified as one of the + * following: + * + * - A number that specifies a (zero-based) index into [window.frames]( + * https://developer.mozilla.org/en-US/docs/Web/API/Window.frames). + * - A {@link WebElement} reference, which correspond to a `frame` or `iframe` + * DOM element. + * - The `null` value, to select the topmost frame on the page. Passing `null` + * is the same as calling {@link #defaultContent defaultContent()}. + * + * If the specified frame can not be found, the returned promise will be + * rejected with a {@linkplain error.NoSuchFrameError}. + * + * @param {(number|WebElement|null)} id The frame locator. + * @return {!promise.Promise} A promise that will be resolved + * when the driver has changed focus to the specified frame. + */ + // google3 local modification. TargetLocator.frame accepts the null type. + frame(nameOrIndex: number | WebElement | null): promise.Promise; + + /** + * Schedules a command to switch the focus of all future commands to another + * window. Windows may be specified by their {@code window.name} attribute or + * by its handle (as returned by {@link WebDriver#getWindowHandles}). + * + * If the specified window cannot be found, the returned promise will be + * rejected with a {@linkplain error.NoSuchWindowError}. + * + * @param {string} nameOrHandle The name or window handle of the window to + * switch focus to. + * @return {!promise.Promise} A promise that will be resolved + * when the driver has changed focus to the specified window. + */ + window(nameOrHandle: string): promise.Promise; + + /** + * Schedules a command to change focus to the active modal dialog, such as + * those opened by `window.alert()`, `window.confirm()`, and + * `window.prompt()`. The returned promise will be rejected with a + * {@linkplain error.NoSuchAlertError} if there are no open alerts. + * + * @return {!AlertPromise} The open alert. + */ + alert(): AlertPromise; + + // endregion +} + +/** + * Used with {@link WebElement#sendKeys WebElement#sendKeys} on file + * input elements ({@code }) to detect when the entered key + * sequence defines the path to a file. + * + * By default, {@linkplain WebElement WebElement's} will enter all + * key sequences exactly as entered. You may set a + * {@linkplain WebDriver#setFileDetector file detector} on the parent + * WebDriver instance to define custom behavior for handling file elements. Of + * particular note is the {@link selenium-webdriver/remote.FileDetector}, which + * should be used when running against a remote + * [Selenium Server](http://docs.seleniumhq.org/download/). + */ +export class FileDetector { + /** @constructor */ + constructor(); + + /** + * Handles the file specified by the given path, preparing it for use with + * the current browser. If the path does not refer to a valid file, it will + * be returned unchanged, otherwisee a path suitable for use with the current + * browser will be returned. + * + * This default implementation is a no-op. Subtypes may override this + * function for custom tailored file handling. + * + * @param {!WebDriver} driver The driver for the current browser. + * @param {string} path The path to process. + * @return {!promise.Promise} A promise for the processed + * file path. + * @package + */ + handleFile(driver: WebDriver, path: string): promise.Promise; +} + +export type CreateSessionCapabilities = Capabilities | { + desired?: Capabilities, + required?: Capabilities +}; + +/** + * Creates a new WebDriver client, which provides control over a browser. + * + * Every WebDriver command returns a {@code promise.Promise} that + * represents the result of that command. Callbacks may be registered on this + * object to manipulate the command result or catch an expected error. Any + * commands scheduled with a callback are considered sub-commands and will + * execute before the next command in the current frame. For example: + * + * var message = []; + * driver.call(message.push, message, 'a').then(function() { + * driver.call(message.push, message, 'b'); + * }); + * driver.call(message.push, message, 'c'); + * driver.call(function() { + * alert('message is abc? ' + (message.join('') == 'abc')); + * }); + * + */ +export class WebDriver { + // region Constructors + + /** + * @param {!(Session|promise.Promise)} session Either a + * known session or a promise that will be resolved to a session. + * @param {!command.Executor} executor The executor to use when sending + * commands to the browser. + * @param {promise.ControlFlow=} opt_flow The flow to + * schedule commands through. Defaults to the active flow object. + */ + constructor(session: Session | promise.Promise, executor: Executor, opt_flow?: promise.ControlFlow); + + // endregion + + // region StaticMethods + + /** + * Creates a new WebDriver client for an existing session. + * @param {!command.Executor} executor Command executor to use when querying + * for session details. + * @param {string} sessionId ID of the session to attach to. + * @param {promise.ControlFlow=} opt_flow The control flow all + * driver commands should execute under. Defaults to the + * {@link promise.controlFlow() currently active} control flow. + * @return {!WebDriver} A new client for the specified session. + */ + static attachToSession(executor: Executor, sessionId: string, opt_flow?: promise.ControlFlow): WebDriver; + + /** + * Creates a new WebDriver session. + * + * By default, the requested session `capabilities` are merely "desired" and + * the remote end will still create a new session even if it cannot satisfy + * all of the requested capabilities. You can query which capabilities a + * session actually has using the + * {@linkplain #getCapabilities() getCapabilities()} method on the returned + * WebDriver instance. + * + * To define _required capabilities_, provide the `capabilities` as an object + * literal with `required` and `desired` keys. The `desired` key may be + * omitted if all capabilities are required, and vice versa. If the server + * cannot create a session with all of the required capabilities, it will + * return an {@linkplain error.SessionNotCreatedError}. + * + * let required = new Capabilities().set('browserName', 'firefox'); + * let desired = new Capabilities().set('version', '45'); + * let driver = WebDriver.createSession(executor, {required, desired}); + * + * This function will always return a WebDriver instance. If there is an error + * creating the session, such as the aforementioned SessionNotCreatedError, + * the driver will have a rejected {@linkplain #getSession session} promise. + * It is recommended that this promise is left _unhandled_ so it will + * propagate through the {@linkplain promise.ControlFlow control flow} and + * cause subsequent commands to fail. + * + * let required = Capabilities.firefox(); + * let driver = WebDriver.createSession(executor, {required}); + * + * // If the createSession operation failed, then this command will also + * // also fail, propagating the creation failure. + * driver.get('http://www.google.com').catch(e => console.log(e)); + * + * @param {!command.Executor} executor The executor to create the new session + * with. + * @param {(!Capabilities| + * {desired: (Capabilities|undefined), + * required: (Capabilities|undefined)})} capabilities The desired + * capabilities for the new session. + * @param {promise.ControlFlow=} opt_flow The control flow all driver + * commands should execute under, including the initial session creation. + * Defaults to the {@link promise.controlFlow() currently active} + * control flow. + * @param {(function(new: WebDriver, + * !IThenable, + * !command.Executor, + * promise.ControlFlow=))=} opt_ctor + * A reference to the constructor of the specific type of WebDriver client + * to instantiate. Will create a vanilla {@linkplain WebDriver} instance + * if a constructor is not provided. + * @param {(function(this: void): ?)=} opt_onQuit A callback to invoke when + * the newly created session is terminated. This should be used to clean + * up any resources associated with the session. + * @return {!WebDriver} The driver for the newly created session. + */ + // This method's arguments are untyped so that its overloads can have correct types. + // Typescript doesn't allow static methods to be overridden with incompatible signatures. + static createSession(...var_args: any[]): WebDriver; + + // endregion + + // region Methods + + /** + * @return {!promise.ControlFlow} The control flow used by this + * instance. + */ + controlFlow(): promise.ControlFlow; + + /** + * Schedules a {@link command.Command} to be executed by this driver's + * {@link command.Executor}. + * + * @param {!command.Command} command The command to schedule. + * @param {string} description A description of the command for debugging. + * @return {!promise.Promise} A promise that will be resolved + * with the command result. + * @template T + */ + schedule(command: Command, description: string): promise.Promise; + + /** + * Sets the {@linkplain input.FileDetector file detector} that should be + * used with this instance. + * @param {input.FileDetector} detector The detector to use or {@code null}. + */ + setFileDetector(detector: FileDetector): void; + + /** + * @return {!promise.Promise.} A promise for this + * client's session. + */ + getSession(): promise.Promise; + + /** + * @return {!promise.Promise.} A promise + * that will resolve with the this instance's capabilities. + */ + getCapabilities(): promise.Promise; + + /** + * Schedules a command to quit the current session. After calling quit, this + * instance will be invalidated and may no longer be used to issue commands + * against the browser. + * @return {!promise.Promise.} A promise that will be resolved + * when the command has completed. + */ + quit(): promise.Promise; + + /** + * Creates a new action sequence using this driver. The sequence will not be + * scheduled for execution until {@link actions.ActionSequence#perform} is + * called. Example: + * + * driver.actions(). + * mouseDown(element1). + * mouseMove(element2). + * mouseUp(). + * perform(); + * + * @return {!actions.ActionSequence} A new action sequence for this instance. + */ + actions(): ActionSequence; + + /** + * TEMPORARY API returns old ActionSequence so we can use actions() to + * return the new Actions object. + * Creates a new action sequence using this driver. The sequence will not be + * scheduled for execution until {@link actions.ActionSequence#perform} is + * called. Example: + * + * driver.actions(). + * mouseDown(element1). + * mouseMove(element2). + * mouseUp(). + * perform(); + * + * @return {!actions.ActionSequence} A new action sequence for this instance. + */ + axtions(): ActionSequence; + + /** + * Creates a new touch sequence using this driver. The sequence will not be + * scheduled for execution until {@link actions.TouchSequence#perform} is + * called. Example: + * + * driver.touchActions(). + * tap(element1). + * doubleTap(element2). + * perform(); + * + * @return {!actions.TouchSequence} A new touch sequence for this instance. + */ + touchActions(): TouchSequence; + + /** + * Schedules a command to execute JavaScript in the context of the currently + * selected frame or window. The script fragment will be executed as the body + * of an anonymous function. If the script is provided as a function object, + * that function will be converted to a string for injection into the target + * window. + * + * Any arguments provided in addition to the script will be included as script + * arguments and may be referenced using the {@code arguments} object. + * Arguments may be a boolean, number, string, or {@code WebElement}. + * Arrays and objects may also be used as script arguments as long as each item + * adheres to the types previously mentioned. + * + * The script may refer to any variables accessible from the current window. + * Furthermore, the script will execute in the window's context, thus + * {@code document} may be used to refer to the current document. Any local + * variables will not be available once the script has finished executing, + * though global variables will persist. + * + * If the script has a return value (i.e. if the script contains a return + * statement), then the following steps will be taken for resolving this + * functions return value: + * + * - For a HTML element, the value will resolve to a + * {@link WebElement} + * - Null and undefined return values will resolve to null + * - Booleans, numbers, and strings will resolve as is + * - Functions will resolve to their string representation + * - For arrays and objects, each member item will be converted according to + * the rules above + * + * @param {!(string|Function)} script The script to execute. + * @param {...*} var_args The arguments to pass to the script. + * @return {!promise.Promise.} A promise that will resolve to the + * scripts return value. + * @template T + */ + executeScript(script: string | Function, ...var_args: any[]): promise.Promise; + + /** + * Schedules a command to execute asynchronous JavaScript in the context of the + * currently selected frame or window. The script fragment will be executed as + * the body of an anonymous function. If the script is provided as a function + * object, that function will be converted to a string for injection into the + * target window. + * + * Any arguments provided in addition to the script will be included as script + * arguments and may be referenced using the {@code arguments} object. + * Arguments may be a boolean, number, string, or {@code WebElement}. + * Arrays and objects may also be used as script arguments as long as each item + * adheres to the types previously mentioned. + * + * Unlike executing synchronous JavaScript with {@link #executeScript}, + * scripts executed with this function must explicitly signal they are finished + * by invoking the provided callback. This callback will always be injected + * into the executed function as the last argument, and thus may be referenced + * with {@code arguments[arguments.length - 1]}. The following steps will be + * taken for resolving this functions return value against the first argument + * to the script's callback function: + * + * - For a HTML element, the value will resolve to a + * {@link WebElement} + * - Null and undefined return values will resolve to null + * - Booleans, numbers, and strings will resolve as is + * - Functions will resolve to their string representation + * - For arrays and objects, each member item will be converted according to + * the rules above + * + * __Example #1:__ Performing a sleep that is synchronized with the currently + * selected window: + * + * var start = new Date().getTime(); + * driver.executeAsyncScript( + * 'window.setTimeout(arguments[arguments.length - 1], 500);'). + * then(function() { + * console.log( + * 'Elapsed time: ' + (new Date().getTime() - start) + ' ms'); + * }); + * + * __Example #2:__ Synchronizing a test with an AJAX application: + * + * var button = driver.findElement(By.id('compose-button')); + * button.click(); + * driver.executeAsyncScript( + * 'var callback = arguments[arguments.length - 1];' + + * 'mailClient.getComposeWindowWidget().onload(callback);'); + * driver.switchTo().frame('composeWidget'); + * driver.findElement(By.id('to')).sendKeys('dog@example.com'); + * + * __Example #3:__ Injecting a XMLHttpRequest and waiting for the result. In + * this example, the inject script is specified with a function literal. When + * using this format, the function is converted to a string for injection, so it + * should not reference any symbols not defined in the scope of the page under + * test. + * + * driver.executeAsyncScript(function() { + * var callback = arguments[arguments.length - 1]; + * var xhr = new XMLHttpRequest(); + * xhr.open('GET', '/resource/data.json', true); + * xhr.onreadystatechange = function() { + * if (xhr.readyState == 4) { + * callback(xhr.responseText); + * } + * } + * xhr.send(''); + * }).then(function(str) { + * console.log(JSON.parse(str)['food']); + * }); + * + * @param {!(string|Function)} script The script to execute. + * @param {...*} var_args The arguments to pass to the script. + * @return {!promise.Promise.} A promise that will resolve to the + * scripts return value. + * @template T + */ + executeAsyncScript(script: string | Function, ...var_args: any[]): promise.Promise; + + /** + * Schedules a command to execute a custom function. + * @param {function(...): (T|promise.Promise.)} fn The function to + * execute. + * @param {Object=} opt_scope The object in whose scope to execute the function. + * @param {...*} var_args Any arguments to pass to the function. + * @return {!promise.Promise.} A promise that will be resolved' + * with the function's result. + * @template T + */ + call(fn: (...var_args: any[]) => (T | promise.Promise), opt_scope?: any, ...var_args: any[]): promise.Promise; + + /** + * Schedules a command to wait for a condition to hold. The condition may be + * specified by a {@link Condition}, as a custom function, or + * as a {@link promise.Promise}. + * + * For a {@link Condition} or function, the wait will repeatedly + * evaluate the condition until it returns a truthy value. If any errors occur + * while evaluating the condition, they will be allowed to propagate. In the + * event a condition returns a {@link promise.Promise promise}, the + * polling loop will wait for it to be resolved and use the resolved value for + * whether the condition has been satisified. Note the resolution time for + * a promise is factored into whether a wait has timed out. + * + * Note, if the provided condition is a {@link WebElementCondition}, then + * the wait will return a {@link WebElementPromise} that will resolve to the + * element that satisified the condition. + * + * *Example:* waiting up to 10 seconds for an element to be present and visible + * on the page. + * + * var button = driver.wait(until.elementLocated(By.id('foo'), 10000); + * button.click(); + * + * This function may also be used to block the command flow on the resolution + * of a {@link promise.Promise promise}. When given a promise, the + * command will simply wait for its resolution before completing. A timeout may + * be provided to fail the command if the promise does not resolve before the + * timeout expires. + * + * *Example:* Suppose you have a function, `startTestServer`, that returns a + * promise for when a server is ready for requests. You can block a `WebDriver` + * client on this promise with: + * + * var started = startTestServer(); + * driver.wait(started, 5 * 1000, 'Server should start within 5 seconds'); + * driver.get(getServerUrl()); + * + * @param {!WebElementCondition} condition The condition to + * wait on, defined as a promise, condition object, or a function to + * evaluate as a condition. + * @param {number=} opt_timeout How long to wait for the condition to be true. + * @param {string=} opt_message An optional message to use if the wait times + * out. + * @return {!WebElementPromise} A promise that will be fulfilled + * with the first truthy value returned by the condition function, or + * rejected if the condition times out. + * @template T + */ + wait(condition: WebElementCondition, opt_timeout?: number, opt_message?: string): WebElementPromise; + + /** + * Schedules a command to wait for a condition to hold. The condition may be + * specified by a {@link webdriver.Condition}, as a custom function, or + * as a {@link webdriver.promise.Promise}. + * + * For a {@link webdriver.Condition} or function, the wait will repeatedly + * evaluate the condition until it returns a truthy value. If any errors occur + * while evaluating the condition, they will be allowed to propagate. In the + * event a condition returns a {@link webdriver.promise.Promise promise}, the + * polling loop will wait for it to be resolved and use the resolved value for + * whether the condition has been satisified. Note the resolution time for + * a promise is factored into whether a wait has timed out. + * + * Note, if the provided condition is a {@link WebElementCondition}, then + * the wait will return a {@link WebElementPromise} that will resolve to the + * element that satisified the condition. + * + * *Example:* waiting up to 10 seconds for an element to be present and visible + * on the page. + * + * var button = driver.wait(until.elementLocated(By.id('foo'), 10000); + * button.click(); + * + * This function may also be used to block the command flow on the resolution + * of a {@link webdriver.promise.Promise promise}. When given a promise, the + * command will simply wait for its resolution before completing. A timeout may + * be provided to fail the command if the promise does not resolve before the + * timeout expires. + * + * *Example:* Suppose you have a function, `startTestServer`, that returns a + * promise for when a server is ready for requests. You can block a `WebDriver` + * client on this promise with: + * + * var started = startTestServer(); + * driver.wait(started, 5 * 1000, 'Server should start within 5 seconds'); + * driver.get(getServerUrl()); + * + * @param {!(promise.Promise| + * Condition| + * function(!WebDriver): T)} condition The condition to + * wait on, defined as a promise, condition object, or a function to + * evaluate as a condition. + * @param {number=} opt_timeout How long to wait for the condition to be true. + * @param {string=} opt_message An optional message to use if the wait times + * out. + * @return {!promise.Promise} A promise that will be fulfilled + * with the first truthy value returned by the condition function, or + * rejected if the condition times out. + * @template T + */ + wait(condition: PromiseLike | Condition | ((driver: WebDriver) => T | PromiseLike) | Function, opt_timeout?: number, opt_message?: string): promise.Promise; + + /** + * Schedules a command to make the driver sleep for the given amount of time. + * @param {number} ms The amount of time, in milliseconds, to sleep. + * @return {!promise.Promise.} A promise that will be resolved + * when the sleep has finished. + */ + sleep(ms: number): promise.Promise; + + /** + * Schedules a command to retrieve they current window handle. + * @return {!promise.Promise.} A promise that will be + * resolved with the current window handle. + */ + getWindowHandle(): promise.Promise; + + /** + * Schedules a command to retrieve the current list of available window handles. + * @return {!promise.Promise.>} A promise that will + * be resolved with an array of window handles. + */ + getAllWindowHandles(): promise.Promise; + + /** + * Schedules a command to retrieve the current page's source. The page source + * returned is a representation of the underlying DOM: do not expect it to be + * formatted or escaped in the same way as the response sent from the web + * server. + * @return {!promise.Promise.} A promise that will be + * resolved with the current page source. + */ + getPageSource(): promise.Promise; + + /** + * Schedules a command to close the current window. + * @return {!promise.Promise.} A promise that will be resolved + * when this command has completed. + */ + close(): promise.Promise; + + /** + * Schedules a command to navigate to the given URL. + * @param {string} url The fully qualified URL to open. + * @return {!promise.Promise.} A promise that will be resolved + * when the document has finished loading. + */ + get(url: string): promise.Promise; + + /** + * Schedules a command to retrieve the URL of the current page. + * @return {!promise.Promise.} A promise that will be + * resolved with the current URL. + */ + getCurrentUrl(): promise.Promise; + + /** + * Schedules a command to retrieve the current page's title. + * @return {!promise.Promise.} A promise that will be + * resolved with the current page's title. + */ + getTitle(): promise.Promise; + + /** + * Schedule a command to find an element on the page. If the element cannot be + * found, a {@link bot.ErrorCode.NO_SUCH_ELEMENT} result will be returned + * by the driver. Unlike other commands, this error cannot be suppressed. In + * other words, scheduling a command to find an element doubles as an assert + * that the element is present on the page. To test whether an element is + * present on the page, use {@link #findElements}. + * + * The search criteria for an element may be defined using one of the + * factories in the {@link By} namespace, or as a short-hand + * {@link By.Hash} object. For example, the following two statements + * are equivalent: + * + * var e1 = driver.findElement(By.id('foo')); + * var e2 = driver.findElement({id:'foo'}); + * + * You may also provide a custom locator function, which takes as input this + * instance and returns a {@link WebElement}, or a promise that will resolve + * to a WebElement. If the returned promise resolves to an array of + * WebElements, WebDriver will use the first element. For example, to find the + * first visible link on a page, you could write: + * + * var link = driver.findElement(firstVisibleLink); + * + * function firstVisibleLink(driver) { + * var links = driver.findElements(By.tagName('a')); + * return promise.filter(links, function(link) { + * return link.isDisplayed(); + * }); + * } + * + * @param {!(by.By|Function)} locator The locator to use. + * @return {!WebElementPromise} A WebElement that can be used to issue + * commands against the located element. If the element is not found, the + * element will be invalidated and all scheduled commands aborted. + */ + findElement(locator: Locator): WebElementPromise; + + /** + * Schedule a command to search for multiple elements on the page. + * + * @param {!(by.By|Function)} locator The locator to use. + * @return {!promise.Promise.>} A + * promise that will resolve to an array of WebElements. + */ + findElements(locator: Locator): promise.Promise; + + /** + * Schedule a command to take a screenshot. The driver makes a best effort to + * return a screenshot of the following, in order of preference: + * + * 1. Entire page + * 2. Current window + * 3. Visible portion of the current frame + * 4. The entire display containing the browser + * + * @return {!promise.Promise} A promise that will be + * resolved to the screenshot as a base-64 encoded PNG. + */ + takeScreenshot(): promise.Promise; + + /** + * @return {!Options} The options interface for this + * instance. + */ + manage(): Options; + + /** + * @return {!Navigation} The navigation interface for this + * instance. + */ + navigate(): Navigation; + + /** + * @return {!TargetLocator} The target locator interface for + * this instance. + */ + switchTo(): TargetLocator; + + // endregion +} + +/** + * A thenable wrapper around a {@linkplain webdriver.IWebDriver IWebDriver} + * instance that allows commands to be issued directly instead of having to + * repeatedly call `then`: + * + * let driver = new Builder().build(); + * driver.then(d => d.get(url)); // You can do this... + * driver.get(url); // ...or this + * + * If the driver instance fails to resolve (e.g. the session cannot be created), + * every issued command will fail. + * + * @extends {webdriver.IWebDriver} + * @extends {promise.IThenable} + * @interface + */ +export interface ThenableWebDriver extends WebDriver, promise.IThenable { } + +export interface IWebElementId { + [ELEMENT: string]: string; +} + +/** + * Represents a DOM element. WebElements can be found by searching from the + * document root using a {@code WebDriver} instance, or by searching + * under another {@code WebElement}: + *

    
    + *   driver.get('http://www.google.com');
    + *   var searchForm = driver.findElement(By.tagName('form'));
    + *   var searchBox = searchForm.findElement(By.name('q'));
    + *   searchBox.sendKeys('webdriver');
    + * 
    + * + * The WebElement is implemented as a promise for compatibility with the promise + * API. It will always resolve itself when its internal state has been fully + * resolved and commands may be issued against the element. This can be used to + * catch errors when an element cannot be located on the page: + *
    
    + *   driver.findElement(By.id('not-there')).then(function(element) {
    + *     alert('Found an element that was not expected to be there!');
    + *   }, function(error) {
    + *     alert('The element was not found, as expected');
    + *   });
    + * 
    + */ +export interface IWebElement { + // region Methods + + /** + * Schedules a command to click on this element. + * @return {!promise.Promise} A promise that will be resolved when + * the click command has completed. + */ + click(): promise.Promise; + + /** + * Schedules a command to type a sequence on the DOM element represented by + * this instance. + * + * Modifier keys (SHIFT, CONTROL, ALT, META) are stateful; once a modifier is + * processed in the key sequence, that key state is toggled until one of the + * following occurs: + * + * - The modifier key is encountered again in the sequence. At this point the + * state of the key is toggled (along with the appropriate keyup/down + * events). + * - The {@link input.Key.NULL} key is encountered in the sequence. When + * this key is encountered, all modifier keys current in the down state are + * released (with accompanying keyup events). The NULL key can be used to + * simulate common keyboard shortcuts: + * + * element.sendKeys('text was', + * Key.CONTROL, 'a', Key.NULL, + * 'now text is'); + * // Alternatively: + * element.sendKeys('text was', + * Key.chord(Key.CONTROL, 'a'), + * 'now text is'); + * + * - The end of the key sequence is encountered. When there are no more keys + * to type, all depressed modifier keys are released (with accompanying + * keyup events). + * + * If this element is a file input ({@code }), the + * specified key sequence should specify the path to the file to attach to + * the element. This is analogous to the user clicking 'Browse...' and entering + * the path into the file select dialog. + * + * var form = driver.findElement(By.css('form')); + * var element = form.findElement(By.css('input[type=file]')); + * element.sendKeys('/path/to/file.txt'); + * form.submit(); + * + * For uploads to function correctly, the entered path must reference a file + * on the _browser's_ machine, not the local machine running this script. When + * running against a remote Selenium server, a {@link input.FileDetector} + * may be used to transparently copy files to the remote machine before + * attempting to upload them in the browser. + * + * __Note:__ On browsers where native keyboard events are not supported + * (e.g. Firefox on OS X), key events will be synthesized. Special + * punctuation keys will be synthesized according to a standard QWERTY en-us + * keyboard layout. + * + * @param {...(number|string|!IThenable<(number|string)>)} var_args The + * sequence of keys to type. Number keys may be referenced numerically or + * by string (1 or '1'). All arguments will be joined into a single + * sequence. + * @return {!promise.Promise} A promise that will be resolved when all + * keys have been typed. + */ + sendKeys(...var_args: Array>): promise.Promise; + + /** + * Schedules a command to query for the tag/node name of this element. + * @return {!promise.Promise} A promise that will be resolved with the + * element's tag name. + */ + getTagName(): promise.Promise; + + /** + * Schedules a command to query for the computed style of the element + * represented by this instance. If the element inherits the named style from + * its parent, the parent will be queried for its value. Where possible, color + * values will be converted to their hex representation (e.g. #00ff00 instead of + * rgb(0, 255, 0)). + *

    + * Warning: the value returned will be as the browser interprets it, so + * it may be tricky to form a proper assertion. + * + * @param {string} cssStyleProperty The name of the CSS style property to look + * up. + * @return {!promise.Promise} A promise that will be resolved with the + * requested CSS value. + */ + getCssValue(cssStyleProperty: string): promise.Promise; + + /** + * Schedules a command to query for the value of the given attribute of the + * element. Will return the current value even if it has been modified after the + * page has been loaded. More exactly, this method will return the value of the + * given attribute, unless that attribute is not present, in which case the + * value of the property with the same name is returned. If neither value is + * set, null is returned. The 'style' attribute is converted as best can be to a + * text representation with a trailing semi-colon. The following are deemed to + * be 'boolean' attributes and will be returned as thus: + * + *

    async, autofocus, autoplay, checked, compact, complete, controls, declare, + * defaultchecked, defaultselected, defer, disabled, draggable, ended, + * formnovalidate, hidden, indeterminate, iscontenteditable, ismap, itemscope, + * loop, multiple, muted, nohref, noresize, noshade, novalidate, nowrap, open, + * paused, pubdate, readonly, required, reversed, scoped, seamless, seeking, + * selected, spellcheck, truespeed, willvalidate + * + *

    Finally, the following commonly mis-capitalized attribute/property names + * are evaluated as expected: + *

      + *
    • 'class' + *
    • 'readonly' + *
    + * @param {string} attributeName The name of the attribute to query. + * @return {!promise.Promise} A promise that will be resolved with the + * attribute's value. + */ + getAttribute(attributeName: string): promise.Promise; + + /** + * Get the visible (i.e. not hidden by CSS) innerText of this element, including + * sub-elements, without any leading or trailing whitespace. + * @return {!promise.Promise} A promise that will be resolved with the + * element's visible text. + */ + getText(): promise.Promise; + + /** + * Schedules a command to compute the size of this element's bounding box, in + * pixels. + * @return {!promise.Promise} A promise that will be resolved with the + * element's size as a {@code {width:number, height:number}} object. + */ + getSize(): promise.Promise; + + /** + * Schedules a command to compute the location of this element in page space. + * @return {!promise.Promise} A promise that will be resolved to the + * element's location as a {@code {x:number, y:number}} object. + */ + getLocation(): promise.Promise; + + /** + * Schedules a command to query whether the DOM element represented by this + * instance is enabled, as dicted by the {@code disabled} attribute. + * @return {!promise.Promise} A promise that will be resolved with + * whether this element is currently enabled. + */ + isEnabled(): promise.Promise; + + /** + * Schedules a command to query whether this element is selected. + * @return {!promise.Promise} A promise that will be resolved with + * whether this element is currently selected. + */ + isSelected(): promise.Promise; + + /** + * Schedules a command to submit the form containing this element (or this + * element if it is a FORM element). This command is a no-op if the element is + * not contained in a form. + * @return {!promise.Promise} A promise that will be resolved when + * the form has been submitted. + */ + submit(): promise.Promise; + + /** + * Schedules a command to clear the {@code value} of this element. This command + * has no effect if the underlying DOM element is neither a text INPUT element + * nor a TEXTAREA element. + * @return {!promise.Promise} A promise that will be resolved when + * the element has been cleared. + */ + clear(): promise.Promise; + + /** + * Schedules a command to test whether this element is currently displayed. + * @return {!promise.Promise} A promise that will be resolved with + * whether this element is currently visible on the page. + */ + isDisplayed(): promise.Promise; + + /** + * @return {!promise.Promise.} A promise + * that resolves to this element's JSON representation as defined by the + * WebDriver wire protocol. + * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol + */ + getId(): promise.Promise; + + // endregion +} + +export interface IWebElementFinders { + /** + * Schedule a command to find a descendant of this element. If the element + * cannot be found, a {@code bot.ErrorCode.NO_SUCH_ELEMENT} result will + * be returned by the driver. Unlike other commands, this error cannot be + * suppressed. In other words, scheduling a command to find an element doubles + * as an assert that the element is present on the page. To test whether an + * element is present on the page, use {@code #findElements}. + * + *

    The search criteria for an element may be defined using one of the + * factories in the {@link By} namespace, or as a short-hand + * {@link By.Hash} object. For example, the following two statements + * are equivalent: + *

    +   * var e1 = element.findElement(By.id('foo'));
    +   * var e2 = element.findElement({id:'foo'});
    +   * 
    + * + *

    You may also provide a custom locator function, which takes as input + * this WebDriver instance and returns a {@link WebElement}, or a + * promise that will resolve to a WebElement. For example, to find the first + * visible link on a page, you could write: + *

    +   * var link = element.findElement(firstVisibleLink);
    +   *
    +   * function firstVisibleLink(element) {
    +   *   var links = element.findElements(By.tagName('a'));
    +   *   return promise.filter(links, function(link) {
    +   *     return links.isDisplayed();
    +   *   }).then(function(visibleLinks) {
    +   *     return visibleLinks[0];
    +   *   });
    +   * }
    +   * 
    + * + * @param {!(Locator|By.Hash|Function)} locator The + * locator strategy to use when searching for the element. + * @return {!WebElement} A WebElement that can be used to issue + * commands against the located element. If the element is not found, the + * element will be invalidated and all scheduled commands aborted. + */ + findElement(locator: Locator): WebElementPromise; + + /** + * Schedules a command to find all of the descendants of this element that + * match the given search criteria. + * + * @param {!(Locator|By.Hash|Function)} locator The + * locator strategy to use when searching for the elements. + * @return {!promise.Promise.>} A + * promise that will resolve to an array of WebElements. + */ + findElements(locator: Locator): promise.Promise; +} + +/** + * Defines an object that can be asynchronously serialized to its WebDriver + * wire representation. + * + * @constructor + * @template T + */ +export interface Serializable { + /** + * Returns either this instance's serialized represention, if immediately + * available, or a promise for its serialized representation. This function is + * conceptually equivalent to objects that have a {@code toJSON()} property, + * except the serialize() result may be a promise or an object containing a + * promise (which are not directly JSON friendly). + * + * @return {!(T|IThenable.)} This instance's serialized wire format. + */ + serialize(): T | promise.IThenable; +} + +/** + * Represents a DOM element. WebElements can be found by searching from the + * document root using a {@link WebDriver} instance, or by searching + * under another WebElement: + * + * driver.get('http://www.google.com'); + * var searchForm = driver.findElement(By.tagName('form')); + * var searchBox = searchForm.findElement(By.name('q')); + * searchBox.sendKeys('webdriver'); + * + * The WebElement is implemented as a promise for compatibility with the promise + * API. It will always resolve itself when its internal state has been fully + * resolved and commands may be issued against the element. This can be used to + * catch errors when an element cannot be located on the page: + * + * driver.findElement(By.id('not-there')).then(function(element) { + * alert('Found an element that was not expected to be there!'); + * }, function(error) { + * alert('The element was not found, as expected'); + * }); + * + * @extends {Serializable.} + */ +export class WebElement implements Serializable { + /** + * @param {!WebDriver} driver the parent WebDriver instance for this element. + * @param {(!IThenable|string)} id The server-assigned opaque ID for + * the underlying DOM element. + */ + constructor(driver: WebDriver, id: promise.Promise | string); + + /** + * @param {string} id The raw ID. + * @param {boolean=} opt_noLegacy Whether to exclude the legacy element key. + * @return {!Object} The element ID for use with WebDriver's wire protocol. + */ + static buildId(id: string, opt_noLegacy?: boolean): Object; + + /** + * Extracts the encoded WebElement ID from the object. + * + * @param {?} obj The object to extract the ID from. + * @return {string} the extracted ID. + * @throws {TypeError} if the object is not a valid encoded ID. + */ + static extractId(obj: IWebElementId): string; + + /** + * @param {?} obj the object to test. + * @return {boolean} whether the object is a valid encoded WebElement ID. + */ + static isId(obj: IWebElementId): boolean; + + /** + * Compares two WebElements for equality. + * + * @param {!WebElement} a A WebElement. + * @param {!WebElement} b A WebElement. + * @return {!promise.Promise} A promise that will be + * resolved to whether the two WebElements are equal. + */ + static equals(a: WebElement, b: WebElement): promise.Promise; + + /** + * @return {!WebDriver} The parent driver for this instance. + */ + getDriver(): WebDriver; + + /** + * @return {!promise.Promise} A promise that resolves to + * the server-assigned opaque ID assigned to this element. + */ + getId(): promise.Promise; + + /** + * Schedule a command to find a descendant of this element. If the element + * cannot be found, a {@link bot.ErrorCode.NO_SUCH_ELEMENT} result will + * be returned by the driver. Unlike other commands, this error cannot be + * suppressed. In other words, scheduling a command to find an element doubles + * as an assert that the element is present on the page. To test whether an + * element is present on the page, use {@link #findElements}. + * + * The search criteria for an element may be defined using one of the + * factories in the {@link By} namespace, or as a short-hand + * {@link By.Hash} object. For example, the following two statements + * are equivalent: + * + * var e1 = element.findElement(By.id('foo')); + * var e2 = element.findElement({id:'foo'}); + * + * You may also provide a custom locator function, which takes as input + * this WebDriver instance and returns a {@link WebElement}, or a + * promise that will resolve to a WebElement. For example, to find the first + * visible link on a page, you could write: + * + * var link = element.findElement(firstVisibleLink); + * + * function firstVisibleLink(element) { + * var links = element.findElements(By.tagName('a')); + * return promise.filter(links, function(link) { + * return links.isDisplayed(); + * }).then(function(visibleLinks) { + * return visibleLinks[0]; + * }); + * } + * + * @param {!(by.By|Function)} locator The locator strategy to use when + * searching for the element. + * @return {!WebElementPromise} A WebElement that can be used to issue + * commands against the located element. If the element is not found, the + * element will be invalidated and all scheduled commands aborted. + */ + findElement(locator: Locator): WebElementPromise; + + /** + * Schedules a command to find all of the descendants of this element that + * match the given search criteria. + * + * @param {!(by.By|Function)} locator The locator strategy to use when + * searching for the element. + * @return {!promise.Promise>} A + * promise that will resolve to an array of WebElements. + */ + findElements(locator: Locator): promise.Promise; + + /** + * Schedules a command to click on this element. + * @return {!promise.Promise.} A promise that will be resolved + * when the click command has completed. + */ + click(): promise.Promise; + + /** + * Schedules a command to type a sequence on the DOM element represented by this + * promsieinstance. + * + * Modifier keys (SHIFT, CONTROL, ALT, META) are stateful; once a modifier is + * processed in the keysequence, that key state is toggled until one of the + * following occurs: + * + * - The modifier key is encountered again in the sequence. At this point the + * state of the key is toggled (along with the appropriate keyup/down events). + * - The {@link Key.NULL} key is encountered in the sequence. When + * this key is encountered, all modifier keys current in the down state are + * released (with accompanying keyup events). The NULL key can be used to + * simulate common keyboard shortcuts: + * + * element.sendKeys('text was', + * Key.CONTROL, 'a', Key.NULL, + * 'now text is'); + * // Alternatively: + * element.sendKeys('text was', + * Key.chord(Key.CONTROL, 'a'), + * 'now text is'); + * + * - The end of the keysequence is encountered. When there are no more keys + * to type, all depressed modifier keys are released (with accompanying keyup + * events). + * + * If this element is a file input ({@code }), the + * specified key sequence should specify the path to the file to attach to + * the element. This is analgous to the user clicking 'Browse...' and entering + * the path into the file select dialog. + * + * var form = driver.findElement(By.css('form')); + * var element = form.findElement(By.css('input[type=file]')); + * element.sendKeys('/path/to/file.txt'); + * form.submit(); + * + * For uploads to function correctly, the entered path must reference a file + * on the _browser's_ machine, not the local machine running this script. When + * running against a remote Selenium server, a {@link FileDetector} + * may be used to transparently copy files to the remote machine before + * attempting to upload them in the browser. + * + * __Note:__ On browsers where native keyboard events are not supported + * (e.g. Firefox on OS X), key events will be synthesized. Special + * punctionation keys will be synthesized according to a standard QWERTY en-us + * keyboard layout. + * + * @param {...(string|!promise.Promise)} var_args The sequence + * of keys to type. All arguments will be joined into a single sequence. + * @return {!promise.Promise.} A promise that will be resolved + * when all keys have been typed. + */ + sendKeys(...var_args: Array>): promise.Promise; + + /** + * Schedules a command to query for the tag/node name of this element. + * @return {!promise.Promise.} A promise that will be + * resolved with the element's tag name. + */ + getTagName(): promise.Promise; + + /** + * Schedules a command to query for the computed style of the element + * represented by this instance. If the element inherits the named style from + * its parent, the parent will be queried for its value. Where possible, color + * values will be converted to their hex representation (e.g. #00ff00 instead of + * rgb(0, 255, 0)). + * + * _Warning:_ the value returned will be as the browser interprets it, so + * it may be tricky to form a proper assertion. + * + * @param {string} cssStyleProperty The name of the CSS style property to look + * up. + * @return {!promise.Promise} A promise that will be + * resolved with the requested CSS value. + */ + getCssValue(cssStyleProperty: string): promise.Promise; + + /** + * Schedules a command to query for the value of the given attribute of the + * element. Will return the current value, even if it has been modified after + * the page has been loaded. More exactly, this method will return the value of + * the given attribute, unless that attribute is not present, in which case the + * value of the property with the same name is returned. If neither value is + * set, null is returned (for example, the 'value' property of a textarea + * element). The 'style' attribute is converted as best can be to a + * text representation with a trailing semi-colon. The following are deemed to + * be 'boolean' attributes and will return either 'true' or null: + * + * async, autofocus, autoplay, checked, compact, complete, controls, declare, + * defaultchecked, defaultselected, defer, disabled, draggable, ended, + * formnovalidate, hidden, indeterminate, iscontenteditable, ismap, itemscope, + * loop, multiple, muted, nohref, noresize, noshade, novalidate, nowrap, open, + * paused, pubdate, readonly, required, reversed, scoped, seamless, seeking, + * selected, spellcheck, truespeed, willvalidate + * + * Finally, the following commonly mis-capitalized attribute/property names + * are evaluated as expected: + * + * - 'class' + * - 'readonly' + * + * @param {string} attributeName The name of the attribute to query. + * @return {!promise.Promise.} A promise that will be + * resolved with the attribute's value. The returned value will always be + * either a string or null. + */ + getAttribute(attributeName: string): promise.Promise; + + /** + * Get the visible (i.e. not hidden by CSS) innerText of this element, including + * sub-elements, without any leading or trailing whitespace. + * @return {!promise.Promise.} A promise that will be + * resolved with the element's visible text. + */ + getText(): promise.Promise; + + /** + * Schedules a command to compute the size of this element's bounding box, in + * pixels. + * @return {!promise.Promise.<{width: number, height: number}>} A + * promise that will be resolved with the element's size as a + * {@code {width:number, height:number}} object. + */ + getSize(): promise.Promise; + + /** + * Schedules a command to compute the location of this element in page space. + * @return {!promise.Promise.<{x: number, y: number}>} A promise that + * will be resolved to the element's location as a + * {@code {x:number, y:number}} object. + */ + getLocation(): promise.Promise; + + /** + * Schedules a command to query whether the DOM element represented by this + * instance is enabled, as dicted by the {@code disabled} attribute. + * @return {!promise.Promise.} A promise that will be + * resolved with whether this element is currently enabled. + */ + isEnabled(): promise.Promise; + + /** + * Schedules a command to query whether this element is selected. + * @return {!promise.Promise.} A promise that will be + * resolved with whether this element is currently selected. + */ + isSelected(): promise.Promise; + + /** + * Schedules a command to submit the form containing this element (or this + * element if it is a FORM element). This command is a no-op if the element is + * not contained in a form. + * @return {!promise.Promise.} A promise that will be resolved + * when the form has been submitted. + */ + submit(): promise.Promise; + + /** + * Schedules a command to clear the `value` of this element. This command has + * no effect if the underlying DOM element is neither a text INPUT element + * nor a TEXTAREA element. + * @return {!promise.Promise} A promise that will be resolved + * when the element has been cleared. + */ + clear(): promise.Promise; + + /** + * Schedules a command to test whether this element is currently displayed. + * @return {!promise.Promise.} A promise that will be + * resolved with whether this element is currently visible on the page. + */ + isDisplayed(): promise.Promise; + + /** + * Take a screenshot of the visible region encompassed by this element's + * bounding rectangle. + * + * @param {boolean=} opt_scroll Optional argument that indicates whether the + * element should be scrolled into view before taking a screenshot. + * Defaults to false. + * @return {!promise.Promise} A promise that will be + * resolved to the screenshot as a base-64 encoded PNG. + */ + takeScreenshot(opt_scroll?: boolean): promise.Promise; + + /** @override */ + serialize(): promise.Promise; +} + +/** + * WebElementPromise is a promise that will be fulfilled with a WebElement. + * This serves as a forward proxy on WebElement, allowing calls to be + * scheduled without directly on this instance before the underlying + * WebElement has been fulfilled. In other words, the following two statements + * are equivalent: + *
    
    + *     driver.findElement({id: 'my-button'}).click();
    + *     driver.findElement({id: 'my-button'}).then(function(el) {
    + *       return el.click();
    + *     });
    + * 
    + * + * @param {!WebDriver} driver The parent WebDriver instance for this + * element. + * @param {!promise.Promise.} el A promise + * that will resolve to the promised element. + * @constructor + * @extends {WebElement} + * @implements {promise.Thenable.} + * @final + */ +export interface WebElementPromise extends promise.IThenable {} +export class WebElementPromise extends WebElement { + /** + * @param {!WebDriver} driver The parent WebDriver instance for this + * element. + * @param {!promise.Promise} el A promise + * that will resolve to the promised element. + */ + constructor(driver: WebDriver, el: promise.Promise); +} + +/** + * Contains information about a WebDriver session. + */ +export class Session { + // region Constructors + + /** + * @param {string} id The session ID. + * @param {!(Object|Capabilities)} capabilities The session + * capabilities. + * @constructor + */ + constructor(id: string, capabilities: Capabilities | Object); + + // endregion + + // region Methods + + /** + * @return {string} This session's ID. + */ + getId(): string; + + /** + * @return {!Capabilities} This session's capabilities. + */ + getCapabilities(): Capabilities; + + /** + * Retrieves the value of a specific capability. + * @param {string} key The capability to retrieve. + * @return {*} The capability value. + */ + getCapability(key: string): any; + + /** + * Returns the JSON representation of this object, which is just the string + * session ID. + * @return {string} The JSON representation of this Session. + */ + toJSON(): string; + + // endregion +} + +// google3 local modification: +} // The close brace for "namespace webdriver" +} // The closing brace for "declare global" +export = webdriver; +// end google3 local modification. diff --git a/typings/opera.d.ts b/typings/opera.d.ts new file mode 100644 index 000000000..b4d118f6a --- /dev/null +++ b/typings/opera.d.ts @@ -0,0 +1,176 @@ +import * as webdriver from './index'; +import * as remote from './remote'; + +/** + * Creates {@link remote.DriverService} instances that manages an + * [OperaDriver](https://github.com/operasoftware/operachromiumdriver) + * server in a child process. + */ +export class ServiceBuilder { + /** + * @param {string=} opt_exe Path to the server executable to use. If omitted, + * the builder will attempt to locate the operadriver on the current + * PATH. + * @throws {Error} If provided executable does not exist, or the operadriver + * cannot be found on the PATH. + */ + constructor(opt_exe?: string); + + /** + * Sets the port to start the OperaDriver on. + * @param {number} port The port to use, or 0 for any free port. + * @return {!ServiceBuilder} A self reference. + * @throws {Error} If the port is invalid. + */ + usingPort(port: number): ServiceBuilder; + + /** + * Sets the path of the log file the driver should log to. If a log file is + * not specified, the driver will log to stderr. + * @param {string} path Path of the log file to use. + * @return {!ServiceBuilder} A self reference. + */ + loggingTo(path: string): ServiceBuilder; + + /** + * Enables verbose logging. + * @return {!ServiceBuilder} A self reference. + */ + enableVerboseLogging(): ServiceBuilder; + + /** + * Silence sthe drivers output. + * @return {!ServiceBuilder} A self reference. + */ + silent(): ServiceBuilder; + + /** + * Defines the stdio configuration for the driver service. See + * {@code child_process.spawn} for more information. + * @param {(string|!Array)} + * config The configuration to use. + * @return {!ServiceBuilder} A self reference. + */ + setStdio(config: string | Array): ServiceBuilder; + + /** + * Defines the environment to start the server under. This settings will be + * inherited by every browser session started by the server. + * @param {!Object.} env The environment to use. + * @return {!ServiceBuilder} A self reference. + */ + withEnvironment(env: Object): ServiceBuilder; + + /** + * Creates a new DriverService using this instance's current configuration. + * @return {!remote.DriverService} A new driver service using this instance's + * current configuration. + * @throws {Error} If the driver exectuable was not specified and a default + * could not be found on the current PATH. + */ + build(): remote.DriverService; +} + +/** + * Sets the default service to use for new OperaDriver instances. + * @param {!remote.DriverService} service The service to use. + * @throws {Error} If the default service is currently running. + */ +export function setDefaultService(service: remote.DriverService): any; + +/** + * Returns the default OperaDriver service. If such a service has not been + * configured, one will be constructed using the default configuration for + * a OperaDriver executable found on the system PATH. + * @return {!remote.DriverService} The default OperaDriver service. + */ +export function getDefaultService(): remote.DriverService; + +/** + * Class for managing {@linkplain Driver OperaDriver} specific options. + */ +export class Options { + /** + * Extracts the OperaDriver specific options from the given capabilities + * object. + * @param {!capabilities.Capabilities} caps The capabilities object. + * @return {!Options} The OperaDriver options. + */ + static fromCapabilities(caps: webdriver.Capabilities): Options; + + /** + * Add additional command line arguments to use when launching the Opera + * browser. Each argument may be specified with or without the '--' prefix + * (e.g. '--foo' and 'foo'). Arguments with an associated value should be + * delimited by an '=': 'foo=bar'. + * @param {...(string|!Array.)} var_args The arguments to add. + * @return {!Options} A self reference. + */ + addArguments(...var_args: string[]): Options; + + /** + * Add additional extensions to install when launching Opera. Each extension + * should be specified as the path to the packed CRX file, or a Buffer for an + * extension. + * @param {...(string|!Buffer|!Array.<(string|!Buffer)>)} var_args The + * extensions to add. + * @return {!Options} A self reference. + */ + addExtensions(...var_args: any[]): Options; + + /** + * Sets the path to the Opera binary to use. On Mac OS X, this path should + * reference the actual Opera executable, not just the application binary. The + * binary path be absolute or relative to the operadriver server executable, but + * it must exist on the machine that will launch Opera. + * + * @param {string} path The path to the Opera binary to use. + * @return {!Options} A self reference. + */ + setOperaBinaryPath(path: string): Options; + + /** + * Sets the logging preferences for the new session. + * @param {!./lib/logging.Preferences} prefs The logging preferences. + * @return {!Options} A self reference. + */ + setLoggingPrefs(prefs: webdriver.logging.Preferences): Options; + + /** + * Sets the proxy settings for the new session. + * @param {capabilities.ProxyConfig} proxy The proxy configuration to use. + * @return {!Options} A self reference. + */ + setProxy(proxy: webdriver.ProxyConfig): Options; + + /** + * Converts this options instance to a {@link capabilities.Capabilities} + * object. + * @param {capabilities.Capabilities=} opt_capabilities The capabilities to + * merge these options into, if any. + * @return {!capabilities.Capabilities} The capabilities. + */ + toCapabilities(opt_capabilities?: webdriver.Capabilities): webdriver.Capabilities; +} + +export class Driver extends webdriver.WebDriver { + /** + * Creates a new session for Opera. + * + * @param {(capabilities.Capabilities|Options)=} opt_config The configuration + * options. + * @param {remote.DriverService=} opt_service The session to use; will use + * the {@link getDefaultService default service} by default. + * @param {promise.ControlFlow=} opt_flow The control flow to use, + * or {@code null} to use the currently active flow. + * @return {!Driver} A new driver instance. + */ + static createSession(opt_config?: webdriver.Capabilities | Options, opt_service?: remote.DriverService, opt_flow?: webdriver.promise.ControlFlow): Driver; + + /** + * This function is a no-op as file detectors are not supported by this + * implementation. + * @override + */ + setFileDetector(): void; +} diff --git a/typings/remote.d.ts b/typings/remote.d.ts new file mode 100644 index 000000000..3ef73c953 --- /dev/null +++ b/typings/remote.d.ts @@ -0,0 +1,242 @@ +import * as webdriver from './index'; + +/** + * A record object that defines the configuration options for a DriverService + * instance. + * + * @record + */ +export interface ServiceOptions { } + +/** + * Manages the life and death of a native executable WebDriver server. + * + * It is expected that the driver server implements the + * https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol. + * Furthermore, the managed server should support multiple concurrent sessions, + * so that this class may be reused for multiple clients. + */ +export class DriverService { + /** + * @param {string} executable Path to the executable to run. + * @param {!ServiceOptions} options Configuration options for the service. + */ + constructor(executable: string, options: ServiceOptions); + + /** + * @return {!promise.Promise} A promise that resolves to + * the server's address. + * @throws {Error} If the server has not been started. + */ + address(): webdriver.promise.Promise; + + /** + * Returns whether the underlying process is still running. This does not take + * into account whether the process is in the process of shutting down. + * @return {boolean} Whether the underlying service process is running. + */ + isRunning(): boolean; + + /** + * Starts the server if it is not already running. + * @param {number=} opt_timeoutMs How long to wait, in milliseconds, for the + * server to start accepting requests. Defaults to 30 seconds. + * @return {!promise.Promise} A promise that will resolve + * to the server's base URL when it has started accepting requests. If the + * timeout expires before the server has started, the promise will be + * rejected. + */ + start(opt_timeoutMs?: number): webdriver.promise.Promise; + + /** + * Stops the service if it is not currently running. This function will kill + * the server immediately. To synchronize with the active control flow, use + * {@link #stop()}. + * @return {!promise.Promise} A promise that will be resolved when + * the server has been stopped. + */ + kill(): webdriver.promise.Promise; + + /** + * Schedules a task in the current control flow to stop the server if it is + * currently running. + * @return {!promise.Promise} A promise that will be resolved when + * the server has been stopped. + */ + stop(): webdriver.promise.Promise; +} + +export namespace DriverService { + /** + * Creates {@link DriverService} objects that manage a WebDriver server in a + * child process. + */ + class Builder { + /** + * @param {string} exe Path to the executable to use. This executable must + * accept the `--port` flag for defining the port to start the server on. + * @throws {Error} If the provided executable path does not exist. + */ + constructor(exe: string); + + /** + * Define additional command line arguments to use when starting the server. + * + * @param {...CommandLineFlag} var_args The arguments to include. + * @return {!THIS} A self reference. + * @this {THIS} + * @template THIS + */ + addArguments(...var_args: string[]): this; + + /** + * Sets the host name to access the server on. If specified, the + * {@linkplain #setLoopback() loopback} setting will be ignored. + * + * @param {string} hostname + * @return {!DriverService.Builder} A self reference. + */ + setHostname(hostname: string): this; + + /** + * Sets whether the service should be accessed at this host's loopback + * address. + * + * @param {boolean} loopback + * @return {!DriverService.Builder} A self reference. + */ + setLoopback(loopback: boolean): this; + + /** + * Sets the base path for WebDriver REST commands (e.g. "/wd/hub"). + * By default, the driver will accept commands relative to "/". + * + * @param {?string} basePath The base path to use, or `null` to use the + * default. + * @return {!DriverService.Builder} A self reference. + */ + setPath(basePath: string | null): this; + + /** + * Sets the port to start the server on. + * + * @param {number} port The port to use, or 0 for any free port. + * @return {!DriverService.Builder} A self reference. + * @throws {Error} If an invalid port is specified. + */ + setPort(port: number): this; + + /** + * Defines the environment to start the server under. This setting will be + * inherited by every browser session started by the server. By default, the + * server will inherit the enviroment of the current process. + * + * @param {(Map|Object|null)} env The desired + * environment to use, or `null` if the server should inherit the + * current environment. + * @return {!DriverService.Builder} A self reference. + */ + setEnvironment(env: Map | {[name: string]: string} | null): this; + + /** + * IO configuration for the spawned server process. For more information, + * refer to the documentation of `child_process.spawn`. + * + * @param {StdIoOptions} config The desired IO configuration. + * @return {!DriverService.Builder} A self reference. + * @see https://nodejs.org/dist/latest-v4.x/docs/api/child_process.html#child_process_options_stdio + */ + setStdio(config: any): this; + + /** + * Creates a new DriverService using this instance's current configuration. + * + * @return {!DriverService} A new driver service. + */ + build(): DriverService; + } +} + +/** + * Manages the life and death of the + * + * standalone Selenium server. + */ +export class SeleniumServer extends DriverService { + /** + * @param {string} jar Path to the Selenium server jar. + * @param {SeleniumServer.Options=} opt_options Configuration options for the + * server. + * @throws {Error} If the path to the Selenium jar is not specified or if an + * invalid port is specified. + **/ + constructor(jar: string, opt_options?: SeleniumServer.Options); +} + +export namespace SeleniumServer { + /** + * Options for the Selenium server + */ + interface Options { + /** Whether the server should only be accessed on this host's loopback address.*/ + loopback?: boolean; + + /** The port to start the server on (must be > 0). If the port is provided + as a promise, the service will wait for the promise to resolve before starting. */ + port?: number|webdriver.promise.IThenable; + + /** The arguments to pass to the service. If a promise is provided, the + service will wait for it to resolve before starting. */ + args?: string[]|webdriver.promise.IThenable; + + /** The arguments to pass to the JVM. If a promise is provided, the service + will wait for it to resolve before starting. */ + jvmArgs?: string[]|webdriver.promise.IThenable; + + /** The environment variables that should be visible to the server process. + Defaults to inheriting the current process's environment.*/ + env?: {[key: string]: string}; + + /** IO configuration for the spawned server process. For more information, + refer to the documentation of `child_process.spawn`*/ + stdio?: string|Array; + } +} + +/** + * A {@link webdriver.FileDetector} that may be used when running + * against a remote + * [Selenium server](http://selenium-release.storage.googleapis.com/index.html). + * + * When a file path on the local machine running this script is entered with + * {@link webdriver.WebElement#sendKeys WebElement#sendKeys}, this file detector + * will transfer the specified file to the Selenium server's host; the sendKeys + * command will be updated to use the transfered file's path. + * + * __Note:__ This class depends on a non-standard command supported on the + * Java Selenium server. The file detector will fail if used with a server that + * only supports standard WebDriver commands (such as the ChromeDriver). + * + * @final + */ +export class FileDetector extends webdriver.FileDetector { + /** + * @constructor + **/ + constructor(); + + /** + * Prepares a `file` for use with the remote browser. If the provided path + * does not reference a normal file (i.e. it does not exist or is a + * directory), then the promise returned by this method will be resolved with + * the original file path. Otherwise, this method will upload the file to the + * remote server, which will return the file's path on the remote system so + * it may be referenced in subsequent commands. + * + * @param {!webdriver.WebDriver} driver The driver for the current browser. + * @param {string} file The path of the file to process. + * @return {!webdriver.promise.Promise} A promise for the processed + * file path. + */ + handleFile(driver: webdriver.WebDriver, file: string): webdriver.promise.Promise; +} diff --git a/typings/safari.d.ts b/typings/safari.d.ts new file mode 100644 index 000000000..bbeb88741 --- /dev/null +++ b/typings/safari.d.ts @@ -0,0 +1,91 @@ +import * as webdriver from './index'; + +export class Server { } + +/** + * @return {!Promise} A promise that will resolve with the path + * to Safari on the current system. + */ +export function findSafariExecutable(): any; + +/** + * @param {string} serverUrl The URL to connect to. + * @return {!Promise} A promise for the path to a file that Safari can + * open on start-up to trigger a new connection to the WebSocket server. + */ +export function createConnectFile(serverUrl: string): any; + +/** + * Deletes all session data files if so desired. + * @param {!Object} desiredCapabilities . + * @return {!Array} A list of promises for the deleted files. + */ +export function cleanSession(desiredCapabilities: webdriver.Capabilities): any[]; + +/** @return {string} . */ +export function getRandomString(): string; + +/** + * @implements {command.Executor} + */ +export class CommandExecutor { +} + +/** + * Configuration options specific to the {@link Driver SafariDriver}. + */ +export class Options { + /** + * Extracts the SafariDriver specific options from the given capabilities + * object. + * @param {!Capabilities} capabilities The capabilities object. + * @return {!Options} The ChromeDriver options. + */ + static fromCapabilities(capabilities: webdriver.Capabilities): Options; + + /** + * Sets whether to force Safari to start with a clean session. Enabling this + * option will cause all global browser data to be deleted. + * @param {boolean} clean Whether to make sure the session has no cookies, + * cache entries, local storage, or databases. + * @return {!Options} A self reference. + */ + setCleanSession(clean: boolean): Options; + + /** + * Sets the logging preferences for the new session. + * @param {!./lib/logging.Preferences} prefs The logging preferences. + * @return {!Options} A self reference. + */ + setLoggingPrefs(prefs: webdriver.logging.Preferences): Options; + + /** + * Converts this options instance to a {@link Capabilities} object. + * @param {Capabilities=} opt_capabilities The capabilities to + * merge these options into, if any. + * @return {!Capabilities} The capabilities. + */ + toCapabilities(opt_capabilities?: webdriver.Capabilities): webdriver.Capabilities; +} + +/** + * A WebDriver client for Safari. This class should never be instantiated + * directly; instead, use the {@linkplain ./builder.Builder Builder}: + * + * var driver = new Builder() + * .forBrowser('safari') + * .build(); + * + */ +export class Driver extends webdriver.WebDriver { + /** + * Creates a new Safari session. + * + * @param {(Options|Capabilities)=} opt_config The configuration + * options for the new session. + * @param {promise.ControlFlow=} opt_flow The control flow to create + * the driver under. + * @return {!Driver} A new driver instance. + */ + static createSession(opt_config?: Options | webdriver.Capabilities, opt_flow?: webdriver.promise.ControlFlow): Driver; +} diff --git a/typings/testing.d.ts b/typings/testing.d.ts new file mode 100644 index 000000000..85bf087df --- /dev/null +++ b/typings/testing.d.ts @@ -0,0 +1,106 @@ +import { promise } from './index'; +import * as Testing from './testing'; + +export const describe: { + /** + * Registers a new test suite. + * @param name The suite name. + * @param fn The suite function, or {@code undefined} to define a pending test suite. + */ + (name: string, fn: Function): void; + + /** + * An alias for {@link #describe()} that marks the suite as exclusive, + * suppressing all other test suites. + * @param {string} name The suite name. + * @param {function()=} opt_fn The suite function, or `undefined` to define + * a pending test suite. + */ + only(name: string, fn: Function): void; + + /** + * Defines a suppressed test suite. + * @param name The suite name. + * @param fn The suite function, or {@code undefined} to define a pending test suite. + */ + skip(name: string, fn: Function): void; +}; + +/** + * Defines a suppressed test suite. + * @param name The suite name. + * @param fn The suite function, or {@code undefined} to define a pending test suite. + */ +export function xdescribe(name: string, fn: Function): void; + +/** + * Register a function to call after the current suite finishes. + * @param fn + */ +export function after(fn: Function): void; + +/** + * Register a function to call after each test in a suite. + * @param fn + */ +export function afterEach(fn: Function): void; + +/** + * Register a function to call before the current suite starts. + * @param fn + */ +export function before(fn: Function): void; + +/** + * Register a function to call before each test in a suite. + * @param fn + */ +export function beforeEach(fn: Function): void; + +export const it: { + /** + * Add a test to the current suite. + * @param name The test name. + * @param fn The test function, or {@code undefined} to define a pending test case. + */ + (name: string, fn: Function): void; + + /** + * An alias for {@link #it()} that flags the test as the only one that should + * be run within the current suite. + * @param {string} name The test name. + * @param {function()=} opt_fn The test function, or `undefined` to define + * a pending test case. + */ + only(name: string, fn: Function): void; + + /** + * Adds a test to the current suite while suppressing it so it is not run. + * @param name The test name. + * @param fn The test function, or {@code undefined} to define a pending test case. + */ + skip(name: string, fn: Function): void; +} + +/** + * Adds a test to the current suite while suppressing it so it is not run. + * @param name The test name. + * @param fn The test function, or {@code undefined} to define a pending test case. + */ +export function xit(name: string, fn: Function): void; + +/** + * @return {!promise.ControlFlow} the control flow instance used by this module + * to coordinate test actions. + */ +export function controlFlow(): promise.ControlFlow; + +/** + * Ignores the test chained to this function if the provided predicate returns + * true. + * @param {function(): boolean} predicateFn A predicate to call to determine + * if the test should be suppressed. This function MUST be synchronous. + * @return {!Object} An object with wrapped versions of {@link #it()} and + * {@link #describe()} that ignore tests as indicated by the predicate. + */ +export function ignore(predicateFn: () => boolean): typeof Testing;