[
  {
    "path": ".circleci/config.yml",
    "content": "version: 2\njobs:\n  build:\n    docker:\n      - image: circleci/node:8.11-browsers\n        environment:\n          # Fix issue with selenium-server in containers.\n          # See http://github.com/SeleniumHQ/docker-selenium/issues/87\n          DBUS_SESSION_BUS_ADDRESS: /dev/null\n    steps:\n      - checkout\n\n      - run: google-chrome --version\n\n      - run:\n          name: NPM Install\n          command: |\n            npm i\n            cd testapp && npm i\n\n      - save_cache:\n          key: node_modules-{{ .Branch }}-{{ checksum \"package-lock.json\" }}\n          paths:\n            - \"node_modules\"\n            - \"testapp/node_modules\"\n\n      - run:\n          name: Lint\n          command: ./node_modules/.bin/gulp lint\n\n      - run:\n          name: Selenium Start\n          background: true\n          command: |\n            ./node_modules/.bin/webdriver-manager update --versions.chrome=2.44\n            ./node_modules/.bin/webdriver-manager start --versions.chrome=2.44\n\n      - run:\n          name: TestApp Start\n          background: true\n          command: |\n            npm start\n\n      # Seems like the new circleci container no longer permits packet introspection on lo, even for root.\n      # - run:\n      #    name: Extra tcp logging for BlockingProxy\n      #    background: true\n      #    command: sudo tcpdump -i lo 'tcp && dst localhost' -w $CIRCLE_ARTIFACTS/localdump.pcap\n\n      - run:\n          name: Test\n          command: npm test\n"
  },
  {
    "path": ".clang-format",
    "content": "Language:        JavaScript\nBasedOnStyle:    Google\nColumnLimit:     100\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE",
    "content": "Hi there!\n\nThanks for submitting an issue to Protractor.\n\nTo help us help you better, please do the following before submitting an issue:\n\n1. Review the questions section of [CONTRIBUTING.md](https://github.com/angular/protractor/blob/master/CONTRIBUTING.md#contributing).\n2. Make sure you are not asking a usage or debugging question. If you are, use [StackOverflow](http://stackoverflow.com/questions/tagged/protractor), [Google Group discussion list](https://groups.google.com/forum/?fromgroups#!forum/angular), or [Gitter](https://gitter.im/angular/protractor) to get help.\n3. Provide a minimally reduced test case. This makes it much more likely that your bug will be fixed. Protractor has a test Angular application available at `http://www.protractortest.org/testapp` which you can use for the reproducible test case.\n4. Fill in the information that corresponds to your type of issue below.\n5. Delete this intro and any unrelated text :smile: (if you do not we'll assume you haven't read these instructions and automatically close the issue.)\n\n\nBug report\n---\n\n- Node Version: ``\n- Protractor Version: ``\n- Angular Version: ``\n- Browser(s): ``\n- Operating System and Version ``\n- Your protractor configuration file\n- A relevant example test\n- Output from running the test\n- Steps to reproduce the bug\n- The URL you are running your tests against (if relevant)\n\n\nFeature Request\n---\n\n- Reasons for adopting new feature\n- Is this a breaking change? (How will this affect existing functionality)\n"
  },
  {
    "path": ".gitignore",
    "content": "# Shared between .npmignore and .gitignore\n\nyarn.lock\nchromedriver.log\nlibpeerconnection.log\nxmloutput*\nnpm-debug.log\n.idea/\n.vscode/\n*.DS_Store\n*.swp\nselenium/\n\n# Build artifacts\n\nbuilt/\nspec/built/\nnode_modules/\nwebsite/bower_components/\nwebsite/build/\nwebsite/docgen/build/\n"
  },
  {
    "path": ".npmignore",
    "content": "# Shared between .npmignore and .gitignore\n\nyarn.lock\nchromedriver.log\nlibpeerconnection.log\nxmloutput*\nnpm-debug.log\n.idea/\n.vscode/\n*.DS_Store\n*.swp\nselenium/\n\n# Development folders\n\n.github/\nbuilt/spec/\nbuilt/**/*.map\ndebugging/\ndocs/\nlib/\nscripts/\nspec/\nstress/\ntestapp/\nwebsite/\nexampleTypescript/*.js\nexampleTypescript/node_modules/\nexampleTypescript/tmp/\n\n# Development files\n\n.clang-format\n.gitattributes\n.gitignore\n.jshintrc\n.npmignore\n.travis.yml\ncircle.yml\nlogo.svg\nrelease.md\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nsudo: false\nnode_js:\n  - \"9\"\n  - \"10\"\n\nenv:\n  global:\n    - SAUCE_USERNAME=angular-ci\n    - SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987\n    - BROWSER_STACK_USERNAME=angularteam1\n    - BROWSER_STACK_ACCESS_KEY=BWCd4SynLzdDcv8xtzsB\n    - LOGS_DIR=/tmp/protractor-build/logs\n    - BROWSER_PROVIDER_READY_FILE=/tmp/sauce-connect-ready\n    - CXX=g++-4.8\n  matrix:\n    - JOB=full\n    - JOB=smoke\n    - JOB=bstack\n\nmatrix:\n  allow_failures:\n    - env: \"JOB=smoke\"\n    - env: \"JOB=bstack\"\n  exclude:\n    - env: JOB=smoke\n      node_js: \"9\"\n    - env: JOB=bstack\n      node_js: \"9\"\n\naddons:\n  apt:\n    sources:\n      - ubuntu-toolchain-r-test\n    packages:\n      - g++-4.8\n\nbefore_install:\n  - travis_wait g++-4.8 --version\n\nbefore_script:\n  - npm run install_testapp\n  - npm run pretest\n  - mkdir -p $LOGS_DIR\n  - scripts/travis_setup.sh\n\n\nscript:\n  - ./scripts/testserver.sh\n  - ./scripts/test_on_travis.sh\n\nafter_script:\n  - ./scripts/print_logs.sh\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# 6.0.0\n\nSelenium 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)\n\n## Breaking changes\n\n- Control flow is removed and you should use async await to run your tests.\n- Other control flow related items:\n  - debugger, explore and element explorer have been removed\n  - jasminewd is no longer a dependency\n- ignoreSynchronization has been deprecated and you should use `waitForAngularEnabled`\n- Types for selenium-webdriver are currently in the types/ directory and are not complete. We are still missing some type definitions for selenium 4.\n- 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.\n\n## Features\n\n- ([cf43651](https://github.com/angular/protractor/commit/cf43651bd7719b2811225bd7aa084aca973df1c8))\n  chore(debugprint): convert debugprint to TypeScript (#5074)\n\n- ([4672265](https://github.com/angular/protractor/commit/4672265773b415e80554f94bdcc9637340b70c25))\n  chore(browser): remove timing issues with restart and fork (#5085)\n\n  - remove .ready since forking should automatically return a browser\n  - getNewDriver should return a promised WebDriver that can be awaited\n  - fix interaction tests and local driver tests\n  - update unit tests for async await due to getNewDriver fix\n   closes #5031\n- ([b4dbcc2](https://github.com/angular/protractor/commit/b4dbcc2621e06fee9768b77d10ca3a3092b4d710))\n  chore(elementexplorer): remove explorer bin file (#5094)\n\n  closes #5092\n\n- ([3b8f263](https://github.com/angular/protractor/commit/3b8f263d744a5e0f2e07abdeaf77ade24ec43ed1))\n  chore(ignoreSynchornization): clean up to use waitForAngularEnabled (#5071)\n\n\n- ([ffa3519](https://github.com/angular/protractor/commit/ffa35196751766028feb7cda38cf4d43ead2a6f7))\n  chore(debugger): remove debugger and explore methods (#5070)\n\n- ([c9db3f3](https://github.com/angular/protractor/commit/c9db3f377c52a4cdd917c9b9d1e69808969ddf15))\n  chore(promises): remove q promises and webdriver promises (#5052)\n\n  - remove q promises and webdriver promises from the runner, launcher, plugins, and taskRunner\n  - add deprecated message to element explorer.\n  - add unhandledRejection\n  - update browser versions used in travis tests\n\n- ([e22065c](https://github.com/angular/protractor/commit/e22065ca6c91c6f49f9bbebcdc6351bdeb6e61ec))\n  chore(promises): clean up driver providers and browser control flow (#5034)\n\n  Driver providers and tests:\n\n  - Use native promises over q promises in driver providers\n  - Remove driverProviderUseExistingWebDriver since the generation of the selenium server is already\n  accomplished when providing a selenium address in driverProvider.ts. Also clean up docs and tests.\n\n  - Enabled the driverProviderLocal tests\n  - Clean up JSDocs for q.promise\n   Basic lib spec:\n\n  - Remove auto unwrap test for a WebElement. Reference PR #3471\n   Browser:\n\n  - Remove control flow from waitForAngularEnabled, waitForAngular, and angularAppRoot in the\n  Browser class.\n\n## Dependencies\n\n- ([96ae17c](https://github.com/angular/protractor/commit/96ae17cdd8acf6cd388ddf691453fdbd7e7dd60e))\n  deps(jasmine): upgrade jasmine 3.3 (#5102)\n\n- ([d213aa9](https://github.com/angular/protractor/commit/d213aa9aea2c10efb497202c6ec2aa98e416684c))\n   deps(selenium): upgrade to selenium 4 (#5095)\n\n  - elements workaround for WebElement.equals\n  - added a better unhandled rejection warning message in the launcher\n control flow)bal function wrappers for mocha (these wrappers went away with\n  - fix the attach to session driver provider\n   Typing exported from Protractor:\n\n  - removed ActionSequence and EventEmitter (actions is currently missing)\n  - removed promise.Promise\n fulfilled, filter, whener, delayed, createFlow, controlFlow, all,\n   Typings exported from WebDriver:\n\n  - removed attachToSession\n  - removed WebDriver instance methods: touchActions, call\n  - removed WebElement getSize and getLocation for getRect\n  - removed redefined global vars for testing\n  - In the typings, we are missing Options.setScriptTimeout method. This should not impact users\n  unless they are using the driver.manage() method.\n   Tests:\n\n  - fix element equals test\n  - add missing 'await' in colorList test that is causing unhandled promise rejections.\n  - remove control flow related tests\n  - disable the install test. Installing from \"file:../../\" is not working.\n  - fix the attach to session driver provider test to exit with a 1 if errors are encountered\n\n- ([509f1b2](https://github.com/angular/protractor/commit/509f1b25762c850ba82bc7527684edc8d8b52275))\n  deps(latest): upgrade to the gulp and typescript (#5089)\n\n  * deps(latest): upgrade to the gulp and typescript\n\n  - add in @types/loglevel and @types/yargs for webdriver-manager\n  - upgrade tslint clean up for tslint\n supported by gulpp 4 and remove run sequence since this feature is\n  - remove compile to es5\n\n- ([2def202](https://github.com/angular/protractor/commit/2def2026de4f68cd5264afca4aa4cb51a9b550a3))\n  deps(webdriver-manager): use replacement (#5088)\n\n publish a beta release of use webdriver-manager-replacement until we\n  webdriver-manager\n   closes #5087\n\n## Miscellaneous\n\nMinor fixes to remove the control flow completely and to prevent random execution order in Jasmine 3.\n\n- ([0a2809e](https://github.com/angular/protractor/commit/0a2809e62f34ed75632c80e1e792214e01eb0afe))\n  chore(types): fix types to use not @types/selenium-webdriver (#5127)\n\n  - Remove the USE_PROMISE_MANAGER test in spec/ts/basic\n  - Remove the check if we are using the control flow or not\n- ([84cdc50](https://github.com/angular/protractor/commit/84cdc50771b23f840bf62cb33f742dff3aaff990))\n  chore(jasmine): prevent random execution order in jasmine 3 (#5126)\n\n\n# 5.4.2\n\n## Features\n\n- ([db1b638](https://github.com/angular/protractor/commit/db1b6381d463c7cecf11dece2bf9412fecbd6f4d))\n  feat(saucelabs): add sauceRegion support for eu datacenters (#5083)\n\n  This change allows user to define the backend region from sauce via the `sauceRegion` property,\n  e.g.\n\n  ```js\n     sauceUser: process.env.SAUCE_USERNAME,\n     sauceKey: process.env.SAUCE_ACCESS_KEY,\n     sauceRegion: 'eu',\n  ```\n   Will run the test against `https://ondemand.eu-central-1.saucelabs.com:443/wd/hub/.`\n\n  ```js\n     sauceUser: process.env.SAUCE_USERNAME,\n     sauceKey: process.env.SAUCE_ACCESS_KEY,\n     sauceRegion: 'us',\n\n     // the default\n     sauceUser: process.env.SAUCE_USERNAME,\n     sauceKey: process.env.SAUCE_ACCESS_KEY,\n  ```\n   Will run the test against https://ondemand.saucelabs.com:443/wd/hub/\n\n##  Fixes\n\n- ([f5dbe13](https://github.com/angular/protractor/commit/f5dbe13ad6755ae812627d8056527e351db8b34c))\n  fix(deps): @types/node is now a dev dependency\n\n# 5.4.1\n\n## Features\n\n- ([7b08083](https://github.com/angular/protractor/commit/7b0808396458fbc2bd46c7e929f4effecb2a3f1e))\n  feat(driverProvider): Add useExistingWebDriver driver provider (#4756)\n\n- ([249e657](https://github.com/angular/protractor/commit/249e657baa605257c268f09078d56219776db9b1))\n  feat(example): add examples of usage protractor framework with angular-material components;\n  (#4891)\n\n## Fixes\n\n- ([39485ca](https://github.com/angular/protractor/commit/39485ca49ad751814ae112b224ae054e697e102f))\n  fix(typo): fixed typo in EC expectation alias (#4952)\n\n- ([07fefeb](https://github.com/angular/protractor/commit/07fefeb9636f8b3506df2eacbdb4ada29f50fbbd))\n  fix(browser): browser.navigate() return type. (#4932)\n\n  Changing return type of browser.navigate() to be Navigation instead of any.\n\n- ([0b1820c](https://github.com/angular/protractor/commit/0b1820c6c1d8b13e8fb2e64f27e4fe1d3cbcdd73))\n  fix(package-lock.json): update package-lock.json to match package.json. (#4931)\n\n## Dependencies\n\n- ([2632bb6](https://github.com/angular/protractor/commit/2632bb67c3ac7773e8b769cd5c5ee5c6d7b69f3d))\n  deps(webdriver_js_extender): update webdriver_js_extender to 2.1 (#4934)\n\n# 5.4.0\n\n## Features\n\n- ([03e2209](https://github.com/angular/protractor/commit/03e22092557240217bbbcf641476db08cc35df77))\n  feat(driverProvider) Adding browserstackProxy param in BrowserStack driverProvider (#4852)\n\n## Fixes\n\n- ([492230a](https://github.com/angular/protractor/commit/492230ab3445ca3aea3e60f55d27cb9825018ef9))\n  fix(generate_doc.sh): Use ES6 lib to compile down to ES5 (#4884)\n\n- ([ed955e5](https://github.com/angular/protractor/commit/ed955e56a839d7f69da43acb6755763220d3681d))\n  fix(travisCI): Update Node versions (#4847)\n\n- ([3702a70](https://github.com/angular/protractor/commit/3702a709fe251fed1de9ee1eab38593c2c371e94))\n  fix(local): Ensure webdriver.gecko.driver path is passed to Selenium (#4502)\n\n- ([1c6a1a8](https://github.com/angular/protractor/commit/1c6a1a82f03f2bd5911dfc37f365329d6f5acb60))\n  fix(circleci): Switch to using circleci v2 syntax and fix build errors (#4837)\n\n## Dependencies\n- ([056eec2](https://github.com/angular/protractor/commit/056eec23a7d1534d5e8624d42a6c9d1f3106cf5d))\n  deps(webdriver_js_extender): update webdriver_js_extender to 2.0 (#4882)\n  1. update webdriver_js_extender to 2.0\n  2. update selenium typing to 3.0.10\n\n# 5.3.2\n\n## Fixes\n\n- ([4e0a57c](https://github.com/angular/protractor/commit/4e0a57cff88b470dc5d05698a1341040f65dceb4))\n  fix(test): fix async tests\n\n  Increase the scripts timeout\n\n- ([c6703a5](https://github.com/angular/protractor/commit/c6703a5ea8ce7a837193ecf478c2096d8c6e99e9))\n  fix(doc): Spelling updates to comments in plugin.ts(#4797)\n\n  Updated some of the spelling errors/ punctuation mistakes for clearer understanding.\n- ([76324b8](https://github.com/angular/protractor/commit/76324b80063152ce67c164b6f048682e71771bb6))\n  fix(cli): add more verbose warning if '_debugger' module cannot be found. (#4800)\n\n\n- ([f8f490a](https://github.com/angular/protractor/commit/f8f490a82d0ed6965248e1f78bd2ac7ca91548d9))\n  updated CHANGELOG based on sauceSeleniumAddress and sauceSeleniumUseHttp (#4793)\n\n\n- ([6290f27](https://github.com/angular/protractor/commit/6290f27720816dd574ff7a1eccfec6ade1539b71))\n  fix(generate-docs): fix generate-docs.sh. (#4765)\n\n  Ignore generated unstaged files before checking out to new branch; otherwise, the git checkout\n  will fail.\n\n## Dependencies\n\n- ([948c7f2](https://github.com/angular/protractor/commit/948c7f267feebfd4c6997d8bc02485b573100197))\n  fix(deps): Update saucelabs for security issue. (#4805)\n\n\n# 5.3.1\n\n## Features\n\n- ([cc2234c](https://github.com/angular/protractor/commit/cc2234c762268acab85b6e5d1c13b6480738651b))\n  feat(logger): Add log level configuration (#1451) (#4068)\n\n## Fixes\n\n- ([c63b99e](https://github.com/angular/protractor/commit/c63b99ee029b6730e4b0702ac7c22b4076049e2a))\n  fix(grep): change excluded tests to disabled instead of pending (#4673)\n\n- ([9348ccf](https://github.com/angular/protractor/commit/9348ccfe65a0488d2929f624e70aef585f72e3ab))\n  docs(page_objects): Remove unnecessary \"await\" and \"async\" (#4732)\n\n- ([4898db0](https://github.com/angular/protractor/commit/4898db0940e0c0084e7c538a40986f94dc21e7ec))\n  docs(plugins) add protractor-cucumber-steps plugin to plugins.md (#4721)\n\n- ([a7411b6](https://github.com/angular/protractor/commit/a7411b6a156d45ec2e61f1b6ec951a19d854f5b2))\n  docs(page_objects): Add async / await example (#4675)\n\n\n# 5.3.0\n\n## Features\n\n- ([9d87982](https://github.com/angular/protractor/commit/9d8798243d23dd9d338c2eabd11d5a43ab3c31d9))\n  feat(config): allow to use newer versions of CoffeeScript (#4567)\n  CoffeeScript lost the hyphen in the module name about 9 months ago, all the new versions are\n  going to be released as coffeescript not the coffee-script\n\n- ([6ba30e0](https://github.com/angular/protractor/commit/6ba30e0b356fdb980cf1f2870ef0b5c6bb22ec4e))\n  feat(driverProviders): Add TestObject and Kobiton as driverProviders\n  Add TestObject and Kobiton as driverProviders\n  1. Add testObject and kobiton to driverProviders\n  2. Add testObject and kobiton items to cli, config and index\n  3. Add instructions for using testObject and kobiton to server-setup\n\n## Fixes\n\n- ([a62a154](https://github.com/angular/protractor/commit/a62a15417d559346a75fb6e208359ffa5b6b65f1))\n  fix(script): fix compile-to-es5 script (#4676)\n  make compile-to-es5 script rely on native es6-promise typing\n\n- ([964baba](https://github.com/angular/protractor/commit/964baba5eac52452350bf1d29a191558595c5f1b))\n  fix(clientsidescript): avoid returning the value of test callback in waitForAngular (#4667)\n  The return value could be interpreted as an error by mistake in some situation\n  Also fix a wrong if-condition in error reporting\n\n- ([83e2ba8](https://github.com/angular/protractor/commit/83e2ba878257e9c85775cd52bf70960f0c45917b))\n  fix(website): Locator by.name('field_name') (#4653)\n\n- ([02746dd](https://github.com/angular/protractor/commit/02746dd37726b2647f8f8dbeb1152cd7f902acb0))\n  fix(browser): Add space after full stop in error message. (#4594)\n  Linkifiers interpret the \".If\" at the end of the URL as part of the URL.\n\n- ([7f968e0](https://github.com/angular/protractor/commit/7f968e022a7f7ebcda8955651f2907c1a25ecc22))\n  fix(direct): Use config's geckoDriver when specified (#4554)\n  This change makes the `firefox` capability more closely match `chrome`'s. The `firefox`\n  capability was not looking for `config_.geckoDriver` like `chrome` was.\n\n- ([f9df456](https://github.com/angular/protractor/commit/f9df45619b1744e6615e183965fe093f0e4526e6))\n  docs(element): fix minor typo in element.ts (#4471)\n\n- ([65f206e](https://github.com/angular/protractor/commit/65f206e70a2d13762c841da247557b0ebb7fde7d))\n  docs(website): updated reference from ignoreSynchronization to waitForAngularEnabled(false).\n  (#4632)\n\n## Dependencies\n\n- ([335680f](https://github.com/angular/protractor/commit/335680f10ab3c5d1de1eab92868c7b30fb7e3d23))\n  fix(circleCI): lock the currect version of all dependencies (#4669)\n  CircleCI was broken by the new release of gulp-clang-format and jasmine\n  1. pin the version of gulp-clang-format to 1.0.23\n  2. pin jasmine version to 2.8.0\n  3. add lock file\n\n# 5.2.2\n## Fixes\n- ([b3c7404](https://github.com/angular/protractor/commit/b3c7404258db55a71e7bc4520973c0665cb0ff06))\n  Revert \"fix(jasmine): Update Jasmine to support Node8 async/await (#4608)\"\n  This reverts commit 5d13b00bca651227eb55616363f7d7eb8a91f8e8.\n  This commit is unnecessary now, revert this commit to avoid breaking changes in 5.2.1\n\n- ([8e5ad1f](https://github.com/angular/protractor/commit/8e5ad1f9b01ec4629fa079609aa8bedee52f0722))\n  fix(doc): remove unnecessary config in debugging doc/example (#4622)\n\n# 5.2.1\n## Features\n- ([a62efc6](https://github.com/angular/protractor/commit/a62efc6e401bc1aa7408e3008ccdaa219b528636))\n  feat(locators): Add support for regex in cssContainingText (#4532)\n\n## Fixes\n- ([e51f0ec](https://github.com/angular/protractor/commit/e51f0ecb31b7eb361dbf8feaa201ad2fccf9cf14))\n  fix(doc): update doc for testing with node 8 async/await and chrome inspector. (#4613)\n- ([b204a83](https://github.com/angular/protractor/commit/b204a835976088131f209a5f873f9f786fa05a2e))\n  doc(browser-support) improved Firefox documentation (#4553)\n- ([8d71a1b](https://github.com/angular/protractor/commit/8d71a1b1b1d314bf0a4ef8c7ecefdd1c7688032e))\n  docs(page-objects.md): Refactor the existing Page Object example (#4576)\n- ([95dd3ca](https://github.com/angular/protractor/commit/95dd3caf4b90b2d42aa1d5b35b0fd48504f802c3))\n  doc(tutorial): added example for element.getAttribute('value') to read text from an input (#4566)\n\n## Dependencies\n\n- ([bb63ab0](https://github.com/angular/protractor/commit/bb63ab00046fc300d898a39c03fb6d974fe20b57))\n  Update to the latest blocking proxy (#4546)\n\n## Breaking Changes\n\n- ([5d13b00](https://github.com/angular/protractor/commit/5d13b00bca651227eb55616363f7d7eb8a91f8e8))\n  fix(jasmine): Update Jasmine to support Node8 async/await (#4608)\n\n  Breaking change for TypeScript:\n  JasmineWD doesn't know anything about async/await, turns off JasmineWD if control flow was\n  disabled.\n\n  It will affect TypeScript tests that are using async/await and\n\n  a. miss some await keyword in the test.(Previously, this might cause the\n  test failed silently and be reported as pass), or\n\n  b. use Promise in jasmine expect function\n\n  **Before**\n    ```ts\n  await expect(getPromise()).toEqual(42);\n    ```\n  **After**\n    ```ts\n  expect(await getPromise()).toEqual(42);\n    ```\n\n# 5.2.0\n\n## Fixes\n- ([f7e17f3](https://github.com/angular/protractor/commit/f7e17f348e738e1a594870d7ff735f2b7ea1853f))\n  fix(clientSideScripts): change protractor to support waiting for hybrid app (#4512)\n\n- ([4b7cada](https://github.com/angular/protractor/commit/4b7cada1317079c20ddf1bb105303e21adba6e32))\n  fix(sauce): bring back sauceProxy as a configuration option (#4419)\n\n- ([b87159b](https://github.com/angular/protractor/commit/b87159b3fcb379b85727a1beb6fd41a914235cf8))\n  fix(website): fix all locator examples to use `element` over `browser.findElement` (#4413)\n\n- ([768fd39](https://github.com/angular/protractor/commit/768fd393d1084a8da0ec6eeaa57508bf17519a3f))\n  fix(local): allow local driver provider to use gecko driver from config (#4412)\n\n- ([c0b8770](https://github.com/angular/protractor/commit/c0b8770ff1a508205b5cf38b5611918e20028fe3))\n  docs(website): fix issue 4246\n\n- ([f79938e](https://github.com/angular/protractor/commit/f79938e3d138c7bedc66f8c6748704402ea721c4))\n  docs(plugins): add ng-apimock plugin to plugins.md\n\n- ([ab1afb0](https://github.com/angular/protractor/commit/ab1afb093107f3a63f6e15f8f315e33576bb414d))\n  fix(blockingproxy): Start bpRunner strictly after setupDriverEnv\n\n- ([b85e7ee](https://github.com/angular/protractor/commit/b85e7ee1c53cdc4cfb23dc3d06d40317a27e50e7))\n  fix(npmignore): .map files in built directory cause stacktrace lines to nowhere Fixes #4371\n\n- ([299fc8d](https://github.com/angular/protractor/commit/299fc8d96b3e5daf632a1c584728214ababcebf8))\n  docs(browser-support): Fixed incorrect example\n\n- ([e5a5d59](https://github.com/angular/protractor/commit/e5a5d59fcabe15860b30944e714bbd8e81ceaeae))\n  docs(frameworks) align cucumberOpts comments\n\n- ([fe8c480](https://github.com/angular/protractor/commit/fe8c480bd860209cc68768de884d050cbf1a5b27))\n  docs(frameworks) update cucumber dry run option\n\n- ([2e9acf5](https://github.com/angular/protractor/commit/2e9acf58b76b553c558f56b6a38c161ad50324de))\n  docs(plugins) add protractor-numerator plugin to plugins.md\n\n- ([3f861ae](https://github.com/angular/protractor/commit/3f861ae069df98a06cfa1ede89f56a8d0ec9d5d2))\n  By.js locator should accept functions\n\n## Dependencies\n\n- ([0fbc2c0](https://github.com/angular/protractor/commit/0fbc2c0ac12992bd61712188a96aef6684bef0c1))\n  chore(release): update selenium-webdriverjs\n\n\n# 5.1.2\n\n## Features\n\n- ([dd2ccbb](https://github.com/angular/protractor/commit/dd2ccbb1b73b7c90647837cd1c4f6b16b3f6b0ac))\n  feat(saucelabs): Add Sauce Labs protocol customization support\n\n  New option `sauceSeleniumUseHttp` available in `protractor.conf.js`\n  If true, uses 'http' instead of 'https' to connect to Sauce Labs defined by `sauceSeleniumAddress`\n\n## Fixes\n\n- ([1a47076](https://github.com/angular/protractor/commit/1a47076875395648038a39fcd258a77bfcabe05c))\n  fix(ci): Use latest pip on CircleCI\n\n- ([fd59c78](https://github.com/angular/protractor/commit/fd59c78407ced4f17e1b4ed4451ce463439aa3c9))\n  fix(elementexplorer): Set script breakpoints with cross-platform safe paths.\n\n  Fixes #4011\n\n- ([1250278](https://github.com/angular/protractor/commit/12502784b306cbedca8684486c31eeb361da5897))\n  fix(cli): Correctly parse list chromeOptions\n\n  Chromedriver requires that certain options always be passed as an array. Optimist passes\n  --single-option as a string instead of an array which is invalid. This ensures that we always pass\n  an array, even if a single option is passed via the cli.\n\n  Fixes #4050\n\n- ([183cd80](https://github.com/angular/protractor/commit/183cd803254f7a3ccb3a8650e8ef06b4fff03446))\n  fix(browser): Fix browser.angularAppRoot()\n\n  By default, it wasn't returning anything. Now it returns a promise that resolves to\n  internalAngularAppRoot. Fixes #4233\n\n- ([bd534fb](https://github.com/angular/protractor/commit/bd534fb8b2dfaca9072914dc84ad662891a8c7b2))\n  fix: Add \"stackTrace\" option to allowedNames in cli.ts\n\n  This fixes a problem I encountered similar to #4196 - where `stackTrace` is listed as an option\n  but an error is given saying it's an \"unknown extra flag\"\n\n- ([8249167](https://github.com/angular/protractor/commit/82491678de71b43311ea68d496ff807e1c72ee5e))\n  fix: export Runner, not just its type. (#4227)\n\n- ([0eb5b76](https://github.com/angular/protractor/commit/0eb5b7608e4bfb770878fe443d97ed9aa426c070))\n  fix(navigation): ignore unknown JS errors in IE (#4149)\n\n  The `err` object doesn't have the `code` property any more (Selenium Server Standalone 3.3.1 +\n  IEDriver win32 3.3.0), so we need a new way to detect those errors. See #841\n- ([4752ad1](https://github.com/angular/protractor/commit/4752ad1287af536c6dd442dd8c74546f978627d9))\n  chore(examples): Fix TSC issues with exampleTypescript (#4132)\n\n\n## Dependencies\n\n- ([a0a1fac](https://github.com/angular/protractor/commit/a0a1fac8568f2bfbd6d5721db438aed390e30d23))\n  chore(deps): Updating webdriver-manager and jasminewd2\n\n\n# 5.1.1\n\n## Features\n\n- ([3edd62e](https://github.com/angular/protractor/commit/3edd62eccccb67ec7cb71b8c3d8b2c2921a6f7ca))\n  feat(saucelabs): Add Sauce Labs HTTPS Support (#4071)\n\n- ([29f975a](https://github.com/angular/protractor/commit/29f975a34f5885a21525c3746bd3e82d5ae0c51e))\n  feat(plugins): allow plugins to know which browser instance to run against (#4066)\n\n  Closes https://github.com/angular/protractor/issues/4054\n\n## Dependencies\n\n- ([9d69a81](https://github.com/angular/protractor/commit/9d69a819c96e408df2b59589d49811a89af1bc74))\n  deps(typescript): use typescript@~2.0.0 (#4062)\n\n  - move noCF tests to install and fix reference to protractor\n  - changed element.ts to not use keyof\n  - remove gulp task tsc:spec\n\n## Breaking Changes\n\n- The protocol for a Sauce Labs selenium relay (sauceSeleniumAddress in protractor.conf.js)\n  is 'https' (previously used 'http'). The protocol is not configurable in this version.\n\n\n# 5.1.0\n\n#### Blocking proxy\n\nBlocking Proxy is a new experimental feature in Protractor 5 and is behind the\n`--useBlockingProxy` or `blockingProxyUrl`. See the\n[lib/config.ts#L100](https://github.com/angular/protractor/blob/master/lib/config.ts#L100).\nOther ways to start blocking proxy include using the\n`--highlightDelay` and `--webDriverLogDir` flags See [lib/config.ts#L501](https://github.com/angular/protractor/blob/master/lib/config.ts#L501).\n\nThis adds two options, both of which are implemented with Blocking\nProxy. `--webDriverLogDir` will create a readable log with timing\ninformation of webdriver commands in the specified directory.\n\n`--highlightDelay` will pause before clicking on elements or sending keys.\nWhile paused, the element that's about to be affected will be\nhighlighted.\n\n#### Webdriver-manager\n\nWebdriver-manager will now by default grab the latest versions of all binaries\n(standalone, chromedriver, iedriver, gecko driver). Use the\n`--versions.(binary name)` to pin to a specific version. Selenium standalone\n3.0.1 has a bug which prevents it from working with any version of FireFox.\nWe have tested version 3.0.0-beta4 and know that it works with\nFireFox 51, and we expect that the 3.0.2 release will also work.\n\n#### TypeScript support\n\nFor jasmine users, in order to get all the type declarations that are used in\njasminewd2, you'll need to install `@types/jasminewd2` in addition to\n`@types/jasmine` as dev dependencies.\n\n## Features\n\n- ([0cd156d](https://github.com/angular/protractor/commit/0cd156d6829f23f93403d865b7fdb7eab4f45446))\n  feat(debugging): Add webdriver logging and highlight delay. (#4039)\n\n  This adds two options, both of which are implemented with Blocking\n  Proxy. --webDriverLogDir will create a readable log with timing\n  information of webdriver commands in the specified directory.\n\n  --highlightDelay will pause before clicking on elements or sending keys.\n  While paused, the element that's about to be affected will be\n  highlighted.\n\n- ([3d98a16](https://github.com/angular/protractor/commit/3d98a1668138d36681bf305c9ea67dd1eea38899))\n  feat(config): Support setting `SELENIUM_PROMISE_MANAGER` flag via the config (#4023)\n\n  Closes https://github.com/angular/protractor/issues/3691\n- ([4e40fb1](https://github.com/angular/protractor/commit/4e40fb175e64820bbab24efb376dac80fa6ba2b0))\n  feat(browser): chain promises in `browser.get` (#4017)\n\n  Closes https://github.com/angular/protractor/issues/3904\n- ([33393ca](https://github.com/angular/protractor/commit/33393cad633e6cb5ce64b3fc8fa5e8a9cae64edd))\n  feat(browser): chain some promises in `lib/browser.ts` + return promise from\n  `waitForAngularEnabled` (#4021)\n\n  Minor breaking change since `waitForAngularEnabled` no longer returns a boolean\n   Part of angular#3904\n   Chaining `browser.get` has proved surprisingly complex, so I'll do that in a different PR\n   Also fixed a minor bug in `lib/clientsidescripts.js` while debuging\n- ([7cb9739](https://github.com/angular/protractor/commit/7cb9739954bc26f0667d671cdb0083f5bd43f2f6))\n  feat(browser.ready): make `browser.ready` wait for all async setup work (#4015)\n\n  Closes https://github.com/angular/protractor/issues/3900\n- ([b77cb92](https://github.com/angular/protractor/commit/b77cb928301fbe4f77ffcdcace424a490581416e))\n  feat(restart): `browser.restart` should return a promise (#4008)\n\n  Also allows `browser.restart` to work when the control flow is disabled, and\n  fixes it for forked browsers.\n\n  Closes #3899 and #3896\n  https://github.com/angular/protractor/issues/3896\n\n- ([4a59412](https://github.com/angular/protractor/commit/4a59412357eb5df592b06dd282d88d6dbc5e4771))\n  feat(angularAppRoot): Replace rootEl with browser.angularAppRoot() (#3996)\n\n  Replace browser.rootEl with browser.angularAppRoot(), which changes the root element in a promise\n  on the control flow. Note that browser.rootEl will immediately return the current value, but\n  browser.angularAppRoot() will return a promise that resolves during the next step in the control\n  flow.\n\n  Also update to BlockingProxy 0.0.3, which allows changing rootSelector.\n\n- ([879aac6](https://github.com/angular/protractor/commit/879aac6ee6c1c36d005b538472e2754b987b3368))\n  chore(blockingproxy): Allow using a pre-existing Blocking Proxy instance (#3970)\n\n- ([bf123ad](https://github.com/angular/protractor/commit/bf123adafc442440b2ca10725113b47342ebb24f))\n  feat(elements): Add isPresent() to ElementArrayFinder. (#3974)\n\n## Bug Fixes\n\n- ([f9bee84](https://github.com/angular/protractor/commit/f9bee84bc03b6cd6872522b8780327423b789e19)) fix(restart): preserve waitForAngularEnabled on restart and add promise chaining\n\n  I noticed I missed `waitForAngularEnabled` in #4037.  This commit fixed that.\n\n  While I was at it I fixed a minor error where the promises implicitly created by\n  setting `rootEl` and `ignoreSynchronization` weren't getting chained properly.\n\n  Also fixed minor (so minor I think it was impossible to trigger) where\n  browser.plugins_ could be undefined.\n\n- ([0b0c224](https://github.com/angular/protractor/commit/0b0c224e4056368c2c0030064b4ca4235163276b))\n  fix(plugins): do not force ManagedPromise in plugins.ts (#4036)\n\n- ([9c2274d](https://github.com/angular/protractor/commit/9c2274d8f218cabc946dbc6a11d725458c1b4e3a))\n  fix(restart): preserve properties like `browser.baseUrl` upon restart (#4037)\n\n  I also fixed a minor issue where `internalRootEl` wasn't being set when blocking proxy was being\n  used.  I also just cleaned up our internal uses of `this.rootEl`.\n\n  Closes #4032\n\n- ([a20c7a7](https://github.com/angular/protractor/commit/a20c7a7cc1df04f96cb1a9dd971df39883ac173b))\n  fix(element chaining): make element chaining work when the control flow is disabled (#4029)\n\n  Also added some tests to `spec/ts/noCF/smoke_spec.ts` double checking that the control flow is off\n\n- ([7481dee](https://github.com/angular/protractor/commit/7481dee75cab1da9d207909e928eee55a9f5a682))\n  fix(cli): Make unknown flag check a warning instead of an error. (#4028)\n\n- ([40bbeca](https://github.com/angular/protractor/commit/40bbeca003017901760e10831c66d383cf5accf8))\n  fix(expectedConditions): Add tests and fix race conditions around visibility (#4006)\n\n  Add test cases to reproduce the missing element race conditions possible in\n  expected condition methods `visibilityOf`, `textToBePresentInElement`,\n  `textToBePresentInValue` and `elementToBeClickable`.\n\n  Add error handler `falseIfMissing` to all expected conditions that depend\n  on the presence of an element.\n\n  Expected conditions check the presence of an element before other checks,\n  but when an element is removed exactly in the moment after the `isPresent`\n  and before `isDisplayed` in `visibilityOf` the condition used to fail.\n\n  This solution does not handle missing elements in (`isEnable`, `isDisplayed`,\n  `isSelected`) and focused only on expected conditions (see #3972)\n\n  This problem was also referenced in #3578 and #3777\n\n- ([5856037](https://github.com/angular/protractor/commit/5856037368ee8d8a21f11eadbfe93d5f46507f60))\n  fix(cli): Allow frameworks to specify flags they recognize. (#3994)\n\n  Fix for #3978.\n   Our initial plan to allow setting --disableChecks with an environment variable is insufficient,\n  since the custom framework isn't even require()'d until after the config is parsed. This moves the\n  unknown flag check into the runner, and gives frameworks a way to specify extra flags they accept.\n- ([e68dcf1](https://github.com/angular/protractor/commit/e68dcf1bfd7f32c59ebd23fa16ca53e1a53f8ddf))\n  fix(driverProviders): Check config in the right place. (#3991)\n\n- ([eb89920](https://github.com/angular/protractor/commit/eb899208457f83853f043edea5e56b07e87803bc))\n  fix(driverProviders): Handle promise rejection when starting selenium (#3989)\n\n  Fixes #3986. Also error if jvmArgs isn't an array.\n\n- ([8d2fc07](https://github.com/angular/protractor/commit/8d2fc07ed28a1b19c03a9869442f76f2963e40a1))\n    chore(browser): deprecate `browser.getLocationAbsUrl()`. (#3969)\n\n    Closes #3185\n- ([15a1872](https://github.com/angular/protractor/commit/15a187204bb8b87255d5f4622094eabc71206315))\n  fix(firefox): Fix directConnect for Firefox 51+ (#3953)\n\n- ([81f56a4](https://github.com/angular/protractor/commit/81f56a449f8988feba21617ef7533cfa2f06c6f8))\n  fix(cli): display disableChecks option in extra flags error message (#3964)\n\n- ([6a4dc7a](https://github.com/angular/protractor/commit/6a4dc7a6a5b796e0215e5b9abf99494ac13cb647))\n  fix: no longer use es6 let statement (#3963)\n\n  * Currently in Protractor v5 the Angular detection script uses ES6 features like the `let` modifier.\n\n  This can break Protractor on browsers, which doesn't support those statements.\n  > See https://saucelabs.com/beta/tests/275f75091dac40a0a3374d29d912caee/commands#11\n\n- ([528338c](https://github.com/angular/protractor/commit/528338c6722219fdcfc51153b0031a02f0fce046))\n  fix(expectedCondition): fix NoSuchElementError in visibilityOf due to a race condition (#3777)\n\n  Handle NoSuchElementError in the expected condition visibilityOf, which occurred when\n  an element disappears between the isPresent() and isDisplayed() check.\n\n## Dependencies\n\n- ([5899b67](https://github.com/angular/protractor/commit/5899b676bc2db0005506ae2306350e6ffea3c808))\n  deps(update): update webdriver-manager to ^12.0.1 (#4042)\n\n  Running `webdriver-manager update` will now by default grab the latest\n  versions of all binaries (standalone, chromedriver, iedriver, gecko\n  driver). You can continue to pin to a specific versions using the\n  command line option. Example `webdriver-manager update --versions.chrome 2.20`.\n  As of this release the latest versions are:\n\n  - gecko v0.14.0\n  - selenium-standalone 3.0.1 is the latest jar file; however, we recommend 3.0.0-beta4. See note\n  below on Firefox support.\n  - chromedriver 2.27\n  - iedriver 3.0.0\n\n   A note on FireFox support: Selenium standalone 3.0.1 has a bug which prevents it from working\n  with any version of FireFox. We have tested version 3.0.0-beta4 and know that it works with\n  FireFox 51, and we expect that the 3.0.2 release will also work.\n\n   closes #4033\n\n- ([cd084a0](https://github.com/angular/protractor/commit/cd084a0ca29cd73aa3ce1650188adf7ddfdb7962))\n  deps(jasmine): update jasmine to ^2.5.3 (#3960)\n\n# 5.0.0\n\nThis version includes big changes around upgrading to selenium-webdriver 3.0.x.\nSee the [selenium-webdriver changelog](https://github.com/SeleniumHQ/selenium/blob/master/javascript/node/selenium-webdriver/CHANGES.md).\n\nFor the 5.0.0 release, we are still using the selenium standalone server 2.53.1\nand recommend using Firefox 47. Firefox 48+ currently is not supported.\n\n## Breaking Changes\n\n- Minimum node version is now 6.9.x.\n- When testing with Firefox 47, use the capability option `marionette: false`\n  to use the legacy Firefox driver.\n\n  Before:\n  ```\n  capabilities: {\n    browserName: 'firefox'\n  }\n  ```\n\n  After:\n  ```\n  capabilities: {\n    browserName: 'firefox',\n    marionette: false\n  }\n  ```\n\n- We moved `@types/jasmine` to a devDependency. This means that Jasmine\n  TypeScript users will need to include the `@types/jasmine` as a project\n  dependency. This is to avoid conflicts for users that prefer mocha typings.\n\n  After:\n  ```\n  \"dependencies\": {\n    \"@types/jasmine\": \"^2.5.38\"\n  }\n  ```\n\n- Selenium-webdriver methods removed: `WebDriver.prototype.isElementPresent`,\n  `WebElement.prototype.getRawId`, `WebElement.prototype.getInnerHtml`, and\n  `WebElement.prototype.getOuterHtml`.\n\n  Before:\n  ```\n  let isPresent = browser.driver.isElementPresent(By.tagName('a'));\n  let i = element(locator).getInnerHtml();\n  let o = element(locator).getOuterHtml();\n  ```\n\n  After:\n  ```\n  let isPresent = element(By.tagName('a')).isPresent();\n  let i = browser.executeScript(\"return arguments[0].innerHTML;\", element(locator));\n  let o = browser.executeScript(\"return arguments[0].outerHTML;\", element(locator));\n  ```\n\n- Selenium-webdriver `ErrorCodes` have been removed.\n- Adding cookies have been changed:\n\n  Before:\n  ```\n  browser.manage().addCookie('testcookie', 'Jane-1234');\n  ```\n\n  After:\n  ```\n  browser.manage().addCookie({name:'testcookie', value: 'Jane-1234'});\n  ```\n- Removed `protractor.wrapDriver()`.\n- You can no longer use `repl` command from within `browser.pause()`. Instead,\n  use `browser.explore()` to directly enter the `repl`.\n- Sending flags that are not recognized by the CLI throws an error. Since flags\n  are a subset of all configuration options, these errors can be silenced with\n  `--disableChecks`.\n- Auto-detection of the root element. This is a breaking change because it\n  changes the default root element behavior and removes the\n  `config.useAllAngular2AppRoots` flag.  Modern angular apps now\n  default to using all app hooks, and ng1 apps now check several places, notably\n  the element the app bootstraps to.\n- `sauceProxy` configuration field has been removed. Use `webDriverProxy`\n  instead.\n\n  Before:\n  ```\n  sauceProxy: 'http://sauceProxy'\n  ```\n\n  After:\n  ```\n  webDriverProxy: 'http://sauceProxy'\n  ```\n\n## Features\n\n- ([ec93c4a](https://github.com/angular/protractor/commit/ec93c4ab882991410ad9d3f52d87c0f5ec947641))\n  chore(cli): **breaking change** throw errors on unknown flags (#3921)\n\n  Unknown flags are options sent that is unrecognized by the CLI. For users that\n  encounter this error but would like to silence it, use: `--disableChecks`.\n\n  closes #3216\n- ([bc58332](https://github.com/angular/protractor/commit/bc583321a233453fc2b89472013b2ec3e1d6b6f9))\n  feat(rootEl): ***breaking change*** auto-detect the root element better (#3928)\n\n  This is a breaking change because it changes the default root element behavior\n and removes the `config.useAllAngular2AppRoots` flag.  Modern angular apps now\n default to using all app hooks, and ng1 apps now check several places, notably\n the element the app bootstraps to.\n\n  closes #1742\n- ([604fdbf](https://github.com/angular/protractor/commit/604fdbf064cc2785a2e745747beeaeb45d27f8ed))\n  cleanup(config): **breaking change** Remove redundant sauceProxy config (#3868)\n\n  Removes the `sauceProxy` config field, and uses `webDriverProxy` when creating\n  the SauceLabs client.\n- ([9465b9f](https://github.com/angular/protractor/commit/9465b9f1e667c9590e05d9ddac16fe5143aa93af))\n  feat(mobile): add extended wd commands for appium (#3860)\n\n  Also had to make some minor changes to the website to handle longer inheritance\n chains\n   Closes https://github.com/angular/protractor/issues/1940\n- ([0e26b21](https://github.com/angular/protractor/commit/0e26b218d5f385dd9871a40553acc174cfdfe26d))\n  feat(blockingproxy): Add synchronization with BlockingProxy. (#3813)\n\n  This adds support for BlockingProxy behind the flag --useBlockingProxy.\n\n  If set, the driver providers will start a proxy during their setup phase,\n  passing the selenium address to the proxy and starting a webdriver client\n  that talks to the proxy.\n\n  Starting a proxy for each driver provider isn't strictly necessary. However,\n  when we run with multiple capabilities it's easier to handle the logging if\n  each Protractor instance has it's own proxy.\n\n  Known issues:\n\n  - Doesn't work with directConnect. You can get the address of chromedriver by\n  mucking around in Selenium internals, but this probably changed for Selenium\n  3.0 and I doubt it's worth figuring out until we upgrade.\n  - Doesn't yet work with webDriverProxy (but it's an easy fix)\n\n- ([ca4f1ac](https://github.com/angular/protractor/commit/ca4f1acda3672942307d0f102d586c8889dd3d68))\n  chore(driverProviders): add warnings to extra driver provider parameters (#3873)\n\n  - builds the driver provider in lib/driverProviders/index instead of lib/runner\n   closes #1945\n\n- ([681b54a](https://github.com/angular/protractor/commit/681b54a21ee1467d5a95c3693cde148759767d62))\n  refactor(browser): Remove protractor.wrapDriver() **breaking change** (#3827)\n\n  Before:\n\n  Users could create their own selenium driver instance and enable Protractor on\n  it like so:\n\n  ```js\n  let capabilities = webdriver.Capabilities.chrome();\n  let driver = new webdriver.Builder().usingServer(seleniumAddress)\n    .withCapabilities(capabilities).build();\n  let browser = protractor.wrapDriver(driver);\n  ```\n\n  Over the years, wrapDriver() has become increasingly broken as Protractor\n  needs extra configuration options that wrapDriver() doesn't set.\n\n  After:\n\n  This method is removed. If users need a new browser instance, they can\n  use `browser.forkNewDriverInstance()`.\n\n- ([86fd569](https://github.com/angular/protractor/commit/86fd56917f039efbff8e6f323f4d91fa8bc821a4))\n  feat(ngUpgrade): Auto detect ngUpgrade apps and make the ng12Hybrid flag\n  unnecessary for most users (#3847)\n\n\n## Bug fixes\n\n- ([de153e7](https://github.com/angular/protractor/commit/de153e769292f6b9a99b2d5152bd2929ab1c48af))\n  fix(launcher): running getMultiCapabilities should reject on errors (#3876)\n\n  closes #3875\n- ([1345137](https://github.com/angular/protractor/commit/1345137dc5173e868de4b9da6ed16b7928e4c50e))\n  fix(isElementPresent): for un-wrapped `WebElement`s, `browser.isElementPresent`\n  was broken (#3871)\n\n  Closes #3864\n- ([4af3b2e](https://github.com/angular/protractor/commit/4af3b2e30e925ea9d8e47537ea0a7fe8f04b579d))\n  fix(element): Fix typing of ElementFinder.then (#3835)\n\n  Type `then` as optional on ElementFinder.\n\n\n## Dependencies\n- ([4d87c9c](https://github.com/angular/protractor/commit/4d87c9c20d6905189c0e7ea7214cf3e87c8efe91))\n  deps(update): update tslint and @types/selenium-webdriver (#3941)\n\n  - use @types/selenium-webdriver ~2.53.39\n  - fix for tslint\n\n  closes #3939\n- ([7376708](https://github.com/angular/protractor/commit/7376708c723976ef8a0a3ad7c245606bef1221db))\n  deps(tslint): set tslint to ~4.2 (#3938)\n- ([cb38ed0](https://github.com/angular/protractor/commit/cb38ed0a8aae2cb862001e0b6f076aa9972f4489))\n  Refactor element explorer to work with selenium-webdriver 3 (#3828)\n\n  This implementation now relies mostly on promises explicitly, so the control\n  flow is only used to add one large task to the queue. This should pave the way\n  for the eventual removal of the control flow, as well as getting element\n  explorer to work immediately.\n\n  BREAKING CHANGE\n\n  You can no longer use the `repl` command from within `browser.pause()`. Instead,\n use `browser.explore()` to directly enter the repl.\n- ([8196059](https://github.com/angular/protractor/commit/819605933d2dfef70b4332a727b3b3830e306817))\n  chore(dependency): switch to webdriver-manager 11.1.0 and remove\n  `--versions.chrome 2.26` from circle.yml (#3865)\n- ([397bf65](https://github.com/angular/protractor/commit/397bf65e088b640cf3612f9da678180f49939b84))\n  deps(update): move @types/jasmine to devDependencies (#3795)\n\n  - update outdated dependencies\n  - move @types/jasmine to devDependencies\n   closes #3792\n- ([a3e8b43](https://github.com/angular/protractor/commit/a3e8b4319d3e8b049e55e5c3c64a7fdb5a132ddf))\n  deps(selenium-webdriver): upgrade to selenium 3 (#3781)\n\n  Please see the [selenium-webdriver changelog](https://github.com/SeleniumHQ/selenium/blob/master/javascript/node/selenium-webdriver/CHANGES.md)\n\n    - Removed method `WebDriver.prototype.isElementPresent`\n    - Removed method `WebElement.prototype.getRawId`\n    - Removed `getInnerHtml` and `getOutterHtml`\n\n    - Dependency required for upgrade: use `jasminewd2@0.1.0`.\n    - Selenium-webdriver requires node version 6+, updating travis and circle yml\n      to use node 6 and 7.\n\n    - Use `instanceof` selenium-webdriver error instead of error code.\n      Selenium-webdriver error codes have been deprecated.\n\n    - Use executor with selenium-webdriver from `lib/http`. Deferred executor has\n      been deprecated.\n    - Fix quitting `driverProviders`. When calling `webdriver.quit`, the control\n      flow is shutdown and will throw an error.\n    - Driver provider for direct connect has been modified to use `ServiceBuilder`\n      and to call the `Service` to `createSession`\n    - Note: Since this upgrade is still using FF 47, direct connect for Firefox is\n      required to pass \"marionette: false\" in the capabilities. If you do not pass\n      marionette to false, it will look for gecko driver in the PATH.\n    - Added a TODO to support FF after 48+ with direct connect and gecko driver.\n\n    - Updated `browser.manage().addCookie('testcookie', 'Jane-1234');` to use\n      `browser.manage().addCookie({name:'testcookie', value: 'Jane-1234'});`\n\n    - Updated debug commons for breakpoint updated to selenium-webdriver\n      `lib/http` line 432.\n\n    - For mocha tests, `selenium-webdriver/testing` uses the global `it` and\n      cannot be reassigned as Protractor's global `it`. Some code has been\n      copied / modified to `lib/frameworks/mocha` to make this work.\n\n    - Capabilities for Firefox 47 requires setting marionette to false.\n    - Setup still requires selenium standalone server 2.53.1 for Firefox tests.\n      Firefox version used is 47.\n    - Using selenium standalone server 3, with Firefox 48+ tests fail with gecko\n      driver still do not work.\n    - Selenium standalone 3 + FF 49 + gecko driver 0.11.1 does not work\n    - Selenium standalone 3 + FF 48 + gecko driver 0.11.1 appears to work for a\n      single test but after it quits, selenium standalone no longer works with\n      firefox. When firefox 48 exists, logs show the following:\n\n      ```\n      20:01:14.814 INFO - Executing: [delete session: e353fa1b-e266-4ec3-afb3-88f11a82473a])\n      [GFX1-]: Receive IPC close with reason=AbnormalShutdown\n      [Child 30665] ###!!! ABORT: Aborting on channel error.: file /builds/slave/m-rel-m64-00000000000000000000/build/src/ipc/glue/MessageChannel.cpp, line 2052\n      [Child 30665] ###!!! ABORT: Aborting on channel error.: file /builds/slave/m-rel-m64-00000000000000000000/build/src/ipc/glue/MessageChannel.cpp, line 2052\n      ```\n- ([eb31c9c](https://github.com/angular/protractor/commit/eb31c9c7755399bcd01630158d900e0b940e9c31))\n  deps(types): update @types/selenium-webdriver dependency (#3886)\n\n  Fixes issue #3879 and adds the protractor.Key.chord method\n\n# 4.0.14\n\n## Bug Fixes\n\n- ([83694f5](https://github.com/angular/protractor/commit/83694f5e66592c5e229db98af3733ff73dac8392))\n  fix(types): update for selenium-webdriver types creating transpile errors (#3848)\n\n  - pin package.json to a specific version of @types/selenium-webdriver\n\n- ([ea09be4](https://github.com/angular/protractor/commit/ea09be4ff0750d1d0873677fac9055a9acb630b3))\n  fix(jasmine): Return the full test name in Jasmine test results. (#3842)\n\n  Fixes #3510\n\n- ([76cb4b4](https://github.com/angular/protractor/commit/76cb4b4c2ac892f9785dc1506cbc0b490eea91fe))\n  fix(element): Fix typing of then function (#3785)\n\n- ([5a12d69](https://github.com/angular/protractor/commit/5a12d69f34fd80234455bbe6457e125a27a07e54))\n  fix(config): cucumberOpts.require should be an optional Array of strings (#3817)\n\n  - Both cucumber options require and format now support `string` or `string[]` see #3822 #3817\n  - Remove no longer available options and fix `dryRun` type\n  - Update comment for `dryRun`\n\n## Features\n\n- ([b337a8e](https://github.com/angular/protractor/commit/b337a8e8547af0b90663a35b07fd30b55ff61dd0))\n  feat(config): add seleniumServerStartTimeout (#3791)\n\n## Dependencies\n\n- ([688f5d6](https://github.com/angular/protractor/commit/688f5d6a089275ac99688196cd66f345c64adfec))\n  deps(update): update vrsource-tslint-rules and webdriver-manager (#3856)\n\n- ([c437fd3](https://github.com/angular/protractor/commit/c437fd3315278e7536a8385a9769e4fbd954d0a6))\n  deps(selenium-webdriver): clean up types selenium-webdriver (#3854)\n\n\n# 4.0.13\n\n## Bug Fix\n\n- ([c3978ec](https://github.com/angular/protractor/commit/c3978ec166760ac07db01e700c4aaaa19d9b5c38))\n  fix(revert): element(locator).then should not appear in the docs and change reverted. (#3808)\n\n  - change comments to not be in JSDoc format\n  - reverts change `element(locator).then`\n\n# 4.0.12\n\n## Bug Fixes\n\n- ([742f264](https://github.com/angular/protractor/commit/742f26465ca926c39bf28f03390a45030c15acf3))\n  fix(driverProviders): quit forked instances w/ local driver provider (#3787)\n\n  - fix driver provider quit session to not throw error and quit\n  - should fix sauce labs test, enabling expected conditions test with forked instance\n  - checked that chromedriver does not persist in the background\n  - add local driver test with forked instance to test suite\n  - organize attach session and local specs to driverProviders directory\n   closes #3780, closes #3779, closes #3772\n- ([7d481d6](https://github.com/angular/protractor/commit/7d481d6886b39cb476b889de5d14186bda6dc066))\n  fix(ExpectedConditions): non-static ExpectedConditions for browser (#3766)\n\n  - Update sauce lab binary to run on travis.\n  - Disable expected conditions test that forks the browser. This issue appears to\n   not be specific to sauce labs. Also can reproduce this with a local driver\n   provider. Additional work is required around driver providers and the runner.\n  - Add TODO to enable test in the future when this is resolved.\n   closes #3761\n\n# 4.0.11\n\n## Features\n\n- ([6ebee72](https://github.com/angular/protractor/commit/6ebee72088016085e93d268effabcbe0b3d0a70f))\n  feat(config): Add 'random' and 'seed' jasmine config options (#3467)\n\n## Bug Fixes\n\n- ([2048182](https://github.com/angular/protractor/commit/2048182206e88dd4cde0e92cfac3ed97cbe38f15))\n  docs(timeout): Update timeout error message and docs (#3723)\n\n- ([f3938f9](https://github.com/angular/protractor/commit/f3938f9fcd1b91272f2f11d4d39e458576fb75b0))\n  fix(jvmArgs): fixes jvmArgs launching selenium from config (#3698)\n\n  closes #3697\n\n- ([21d534f](https://github.com/angular/protractor/commit/21d534fad28f84e8cef166348119387e49661227))\n  fix(types): Make element.then()'s signature more broad. (#3719)\n\n- ([e9061b3](https://github.com/angular/protractor/commit/e9061b30c3673344d776187682d1c735aaad69f7))\n  chore(types): make plugins.ts more strongly-typed (#3685)\n\n- ([f42e0b3](https://github.com/angular/protractor/commit/f42e0b3dc824404ac0c86364bf5de4b657cf30ea))\n  fix(globals): Resolve exported globals dynamically. (#3716)\n\n  fixes #3677\n\n- ([d7fa744](https://github.com/angular/protractor/commit/d7fa74464709c532d451fdead78bc57b21956784))\n  fix(util): Fix adding stack traces to Errors thrown as strings (#3687)\n\n## Dependencies\n\n- ([a1c8a23](https://github.com/angular/protractor/commit/a1c8a23fea3542eabeae6e7bc59f3c2ffa0fda94))\n  chore(deps): Downgrade jasmine to 2.4.1 (#3715)\n\n  Upgrading to 2.5.2 causes #3606. We'll need to wait on a fix for jasmine/jasmine-npm#95 before\n  upgrading.\n\n# 4.0.10\n\n## Features\n\n- ([7083426](https://github.com/angular/protractor/commit/70834269ac375f2be5a6cadf8ec9169cf19abff6))\n  feat(hybrid): set ng12hybrid flag in the config (#3452)\n\n## Bug Fixes\n\n- ([b67d8eb](https://github.com/angular/protractor/commit/b67d8eb4101ee80c53476d1640865ca612793436))\n  fix(restart): typescript fix for browser.restart (#3658)\n\n  closes #3648\n\n- ([6626ce7](https://github.com/angular/protractor/commit/6626ce7690e1120c3b246eff7793a26cb038b091))\n  fix(launcher) Ignore uncaught exceptions from webdriver. (#3608)\n\n\n- ([5cef1bf](https://github.com/angular/protractor/commit/5cef1bf20f88a2c3bb8944afbc6a328fc273aedd))\n  Explicitly remove newlines from getText() (#3618)\n\n  MSEdge does not properly remove newlines, which causes false negatives when using\n  `textToBePresentInElement()`\n\n## Dependencies\n\n- ([c11945a](https://github.com/angular/protractor/commit/c11945a1b67bdc774b267f101887dc3f575ad00b))\n  deps(outdated): webdriver-manager and @types updated\n\n  - webdriver-manager 10.2.6 uses the latest chromedriver, version 2.25\n\n# 4.0.9\n\nThis version includes a breaking change to the TypeScript import statement.\nPlease see the feature below.\n\n## Features\n\n- ([5034c89](https://github.com/angular/protractor/commit/5034c89242794dd14aba294ba3468937e06a7b69))\n  feat(typescript): move typescript variable instances from protractor/… (#3565)\n\n  Breaking change for TypeScript:\n  Instead of importing globals like `browser` from `protractor/globals`,\n  import from `protractor`.\n\n  Before:\n\n   ```ts\n   import {browser, element} from 'protractor/globals';\n   import {ElementFinder} from 'protractor';\n\n   describe('my app', () => {\n     myElement: ElementFinder;\n\n     beforeEach(() => {\n       browser.get('example.com');\n       myElement = element(by.css('foo'));\n     });\n   });\n   ```\n\n   After\n\n   ```ts\n   import {browser, element, ElementFinder} from 'protractor';\n\n   describe('my app', () => {\n     myElement: ElementFinder;\n\n     beforeEach(() => {\n       browser.get('example.com');\n       myElement = element(by.css('foo'));\n     });\n   });\n   ```\n\n   Closes #3564\n\n# 4.0.8\n\n## Bug Fixes\n\n- ([58459a9](https://github.com/angular/protractor/commit/58459a94b9e7a54f4b48614b93c0614177a8a522))\n  fix(types): do not publish built/globals.d.ts (#3546)\n\n  - do not publish built/globals.d.ts\n  - remove type interface for HttpProxyAgent and set to to any\n\n# 4.0.7\n\n## Dependencies\n\n- ([a68dd3f](https://github.com/angular/protractor/commit/a68dd3f0c6e33f93a5b7e9674197154b0e68cedd))\n  deps(jasmine): lower jasmine version down to 2.4.1 (#3540)\n\n  - upgrading to 2.5.x no longer logs jasmine output\n\n# 4.0.6\n\n## Bug Fixes\n\n- ([d18bba3](https://github.com/angular/protractor/commit/d18bba3e288610dd606aac4b656581da0dc65491))\n  fix(types): remove relative path used for @types/node and @types/jasmine (#3535)\n\n\n## Dependencies\n\n  closes #3533\n- ([4e7e8ec](https://github.com/angular/protractor/commit/4e7e8ec2c0a018e6159b557decee6b2df53958b5))\n  deps(outdated): update types/q and jasmine (#3525)\n\n## Other\n\n- ([9d5edbe](https://github.com/angular/protractor/commit/9d5edbe315ea70aad1fd0a2eaeff3328a2f8ee93))\n  chore(node): require the minimum node version 4.2.x required by selenium-webdriver (#3534)\n\n# 4.0.5\n\nIn this version, there are several small changes that affect TypeScript users\nfrom the previous version 4.0.4. Here are some of the steps to resolve any\ntranspiling errors:\n\n- In your package.json, use TypeScript 2.0.0. This will allow Protractor to use\n  the `@types/node` and `@types/jasmine` installed in node\\_modules.\n- Remove `jasmine` and `node` from your `typings.json` since these types are\n  already included via `@types`. If these were the only ambient typings\n  installed, remove the `typings.json` file.\n- If you still have a `typings.json` file, remove `typings` directory and\n  install a fresh set of ambient typings with: `typings install`.\n\n## Features\n\n- ([30102fb](https://github.com/angular/protractor/commit/30102fbdaa6354e8ba1a067c6731799aa0f0ff42))\n  feat(util): Allow more verbose logging with multiple sessions (#2985). (#3499)\n\n## Bug fixes\n\n- ([c5cc75b](https://github.com/angular/protractor/commit/c5cc75b41bc1a860061a5da1c23b718d440815ed))\n  fix(logger): Set the log level based on the config at startup. (#3523)\n\n  Fixes #3522. Also fix the mocha spec to stop yelling at us about ES6 arrow functions.\n\n- ([c7fff5e](https://github.com/angular/protractor/commit/c7fff5e9182c5a2a96b57f4f23889b5a5a13f44e))\n  fix(jasmine): Pass control flow to Jasminewd (#3519)\n\n  Fixes #3505 and #2790, which is caused by JasmineWd and Protractor using different controlflow\n  instances\n\n- ([64b4910](https://github.com/angular/protractor/commit/64b491034c0373755a2f34db5db1810b8d90187a))\n  fix(debugger): Fix issues when calling pause() multiple times (#3501) (#3504)\n\n- ([143c710](https://github.com/angular/protractor/commit/143c710b5612667c183eacc7e080b1e172d9f97e))\n  chore(types): webdriver typings for elements and browser (#3513)\n\n  - include node and jasmine dependency to built/index.d.ts\n  - update example and spec/install to not need @types/jasmine and @types/node to install\n  - add more selenium-webdriver to gulp task\n  - added an interface in globals for Error to include a code and stack\n  - improve webdriver typings to elements and browser\n\n- ([8ca9833](https://github.com/angular/protractor/commit/8ca98339341434fcff500accd34acfe97b5840e1))\n  fix(mocha): Wrap it.only with the selenium adapter. (#3512)\n\n  Fixes #3045. Since mocha 2.4.1, we should be wrapping global.it.only.\n\n- ([f23d027](https://github.com/angular/protractor/commit/f23d0277e8796fef4b4679043d52009149e22ce9))\n  chore(types): webdriver typings for locators (#3507)\n\n  - temporarily add typings for selenium-webdriver.d.ts\n  - include selenium-webdriver dependency to built/index.d.ts\n  - add webdriver typings to locators\n  - update example and spec/install to not use typings.json\n   - spec test updated to get the tsc test to pass\n  - includes clang formatting fixes\n\n- ([e0b151a](https://github.com/angular/protractor/commit/e0b151a8b4e40364d4b7ac369faf7c5702dcf0a0))\n  fix(launcher): Handle uncaught exceptions that are strings. (#3506)\n\n  Also clean up instances where we were throwing strings instead of Errors.\n\n# 4.0.4\n\n## Features\n\n- ([c5faf08](https://github.com/angular/protractor/commit/c5faf084b1ad16bda731140d91644487984e4600))\n  feat(browser): auto-unwrap ElementFinder into WebElement for selenium funtions (#3471)\n\n- ([a379b33](https://github.com/angular/protractor/commit/a379b33a6a472ff1a2a1d2da935e11ecb11573d1))\n  feat(plugins): support onPrepare in plugins (#3483)\n\n\n## Dependencies\n\n- ([d10bc99](https://github.com/angular/protractor/commit/d10bc99198fa1163356ff5937bd5cbed89d58f8b))\n  deps(outdated): update types/q and saucelab\n\n\n- ([4252000](https://github.com/angular/protractor/commit/4252000dd847399c5c05c561aaf71a5467f94846))\n  deps(types): typescript and typings dependencies (#3485)\n\n  - remove typings dependency, scripts, and gulp task\n  - add @types dependencies\n  - clean up globals.ts from npm publishing\n  - add declaration flag for tsc globals gulp task\n  - ignore globals.d.ts from tsconfig and .gitignore\n   closes #3484\n\n# 4.0.3\n\n## Bug fixes\n\n- ([5f690fe](https://github.com/angular/protractor/commit/5f690fe0d0526d5ed4cc482fb5915d28eedbe11e))\n  fix(export): export selenium-webdriver (#3433)\n\n  - rename to ProtractorBrowser to be able to export selenium-webdriver Browser as Browser\n  - export all selenium-webdriver items and subfolders in ptor\n  - update dependency tests for selenium\n  - add tests when protractor is installed\n closes #209227\n- ([27f7981](https://github.com/angular/protractor/commit/27f798117fc599ce369026ebbbf28b818bbbaac6))\n  fix(config): fix interface for functions such as onPrepare (#3434)\n\n  closes #3431\n\n# 4.0.2\n\n## Bug fixes\n\n- ([767d552](https://github.com/angular/protractor/commit/767d552c4ba7972085406b8b9f40fc57da1d214f))\n  fix(types): typescript global reference and type declaration fixes (#3424)\n\n# 4.0.1\n\n## Bug fixes\n\n- ([ee8ec91](https://github.com/angular/protractor/commit/ee8ec9124477ed20702d6a09a51274864867da1a))\n  fix(element): set variables to public in constructor (#3417)\n\n  closes #3414\n- ([7266902](https://github.com/angular/protractor/commit/72669029636e56911de59ec90f0d893e7406dc1d))\n  fix(sauce): sauceAgent passed incorrectly to sauce node module (#3415)\n\n  closes #3410\n- ([828e80c](https://github.com/angular/protractor/commit/828e80c2f14f3d1a4ac9b1b3b0ae0c5cd322e118))\n  fix(browserstack): mark test suite as failed/passed on BrowserStack (#3409)\n\n  closes #3256\n- ([71532f0](https://github.com/angular/protractor/commit/71532f055c720b533fbf9dab2b3100b657966da6))\n  fix(hybrid): add flag specifying that an app is an ng1/ng2 hybrid (#3403)\n\n  Needed for angular2 after rc2\n- ([2a3a0dc](https://github.com/angular/protractor/commit/2a3a0dc80edccbb72e6b2ca8c487b1eaacf15a20))\n  fix(exports): fix type exports and require('protractor') exports (#3404)\n\n  * fix(package): set main to ptor instead of browser\n  * fix(exports): fix type exports and require('protractor') exports\n- ([b2eaa29](https://github.com/angular/protractor/commit/b2eaa290bbd1d069fdaf8f25eee5eb3da611b589))\n  fix(types): output plugin typings (#3389)\n\n  * output plugin typings\n  * change ProtractorPlugin to an interface\n  * doc clean up\n   closes #3365\n- ([d2145b1](https://github.com/angular/protractor/commit/d2145b129af3e220abf656731c2491cdf29030d1))\n  fix(launcher): output uncaught exception error (#3390)\n\n  * split out message and stack to hopefully provide more information to the error\n   closes #3384\n- ([d7cf42e](https://github.com/angular/protractor/commit/d7cf42e85f0a3c9288722ee47c15d08f8b8ab115))\n  fix(protractor): export class definitions under the protractor namespace (#3393)\n\n  closes #3377\n- ([2e83dcd](https://github.com/angular/protractor/commit/2e83dcd95d11e1fd10f011ac2a058bb33a1607ff))\n  fix(types): add webdriver.promise and webdriver.WebElement to namespace (#3392)\n\n  * fix(types): add webdriver.promise and webdriver.WebElement to namespace\n   closes #3391\n\n  * fix(protractor): export class definitions under the protractor namespace\n   closes #3377\n\n- ([dcbc832](https://github.com/angular/protractor/commit/dcbc832b6abdbdeb408c1741198bb20b5b9042a2))\n  fix(types): use protractor from global namespace (#3388)\n\n\n- ([ee038f9](https://github.com/angular/protractor/commit/ee038f945844490e7e57c78a57ee2a049d5a823d))\n  fix(error message): do not crash of thrown error has made `stack` readonly (#3372)\n\n# 4.0.0\nThis version includes some big changes, so we've decided to make it version 4.0!\n\n- webdriver-manager is now it's [own NPM](https://www.npmjs.com/package/webdriver-manager), so you\ncan use it in your own projects. Protractor depends on it, though, so you shouldn't need to change\nanything. However, because it is a new dependency you'll need to rerun `webdriver-manager update`.\n\n- Protractor has TypeScript typings! See the [example](https://github.com/angular/protractor/tree/master/exampleTypescript)\nfor details on how to use TypeScript in your tests.\n\n## Breaking changes\n- ([d932ad7](https://github.com/angular/protractor/commit/d932ad7e853c0bda5d45b478a5c0271d072b6794))\n  chore(browser): rename protractor to browser and add a protractor namespace (#3214)\n\n  * added wrapDriver method from the browser.ts and ExpectedConditions to the protractor namespace\n  * imported selenium webdriver ActionSequence, Key, promise, Command, and CommandName to the\n  protractor namespace\n\n- Selenium Webdriver has deprecated getInnerHtml and getOuterHtml. You'll need to update your tests to\nnot use these methods.\n\n- Protractor node module no longer has a config.json file. This is now handled in the webdriver-manager\nnode module and the files are also downloaded to the webdriver-manager/selenium folder.\n\n## Bug fixes\n- ([d6910c1](https://github.com/angular/protractor/commit/d6910c168550da590b3d4db42f5c853e81cf83b6))\n  fix(edge): Use resetUrl about:blank for MicrosoftEdge (#3267)\n\n- ([f205518](https://github.com/angular/protractor/commit/f2055181e60fd358c1764fe716af3d64fc64810b))\n  fix(launcher): resolve promise for getMultiCapabilities (#3195)\n\n  closes #3191\n\n- ([f149bd1](https://github.com/angular/protractor/commit/f149bd1e91e749a77e8ee147fdb3881584ae6851))\n  fix(docs): Change extension for docs links to .ts (#3187)\n\n  closes #3170\n\n- ([67474e0](https://github.com/angular/protractor/commit/67474e05e73d3facead7c60150c18a2d866185c7))\n  chore(configParser): allow non-glob file pattern (#2754)\n\n  Cucumber allows line numbers to be passed in the filename in the form of\n  `features/some.feature:42`. Glob expanding that results in an empty array and nothing being passed\n  to the framework runner. This change checks for glob magic characters and only tries expanding it\n  if found. Otherwise it just passes the filename verbatim. This was previously handled in [#2445]\n  by stripping the line number first, but this is a more generic (non-cucumber) way to do it.\n   Glob needed to be upgraded for this which resulted in a weird [npm 3 bug]\n  (https://github.com/npm/npm/issues/10637). Removing the rimraf package resolved this. It was only\n  used to generate documentation which itself was removed a while ago.\n\n- ([f311320](https://github.com/angular/protractor/commit/f311320a1aed09b07d926d0c2aa586202f591b5b))\n  fix(website): edit getText JSDoc for shortDescription (#3310)\n\n  closes #3233\n- ([ba63a92](https://github.com/angular/protractor/commit/ba63a92de021193c90794c54fefae39d806fba4a))\n  fix(util): check stack exists before filtering the stack trace (#3309)\n\n  closes #3224\n\n- ([c86acd4](https://github.com/angular/protractor/commit/c86acd44bca821491558506964fe1ba8ed5b702a))\n  chore(website): fix website for items to appear properly (#3314)\n\n  - Fix order for website (see #3163. Does not include $ / $$)\n  - Replace @return with @returns so descriptions will appear\n\n- ([e9b49f2](https://github.com/angular/protractor/commit/e9b49f24f34730e0648d262be1c410a7f585703a))\n  fix(config): do not flatten capabilities (#3291)\n\n  This is no longer necessary in the latest version of selenium-webdriver. Without this change,\n  `--capabilities.chromeOptions.binary` will do nothing, for example.\n   Closes #3290\n\n## Features\n- ([78f3c64](https://github.com/angular/protractor/commit/78f3c64e6d466b44174417d4d6fbc382dbad34b1))\n  chore(exitCodes): adding exit code for browser connect errors (#3133)\n\n  * add exit code for browser connect errors\n  * add exit code for browserstack error\n  * add browser error for debug with multiple capabilities\n  * use thrown stack traces for errors (instead of creating new ones) with captureStackTrace\n  * allow for errors to suppress exit code for config parser thrown error\n\n- ([85209f4](https://github.com/angular/protractor/commit/85209f42621b8992c777263458e9fc4772968777))\n  feat(webdriver): extract webdriver-manager into a separate node module (#3068)\n\n  closes #607, #2402\n\n  * Removed the config.json. This will be managed now by webdriver-manager.\n  * Wedriver-manager downloads the file to the node_modules/webdriver-manager/selenium folder. This\n  will no longer appear in the protractor directory.\n\n- ([8316917](https://github.com/angular/protractor/commit/83169174243c7ef9767a52d86e649838aa4759f9))\n  feat(expectedConditions): adding urlIs and urlContains (#3237)\n\n  * adding urlIs and urlContains\n  * tests for UrlIs and UrlContains\n\n## Dependency Upgrades\n- ([4353069](https://github.com/angular/protractor/commit/43530693f6cafd1d3cd3407bd5d1088b51ab8101))\n  deps(outdated): Update outdated dependencies (#3251)\n\n  Updated the following outdated packages: body-parser, chai, chai-as-promised, glob, jshint, mocha,\n  request, saucelabs, typescript, typings\n\n- ([a6cae73](https://github.com/angular/protractor/commit/a6cae73e2266a20751548047f0d3721a5bd73807))\n  deps(selenium): upgrade to selenium-webdriver 2.53.2 (#3223)\n\n  closes #3173, closes #3167, closes #3058\n\n- ([128f8e1](https://github.com/angular/protractor/commit/128f8e197e28601cc93228d917a0e37d4ab29a15))\n  dep(webdrivermanager): upgrade to 10.1.0 (#3312)\n\n## Other\n- ([2a391bc](https://github.com/angular/protractor/commit/2a391bc1264bac2f9906b3cba58b944a42c692e3))\n  chore(es7): async/await example\n\n- ([bb65e5a](https://github.com/angular/protractor/commit/bb65e5aff2719eaf247d0821bdf48c512cf18602))\n  chore(website): clean up documentation (#3334)\n\n  - Remove getInnerHtml and getOuterHtml from inherited WebElement docs.\n  - Remove some of the goog.provide. Only one is required to build the website.\n\n- ([f5dc4f9](https://github.com/angular/protractor/commit/f5dc4f9f9a26699b847ed8a89ce64f332ee78d6d))\n  chore(example): add a protractor typescript example (#3323)\n\n# 3.3.0\n_The [Protractor Website](http://www.protractortest.org) API docs have been streamlined. We've also, internally, moved to using TypeScript and building down to JS! Also, the logger has been improved._\n\n## Bug Fixes\n- ([6f22d5a](https://github.com/angular/protractor/commit/6f22d5ade48f0d97990cbe69d956da122f2f8358))\n  fix(bootstrap): fix bootstrap for older versions of angular\n\n  Trying to use the debug label for window.name fails for versions of angular older than 1.2.24. See [#3115](https://github.com/angular/protractor/issues/3115)\n\n- ([bd78dfc](https://github.com/angular/protractor/commit/bd78dfc79b1435d124c994482df6879066079a4d))\n  fix(protractor): isPresent() should work with out of bounds errors (#3108)\n\n- ([88dd568](https://github.com/angular/protractor/commit/88dd568c5295234a5211a23e12666e199606e437))\n  fix(NoSuchElementError): add 'new' keyword to instantiate class\n\n  The class NoSuchElementError is called without the new keyword in the\n  `ElementArrayFinder.prototype.count` causing a `Class constructors cannot be invoked without\n  'new'`\n\n  closes #3079\n\n## Features\n- ([afdd9d7](https://github.com/angular/protractor/commit/afdd9d730c198dc97e91bb275c036a754c15140e))\n  feat(logger): improve logging methods (#3131)\n\n- ([5fa94db](https://github.com/angular/protractor/commit/5fa94db5a9a4787f480389d382eef7e636b45f81))\n  feat(exitCodes): adding exit codes for configuration file errors (#3128)\n\n- ([76861fd](https://github.com/angular/protractor/commit/76861fdc4c57c2aaa984e05e46ff9789ce750260))\n  feat(element): equals\n\n  Easier to use version of webdriver.WebElement.equals\n\n- ([582411b](https://github.com/angular/protractor/commit/582411b7ad6c0f9176b231dc51dc328b98affbdf))\n  feat(driverProvider/sauce) Add build id as a configurable option\n\n## Dependency Upgrades\n- ([b4d1664](https://github.com/angular/protractor/commit/b4d1664141b609c5e5790108e3003fe777c248ca))\n  deps(jasminewd2): bump jasminewd2 to 0.0.9\n\n- ([b6f1a63](https://github.com/angular/protractor/commit/b6f1a63da77ec88a3f487e5a099df8febe3742aa))\n  feat(protractor): Shows locator used when a timeout occurs\n\n- ([de4b33e](https://github.com/angular/protractor/commit/de4b33eaab1546d0ef3a746cfd222e80f0ec28a1))\n  feat(webdriver): Added NO_PROXY environment variable support to webdriver-manager\n\n  closes #3046\n\n# 3.2.2\n_This release is a hotfix for webdriver-manager iedriver_\n\n## Bug Fix\n- ([c6a3b5e](https://github.com/angular/protractor/commit/c6a3b5eab09d95f9d2170e4aface5559cd6b0132))\n  fix(webdriver) - fix file type for internet explorer driver file\n\n- ([d3bd170](https://github.com/angular/protractor/commit/d3bd1702040cde5b9d0a3c1578d0d8e16597224c))\n  fix(bootstrap): enable debug info before setting defer label\n\n  Note that in most cases, this should not have surfaced as an issue because the base test mock\n  modules will also try to turn on debug info.\n\n  Closes #3009\n\n# 3.2.1\n_This release is a hotfix for modules that require protractor_\n\n## Bug Fix\n- ([6e02934](https://github.com/angular/protractor/commit/6e029341a67cd985bf727285dec2ef10aafe7b6a))\n  fix(package): Update module main in package.json to use built.\n\n# 3.2.0\n\n## Features\n- ([cae175c](https://github.com/angular/protractor/commit/cae175cbe632828172e9a7065aacfe67dd51d8dd))\n  feat(plugins) Calculate skipAngularStability dynamically.\n\n  This allows plugins to turn Protractor's default synchronization on and off as needed.\n\n- ([7372267](https://github.com/angular/protractor/commit/7372267f23cc8586409f9ef914ab801c78992ccd))\n  feat(webdriver): add support for custom versions for selenium, chrome driver, and ie driver\n\n- ([1cbbe4f](https://github.com/angular/protractor/commit/1cbbe4fef5c5f2bc0923fd54c53afad71a44af6c))\n  feat(config): no globals option\n\n- ([9608201](https://github.com/angular/protractor/commit/960820120cf7df08cff8cfe15a5a9f08612c9773))\n  feat(typescript): adding typescript to protractor\n\n  Converting a 3 files over to typescript.\n\n  Adding an `npm prepublish` step that will use gulp to download the typings, transpile the files\n  with tscto the built/ directory and copy the rest of the javascript files from lib/ to the built/\n  folder.\n\n  Also adding scripts to package.json for `npm run tsc` and `npm run tsc:w` for transpiling help.\n\n- ([a4a7209](https://github.com/angular/protractor/commit/a4a72095d2f95227f1ba293ae047beab28eb761d))\n  feat(plugins): skipAngularStability\n\n## Dependency Upgrades\n- ([29627f4](https://github.com/angular/protractor/commit/29627f42bb7404f66e3a76ba3cbd85256b408fb6))\n  chore(selenium) - upgrade to selenium webdriver v 2.52.0\n\n  See the full changelog at https://github.com/SeleniumHQ/selenium/blob/master/javascript/node/selenium-webdriver/CHANGES.md#v2520\n\n## Bug Fixes\n- ([a2c7a4b](https://github.com/angular/protractor/commit/a2c7a4bf1fb2a3a509040ae8ec7737cc002b764e))\n  fix(config): Do not sort spec keys\n\n  Fixes #2928\n\n# 3.1.1\n\n## Bug Fixes\n\n- ([4db52f2](https://github.com/angular/protractor/commit/4db52f2a21171ebbc6fed0ca3df760553afc264a))\n  test(config): add test for config files using only per-capability specs\n\n  To prevent bugs like #2925 in the future.\n\n- ([edfb52f](https://github.com/angular/protractor/commit/edfb52fadccf10c34d885c37e990dea0efbb0081))\n  fix(configParser): use all the suites if no other spec sources are provided\n\n# 3.1.0\n\n## Dependency Version Upgrades\n\n- ([f699718](https://github.com/angular/protractor/commit/f699718e951c07f18c2e3e5414f92b9a33f7b19c))\n  updates(selenium): update chromedriver and selenium-standalone\n\n  Selenium-standalone update to 2.51.0. Update chromedriver to 2.21.\n\n  Chromedriver changelog: http://chromedriver.storage.googleapis.com/2.21/notes.txt Selenium\n  changelog: https://github.com/SeleniumHQ/selenium/blob/master/dotnet/CHANGELOG\n\n- ([5930d14](https://github.com/angular/protractor/commit/5930d1444aef2f053c132eb437d07f9b000d7803))\n  chore(deps): update various npm dependencies to latest stable releases\n\n## Features\n\n- ([3f3805f](https://github.com/angular/protractor/commit/3f3805f9496fb130ae01b3e3278ee1ea7684d8e7))\n  feat(attachSession): attach protractor to existing webdriver session\n\n  Attaching an existing selenium browser session to protractor rather than always creating new one.\n  The session can be passed into the config file as a string via the sessionId.\n\n- ([b693256](https://github.com/angular/protractor/commit/b6932560d66730203e0e7b0c65c80a44ad4747de))\n  feat(webdriver): allow configuration of all SeleniumServer arguments\n\n  You can now pass a complete object of configuration for the SeleniumServer class. This allows\n  enabling loopback on the selenium server.\n\n- ([148f020](https://github.com/angular/protractor/commit/148f020bf4bbd71e17326581a2f7ed6f4ff55832))\n  feat(protractor): Add support to allow Protractor to test an Angular application which has\n  debugging info disabled\n\n  Currently Angular application which for performance reasons, have debug information turn off\n  cannot be tested. This PR allows users to add the Angular debug logging flag to the Protractor\n  run.\n\n- ([aa5ceb5](https://github.com/angular/protractor/commit/aa5ceb576f0283b6591faaa95e342ef3c603c717))\n  feat(webdriver): add support for selenium webdriver proxy\n\n- ([b110ed9](https://github.com/angular/protractor/commit/b110ed92442eb8b14768c512a890bb3ceb0e4973))\n  feat(debugger): allow multiple browser.pause()\n\n  After this change, you can put multiple browser.pause() in the code. Each browser.pause() is like\n  a traditional breakpoint.\n\n  To detach from the debugger, press ^D (CTRL+D). This will continue code execution until it hits\n  the next browser.pause() or code finishes running.\n\n  This PR also fixes a number of small formatting issues.\n\n- ([fb10c5c](https://github.com/angular/protractor/commit/fb10c5caffb895e909ad91d629e2192c74c8e064))\n  feat(webdriver): Allow users to use webdriver's disableEnvironmentOverrides\n\n  Fixes #2300\n\n- ([fa0c692](https://github.com/angular/protractor/commit/fa0c692414fa98721dff80202ef95e9b3ccadebb))\n  feat(config): allow onComplete to return a promise\n\n  Closes #1944\n\n## Bug Fixes\n\n- ([f533341](https://github.com/angular/protractor/commit/f533341085921409d16d577e38ba1745c37a17b7))\n  bug(driverProvider): fix driver path generation for *nix platforms\n\n  Makes error messages better\n\n# 3.0.0\n\n_We're releasing version 3.0 with some breaking changes. In summary - Jasmine 1.3 is removed, only Jasmine 2 is now supported, old Node.JS support is dropped, and plugins now need to be explicitly required. Full details below._\n\n## Dependency Version Upgrades\n\n- ([18e1f71](https://github.com/angular/protractor/commit/18e1f71a4dd868709f4e259e05a8a196921e22be))\n  chore(webdriver): upgrade Protractor to webdriver 2.48.2\n\n- ([1f44a6e](https://github.com/angular/protractor/commit/1f44a6ef3f7ae8680a03a3cc7a7c06f75a8c7d4c))\n  chore(deps): bump chromedriver and iedriver versions IEDriver from 2.47.0 to 2.48.0\n\n  ChromeDriver from 2.19 to 2.20. Changelog:\n  http://chromedriver.storage.googleapis.com/2.20/notes.txt\n\n## Features\n\n- ([97e6703](https://github.com/angular/protractor/commit/97e6703eb0e7a5dffc1017d0a44f0dfeeb91f327))\n  feat(protractor): Add protractor.prototype.restart\n\n- ([2007f06](https://github.com/angular/protractor/commit/2007f06078b6569a2cfd9f361f17d765c07bc7f8))\n  feat(protractor): add flag to stop protractor from tracking $timeout\n\n- ([a40a4ba](https://github.com/angular/protractor/commit/a40a4ba2a509bc762f1f5937454fdbf074405f07))\n  feat(ElementArrayFinder#get): Implemented ability to pass promise as index to\n  ElementArrayFinder#get\n\n- ([a54c0e0](https://github.com/angular/protractor/commit/a54c0e0d72b9d7d1d8364ade5046e5007ff53906))\n  feat(plugins): Add config option to disable logging of warnings in console plugin\n\n  Running tests in multiple browsers ends up printing out a lot of useless warnings I'm already\n  aware of. To make skimming through logs in the case of an actual failure easier, I want to be able\n  to disable the logging of warnings in the console plugin.\n\n- ([7015010](https://github.com/angular/protractor/commit/7015010188dfb70c1e49e4f2eae19d5ba1126b34))\n  feat(driver providers): Add BrowserStack support.\n\n  Also added BrowserStack to CI\n\n## Bug Fixes\n\n- ([7cba4ec](https://github.com/angular/protractor/commit/7cba4ecf0f3915dfec33dbc04decd42857744b37))\n  fix(ng-repeat): properly detect the end of an ng-repeat-start block\n\n  Discovered while investigating issue #2365\n\n## Breaking Changes\n\n- ([2bde92b](https://github.com/angular/protractor/commit/2bde92b3e745e09ad3876932b2d187365e9aaa31))\n  chore(jasmine): remove jasmine 1.3\n\n  and update docs. Also, use jasmine 2 for running all Protractor's unit tests.\n\n  BREAKING CHANGE: Now, both jasmine and jasmine2 frameworks use jasmine 2.3. Users still using\n  jasmine version <2 will have to upgrade.\n\n- ([18e1f71](https://github.com/angular/protractor/commit/18e1f71a4dd868709f4e259e05a8a196921e22be))\n  chore(webdriver): upgrade Protractor to webdriver 2.48.2\n\n  BREAKING CHANGE: 1) Users will no longer be able to use node versions <4. 2) There is significant\n  changes to the control flow, and tests may need to be\n    modified to be compliant with the new control flow. See\n  https://github.com/SeleniumHQ/selenium/blob/master/javascript/node/selenium-webdriver/CHANGES.md\n\n- ([ac1e21e](https://github.com/angular/protractor/commit/ac1e21e7e09a773de981bf9e70b0fcd489d17a83))\n  chore(plugins): Split first party plugins into seperate repos\n\n  BREAKING CHANGE:\n\n  The Accessibility, NgHint, Timeline, and Console plugins are now located in their own separate\n  node modules. You will need to explicitly require each module you use. See https://github.com/angular/protractor/blob/master/docs/plugins.md#first-party-plugins for info for each module.\n\n- ([ddb8584](https://github.com/angular/protractor/commit/ddb8584a59343284904676ef6d8db5c1c996b900))\n  chore(cucumber): Remove cucumber from internal implementation\n\n  BREAKING CHANGE:\n\n  Cucumber has been community maintained for a while, and in order to allow it to move faster, it is no longer part of the core Protractor library. It is now published on npm under `protractor-cucumber-framework` and will require setting `frameworkPath` in your configuration file. See https://github.com/angular/protractor/blob/master/docs/frameworks.md#using-cucumber for full instructions on use now.\n\n\n# 2.5.1\n_This release is a hotfix for node 0.10 support_\n\n- ([039ffa7](https://github.com/angular/protractor/commit/039ffa7bfa291084263ae3fa944bbf21394ce9a3))\n  fix(configParser): Remove path.parse so protractor works with node < v0.12\n\n  Closes #2588\n\n# 2.5.0\n_This release contains a hotfix for windows path issues and early support for Angular2 apps_\n\n\n## Features\n- ([c5d37c2](https://github.com/angular/protractor/commit/c5d37c2abebf9aa9dd3324df93ac447529eea53b))\n  feat(lib): add useAllAngularAppRoots option\n\n  This allows waiting for all angular applications on the page, for angular2 apps only.\n\n- ([f246880](https://github.com/angular/protractor/commit/f24688030a63c9de4ce759ac9c6fab79ef773ed5))\n  feat(lib): add support for waiting for angular2\n\n  Use Angular2's testability API, if present, when waiting for stability or loading a page.\n\n  Closes #2396\n\n## Bug Fixes\n\n- ([d6aebba](https://github.com/angular/protractor/commit/d6aebbad6e9b191fef141472887637ee4318438e))\n  fix(config): Fixes absolute path parsing in windows\n\n  This allows absolute paths absolute paths in to be properly parsed in windows. This should\n  maintain the line-number feature introduced in ff88e without breakage.\n\n- ([04e5bfb](https://github.com/angular/protractor/commit/04e5bfbfcade0cbbef58213bc7b227b5db753d57))\n  chore(runner): make plugins optional param for createBrowser\n\n\n# 2.4.0\n\n_This release contains only a version update to `selenium-webdriver`, webdriver javascript bindings, and associated bug fixes._\n\n## Dependency Version Upgrades\n- ([9a202ab](https://github.com/angular/protractor/commit/9a202ab5573f24c9919639f1b75fd9cd2b383383))\n  chore(dependencies): update selenium-webdriver to 2.47.0\n\n  Along with it, update `jasminewd2` to avoid situations where the control flow gets locked up and\n  hangs.\n\n  *Potential Breaking Change*:\n\n  This is passing all existing Protractor tests, but there is a possibility that the changes to the\n  control flow will cause some test flows to hang. If this causes issues, such as tests hanging or commands executing out of order, please revisit your use of functions affecting the control flow, such as `flow.execute`.\n\n  See the selenium-webdriver changelog at https://github.com/SeleniumHQ/selenium/blob/master/javascript/node/selenium-webdriver/CHANGES.md\n\n\n## Bug Fixes\n- ([f034e01](https://github.com/angular/protractor/commit/f034e010156a85cf1826b95eb7f41f50ef5a1791))\n  fix(synchronizing): use the same control flow when ignoring sync\n\n  Previously, the order of frames and tasks on the control flow was different depending on\n  `browser.ignoreSynchronization`. This fixes the inconsistency by creating an empty task when\n  ignoreSynchronization is true.\n\n  Practically, this fixes the polling spec failing after the update to selenium-webdriver@2.47.0.\n\n\n# 2.3.0\n\n_This release contains updates which fix some issues with dependencies that had gotten stale. However, it does not yet contain an update to the selenium-webdriver dependency, because of potential breaking changes. That update will be done in a separate Protractor@2.4.0 release. See [issue 2245](https://github.com/angular/protractor/issues/2245)._\n\n## Dependency Version Upgrades\n\n- ([cfd8d00](https://github.com/angular/protractor/commit/cfd8d000c2aa1686c4a90164baf4e04976ee0587))\n  feat(webdriver): update webdriver and chromedriver to latest version\n\n  Updating Selenium standalone from 2.45.0 to 2.47.0 Updating ChromeDriver from 2.15 to 2.19\n  Selenium Changelog:\n  https://github.com/SeleniumHQ/selenium/blob/master/javascript/node/selenium-webdriver/CHANGES.md\n  ChromeDriver Changelog: http://chromedriver.storage.googleapis.com/2.19/notes.txt\n\n- ([802b20f](https://github.com/angular/protractor/commit/802b20f153f2c201d8b37378bf8feb93f649a95f))\n  chore(selenium): update selenium from 2.47.0 to 2.47.1\n\n- ([7a7aca8](https://github.com/angular/protractor/commit/7a7aca8a264ae07cbbb90e7e7469533a52276488))\n  chore(jasmine): bump jasmine version from 2.3.1 to 2.3.2\n\n- ([eab828e](https://github.com/angular/protractor/commit/eab828e12c671cbf5cdf9b09df050cc59f0dd862))\n  chore(travis): test against node 4\n\n  Test against node 4 on Travis, and remove support for node 0.10.\n\n- ([96def81](https://github.com/angular/protractor/commit/96def81dc7d364e789fc290e97aee0f898648a10))\n  chore(saucelabs): updated saucelabs dependency to 1.0.1 to support proxy\n\n## Features\n\n- ([c989a7e](https://github.com/angular/protractor/commit/c989a7eeed5a0a55d2fbd37dc7278a7967889852))\n  feat(webdriver-manager): add --ie32 commandline option\n\n  The new option allows to download the 32-bit version of the IE driver on a 64-bit system, as the\n  64-bit version has been broken for over a year now (the sendKeys() function works very slowly on\n  it).\n\n- ([ff88e96](https://github.com/angular/protractor/commit/ff88e969d55585cc4267d75c12c0cafc78a01895))\n  feat(cucumber): Allow cucumber tests containing line numbers\n\n  example:\n  ```js\n  specs: [\n     'cucumber/lib.feature:7'\n   ]\n  ```\n\n## Bug Fixes\n\n- ([1487e5a](https://github.com/angular/protractor/commit/1487e5abf69bc1540226502aacadc8b3b42b0092))\n  fix(protractor.wrapDriver): allow browser instances to work even if they were not set up through\n  the runner\n\n  Fixes #2456\n\n- ([2ff7a07](https://github.com/angular/protractor/commit/2ff7a0771b6695dc49566ed81548b3fe2cebf11c))\n  fix(Chrome Accessibility Plugin): resolving the location of AUDIT_FILE\n\n- ([f9b0a92](https://github.com/angular/protractor/commit/f9b0a92079b55384d4560fef9400bb473672ce9c))\n  fix(debugger): Fix potential debugger lockups\n\n# 2.2.0\n\n## Breaking Changes\n\n- If you use the plugin API, it has changed a lot.  See\n  [`docs/plugins.md`](docs/plugins.md) for details.  The good news is that it\n  is being taken out of beta, so it should be more stable in the future.\n\n## Dependency Version Upgrades\n\n- ([786ab05](https://github.com/angular/protractor/commit/786ab0541bff9b561b35dbbf0ffc1e9d4a788d10))\n  chore(dependencies): update request to 2.57\n\n  Closes #2205\n\n## Features\n\n- ([f2a11a7](https://github.com/angular/protractor/commit/f2a11a7369319edac0f1221a1c6ab0f9a2cc73eb))\n  feat(plugins): Changed and expanded the plugin API\n\n  * Changed the plugin API so that it is more uniform (see docs/plugins.md)\n  * Updated existing plugins to the new API\n  * Added the `onPageLoad` and `onPageSync` entry points for plugins for modifying `browser.get`\n  * Added the `waitForPromise` and `waitForCondition` entry points for plugins for modifying\n  `waitForAngular`\n  * Added tests\n\n  This closes #2126 and helps out @andresdominguez\n\n- ([aded26b](https://github.com/angular/protractor/commit/aded26bc9ee6172d6f64361207f6a8b04da09b0d))\n  feat(webdriver-manager): update download logic with streaming\n\n  Also adds a content length check to make sure the downloaded binaries are the correct size. This\n  seems to fix up some unreliable download issues that we were previously having.\n\n- ([7aeebd6](https://github.com/angular/protractor/commit/7aeebd6543d89b7d8b9474bc45651b88c5c2d396))\n  feat(plugins): reporting loaded plugins\n\n- ([6c10378](https://github.com/angular/protractor/commit/6c10378b9a4e7cae9ba491a63ae11f942c833100))\n  feat(protractor): expose pending $http and $timeout on a timeout\n\n  Now when a test times out while waiting for Angular to be stable, pending\n  $timeout and $http tasks will be reported to the console.\n\n## Bug Fixes\n\n- ([c30afdd](https://github.com/angular/protractor/commit/c30afddb80b6138fc5f648f70f2891067d8eeef4))\n  fix(test): fixed tests under npm test\n\n- ([3508335](https://github.com/angular/protractor/commit/3508335199fee5dad74c66d9ee19b8bf448c2e62))\n  fix(element): ElementArrayFinder.count was slow\n\n  The bug fix for #1903\n  (https://github.com/angular/protractor/commit/2a765c76e121de13ff86a279fe3f21cb15c17783) was\n  causing ElementArrayFinder.prototype.count to be slow because of the extranous checks that must be\n  performed. However, the checks could be delayed into the isPresent check, which will mitigate this\n  issue\n\n  fixes(#2359)\n\n- ([b147033](https://github.com/angular/protractor/commit/b14703319c0d6bb6a69b76d0fd3732e2ea1fbebc))\n  fix(by.exactRepeater): should split by \"=\"\n\n  Closes #2335\n\n- ([4c9886b](https://github.com/angular/protractor/commit/4c9886b410ea4d82098af322044a37908a112138))\n  fix(Chrome Accessibility Plugin) No error context\n\n  If your tests fail because of StaleElementReferenceError then there is no context about where this\n  is coming from. By having the failure be handled inside of the plugin then grunt can fail\n  gracefully. Additionally, this provides context about where the error originated from.\n\n  Fixes #2331\n\n- ([d15ccdc](https://github.com/angular/protractor/commit/d15ccdcf82bda29c803c3a5642896f16d7e4f938))\n  fix(phantomJS): Reset URL cannot be a data url for PhantomJS\n\n  When trying to use the lates version of Angular with PhantomJS we get a message complaining about\n  \"Detected a page unload event\". This was fixed in earlier versions of Angular, see issue #85, but\n  reappeared now. The problem is that using data urls to reset the page causes this issue, so we\n  have to do as we do with Safari and use \"about:blank\" instead\n\n- ([e6369ac](https://github.com/angular/protractor/commit/e6369ac973d58a17415ab1211050af26236c6105))\n  docs(tutorial): use Jasmine v2 in the tutorial\n\n- ([e60dc0f](https://github.com/angular/protractor/commit/e60dc0ff4fc4a6d22e877d4182605913fae5d5cb))\n  fix(browser.refresh): use timeout in milliseconds\n\n  When invoked without arguments browser.refresh used a 10-millisecond timeout, wrongly documented\n  as seconds. It now delegates timeout handling to browser.get, which defaults to\n  DEFAULT_GET_PAGE_TIMEOUT (10 seconds).\n\n- ([0262268](https://github.com/angular/protractor/commit/0262268fa43b9eefac815d986740efa07bb15818))\n  fix(cucumber): fix beforeFeature event handler call guard\n\n  Fixes the run failures reported in https://github.com/cucumber/cucumber-js/issues/342.\n\n# 2.1.0\n\n## Dependency Version Upgrades\n\n- ([25b1fa0](https://github.com/angular/protractor/commit/25b1fa052c195f6f56adcd3f5a116cb5eac238b0))\n  feat(jasmine): update jasmine dependency to 2.3.1\n\n  Updated from 2.1.1. See Jasmine's changelog at\n  https://github.com/jasmine/jasmine/tree/master/release_notes\n\n  Closes #1795. Closes #2094. Closes #1768.\n\n- ([25e3b86](https://github.com/angular/protractor/commit/25e3b8611f7333f0829aa3c315a0397891bbada7))\n  chore(chromedrivier): Update chromedriver to 2.15\n\n## Features\n\n- ([45341c9](https://github.com/angular/protractor/commit/45341c944bf60537849776c7f362ee0442b219e6))\n  feat(explorer): allow element explorer to start as a server\n\n  If element explorer is run with a port (i.e. --debuggerServerPort 1234), it will start up a server\n  that listens to command from the port instead of a repl that listens to process.stdin.\n\n- ([cf9a26f](https://github.com/angular/protractor/commit/cf9a26f1b4bdca7d928794d24738786be074619a))\n  feat(plugins): allow plugins.postTest to know what test just ran\n\n- ([0f80696](https://github.com/angular/protractor/commit/0f806964324cbeb4bceb54c5bd82c639b9f99a49))\n  feat(plugins): inline plugins\n\n- ([de49969](https://github.com/angular/protractor/commit/de499696eb88721cb073a195025ae48f59cbc905))\n  feat(debugger): make element explorer work with node 0.12.0\n\n  Node has changed its debugger significantly in 0.12.0, and these changes are necessary to get it\n  to work now.\n\n  Specifically here are the key points to take note:\n\n  * Before, the process continues running after `process._debugProcess(pid);` is called, but now,\n  the process stops.\n  > To over come this, the call to `process._debugProcess(pid)` is moved from protractor into the\n  debugger client. Secondly, because the process stops after the call, we call `reqContinue` once\n  the debugger connection is established, so that protractor continues into the first break point\n  (for backwards compatibility, we added an extra empty webdriver command so that in earlier\n  versions of node, protractor doesn't go past the first break point from the reqContinue).\n\n  * Before repl provides '(foobar\\n)' when an user inputs 'foobar'. Now it is just 'foobar\\n'.\n  > We will parse and strip away both the parenthesis and '\\n' to support all versions of node.\n\n  * Additionally (non-related to node 0.12.0), this change makes debugger processes fail fast if the\n  port is taken.\n\n- ([7b96db0](https://github.com/angular/protractor/commit/7b96db0ffb06608b12aa14765133cce42a0cad7f))\n  feat(browser.get): Return a promise that handles errors in browser.get\n\n## Bug Fixes\n\n- ([815ff5d](https://github.com/angular/protractor/commit/815ff5d092c8dda88e2a1d4c5c80f66bf20892fa))\n  fix(jasmine2): be consistent about passing assertions in output JSON\n\n  See #2051\n\n- ([e6e668c](https://github.com/angular/protractor/commit/e6e668c8d7353c8e797f730b30b996c6a6eb770f))\n  chore(jasmine): update jasminewd2 to 0.0.4\n\n  This improves the control flow schedule messages for debugging and fixes an issue with the `this`\n  variable inside tests. See https://github.com/angular/jasminewd/issues/22\n\n- ([e599cf3](https://github.com/angular/protractor/commit/e599cf301d1ff0fe705b2362fa6b4b17859ab8da))\n  fix(taskscheduler): label sharded tasks with numbers instead of letters\n\n  Letters run into a problem with a maximum of 26. See #2042\n\n- ([fda3236](https://github.com/angular/protractor/commit/fda323664db65a5c8f1590b9841fcfa53d4ab8fd))\n  fix(config): add sauceAgent property to protractor config\n\n  Closes #2040\n\n- ([fa699b8](https://github.com/angular/protractor/commit/fa699b8f1a6b807f2405d6829609797a9a3f8768))\n  fix(debugger): fix 'getControlFlowText()' broken in webdriver 2.45\n\n- ([b783dd8](https://github.com/angular/protractor/commit/b783dd865dfd16e5099a863d36d497499026b208))\n  fix(browser): remove subsequent duplicate module\n\n  browser.removeMockModule() misses next duplicate module because of iteration over an array it's\n  modifying.\n\n- ([e3d4ad1](https://github.com/angular/protractor/commit/e3d4ad164a04451fce3a57f06121355343c97f48))\n  fix(stacktrace): remove jasmine2 specific stacktraces\n\n- ([3cded9b](https://github.com/angular/protractor/commit/3cded9b8da247dc69c78a6fa0e25fe70f6c0f801))\n  fix(locators): escape query in byExactBinding\n\n  See http://stackoverflow.com/q/3561711\n\n  Closes #1918\n\n- ([e18d499](https://github.com/angular/protractor/commit/e18d4990878d70f77b29e8678eecbaab7c8cefb5))\n  fix(cucumber): process no-snippets param for cucumber framework\n\n# 2.0.0\n\n_Why is this change version 2.0? Protractor is following semver, and there's some breaking changes here._\n\n## Dependency Version Upgrades\n\n- ([34f0eeb](https://github.com/angular/protractor/commit/34f0eebd7e73b10e9b990caa06b63b6fd22b2589))\n  fix(element): update to selenium-webdriver@2.45.1 and remove element.then\n\n  This change updates the version of WebDriverJS (selenium-webdriver node module) from 2.44 to\n  2.45.1. See the full changelog at\n  https://github.com/SeleniumHQ/selenium/blob/master/javascript/node/selenium-webdriver/CHANGES.md\n\n- ([8976e75](https://github.com/angular/protractor/commit/8976e75dc1817817e6bd2dbb0b6cbc78d72035e9))\n  chore(jasmine): bump version of jasminewd2\n\n## Features\n\n- ([997937d](https://github.com/angular/protractor/commit/997937d189fb3a9fb51f1b2e4756981c8958ceba))\n  feat(console plugin): Added new console plugin\n\n- ([ef6a09d](https://github.com/angular/protractor/commit/ef6a09d798fd04124224f6ca48eb64d13eb01eff))\n  feat(webdriver-manager): allow a custom cdn for binaries\n\n  Added a cdn value for each binary to be overrided by the cli argument\n  `alternate_cdn`.\n\n## Bug Fixes\n\n- ([fb92be6](https://github.com/angular/protractor/commit/fb92be6d588e7302989bd171a064739359bb3c74))\n  fix(accessibility): improve output for long elements\n\n  Instead of just printing the first N characters of the element's template, print the first and\n  last N/2.\n\n  See #1854\n\n- ([2a765c7](https://github.com/angular/protractor/commit/2a765c76e121de13ff86a279fe3f21cb15c17783))\n  fix(element): return not present when an element disappears\n\n- ([8a3412e](https://github.com/angular/protractor/commit/8a3412e98614bb69978869b34b5b7243619f015d))\n  fix(bug): by.buttonText() should not be effected by CSS style\n\n  Closes issue #1904\n\n- ([5d23280](https://github.com/angular/protractor/commit/5d232807f1e32a9a3ba5a5e4f07ace5d535fc3cd))\n  fix(debugger): fix issue where output does not display circular dep and functions\n\n- ([ef0fbc0](https://github.com/angular/protractor/commit/ef0fbc096035bb01d136897ca463892ceca56b73))\n  fix(debugger): expose require into debugger\n\n## Breaking Changes\n\n- ([34f0eeb](https://github.com/angular/protractor/commit/34f0eebd7e73b10e9b990caa06b63b6fd22b2589))\n  fix(element): update to selenium-webdriver@2.45.1 and remove element.then\n\n  This change updates the version of WebDriverJS (selenium-webdriver node module) from 2.44 to\n  2.45.1. See the full changelog at\n  https://github.com/SeleniumHQ/selenium/blob/master/javascript/node/selenium-webdriver/CHANGES.md\n\n  To enable the update and remove confusion, this removes the `element().then` function unless there\n  is an action result. This function is completely unnecessary, because it would always resolve to\n  itself, but the removal may cause breaking changes.\n\n  Before:\n  ```js\n  element(by.css('foo')).then(function(el) {\n    return el.getText().then(...);\n  });\n  ```\n\n  After:\n  ```js\n  element(by.css('foo')).getText().then(...);\n  ```\n\n  In other words, an ElementFinder is now no longer a promise until an action has been called.\n\n  Before:\n  ```js\n  var el = element(by.css('foo'));\n\n  protractor.promise.isPromise(el); // true\n  protractor.promise.isPromise(el.click()); // true\n  ```\n\n  After:\n  ```js\n  var el = element(by.css('foo'));\n\n  protractor.promise.isPromise(el); // false\n  protractor.promise.isPromise(el.click()); // true\n  ```\n\n  Also, fixes `filter` and `map` to work with the new WebDriverJS.\n\n- ([3c04858](https://github.com/angular/protractor/commit/3c048585ac811726d6c6d493ed6d43f6a3570bee))\n  chore(config): remove deprecated `chromeOnly`\n\n  This has been replaced with `directConnect`.\n\n- Due to ([1159612](https://github.com/angular/protractor/commit/1159612ed76bb65612dbb2cc648e45928a251b10))\n\n  Due to changes in how scheduling works on the control flow, specs\n  in Jasmine1 will no longer wait for multiple commands scheduled in `onPrepare`\n  or in the global space of the test file.\n\n  Before:\n  ```js\n  onPrepare: function() {\n    browser.driver.manage().window().maximize();\n\n    // This second command will not finish before the specs start.\n    browser.get('http://juliemr.github.io/protractor-demo');\n  }\n  ```\n\n  To fix, return the last promise from onPrepare:\n\n  After:\n  ```js\n  onPrepare: function() {\n    browser.driver.manage().window().maximize();\n    return browser.get('http://juliemr.github.io/protractor-demo');\n  }\n  ```\n\n- Due to ([1159612](https://github.com/angular/protractor/commit/1159612ed76bb65612dbb2cc648e45928a251b10))\n\n  Due to changes in WebDriverJS, `wait` without a timeout will now default\n  to waiting for 0 ms instead of waiting indefinitely.\n\n  Before:\n  ```js\n  browser.wait(fn); // would wait indefinitely\n  ```\n\n  After\n  ```js\n  browser.wait(fn, 8000) // to fix, add an explicit timeout\n  ```\n\n  This will be reverted in the [next version of WebDriverJS](https://github.com/SeleniumHQ/selenium/blob/master/javascript/node/selenium-webdriver/CHANGES.md#v2460-dev).\n\n\n# 1.8.0\n\n## Dependency Version Upgrades\n\n- ([1159612](https://github.com/angular/protractor/commit/1159612ed76bb65612dbb2cc648e45928a251b10))\n  fix(webdriver): bump selenium to 2.45.0\n\n  Bump the selenium standalone binary to 2.45.0.\n\n  See https://code.google.com/p/selenium/source/browse/java/CHANGELOG for a full list of changes to\n  the selenium server.\n\n  Closes #1734\n\n## Features\n\n- ([54163dc](https://github.com/angular/protractor/commit/54163dcd22cee27cf16685fbb4d53a2712233d26))\n  feat(a11yPlugin): plugin for integrating with Chrome Accessibility Developer Tools\n\n  Also includes missing Angular map files. See plugins/accessibility/index.js for usage.\n\n- ([658902b](https://github.com/angular/protractor/commit/658902bd04bf809bde2751db79e93ae00de2f810))\n  feat(plugins): add postTest hook for plugins\n\n  Additionally, add some tests to make sure that plugins can fail properly.\n\n  Closes #1842\n\n- ([13d34c9](https://github.com/angular/protractor/commit/13d34c9192a06634827d89bf356bea33fea75747))\n  feat(a11yPlugin): add support for Tenon.io\n\n- ([5f8cffd](https://github.com/angular/protractor/commit/5f8cffd95c50ab4e7949376425f10e13747eb922))\n  feat(plugins): allow plugins to export a name for use in reporting\n\n## Bug Fixes\n\n- ([aabdd56](https://github.com/angular/protractor/commit/aabdd567ee62d0d48fad499ee5decbb5d7d6b939))\n  fix(debugger): breakpoint isn't set properly for windows\n\n- ([361ae21](https://github.com/angular/protractor/commit/361ae21ee761eb78d1e2c9b2b7d270873a28ef81))\n  fix(plugins): add a 'test' or 'fail' string to plugins\n\n  Closes #1843\n\n- ([847e739](https://github.com/angular/protractor/commit/847e73961e52caa1537df269589d9cfe6373b986))\n  fix(webdriver-manager): unzipping ie driver should overwrite old version\n\n# 1.7.0\n\n## Dependency Version Upgrades\n\n- ([2658865](https://github.com/angular/protractor/commit/2658865640d82617e69208cdb2263a2073a20156))\n  feat(webdriver): bump chromedriver to 2.14\n\n  Chromedriver 2.14 contains support for accessing elements inside the shadow DOM.\n\n## Features\n\n- ([d220ecf](https://github.com/angular/protractor/commit/d220ecf5ebc7ba023eab728d4a684e978ff77c83))\n  feat(locators): add by.deepCss selector for finding elements in the shadow dom\n\n  Usage:\n\n  ```\n  element(by.deepCss('.foo'))\n  equivalent to 'element(by.css('* /deep/ .foo'))\n  ```\n\n- ([324f69d](https://github.com/angular/protractor/commit/324f69d6aa7c23ad77f1d50e26e0a56bade40132))\n  feat(locators): add by.exactRepeater\n\n- ([eb9d567](https://github.com/angular/protractor/commit/eb9d56755fa93401502e7608c7c3d0f16927c082))\n  feat(frameworks): add support for custom frameworks\n\n  Usage:\n\n  ```js\n  exports.config = {\n    framework: 'custom',\n    frameworkPath: '/path/to/your/framework/index.js'\n  }\n  ```\n\n- ([9bc1c53](https://github.com/angular/protractor/commit/9bc1c53e40161521b0c125a810f86235c974f100))\n  feat(expectedConditions): add helper library for syncing with non-angular apps\n\n  Usage:\n\n  ```javascript\n  var EC = protractor.ExpectedConditions;\n  var button = $('#xyz');\n  var isClickable = EC.elementToBeClickable(button);\n\n  browser.get(URL); browser.wait(isClickable, 5000); //wait for an element to become clickable\n  button.click();\n  ```\n\n  You can also customize the conditions:\n\n  ```javascript\n  var urlChanged = function() {\n    return browser.getCurrentUrl().then(function(url) {\n      return url != 'http://www.angularjs.org';\n    });\n  };\n\n  // condition to wait for url to change, title to contain 'foo', and $('abc') element to contain text 'bar'\n  var condition = EC.and(urlChanged, EC.titleContains('foo'),\n      EC.textToBePresentInElement($('abc'), 'bar'));\n  $('navButton').click(); browser.wait(condition, 5000); //wait for condition to be true.\n  // do other things\n  ```\n\n- ([fb099de](https://github.com/angular/protractor/commit/fb099dedf92a64732d88401dd1b0d4d30b22650d))\n  feat(elementExplorer): Combine browser.pause with elementExplorer\n\n   * reuse logic for browser.pause for elementExplorer\n   * introduce browser.enterRepl\n   * allow customization of driver for elementExplorer\n   * fix bug where repl cannot return an ElementFinder (related #1600)\n\n    Closes #1314, #1315\n\n- ([9def5e0](https://github.com/angular/protractor/commit/9def5e0e67e031949010fed4ed47178a534c99e8))\n  feat(runner): add browser.getProcessedConfig method\n\n  Now, instances of the `browser` object have a `getProcessedConfig` method which returns a promise\n  that resolves to the current Protractor configuration object for the current runner instance. This\n  means that if multiCapabilities are being used or tests are sharded, `getProcessedConfig` will\n  return an object with the `capabilities` and `specs` property specific to the current instance.\n\n  Closes #1724\n\n## Bug Fixes\n\n- ([ccb165d](https://github.com/angular/protractor/commit/ccb165d99b69e1ae66e4c1badd2f4e04f1481e75))\n  fix(webdriver-manager): unzipping chromedriver should override old version\n\n  See #1813\n\n# 1.6.1\n\n## Bug Fixes\n\n- ([92c5d17](https://github.com/angular/protractor/commit/92c5d17844a2b4dc56c483ab4a65e2bf631175f9))\n  fix(element): test crashes when using certain locators with `fromWebElement_`\n\n  Protractor crashes when one uses locators with findElementsOverride (i.e. any custom protractor\n  locator like by.binding/repeater/etc) in map/filter/then/each/reduce\n\n# 1.6.0\n\n## Features\n\n- ([1e60a95](https://github.com/angular/protractor/commit/1e60a9504c883a95f3500eafa38e1fc11dc28c9b))\n  feat(frameworks): add jasmine2 framework\n\n  Jasmine2.x may now be used by setting `framework: jasmine2` in your config.\n  See https://github.com/angular/protractor/blob/master/docs/jasmine-upgrade.md\n\n- ([0b93003](https://github.com/angular/protractor/commit/0b930035905d1868225667de358222e51394f3ac))\n  feat(jasmine2): add 'grep' option to jasmine2\n\n  Allow users to filter the specs that they want to run using simple string match. To use this\n  feature, either: 1) specify jasmineNodeOpts.grep in your conf.js file\n   or 2) via commandline like \"protractor conf.js --grep='pattern to match'\"\n\n- ([4368842](https://github.com/angular/protractor/commit/4368842da73d4ed501df21b61daf71951e59524b))\n  feat(wddebugger): enable repl (with autocomplete) for browser.pause\n\n  See https://github.com/angular/protractor/blob/master/docs/debugging.md for\n  usage.\n\n- ([9c9ed31](https://github.com/angular/protractor/commit/9c9ed31591f5a3c552222ad7feb1ecd650973902))\n  feat(launcher): allow multicapabilities to take array of promises\n\n  Enables adding `getMultiCapabilities: function(){}` to your configuration file. The function\n  returns either multiCapabilities or a promise of a multiCapabilities that is resolved after\n  `beforeLaunch` and before driver set up. If this is specified, both capabilities and\n  multiCapabilities will be ignored.\n\n  Also allows specifying `seleniumAddress` in the capabilities/multiCapabilities object, which will\n  override the global `seleniumAddress`. This allows you to use a different `seleniumAddress` per\n  capabilities.\n\n  Breaking Changes:\n  `capabilities` can no longer be a promise. Use getMultiCapabilities if you need to return a\n  promise.\n  `seleniumAddress` can no longer be a promise. Likewise, use getMultiCapabilities.\n\n- ([1670384](https://github.com/angular/protractor/commit/167038499aacfd5def03472f9f548529b273e1e0))\n  feat(runner): allow protractor to restart browser between tests\n\n  Enables adding `restartBrowserBetweenTests: true` to your configuration file. Note that this will\n  slow down test suites considerably. Closes #1435\n\n- ([56beb24](https://github.com/angular/protractor/commit/56beb24b9473ceedc491f3ca00fbce1bb9a18f29))\n  feat(protractor): add browser.getRegisteredMockModules()\n\n  Now `browser.getRegisteredMockModules()` returns a list of the functions or strings that have\n  been registered as mock modules. For troubleshooting.\n\n  Closes #1434.\n\n- ([5a404c2](https://github.com/angular/protractor/commit/5a404c27326fdb130e5d4ac5c4704b4013c78853))\n  feat(timeline): add timeline plugin\n\n  This plugin gathers test timeline information from the protractor test process, the selenium\n  client logs (if available), and sauce labs (if available), and presents the output visually. This\n  improves understanding of where latency issues are in tests. See #674\n\n  Usage:\n\n  Add the plugin to your configuration file:\n\n  ```js\n  exports.config = {\n   plugins: [{\n     path: 'node_modules/protractor/plugins/timeline/index.js',\n\n      // Output json and html will go in this folder.\n     outdir: 'timelines',\n\n      // Optional - if sauceUser and sauceKey are specified, logs from\n     // SauceLabs will also be parsed after test invocation.\n       sauceUser: 'Jane',\n       sauceKey: 'abcdefg'\n     }],\n   // other configuration settings\n  };\n  ```\n\n- ([a9d83f7](https://github.com/angular/protractor/commit/a9d83f7ebbce1be7f7f8c2986d1bfebccff1d6f3))\n  feat(plugins): add postResults hook for plugins\n\n  Allows plugins to include a postResults function, which will be called after webdriver has been\n  quit and the environment has been torn down. This step may not modify the contents of the test\n  results object.\n\n## Dependency Version Upgrades\n\n- ([2b4ac07](https://github.com/angular/protractor/commit/2b4ac07eaccafec2ad88c05747a75268a3529759))\n  feat(webdriver): version bumps for chromedriver and supported browsers\n\n  Chromedriver to 2.13. CI browser version bumps for Chrome 39 and Firefox 34.\n\n\n## Bug Fixes\n\n- ([adf30ba](https://github.com/angular/protractor/commit/adf30ba701d2a1ec992912001723de19366bea57))\n  fix(test): use a platform agnostic way to run minijasminenode\n\n- ([50ee0b4](https://github.com/angular/protractor/commit/50ee0b4d1a1b93cedf3d099d349b937b25ee9e79))\n  fix(test): allow to run 'npm start' or 'npm test' from windows too\n\n- ([b28355d](https://github.com/angular/protractor/commit/b28355dabde4c507ac620b973104e98e96279f2a))\n  fix(cucumber): emit on cucumber scenario instead of step\n\n- ([33dcd77](https://github.com/angular/protractor/commit/33dcd777fe34c6682b64bda0adc4f3595b03394b))\n  fix(util): webdriver could deadlock\n\n  when prepare scripts containing promises are wrapped in a flow.execute\n\n- ([a877268](https://github.com/angular/protractor/commit/a877268f35cb0df8f34f60b71ad7201fef58d189))\n  fix(locators): ng-repeat-start should not return extra null element\n\n- ([d505249](https://github.com/angular/protractor/commit/d505249fff773d0eaee8b17435ab751be8fbefa6))\n  fix(waitforangular): improve error messages when waitForAngular fails\n\n  Previously, caught errors were being interpreted as an empty object, causing lots of errors such\n  as\n  'Uncaught exception: Error while waiting for Protractor to sync with the page: {}' Now the error\n  message will be displayed, and a more useful custom message will be thrown if the variable\n  'angular' is not present or the root element is not part of the ng-app.\n\n  See #1474\n\n## Breaking Changes\n\n- Due to ([9c9ed31](https://github.com/angular/protractor/commit/9c9ed31591f5a3c552222ad7feb1ecd650973902))\n  feat(launcher): allow multicapabilities to take array of promises\n\n  Breaking Changes:\n  `capabilities` can no longer be a promise. Use getMultiCapabilities if you need to return a\n  promise.\n  `seleniumAddress` can no longer be a promise. Likewise, use getMultiCapabilities.\n\n  Why is this breaking change not causing a major version bump? This feature was\n  not fully supported previously and we worked with all known users when making\n  the change.\n\n\n# 1.5.0\n\n## Features\n\n- ([55a91ea](https://github.com/angular/protractor/commit/55a91ea137395891248db148df75dd6408c3b3a2))\n  feat(launcher): reorganize launcher + add option to store test results as JSON\n\n  You may now use `config.resultJsonOutputFile` to specify a location for\n  output. See docs/referenceConf.js for more usage.\n\n- ([6a88642](https://github.com/angular/protractor/commit/6a886425a11b28fce83b6eec1f52296c4f78b7f0))\n  feat(plugins): basic tools for adding plugins\n\n- ([2572feb](https://github.com/angular/protractor/commit/2572febe2c607d459a21e2ba99a1dcece2083d2d))\n  feat(plugin): ngHint plugin\n\n  For information on usage, see `plugins/ngHintPlugin.js`. More documentation\n  on plugins will be added soon.\n\n- ([0bbfd2b](https://github.com/angular/protractor/commit/0bbfd2b6d38392938781d846ad37b5a0fd964004))\n  feat(protractor/runner): allow multiple browser in test\n\n  Closes https://github.com/angular/protractor/issues/381\n  Usage: `browser.forkNewDriverInstance`.\n\n- ([8b5ae8b](https://github.com/angular/protractor/commit/8b5ae8ba3d2b3f1de75c0add91694e39e9c591a8))\n  feat(troubleshoot): Add more information when the --troubleshoot flag is used\n\n  Improve error messages and add debug info when\n  - the configuration file cannot be parsed\n  - a webdriver session cannot be started\n  - more than one element is found using `element`\n\n  Unify format used for warnings and errors.\n\n## Bug Fixes\n\n- ([30023f2](https://github.com/angular/protractor/commit/30023f2689171bc4f51a173d9cfd62a18fe276c5))\n  fix(runner): setTestPreparer does not work\n\n  setTestPreparer would always set the testPrepare to config.onprepare during\n  `runner.run()`. This is breaking for code that relies on setTestPreparer directly.\n\n- ([47f12ba](https://github.com/angular/protractor/commit/47f12ba31754346062a1e1d20380346a1c7a0659))\n  fix(clientsidescripts): make findByCssContainingText tolerate elements with no\n  textContent/innerText\n\n- ([6a9b87c](https://github.com/angular/protractor/commit/6a9b87cac9b85cde6ae464eafe4abbba27e4fe4f))\n  fix(elementexplorer): eval always treat result as promise\n\n- ([289dbb9](https://github.com/angular/protractor/commit/289dbb91a0676add40c12bb85d134904c57dcefd))\n  fix(util): properly handle exceptions from onPrepare and onExit\n\n- ([a132fac](https://github.com/angular/protractor/commit/a132fac0afed5dc5fe8e2663e5aa1c1a90586920))\n  fix(jasmine): fix errors when iit was used\n\n  Errors were due to Jasmine not calling reportSpecStarting when iit was used, but calling\n  reportSpecResults.\n\n  Closes #1602\n\n## Breaking Changes\n\n- ([0bbfd2b](https://github.com/angular/protractor/commit/0bbfd2b6d38392938781d846ad37b5a0fd964004))\n  feat(protractor/runner): allow multiple browser in test\n\n  `protractor.getInstance()` had been unused (replaced by global `browser` in v0.12.0)\n  and is now removed.\n\n  Before:\n  ```js\n  var myBrowser2 = protractor.getInstance();\n  ```\n\n  After:\n  ```js\n  // In normal tests, just use the exported global browser\n  var myBrowser2 = browser;\n  ```\n\n  If you are creating your own instance of the Protractor class, you may still\n  use `protractor.wrapDriver` as before.\n\n\n# 1.4.0\n\n## Features\n\n- ([adef9b2](https://github.com/angular/protractor/commit/adef9b208fcba2a9d60347bda38a3fe3fac6bf50))\n  feat(runner): add a new method of getting browser drivers - directConnect\n\n  directConnect as an option on the configuration will replace chromeOnly. Now, WebDriverJS allows\n  Firefox to be used directly as well, so directConnect will work for Chrome and Firefox, and throw\n  an error if another browser is used.\n\n  This change deprecates but does not remove the chromeOnly option.\n\n- ([0626963](https://github.com/angular/protractor/commit/06269636f52f9b3a9c73beb6191ae89a7a376cfb))\n  feat(config): Option to exclude test for specific capability\n\n  Add the option to exclude spec files for a specific capability. This way you can ignore spec\n  files for one capability only. For example if the test is known to fail in the capability.\n\n  Closes #1230\n\n- ([710cad7](https://github.com/angular/protractor/commit/710cad7c5a2d838a0c4184defa1b7d4240f577f6))\n  feat(runner/frameworks): Change interface contract of the protractor runner instance so that it\n  returns a promise instead of calling a callback function\n\n- ([50f44f4](https://github.com/angular/protractor/commit/50f44f430851cbd76dbb3a41d6071198f6f479a4))\n  feat(protractor): add clone methods for ElementFinder and ElementArrayFinder\n\n- ([eedf50b](https://github.com/angular/protractor/commit/eedf50b48ca55f18e8555ce5aa64ad92b03887c8))\n  feat(launcher): add beforeLaunch and afterLaunch\n\n- ([8dd60b7](https://github.com/angular/protractor/commit/8dd60b73a3013bd29213c8d281819da6e545c7ff))\n  feat(protractor): wrap negative indices for ElementArrayFinder.get(i)\n\n  Closes #1213\n\n- ([be236e7](https://github.com/angular/protractor/commit/be236e7f44c5306df36b62bb21bb3ba940c86944))\n  feat(debugging): use custom messages when executing scripts to improve stack traces\n\n  Now, instead of asynchronous events during executeScript all being described as\n  `WebDriver.executeScript`, they have their own custom messages. The schedule shown when debugging\n  will be more informative.\n\n## Dependency Version Upgrades\n\n- ([889a5a7](https://github.com/angular/protractor/commit/889a5a70c1f980d09a615cf1e8ceaea33272ba8e))\n  feat(webdriver): version bumps for webdriver, chromedriver, webdriverJS\n\n  Upgrade to WebDriver 2.44.0 and ChromeDriver 2.12.\n\n## Bug Fixes\n\n- ([2fbaf52](https://github.com/angular/protractor/commit/2fbaf52fd59f03929e173ebf760a97de34bf91d4))\n  fix(element): use the root element only to find the testability API, not scope searches\n\n  In 9a8f45a a change was introduced which made Protractor's custom locators (by.binding, by.model,\n  etc) use config.rootElement as the root for all their searches. This meant that\n  config.rootElement was used both to specify how to get hold of Angular's injector as well as\n  where to begin searching for elements. This does not work for all cases, for example if a dialog\n  should be searched for elements but is a sibling, not a child, of ng-app.\n\n  This reverts that change, and uses document as the parent for all searches. This is consistent\n  with the behavior of the native locators by.id, by.css, and friends, which do not scope their\n  search based on config.rootElement.\n\n- ([9db5327](https://github.com/angular/protractor/commit/9db5327e4ada7eb3caa271b394bcda0ba5e8fd62))\n  fix(ElementFinder): ElementFinder should allow null as success handler. Passes the value to the\n  next in the chain.\n\n- ([0858280](https://github.com/angular/protractor/commit/0858280db156f924ef126c3aaeae6973b8d44067))\n  fix(locators): by.cssContainingText now operates on pre-transformed text\n\n  Previously, the implementation depended on the browser. Now, it will always operate on the text\n  before text-transform is applied. Closes #1217\n\n- ([1a4eea4](https://github.com/angular/protractor/commit/1a4eea4eb89362822dc86be6904c1ddfba95661e))\n  fix(elementexplorer): elementexplorer hangs when returning ElementFinder\n\n- ([f4e6b40](https://github.com/angular/protractor/commit/f4e6b40c597dc1c59dc7eccfe236abcc336a46a9))\n  fix(runner): webdriver could get into lock when there is async promise\n\n- ([cf284b9](https://github.com/angular/protractor/commit/cf284b994fb6766c8ab34d0af9b4ccf8fd866bd1))\n  fix(clientsidescripts): by.exactBinding not working because of regex typo\n\n  Closes #1441\n\n- ([9cc0f63](https://github.com/angular/protractor/commit/9cc0f6398146ed9bfc757c1efc05d1806bab1e16))\n  fix(runner): gracefully shutdown browsers after test\n\n- ([86ead2c](https://github.com/angular/protractor/commit/86ead2c5a20d474e59c3b9796b5438dc2090a6ed))\n  fix(webdriver-manager): Avoid incompatibility between request with callback and pipe.\n\n- ([7283fdf](https://github.com/angular/protractor/commit/7283fdfa1e4c69bcab6af8f28f8f1b77634a50fd))\n  fix(launcher): exit code is always 100 for sharded and 1 for nonsharded tests\n\n# 1.3.1\n\n## Bug Fixes\n\n- ([714e4e2](https://github.com/angular/protractor/commit/714e4e28ab90fb5dfeca4375a68469ef609e2722))\n  fix(locators): fix regression passing root element to locator scripts\n\n  Closes #1378\n\n# 1.3.0\n\n## Features\n\n- ([4f1fe68](https://github.com/angular/protractor/commit/4f1fe68882dedba662752e722b9e7b76bfed19b6))\n  feat(runner): Allow onCleanup to accept a file\n\n- ([548f0c0](https://github.com/angular/protractor/commit/548f0c09748502cb6ae87e602db09e6df78df348))\n  feat(webdriver): bump WebDriver to version 2.43\n\n- ([466b383](https://github.com/angular/protractor/commit/466b3831569dc28c5fc2be31fbdf96574e57c3f0))\n  feat(protractor): allow advanced features for ElementArrayFinder\n\n  changed ElementFinder as a subset of an ElementArrayFinder.\n\n  This enables actions on ElementArrayFinders, such as:\n  `element.all(by.css('.foo')).click()`\n\n  The function `filter` now returns an ElementArrayFinder, so you may also do:\n  `element.all(by.css('.foo')).filter(filterFn).click()`\n\n  or\n\n  `element.all(by.css('.foo')).filter(filterFn).last().click()`\n\n- ([7bd2dde](https://github.com/angular/protractor/commit/7bd2dde0a6fca8c8481ad68d0683b4f411d611b9))\n  chore(angular): upgrade angular to version 1.3.\n\n  This change updates Protractor's test application from 1.2.9 to 1.3.0-r0.\n\n  There is a significant behind-the-scenes change in the implementation of locating elements and\n  waiting for the page to be stable. If you are updating your application to Angular 1.3, you may\n  run into some changes you will need to make in your tests:\n\n   - `by.binding` no longer allows using the surrounding `{{}}`. Previously, these\n     were optional.\n     Before: `var el = element(by.binding('{{foo}}'))`\n     After: `var el = element(by.binding('foo'))`\n\n   - Prefixes `ng_` and `x-ng-` are no longer allowed for models. Use `ng-model`.\n\n   - `by.repeater` cannot find elements by row and column which are not children\n    of the row. For example, if your template is\n    `<div ng-repeat=\"foo in foos\">{{foo.name}}</div>`\n    Before: `var el = element(by.repeater('foo in foos').row(2).column('foo.name'))`\n    After: You may either enclose `{{foo.name}}` in a child element or simply use:\n    `var el = element(by.repeater('foo in foos').row(2))`\n\n- ([ee82f9e](https://github.com/angular/protractor/commit/ee82f9e3d0656b3c88f041f0115743352bc08941))\n  feat(webdriver-manager): ignore ssl checks with --ignore_ssl option\n\n  Allow ability to ignore SSL checks when downloading webdriver binaries. Usage: `webdriver-manager\n  update --ignore_ssl`\n\n## Bug Fixes\n\n- ([838f5a2](https://github.com/angular/protractor/commit/838f5a2b248b1539b7ece13a8ccb921eda08ee45))\n  fix(element): isPresent should not throw on chained finders\n\n  Now, `$('nonexistant').$('foo').isPresent()` will return false instead of throwing an error. This\n  change also adds tests that ensure that catching errors from promises works as expected.\n\n## Breaking Changes\n\n- ([f7c3c37](https://github.com/angular/protractor/commit/f7c3c370a239218f6143a4992b1fc4763f4cdd3d))\n  feat(webdriver): update to WebDriverJS 2.43.5\n\n  Breaking Changes WebDriverJS has introduced changes in the way that Promises are handled in\n  version 2.43. See\n  https://github.com/SeleniumHQ/selenium/blob/master/javascript/node/selenium-webdriver/CHANGES.md\n  - `webdriver.WebElement` has now been split into `webdriver.WebElementPromise`\n    and `webdriver.WebElement` so that it does not resolve to itself. This change\n    should be largely transparent to users.\n  - `WebElement.toWireValue` has been removed.\n\n\n# 1.2.0\n\n## Features\n\n- ([830f511](https://github.com/angular/protractor/commit/830f51128d1ca6c8858c99617b2752172044a752))\n  feat(protractor): allow file:// baseUrls\n\n  Modified protractor to support testing node-webkit by using string concatenation vs url.resolve()\n  when the baseUrl begins with file://\n\n  Closes #1266.\n\n- ([71b9c97](https://github.com/angular/protractor/commit/71b9c97432316a8409c7c83e28a3b1eba2d83f25))\n  feat(cucumber): process the Cucumber 'coffee' param\n\n## Bug Fixes\n\n- ([ade9a92](https://github.com/angular/protractor/commit/ade9a9277558a564e15e46266a82aeb43261d958))\n  fix(webdriver-manager): always use https for downloading webdriver binaries\n\n  This fixes issues with unzipping - see #1259\n\n- ([9a8f45a](https://github.com/angular/protractor/commit/9a8f45af49633f1637c88960ba079d7d425ca72c))\n  fix(locators): locators should use the root element provided in config\n\n  Previously, locators used 'document' as the root for their search. After this change, they will\n  use the root element provided in the config file -\n  `config.rootElement`. This will make sure behavior is correct if there are multiple angular apps\n  on one page, and also enables the getTestability path, because that requires a root element under\n  an ng-app.\n\n# 1.1.1\nThis is a minor release with no functional changes. It contains a couple\nimplementation switches that new versions of Angular will use.\n\n# 1.1.0\n\n## Features\n\n- ([316961c](https://github.com/angular/protractor/commit/316961c6a5d7410d73a2784a9622b106008b0930))\n  feat(runner/hosted): add support for promises for seleniumAddress and capabilities\n\n  Change driverProviders/hosted to resolve promise values in configuration to allow async jobs in\n  setup. Specifically, seleniumAddress, capabilities, and multiCapabilities may be promises.\n  Primarily, this would be for a network call to acquire a selenium host or to start a proxy\n  server.\n\n- ([953faf7](https://github.com/angular/protractor/commit/953faf7ebee345f686bfedff61ebcb29c5170083))\n  feat(runner): allow onPrepare functions to return a promise\n\n  If onPrepare is a function which returns a promise (or a file which exports a promise), the test\n  runner will now wait for that promise to be fulfilled before starting tests.\n\n- ([6de2e32](https://github.com/angular/protractor/commit/6de2e32328fc30b43428973457db08f34b7c1839))\n  feat(runner): Add support for async onCleanUp functions\n\n  If the onCleanUp function returns a promise, the process will allow it to resolve before exiting.\n  This is useful for performing async operations like writing to a file or calling an API at the\n  end of a test run.\n\n- ([cd575ee](https://github.com/angular/protractor/commit/cd575ee3a4d8c0930db23ad66649bf0d665ce2d6))\n  feat(sauce provider): allow for custom server addresses when running against SauceLabs.\n\n  Use `config.sauceSeleniumAddress` to connect to a custom URL for Sauce Labs.\n\n- ([1b16c26](https://github.com/angular/protractor/commit/1b16c26ac143910d3f3e92a3db4ac6ab168a8544))\n  feat(suites): allow more than one suite from the command line\n\n  Allow a comma-separated list of suites be provided on the command line, like\n  `--suite=suite1,suite2`\n\n- ([25cf88c](https://github.com/angular/protractor/commit/25cf88c29449cef6b925d19ec9cd17671f1befc9))\n  feat(ElementArrayFinder): keep a reference to the original locator\n\n## Bug Fixes\n\n- ([d15d35a](https://github.com/angular/protractor/commit/d15d35a82a5a267bb7ae9c675017f091901c019f))\n  fix issue where ElementFinder.then does not return a promise\n\n  See https://github.com/angular/protractor/issues/1152\n\n- ([9e36584](https://github.com/angular/protractor/commit/9e365848820a9a56547e884592e5ea13ef8460ea))\n  fix(webdriver-manager): removed ssl on chromedriver url for consistency\n\n  Other URLs use http, make chromedriver use this as well.\n\n- ([0da1e0c](https://github.com/angular/protractor/commit/0da1e0c65ba7a2b55ad2f5a4582e229dd876f940))\n  fix(protractor): add dummy isPending function\n\n  See https://github.com/angular/protractor/issues/1021\n\n- ([9814af1](https://github.com/angular/protractor/commit/9814af11f35973f0b4a3325fcd0d9e0d91233e61))\n  fix issue where color formatting text is leaking\n\n  See https://github.com/angular/protractor/issues/1131\n\n- ([8f1b447](https://github.com/angular/protractor/commit/8f1b4472430ec2d24f102d284e807b073d17ad81))\n  fix(launcher): fix issue where test passes on unexpected failures\n\n\n# 1.0.0\n\nNo changes from rc6.\n\n# 1.0.0-rc6\n\n## Dependency Version Upgrades\n\n- ([b6ab644](https://github.com/angular/protractor/commit/b6ab644dd8105d3f64e347342a0ae2ad2f0100fc))\n  chore(jasminewd): update to version 1.0.4\n\n  This version contains a fix for too many timeout messages.\n\n## Bug Fixes\n\n- ([0c4a70e](https://github.com/angular/protractor/commit/0c4a70e0ffbbf4373dbd9f1ab29daabe9338d57b))\n  fix(protractor) fix stack traces for WebElement errors\n\n  When angular/protractor@3c0e727136ab3d397c1a9a2bb02692d0aeb9be40 refactored `element()` into the\n  ElementFinder object, the function lost some of its error handling.  This removed references to\n  frames inside tests (`it()` blocks), making it hard to tell where the error was actually\n  occurring.\n\n  This commit fixes these problems, showing full stack traces for WebElement errors.\n\n# 1.0.0-rc5\n\n## Features\n\n- ([51a5e89](https://github.com/angular/protractor/commit/51a5e89f7dace45e61d8eab70e1ea6e9354d4de6))\n  feat(config): allow setting the get page timeout globally from the config\n\n  To change the timeout for how long a page is allowed to stall on `browser.get`, change\n  `getPageTimeout: timeout_in_millis` in the configuration. As before, you may also change the\n  timeout for one particular `get` call by using a second parameter:\n  `browser.get(url, timeout_in_sec)`\n\n## Bug Fixes\n\n- ([985ff27](https://github.com/angular/protractor/commit/985ff27c9a94cca83af8db5bf7e570d826b23838))\n  fix(configParser): load new functions from configs\n\n  Closes #1043\n\n## Breaking Changes\n\n- ([51a5e89](https://github.com/angular/protractor/commit/51a5e89f7dace45e61d8eab70e1ea6e9354d4de6))\n  feat(config): allow setting the get page timeout globally from the config\n\n  This change contains a small breaking change for consistency. Previously, the second parameter to\n  `get` changed the timeout in seconds. Now, the units are milliseconds. This is consistent with\n  all the other timeouts, as well as base JavaScript functions like setTimeout.\n\n   - before: `browser.get(url, 4)`\n  - after: `browser.get(url, 4000)`\n\n# 1.0.0-rc4\n\n## Bug Fixes\n\n- ([ab1d0be](https://github.com/angular/protractor/commit/ab1d0be8cd83b37906b9b8750dd9d85d72))\n  fix(navigation): fix using browser.get with safari driver\n\n  SafariDriver fails with data urls - see #1049. Reverting to use about:blank for now.\n\n# 1.0.0-rc3\n\n## Features\n\n- ([f0e7984](https://github.com/angular/protractor/commit/f0e7984cdd169df947142c1cff0bd1bc33ac995b))\n  feat(launcher): append capability tag for all output\n\n## Bug Fixes\n\n- ([1198dde](https://github.com/angular/protractor/commit/1198ddef9e353383819fca3a40bdaba0db22f96f))\n  fix(navigation): use empty html data urls for page resets instead of about:blank\n\n  Except on internet explorer, which does not allow data urls.\n\n  Closes #1023.\n\n# 1.0.0-rc2\n\n## Dependency Version Updates\n\n- ([e10e1a4](https://github.com/angular/protractor/commit/e10e1a4a8ae5013982f00d209e6fab1ff2b1d2a1))\n  chore(minijasminenode): update minijasminenode dependency to v1.1.0\n\n  This adds several options for the reporter, which can be included in protractor's\n  `config.jasmineNodeOpts`\n  ```js\n  // If true, output nothing to the terminal. Overrides other printing options.\n  silent: false,\n  // If true, print timestamps for failures\n  showTiming: true,\n  // Print failures in real time.\n  realtimeFailure: false\n  ```\n\n- ([be0bb00](https://github.com/angular/protractor/commit/be0bb00db6f51e381e31e80c6808a202270ecb20))\n  chore(jasminewd): update jasminewd to v1.0.3\n\n  This fixes extra logging when a timeout occurs.\n\n## Features\n\n- ([82c1d47](https://github.com/angular/protractor/commit/82c1d47462779688bb8c9ac74ba3a6ecfefb7775))\n  feat(protractor): add iteration index to ElementArrayFinder.each\n\n- ([62bcf7e](https://github.com/angular/protractor/commit/62bcf7e1c84ed720bc17435c40e1f78c50ba194c))\n  feat(webdriver-manager): minor proxy enhancements\n\n  Added error handling for request - previously, any errors coming from the request module were\n  silently swallowed.\n\n  Fixed error handling to remove empty files when a download fails for some reason.\n\n  Also evaluating both uppercase and lowercase proxy variables. Many tools use proxy variables in\n  the form https_proxy, others use HTTPS_PROXY.\n\n## Bug Fixes\n\n- ([dbf7ab5](https://github.com/angular/protractor/commit/dbf7ab5fdf7832676f37328e2ad96b9097db3f62))\n  fix(mocha): mocha globals should be re-wrapped for every new suite\n\n  Closes #523, closes #962\n\n\n# 1.0.0-rc1\n\n## Dependency Version Updates\n\n- ([0dc0421](https://github.com/angular/protractor/commit/0dc04217a6a5b772d42b1463c91d89beca7df258))\n  chore(selenium): version bumps to selenium 2.42.2\n\n## Features\n\n- ([6906c93](https://github.com/angular/protractor/commit/6906c9326a4a83d81a0d09bdc1446cccb579d699))\n  feat(webdriver-manager): use proxy for webdriver-manager\n\n- ([7d90880](https://github.com/angular/protractor/commit/7d9088025c5a1c37428ea3f1cee3dc8d3793f79e))\n  feat(locators): implement by.options\n\n- ([4e1cfe5](https://github.com/angular/protractor/commit/4e1cfe5ad0f22947d21b4ebecd7cd05e0319af1a))\n  feature(launcher): aggregate failures at the end and output message from the launcher\n\n- ([ff3d5eb](https://github.com/angular/protractor/commit/ff3d5ebc071a8806259f5da20018f2d937409455))\n  feat(locators): add toString() wrapper for this.message\n\n- ([c892c2a](https://github.com/angular/protractor/commit/c892c2a1a773cc24cc6565efe2db892776143104))\n  feat(protractor): implement reduce and filter for ElementArrayFinder\n\n  See https://github.com/angular/protractor/issues/877\n\n- ([8920028](https://github.com/angular/protractor/commit/8920028f42e683dc45e18a6dd9386bd862548010))\n  feat(pause): allow the user to specify a port to be used for debugging\n\n  Using browser.pause(portNumber) will now start the debugger on the specified port number.\n\n  Closes #956\n\n## Bug Fixes\n\n- ([f9082d0](https://github.com/angular/protractor/commit/f9082d0460c7b6465d53c37f326a0f66412c21ce))\n  fix(clientsidescripts): make exactBinding more exact\n\n  See https://github.com/angular/protractor/issues/925\n\n- ([6641c81](https://github.com/angular/protractor/commit/6641c8168d74914d4826c5968771a2aec8299833))\n  fix(launcher): report summary when specs fail\n\n- ([36e0e0a](https://github.com/angular/protractor/commit/36e0e0aaf090b0c9b5450fa59ba2da4c4237442a))\n  fix(protractor): allow exceptions from actions to be catchable\n\n  See https://github.com/angular/protractor/issues/959\n\n- ([e86eb72](https://github.com/angular/protractor/commit/e86eb726ad20737d463341afdb4c56b4d19ef414))\n  fix(protractor): removing a mock module that was never added now is a noop\n\n  It used to remove the last module - now is a noop.\n\n  Closes #764\n\n- ([bf26f76](https://github.com/angular/protractor/commit/bf26f76ba5dc99d02ea4cd7fc198dce410a9f58c))\n  fix(locators): findind elements by text should trim whitespace\n\n  WebDriver always trims whitespace from around the text of an element, so to be consistent we\n  should trim the text from button elements before doing a by.buttonText.\n\n  Closes #903, Closes #904.\n\n- ([48798b0](https://github.com/angular/protractor/commit/48798b0a8ac1fc56d0cdd80e177d67fdf592069c))\n  fix(elementexplorer): element.all hangs in interactive mode\n\n# 0.24.2\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Bug Fixes\n\n- ([a43f983](https://github.com/angular/protractor/commit/a43f98391d36cead7378d1dd26f54248f39300b7))\n  fix(protractor): make ElementFinder.then resolve to itself instead of null\n\n- ([31d42a3](https://github.com/angular/protractor/commit/31d42a3875c5b95893d8a20d00dc5365c289ff98))\n  fix(protractor): throw index-out-of-bounds\n\n  See https://github.com/angular/protractor/issues/915\n  - to make error more specific instead of propagate later\n\n# 0.24.1\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Bug Fixes\n\n- ([59af936](https://github.com/angular/protractor/commit/59af936e1ef6e21432f7876144554db145083d46))\n  fix(locators): Missing information in warning/error messages\n\n  Webdriver's built-in locators (such as `by.css()`) appeared as\n  'undefined' in protractor's messages.\n\n  For instance, if a locator matched multiple elements, protractor would print the following\n  message: 'warning: more than one element found for locator undefined- you may need to be more\n  specific'.\n\n- ([13373f5](https://github.com/angular/protractor/commit/13373f5de18690e1994b32e092105cfe3ad1753d))\n  fix(launcher): output error messages when child processes exit with error\n\n  Version 0.24.0 introduced a bug where child processes would error without outputting the error\n  message. Fix. See #902.\n\n- ([72668fe](https://github.com/angular/protractor/commit/72668fe5ebbdc8126ff16887814f763198128ab5))\n  fix(cssShortcut): fix $$ global throwing error\n\n# 0.24.0\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Features\n\n- ([7299155](https://github.com/angular/protractor/commit/729915554cfa440bda0eec8a1c4bf423f4089481))\n  feat(sauceprovider): append spec filename to capabilities.name\n\n- ([f22456d](https://github.com/angular/protractor/commit/f22456d3cf0768a577371776d716b8888a74397d))\n  refactor(jasminewd): use jasminewd from its own node module\n\n  The Jasmine Webdriver Adapter is now its own npm module. The code has been moved to\n  http://www.github.com/angular/jasminewd.\n\n  Remove the code from Protractor, and add a dependency on jasminewd@1.0.0.\n\n- ([f23565d](https://github.com/angular/protractor/commit/f23565d5db4fbb102cfec8311ce9dfaee52e9113))\n  feat(protractor): new API allowAnimations(bool) on protractor elements.\n\n- ([876a3c0](https://github.com/angular/protractor/commit/876a3c04c07a9f8d97e1edca3ec1f76e51e1a310))\n  feat(runner): support running dart2js spec files\n\n  This commit supports running Dart2JS output in NodeJS.  Officially, Dart2JS in supposed to only\n  generate code for running in a real webbrowser.  With this patch, the dart2js code can also be\n  executed in NodeJS.\n\n  Ref:\n  https://code.google.com/p/dart/source/browse/branches/bleeding_edge/dart/sdk/lib/js/dart2js/js_dart2js.dart?spec=svn32943&r=32943#487\n\n- ([8d46e21](https://github.com/angular/protractor/commit/8d46e210b91ed1521f6692a2cf35f60740c0ace6))\n  feat(runner): support sourcemaps in spec files\n\n  This feature allows folks who are generating their spec files from a different language to see\n  stack traces that use the line numbers from their sources before translation.\n\n  This commit introduces a dependency on the `source-map-support` library.\n\n  For general information about sourcemaps, refer:\n  -  http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/\n  -  https://github.com/evanw/node-source-map-support\n  -  https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/view\n\n## Bug Fixes\n\n- ([56daa54](https://github.com/angular/protractor/commit/56daa54e2e269064bd44bc05ed0bbf2c44657ca8))\n  fix(clientsidescripts): convert non-Error exceptions to Errors\n\n  If any functions called by clientSideScripts throws a an exception that doesn't inherit from\n  `Error`, the stack trace is completely unhelpful and the message is just \"unknown error.\"  This\n  commit wraps such errors into\n  `Error` instances so that we have meaningful stack traces and the correct exception message. \n  (e.g. This is the common case when running dart2js code.  This commit gives us the Dart stack\n  trace and exception message.)\n\n  In addition, I've pushed the construction of the string to install into the browser into\n  clientsidescripts.js.\n\n- ([00c6abe](https://github.com/angular/protractor/commit/00c6abef16c47868974eed8ad1a4c38494b2a504))\n  fix(element): fix WebElement.$ using the incorrect By object\n\n  Closes #852\n\n- ([0500b2c](https://github.com/angular/protractor/commit/0500b2c3b2698fe41bedf694b92aad884f3b0d0e))\n  fix(navigation): ignore unknown JS errors when looking for the URL\n\n  This should address #841\n\n  Ignoring the error and trying again has worked for all of my test cases, and the error has never\n  occurred more than once in a row.\n\n- ([c8c85e0](https://github.com/angular/protractor/commit/c8c85e0d94d7a7211b000650f01af714663611ad))\n  fix(locators): fix by.repeater finding all rows for IE\n\n  Previously, element.all(by.repeater('foo in foos')) would find non-element nodes for\n  ng-repeat-start elements, which could cause IEDriver to fall over if the test tried to get text\n  from those nodes.\n\n## Breaking Changes\n\n- ([3c0e727](https://github.com/angular/protractor/commit/3c0e727136ab3d397c1a9a2bb02692d0aeb9be40))\n  refactor(protractor): reorganize internal structure of elementFinder/webelement\n\n  - Allow chaining of actions (i.e. `element(By.x).clear().sendKeys('abc')`)\n  - first(), last(), and get(index) are not executed immediately, allowing\n      them to be placed in page objects\n  - Rework the way that elementFinder and wrappedWebElement is represented\n  - Breaking changes:\n    - element.all is chained differently\n        ```\n        Before: element(By.x).element.all(By.y)\n        Now:    element(By.x).all(By.y)\n\n        However, using element.all without chaining did not change,\n          i.e. `element.all(By.x)`\n        ```\n\n    - Changed the way for retrieving underlying webElements\n        ```\n        Before: element(By.x).find(), element(By.x).findElement(By.y),\n                  and element(By.x).findElements(By.y)\n        Now:    element(By.x).getWebElement(),\n                  element(By.x).element(By.y).getWebElement(),\n                  and element(By.x).element(By.y).getWebElements(),\n                  respectively\n        ```\n    - browser.findElement returns a raw WebElement so $, $$, and\n        evaluate will no longer be available\n\n- ([fbfc72b](https://github.com/angular/protractor/commit/fbfc72bad15667990232bb9ff1da503e03d16230))\n  feat(launcher): Add support for maxSession\n\n  - add support for maxSession and capability-specific specs\n  - cleaned up launcher (refactored out taskScheduler.js)\n  - Breaking change:\n    - changed the config to shard test files; also sharding is specific to\n  capabilities now\n      ```\n      Before: config.splitTestsBetweenCapabilities\n      Now: config.capabilities.shardTestFiles or config.multiCapabilities[index].shardTestFiles\n      ```\n\n- ([9e5d9e4](https://github.com/angular/protractor/commit/9e5d9e4abb7d0928e6092a711fda527554994be7))\n  feat(locators): remove deprecated locator APIs\n\n  This is a **breaking change**. The following deprecated Locator APIs have been removed.\n\n  - `by.input`\n  - `by.select`\n  - `by.selectedOption`\n  - `by.textarea`\n\n  `input`, `select`, and `textarea` can be replaced by `by.model`.\n\n  `element(by.selectedOption('foo'))` can be replaced by\n  `element(by.model('foo')).$('option:checked')`\n\n# 0.23.1\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Bug Fixes\n\n- ([59533d9](https://github.com/angular/protractor/commit/59533d95219796ce18f796434f8c3396ada7402c))\n  fix(navigation): revert changes to the page reset\n\n  Navigating to an empty data URL won't work for internet explorer, sadly.\n\n  Reverting to about:blank. Will watch for flakes and explore other options.\n\n\n# 0.23.0\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Features\n\n- ([b7afa87](https://github.com/angular/protractor/commit/b7afa8791ba91b83fd6613cdd9ad4c4c26d04f7e))\n  feat(addMockModule): allow additional parameters\n\n  Allow Protractor’s 'addMockModule' method to pass context to its mocks, providing an argument to\n  the script which overrides a module. Rely on the WebDriver’s 'executeScript' method.\n\n  Closes #695\n\n- ([546d41f](https://github.com/angular/protractor/commit/546d41faeb75342c875e0f9bb7702309c1aa186d))\n  feat(sauceprovider): runner now prints a link to saucelabs test URL\n\n- ([fd7fe4a](https://github.com/angular/protractor/commit/fd7fe4a8c2c6fab6678d0c1f4d5619f7a2376990))\n  feat(launcher): Add support for splitTestsBetweenCapabilities.\n\n- ([b93bf18](https://github.com/angular/protractor/commit/b93bf18feaf3c44b406a41bf87d70c95e7a900e0))\n  feat(elementFinder): keep a reference to the original locator\n\n- ([98f4ba5](https://github.com/angular/protractor/commit/98f4ba590207e3f468b3cb2a30ff6ab6ae10fea1))\n  feat(locators): add by.exactBinding\n\n## Bug Fixes\n\n- ([43ff9e5](https://github.com/angular/protractor/commit/43ff9e5e2a05b4e51d04133122d763ef4ed3f2d1))\n  fix(jasminewd): allow asynchronous callbacks for jasmine tests\n\n  Closes #728, Closes #704\n\n- ([6249efe](https://github.com/angular/protractor/commit/6249efe57109d238044394636d623e0bd93dd4ad))\n  fix(webdriver-manager): use request module instead of http\n\n  Google changed selenium-server-standalone.jar's location and is returning 302 http module does\n  not follow redirects\n\n  Closes #826\n\n- ([95093c3](https://github.com/angular/protractor/commit/95093c3011431d1a1bdd6ec4d6139a6ff1c3e491))\n  fix(configParser): don't run suite if specs are supplied\n\n- ([27a5706](https://github.com/angular/protractor/commit/27a5706a23e33bc898a5a9c7b301e79f962e3a7b))\n  fix(loading): fix timeouts with about:blank removal\n\n  As documented at https://github.com/jnicklas/capybara/pull/1215 there are sometimes issues with\n  webdriver and about:blank pages.\n\n  Switching instead to try a data url.\n\n- ([cbcdb48](https://github.com/angular/protractor/commit/cbcdb483002e51bc3cc4061fd5162627bbac7699))\n  fix(runner): add -r for each cucumber require\n\n- ([e36c32a](https://github.com/angular/protractor/commit/e36c32a975739a99f6d434e1c9844d37382bda3a))\n  fix(jasminewd): Use promise.all to combine promises and done\n\n  - Make the flow promise explicit and use promise.all to wait for both\n   promises to be fulfilled before calling the done callback\n\n- ([b5c18db](https://github.com/angular/protractor/commit/b5c18dbb746e63496809460d6ed6e2100909659e))\n  fix(drivers): prevent Sauce Labs login credentials from showing up in logs\n\n  Closes #754\n\n- ([b85af50](https://github.com/angular/protractor/commit/b85af5031241d424e2952db0eb8d7d0c8ce4475b))\n  fix(protractor): change angular-bootstrap wrapper for navigation\n\n- ([8abea3c](https://github.com/angular/protractor/commit/8abea3cbb6f054c20e4f5abcbf61813d5b671239))\n  fix(jasminewd): fix timeout for beforeEach and afterEach\n\n# 0.22.0\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Features\n\n- ([8b088fd](https://github.com/angular/protractor/commit/8b088fd6bf83696fd2ad294d8818e20894332693))\n  feat(locators): Added a By.cssContainingText locator.\n\n  This new locator find elements by css selector and inner text in response to the lack of\n  ':contains' selector.\n\n  Example: By.cssContainingText('ul .pet', 'Dog') will find all ul children with class 'pet'\n  containing the text 'Dog'.\n\n  Closes #488, Closes #709\n\n- ([54060b7](https://github.com/angular/protractor/commit/54060b7cef4eb2f4c184c360cef7c2eb25c0ff6a))\n  feat(protractor): add the browser.setLocation method to perform in-page navigation\n\n  Allow a faster way to navigate within the app. The current browser.get method forces the entire\n  app to load every time you navigate to a new page. The proposed browser.setLocation method uses\n  the same format as $location.url().\n\n  Closes #368\n\n- ([74761e8](https://github.com/angular/protractor/commit/74761e8b25395dd78e1c301ee23a7730fef36db9))\n  feat(cli): use protractor.conf.js as a default config file if none is passed\n\n  Closes #615\n\n## Chores and updates\n\n- ([b81cf5a](https://github.com/angular/protractor/commit/b81cf5a949dee25c9070491edd1eb9e9feee556f))\n  chore(webdriver): update WebDriverJS version to 2.41.0\n\n- ([a96df4d](https://github.com/angular/protractor/commit/a96df4d60a1f2e09de865bf7ca9c5c780f945239))\n  chore(minijasminenode): update to version 0.4.0.\n\n  This allows the use of `because('message')` before expectations, to give additional information\n  when a failure occurs.\n\n  It also removes warnings for Node 0.11.* users about util.print being deprecated.\n\n  Closes #377\n\n- ([6f31b56](https://github.com/angular/protractor/commit/6f31b5619de4fdb9b1b6e9a29a62dac09b781c6b))\n  chore(package): npm start now brings up the testapp\n\n  Closes #712\n\n## Bug Fixes\n\n- ([1137d12](https://github.com/angular/protractor/commit/1137d12b95435438d2b84448796f9fe32d2f87b2))\n  fix(mocha): fix it.only so that it does not double-wrap\n\n  Closes #469\n\n- ([bde56a0](https://github.com/angular/protractor/commit/bde56a0d92a79570f377490929dd1d05107f4e25))\n  fix(cli): fix --exclude command line flag\n\n  Accidentally got changed to 'excludes'. As discussed earlier, should be single to be consistent\n  with Karma.\n\n  Closes #637\n\n- ([9e426df](https://github.com/angular/protractor/commit/9e426dfd300a11f513c5d7202bbb632f4b1c41d8))\n  fix(locators): using $().$$() should return an ElementArrayFinder\n\n  Prior, $(foo).$$(bar) would return a promise which resolved to an array of WebElements. This is\n  unexpected, since $(foo).$(bar) returns an ElementFinder, and\n  element(by.css(foo)).element.all(by.css(bar)) returns an ElementArrayFinder. Fixed so things are\n  more consistent.\n\n  Closes #640\n\n- ([b67810a](https://github.com/angular/protractor/commit/b67810a08d19940cd144fea25f08af4478166231))\n  fix(webdriver-manager): do not download files if HTTP response is not 200\n\n  Closes #656\n\n- ([28912f0](https://github.com/angular/protractor/commit/28912f0a77b44cce19ef5367c92b023388f7ff10))\n  fix(webdriver-manager): fix download paths\n\n# 0.21.0\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Features\n\n- ([0c4ef69](https://github.com/angular/protractor/commit/0c4ef69c1f61a2fb41829fa6d0afae3493148eee))\n  feat(launcher): launcher outputs a final summary of how the browsers did\n\n- ([f1db8b4](https://github.com/angular/protractor/commit/f1db8b438fd154cef241895c01ed902b3f343315))\n  feat(runner): make runner an event emitter and log passes or failures from the launcher\n\n  Now, for runs with multiple capabilities, the launcher will output '.' or 'F' for each pass or\n  fail instead of just '.' for every chunk of data. TODO - complete the event emitter API for the\n  Cucumber runner.\n\n- ([f9c4391](https://github.com/angular/protractor/commit/f9c43910021095e1bee1d1074e8788f4b0aee145))\n  feat(cli+config): allow defining multiple test suites in the config and running them separately\n  from the command line.\n\n- ([06bd573](https://github.com/angular/protractor/commit/06bd573cbc2471c719a8504f906468fb672d4097))\n  feat(pause): add the browser.pause method to enter a webdriver-specific debugger\n\n  Warning: this is still beta, there may be issues. Usage: In test code, insert a `browser.pause()`\n  statement. This will stop the test at that point in the webdriver control flow. No need to change\n  the command line you use to start the test. Once paused, you can step forward, pausing before\n  each webdriver command, and interact with the browser. Exit the debugger to continue the tests.\n\n## Bug Fixes\n\n- ([43aff83](https://github.com/angular/protractor/commit/43aff830bb74aa97fc4704f3aea9ef38feaee1b6))\n  fix(pageload): Changing how `about:blank` unload waits Also changing `executeScript` script\n  comment from `//` to `/**/` format. These two small changes should not affect functionality but\n  make Protractor work with Selendroid.\n\n- ([1334662](https://github.com/angular/protractor/commit/1334662905d8d6b642a294fbf1e97ec3bc371084))\n  fix(locators): Improve custom locators message\n\n  Increase readability of custom locator message by displaying each argument instead of the\n  arguments object.\n\n- ([c9dbbaa](https://github.com/angular/protractor/commit/c9dbbaa94e2b4378bcc2db580dcad637b609a868))\n  refactor(launcher): skip the child process if only one capability is requested\n\n  Closes #603\n\n- ([26d67a2](https://github.com/angular/protractor/commit/26d67a29a8a12aa52331a1ec4ae8013cf63257f2))\n  fix(launcher): launcher should report a failure when only one capability is running\n\n- ([9530a0c](https://github.com/angular/protractor/commit/9530a0cab2791cb0350f81eae3f619d68fb620c3))\n  (fix): Convert test.sh to test.js\n\n  This would enable the tests to be run on both Linux and Windows.\n\n- ([6d85ab4](https://github.com/angular/protractor/commit/6d85ab4b9f3b5824db3307df5aca77a0720dc2e6))\n  fix(jasminewd): display stack traces in correct order and with WebElement method failure details\n\n- ([8964ac9](https://github.com/angular/protractor/commit/8964ac97cb994eb6cf7cf7ce77b7eb40882e852b))\n  fix(test): Fixed path of configuration file to pass on windows\n\n- ([99bda1a](https://github.com/angular/protractor/commit/99bda1aa732288f74126c9a77c48dd7cff63531a))\n  fix(waitForAngular): when timeout overflows, at least pass the negative to error messages\n\n  Closes #622\n\n- ([4fd060a](https://github.com/angular/protractor/commit/4fd060a38faa1f938f880fa52746e1a481a9122d))\n  fix (element): Allow ElementFinder to be passed to actions directly.\n\n  Previously, do to an action such as drag and drop, one would have to use\n  `element(by.foo).find()`. Now, just passing `element(by.foo)` works. For example:\n\n  ```javascript\n  browser.actions().doubleClick(element(by.id('mybutton'))).perform();\n  ```\n\n- ([b2a4ffc](https://github.com/angular/protractor/commit/b2a4ffced58964826125ea00705e6e257cdb588b))\n  fix(configParser): always return \"this\" from addFileConfig\n\n# 0.20.1\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Bug Fixes\n- ([17de697](https://github.com/angular/protractor/commit/17de697fe9f64e238a8df0fbc6358b8e578e45f2\n  fix(debug): make new debug work on windows too\n\n  Closes #580\n\n# 0.20.0\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Features\n\n- ([220d793](https://github.com/angular/protractor/commit/220d79372fb93d3b58c5131188b24e48be8176ab)), ([6603a7e](https://github.com/angular/protractor/commit/6603a7e964c8f1632db4790081a71648360cf1f9))\n  chore(webdriver): update selenium version to 2.40.0 and download location\n\n- ([ad5f3aa](https://github.com/angular/protractor/commit/ad5f3aa77fc3429fcf83f825a14fdb43fd7cc8a7))\n  feat(jasminewd): allow custom matchers to return promises\n\n  Allow custom jasmine matchers to return a promise which resolves to a boolean and match against\n  the resolution of the promise\n\n- ([41feaca](https://github.com/angular/protractor/commit/41feaca58c81fbd578c77424abf745acaf26f84f))\n  feat(framework.cucumber): Allow multiple tags on cucumber tests.\n\n  Motivation: Support for multiple tags on the cucumber test execution, to be able to filter with\n  more complex expressions the scenarios to run.\n\n  How to use:\n  ```\n  cucumberOpts: {\n     tags: '@dev'\n  }\n  ```\n\n  or\n\n  ```\n  cucumberOpts: {\n     tags: ['@dev', '~@ignore']\n  }\n  ```\n\n  More information on tags: https://github.com/cucumber/cucumber/wiki/Tags\n\n## Bug Fixes\n\n- ([2ca6541](https://github.com/angular/protractor/commit/2ca654114a2bf937313ff027583308f87e909892))\n  fix(debug): make protractor debug work in the new runner/launcher world\n\n  Closes #552\n\n- ([a68627b](https://github.com/angular/protractor/commit/a68627b3581c0551e04460682cfc13f8f91be366))\n  fix(launcher): command line args should be passed as-is to the runner\n\n  This allows users to continue to use optimist (or other process.argv) processing within their\n  tests and grab values from the command line.\n\n  Closes #571.\n\n- ([767c306](https://github.com/angular/protractor/commit/767c306102956ba6015cfe3998affb7e8430f259)), ([02defe3](https://github.com/angular/protractor/commit/02defe360dce41ee6841df9012166d249acfeca0))\n  fix(jasminewd): include full pre-async-call stack trace in expectation failure message\n\n- ([b6df2cf](https://github.com/angular/protractor/commit/b6df2cfcfd35b31e2e473604b6df9add744c6c2d))\n  fix(configParser): load coffee and LiveScript for child processes\n\n  Without loading coffee in configParser.js, child processes which try and load a coffeescript\n  config file do not have coffee registered with node's required, and child tests fail.\n\n  Fixes an issue with using coffeescript config files.\n\n- ([64bee25](https://github.com/angular/protractor/commit/64bee252f6df52f9243c0f5d7e40f39bf5407134))\n  fix(locators): add locator with multiple arguments\n\n  When using a custom locator with multiple arguments, only the first argument was used when\n  calling `webdriver.findElements`.\n\n\n- ([87b0c7f](https://github.com/angular/protractor/commit/87b0c7f2ecc8befa4fa1ebd5d8238c811a869aff))\n  fix(debug): display error message when runner fails\n\n# 0.19.0\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Features\n- ([77393d0](https://github.com/angular/protractor/commit/77393d08343ef16ddc2b8042e187c9d68fe7bf2f)), ([6848180](https://github.com/angular/protractor/commit/68481801d506941ebf00fab71f87be510c7a87ba)), ([cca82ca](https://github.com/angular/protractor/commit/cca82caab6ae444b368eebe040a69967d774737e))\n  feat(runner/launcher): major runner updates to allow multiple capabilities\n\n  Adding simultaneous runner capability (grid-style), refactoring launch/runner init system, and\n  abstracting out configParser module.\n\n- ([642de06](https://github.com/angular/protractor/commit/642de06e8bbabf82c7b8e0a64a280df5c4daf01c))\n  feat(protractor): add removeMockModule method\n\n- ([88c339f](https://github.com/angular/protractor/commit/88c339fc1d392717a0a5b8265806934b40158c5f))\n  feat(runner): add adapter for cucumber.js\n\n  Conflicts:\n  lib/runner.js\n\n## Bug Fixes\n- ([8924bbc](https://github.com/angular/protractor/commit/8924bbca9e8f04073a29534bf16b0867a1ede7a0))\n  fix(cli): convert capabilities arguments to dot-notation for WebDriver compatibility\n\n- ([a96d32f](https://github.com/angular/protractor/commit/a96d32f44a92ba9447fc843bc0aca7b91b777635))\n  fix(webdriver-manager): upcase in IE download url\n\n  The url for the Win32 version of the IEDriverServer is apparently case sensitive: _win32_ vs\n  _Win32_\n\n## Breaking Changes\n- ([05eb42b](https://github.com/angular/protractor/commit/05eb42bb482c7cb36b48af1a86210afc442aa112))\n  refactor(locators): moves scope in locators to last argument\n\n  scope defaults to document, and is an optional argument so now be moved to the end. Came up from\n  debugging and trying to use window.clientSideScripts.findInputs('username'); which failed.\n  Refactored to match original intent.\n\n  BREAKING CHANGE: anything relying on clientsidescripts should no longer pass\n     element scope as first argument.\n\n      Before:\n\n      window.clientSideScripts.findInputs(document, 'username');\n\n      After:\n\n      window.clientSideScripts.findInputs('username', document);\n      // or simply\n      window.clientSideScripts.findInputs('username');\n\n    Also, any custom locators using addLocator will now break since the\n    arguments order has changed. To migrate the code follow the example below:\n\n      Before:\n\n      var findMenuItem = function() {\n        var domScope = arguments[0];\n        var myArg = arguments[1];\n        // balh blah blah\n      };\n      by.addLocator('menuItem', findMenuItem);\n\n      After:\n\n      var findMenuItem = function() {\n        var myArg = arguments[0];\n        var domScope = arguments[1];\n        // balh blah blah\n      };\n      by.addLocator('menuItem', findMenuItem);\n\n  Closes #497\n\n\n# 0.18.1\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Bug Fixes\n\n- ([a79aa73](https://github.com/angular/protractor/commit/a79aa73df5df598ccad695af882d23ddaac2c2d9))\n  fix(cli): specs was being processed as a string, not a list\n\n  Fixes #495\n\n# 0.18.0\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Features\n\n- ([e3b1e7c](https://github.com/angular/protractor/commit/e3b1e7cec7af35f2e245ca64e4f94227ecaa1c57))\n  feat(config): add option to exclude specs based on file patterns\n\n  The config now accepts `exclude`, an array of patterns to exclude.\n\n- ([88a1e58](https://github.com/angular/protractor/commit/88a1e587a40f0e6d978b20fe55160a18e2855493))\n  Feat(clientSideScripts): Add by.buttonText, by.partialButtonText\n\n  Adds client side JS implementations of by.buttonText and by.partialButtonText, enabling element\n  lookup based on innerText.\n\n  Closes #452\n\n- ([8d29c93](https://github.com/angular/protractor/commit/8d29c939766f044d910401e60834769cf8e5e44b))\n  feat(config): allow LiveScript configuration files\n\n## Bug Fixes\n\n- ([d06d931](https://github.com/angular/protractor/commit/d06d931e1cb2c2bd38c2c50965a6f78690bdc336))\n  fix(timeouts): fix an obscure cause of firefox timeouts\n\n  Fixes #493\n\n- ([de39e50](https://github.com/angular/protractor/commit/de39e5077d09daaeb885767e968a5cef78c9cac7))\n  fix(jasminewd): support multi-argument matchers\n\n  Implement support for multi-argument matchers in promise wrapper.\n\n  Closes #477\n\n- ([11c4210](https://github.com/angular/protractor/commit/11c4210fe740771707d5421a4940bdce43d3d33e))\n  fix(testForAngular): add a message when page load does not complete in time\n\n- ([6ae6261](https://github.com/angular/protractor/commit/6ae626158ee0610b70501af5d57ad4ff379c5ead))\n  refactor(waitForAngular): improve error messages when timeouts occur\n\n- ([5dd93c2](https://github.com/angular/protractor/commit/5dd93c2397a401011e16271f6472c72037c871b6))\n  fix(config): allow CoffeeScript 1.7 to be used\n\n  CoffeeScript now requires a register call to be made.\n\n- ([10aec0f](https://github.com/angular/protractor/commit/10aec0ff212987bfdb9ab4011e6cb2f9c646fca2))\n  fix(pageload): increase wait timeout\n\n  The 300 ms wait caused problems when testing IE on Sauce Labs. It seems way too short.\n  \"browser.get()\" invariably timed out. Increasing it solved our problem.\n\n\n# 0.17.0\n\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Features\n\n- ([cc4f7b5](https://github.com/angular/protractor/commit/cc4f7b55e1fe46fcef1b8c3ca39d702a32ee6d82)), ([8348803](https://github.com/angular/protractor/commit/834880368115ecade154b3a090e06159667c0c2d))\n  feat(element): allow chaining of element finders with element().element()...\n\n  Chaining calls to element will now build a scoped element finder. No webdriver functions will be\n  called until a method (such as getText) is called on the final element. Example:\n\n      var elem = element(by.id('outer')).element(by.css('inner'));\n     browser.get('myPage');\n     elem.click();\n\n  Closes #340.\n\n- ([088a581](https://github.com/angular/protractor/commit/088a58150f992a6520da983fc461fec4eac1a0ed))\n  feat(runner): add a callback for when the tests are done\n\n  Add an onCleanUp callback to be able to hook into when all the tests have been run.\n\n  Conflicts:\n  referenceConf.js\n\n- ([66c4774](https://github.com/angular/protractor/commit/66c4774aa18d94d4da81c101b82db4a748cf69a4))\n  feat(runner): add mocha options to config file\n\n  change lib/runner to allow setting mocha options from config.\n\n- ([092fe1f](https://github.com/angular/protractor/commit/092fe1fc1e7d1b58b786870ff1ce33f95e652d78)), ([3151ca7](https://github.com/angular/protractor/commit/3151ca7daaeeec9f537561b31c6dfd42c678f7bb))\n  feat(locators): Add map() function to element.all\n\n  Added a map function to element.all to apply a function to each element and return the result of\n  the transformation.\n\n  Resolve promises if there is an object that contains multiple promises. Added index as a second\n  argument to the map function callback.\n\n  Closes #392\n\n- ([7259614](https://github.com/angular/protractor/commit/7259614a326802b8e7a906346bd9830b92e1514d)), ([0257b5f](https://github.com/angular/protractor/commit/0257b5f225052ab0a075d96811dd56961f9278ae))\n  feat(config): allow CoffeeScript configuration files\n\n  Require CoffeeScript in the cli file to enable CS configuration and spec files.\n\n  Possibly fixes #38\n\n- ([e7d9e08](https://github.com/angular/protractor/commit/e7d9e081cdc7fcf100e0346b1dcf0f7fdad7d889))\n  feat(global): export By (== by) on the global for use with coffeescript (or others who prefer it)\n\n## Bug Fixes\n\n- ([a0bd84b](https://github.com/angular/protractor/commit/a0bd84b9a28ec92eccd2784f8b849388985a4480))\n  fix(pageload): add a wait during protractor.get() to solve unload issues\n\n  Some systems would not wait for the browser unload event to finish before beginning the\n  asynchronous script execution.\n\n  Closes #406. Closes #85.\n\n- ([4b053eb](https://github.com/angular/protractor/commit/4b053ebe587d51562d77ca512848be28195ae0cc))\n  fix(runner): only run selenium with spec files\n\n  Only setup Selenium if there are actual spec files passed in\n\n- ([8e096b9](https://github.com/angular/protractor/commit/8e096b9a91af9c37ab4bf84e100568544351efc8))\n  fix(Protractor.prototype.get): resolve `baseUrl` before ignoring synchronization\n\n  Fixes issues where setting `ignoreSynchronization = true` ignores the value of `baseUrl`\n  entirely.\n\n# 0.16.1\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n_Note: 0.16.0 was released as a canary - no changelog for it._\n\n## Features\n\n- ([a75fa04](https://github.com/angular/protractor/commit/a75fa04a70f64e0da29b9a0a9100bd60d9ebf93f))\n  docs(readme): add the travis status widget\n\n- ([478c00a](https://github.com/angular/protractor/commit/478c00a01dc9b93de68983b6ef2dfa55f0b42649))\n  feat(runner): add beta support for using mocha as your test framework\n\n  This change adds limited support for using mocha as the test framework instead of jasmine. Make\n  the switch by using `--framework=mocha` on the command line or adding `framework: 'mocha'` to the\n  config. Tests will be run using the BDD interface. The interface is adapted so that tests run\n  asynchronously without needing to call `done()`.\n\n  Note that there is currently no support for an assertion framework, so you will need to require\n  whichever assertion framework you prefer. This means there is no adapter to make the assertions\n  unwrap promises, so you will need to resolve promises yourself and run the assertions afterwards.\n\n- ([3731abf](https://github.com/angular/protractor/commit/3731abf901c4278b4470336c3a58765161b08bcc))\n  feat(webdriver-manager): add seleniumPort command line option\n\n  Added seleniumPort command line option so that the standalone selenium server can be started with\n  the supplied port number as opposed to the default port 4444.\n  ```\n  $ webdriver-manager start --seleniumPort 4443\n  ```\n\n## Bug Fixes\n\n- ([bc18c42](https://github.com/angular/protractor/commit/bc18c42dab6207d111f88ea1f1deefb9bcc28f23))\n  chore(config): saucelabs requires tunnel identifier to be a string\n\n## Breaking Changes\n\n - ([478c00a](https://github.com/angular/protractor/commit/478c00a01dc9b93de68983b6ef2dfa55f0b42649))\n  feat(runner): add beta support for using mocha as your test framework\n\n  To allow the user to customize their framework, the protractor runner will now wait\n  until just before `onPrepare` to load the framework. This means that `jasmine` will\n  not be available in global until `onPrepare`. For example, this means that requiring\n  the jasmine-reporters module must be done inside onPrepare, since that module expects\n  jasmine to be available at the time it is loaded.\n\n\n# 0.15.0\n\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Features\n\n- ([f8d0291](https://github.com/angular/protractor/commit/f8d02910340f54af92a8ed5fdd067fa03ca2cef8)) chore(version): update the version of dependency minijasminenode\n\n  This is notable because in the newer 0.2.6 version of minijasminenode,\n  ddescribe and iit are supported. These should be available after running\n  an 'npm update'.\n\n- ([6165023](https://github.com/angular/protractor/commit/6165023a9593f4f69fe342761b8b2d75923baf7a)) feat(runner): return a promise from runner.runOnce\n\n  In some cases knowing when the runner has finished is a requirement (e.g. an async grunt task).\n\n- ([d44ef01](https://github.com/angular/protractor/commit/d44ef01c64023b4e3a24a9959740676b691f6074)) feat(debugging): remove webdriver lines from stacktraces by default to improve readability\n\n- ([33fa4a4](https://github.com/angular/protractor/commit/33fa4a43acfbe87f3a4d4c84fa93c5c20b3cca0c)) feat(locators): by model works for anything with a model, not just input\n\n  Notably, by.model will now find selects and textareas.\n\n  Closes #321.\n\n- ([238bb74](https://github.com/angular/protractor/commit/238bb7429572f9a9f6620bf1317690f1ac825960)) feat(ignoresync): ignoreSynchronization now affects the behavior of browser.get\n\n  Now, when ignoring synchronization, calls to browser.get are equivalent to calling\n  browser.driver.get.\n\n  Closes #306\n\n- ([30c0ceb](https://github.com/angular/protractor/commit/30c0ceb3e2745d3bcc549f4d4963d9fade132e71)) feat(element) element.all exports an 'each' method\n\n  Usage:\n  ```\n  element.all(by.model('foo')).each(function(webElement) {\n    // Do stuff with webElement.\n  });\n  ```\n  Closes #298\n\n- ([6a73a25](https://github.com/angular/protractor/commit/6a73a25c61a72ef991a604eadae010c90a157266)) feat(by.repeat) by.repeat support for multi ng-repeat\n\n  Make by.repeat (and its column and row friends) work with ng-repeat-start\n  and ng-repeat-end elements.\n\n  Closes #366. Closes #182.\n\n## Bug Fixes\n\n- ([50d6fde](https://github.com/angular/protractor/commit/50d6fde25148e24d7ef22be371b04333cdf61e50)) fix(clientSideScripts): bind-template directive shouldn't break bind locators\n\n  Fix \"UnknownError: angular.element(...).data(...).$binding[0] is\n  undefined\" error raised when trying to use \"by.binding\" locator in any\n  element of a page that contains at least one \"bind-template\" directive.\n\n- ([f8c606b](https://github.com/angular/protractor/commit/f8c606bae7b2f414a67b6349f841881132d9cc97)) fix(webdriver-manager): make sure selenium standalone shuts down nicely\n\n  This addresses selenium server shutdown in two ways\n   - the node process will stay open until selenium has exited\n   - if the user inputs to STDIN (e.g. press space) selenium will shut down gracefully\n\n- ([e98f71e](https://github.com/angular/protractor/commit/e98f71ebd7778d5c77c41bbecc73e31f1aeca177)) fix(webdriver-manager): fix IEDriver install and running via windows\n\n  Changed the binaries.ie.url function to return the correct URL for the IEDriverServer.\n  Created the zip object in the win32 section to be able to decompress IEDriverServer.\n  Added a function to normalize a command across OS and spawn it. It allows start the webdriver in win32.\n\n  Seen here:\n  https://github.com/yeoman/generator/blob/master/lib/actions/spawn_command.js\n\n\n# 0.14.0\n\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Features\n\n- ([c579a1a](https://github.com/angular/protractor/commit/c579a1a01bae6798a87a5ca8915417775e1b6fb2)), ([f54fd5d](https://github.com/angular/protractor/commit/f54fd5d7c3caa8df319a0115086bb4db8443d856)) feat(webdriver-manager): redo the script to run and install selenium/webdriver\n\n  Breaking Change.\n  As outlined in Issue #296, redoing the way the selenium/webdriver\n  install and run helper scripts work. Now, the 'webdriver-manager' script\n  will be available either locally or globally (depending on how protractor\n  was installed). It replaced install_selenium_standalone and the 'start' script\n  that was provided after install. Run `webdriver-manager update` to download\n  new versions of selected webdriver binaries. Run `webdriver-manager start`\n  to start the standalone server. In addition, this fixes issues with running\n  the server starter in Windows, and allows automated downloading of the IEDriver.\n\n  Thanks to kurthong and vipper for their PRs with windows fixes, which were\n  very useful in preparing this.\n\n- ([a69ebc3](https://github.com/angular/protractor/commit/a69ebc3b783fb7bf42877a658498de90d3d196c3)) feat(runner): use selenium and chromedriver from the default location if nothing else is specified\n\n## Bug Fixes\n\n- ([1fa090c](https://github.com/angular/protractor/commit/1fa090c656cbab55bdbfb101b503b53811b50dff)) fix(runner): merge should override entire arrays, not just parts of them\n\n  Closes #304\n\n- ([a2afb4d](https://github.com/angular/protractor/commit/a2afb4d8399ba980674c79138dd98efb683e9ab9)) fix(element): element.all.get and element.all.first/last should wrap web elements\n\n  Closes #307\n\n- ([f3be172](https://github.com/angular/protractor/commit/f3be1727cf95dea50b597d20c6510e62a605dee2)) fix(runner): running with chromeOnly should try to find chromedriver with .exe extension\n\n  Closes #283\n\n## Breaking Changes\n\n- ([c579a1a](https://github.com/angular/protractor/commit/c579a1a01bae6798a87a5ca8915417775e1b6fb2)) feat(webdriver-manager): redo the script to run and install selenium/webdriver\n\n  Breaking Change.\n  Your old selenium/start script will continue to work, but install_selenium_standalone no longer exists.\n  To do a clean update, remove the selenium folder. Then run\n  `webdriver-manager update`\n\n- ([a1c91a2](https://github.com/angular/protractor/commit/a1c91a29af5c1e1f35744462ca16ef4b33ad6c48)) fix(config): Make all file paths in config files relative to the config file itself\n\n  Breaking Change\n  Previously, onPrepare and specs were relative to the location of the config,\n  but seleniumServerJar and chromeDriver were relative to the cwd when the\n  test was called. If you were calling the tests from somewhere other than\n  the same directory as the config location, you will need to change the paths of\n  seleniumServerJar and/or chromeDriver.  Closes #222.\n\n\n# 0.13.0\n\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Features\n\n- ([ce5f494](https://github.com/angular/protractor/commit/ce5f494289c3750b84c6783339a14342a1b74f3d)) feat(element): element.all now has 'first' and 'last' methods\n\n- ([ef61662](https://github.com/angular/protractor/commit/ef6166232186b3385769f63430819a722052cc44)) feat(runner): allow bypassing the selenium standalone server if running only chrome\n\n  Using the config option `chromeOnly` now enables running ChromeDriver directly,\n  without going through the Selenium Standalone. The chromedriver binary should be\n  available in your PATH, or should be specified with the config option\n  `chromeDriver`.\n\n- ([76c094a](https://github.com/angular/protractor/commit/76c094a3fa69511b0311011b0ef2c7343b8e655b)) feat(getLocationAbsUrl) - allows current url to be obtained on IE (and Chrome/Firefox)\n\n- ([6a1c918](https://github.com/angular/protractor/commit/6a1c91848858453d0af712588b51c0bdaa0c9445)) feat(runner): add error message for bad jar path\n\n- ([98bce7e](https://github.com/angular/protractor/commit/98bce7e2ac1e659faf2d8727e1fda210b796525e)) feat(locators): add the ability to add custom element locators with by.addLocator\n\n  Custom locators can now be added using by.addLocator(name, script), where\n  script is a self-contained snippet to be executed on the browser which returns\n  an array of elements. Closes #236.\n\n- ([c7bcc20](https://github.com/angular/protractor/commit/c7bcc20c07416237f69f7934d257b5ba5bfe8c1f)) chore(angular): update to angular 1.2\n\n\n## Bug Fixes\n\n- ([a24eeee](https://github.com/angular/protractor/commit/a24eeee4f08e973ffcecd107b6610ce1c2c5e3f6)) fix(runner): do not error out if only one spec pattern does not match any files\n\n  Previously, the runner would throw an error if any one of the spec patterns did not\n  match any files. Now it logs a warning in that case, and errors out only if there\n  are no found files in any spec patterns. Closes #260\n\n- ([f3b3fdb](https://github.com/angular/protractor/commit/f3b3fdbcbc8fe4f3c5915ef0f6eb7c89e339a62e)) fix(element): fix an error where all.then() wasn't calling callbacks.\n\n  Closes #267\n\n- ([137d804](https://github.com/angular/protractor/commit/137d8040778215fd841654d3ca465b71f8719ea5)) fix(jasminewd): patched matcher should understand 'not'\n\n  Closes #139.\n\n\n# 0.12.1\n\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Minor features\n\n- ([201b59c](https://github.com/angular/protractor/commit/201b59c2e728c56d2a88a1167ed3007b22ab9034)) feat(jasminewd): better error messaging when expect is called with a WebElement\n\n- ([d383770](https://github.com/angular/protractor/commit/d383770499da4b08b74ad53c20ffa288147f94e9)) feat(clientsidescripts): better error reporting from testForAngular and waitForAngular\n\n## Bug fixes\n\n- ([8580c0c](https://github.com/angular/protractor/commit/8580c0c76c5ccd3c55d053e59d8df37b3c4cf35a)) fix(install-selenium): update to chromedriver 2.6\n\n  Update to the latest version of Chromedriver. This fixes the issue with\n  OS X 10.9. Closes #181.\n\n- ([ebc528f](https://github.com/angular/protractor/commit/ebc528fec2c2e88b0f9e32cee0661ecd79da2252)) fix(debugging): switch debugging tests to the new test app urls.\n\n- ([8ff4787](https://github.com/angular/protractor/commit/8ff47875488647513f4199bab36e3b0023dd305d)) fix(runner): exit with proper code when tests fail\n\n  When errors with messages matching /timeout/ were created, Protractor\n  clears the control flow so that the remainder of the tasks scheduled\n  for that spec don't bleed over into the next spec. This was messing up\n  the promises used in the runner, since they are also webdriver promises.\n  Long term, the runner should _not_ use webdriver promises. For now, fix by\n  having the runner resolve promises directly rather than through chaining,\n  and add a TODO to use promises which aren't connected to WebDriver's\n  control flow in the runner.\n\n  Closes #214.\n\n- ([81501c5](https://github.com/angular/protractor/commit/81501c5d941cd7edb15439cef7c7a64c0e773e27)) fix(clientsidescripts): workaround for IE 8 \"async page reload\" init problem\n\n- ([21264fd](https://github.com/angular/protractor/commit/21264fdc2f6cb3345c8f005936c74985ecd811dc)) fix(find): fix error when exposed to ng-options element with a default option\n\n  Protractor will now ignore elements with the ng-bind class that don't have\n  a proper binding on their data, instead of blowing up when encoutering them.\n\n  Closes #165, may fix #170\n\n  - ([f672648](https://github.com/angular/protractor/commit/f6726482cd2ce9a7dda9ccdeeb93574d3b9293e3)) fix(findelements): fix isPresent for repeaters by row for real\n\n## Breaking Changes\n\n- ([bf5b076](https://github.com/angular/protractor/commit/bf5b076cb8897d844c25baa91c263a12c61e3ab3)) fix(cli): remove boolean verbose and stack trace options\n\n  Also add better description for what the command line options are.\n\n  Tiny breaking change:\n    Rename the 'includeStackTrace' command line option to 'stackTrace' for brevity.\n\n# 0.12.0\n\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\nThis change introduces major syntax updates. Using the new syntax is recommended, but the old version is still supported for now. Note also that the test application, docs, and example tests have been updated.\n\n## Features\n\n- ([a2cd6c8](https://github.com/angular/protractor/commit/a2cd6c8baf242a81c4efea1f55249d597de95329)) feat(syntax): big syntax reboot, expose global $, $$, element, and by\n\nIn an effort to make tests more readable and clear, a few more global variables\nwill now be exported.\n\n`browser` is an instance of protractor. This was previously accessed using\n`protractor.getInstance`.\n\n`by` is a collection of element locators. Previously, this was `protractor.By`.\n\n`$` is a shortcut for getting elements by css. `$('.foo')` === `element(by.css('.foo'))`\n\nAll changes should be backwards incompatible, as tested with the new 'backwardscompat'\ntests.\n\n## Bug fixes\n\n- ([8c87ae6](https://github.com/angular/protractor/commit/8c87ae6b430479445744a2f5c8eaca7f5f03d61d)) fix(onPrepare): onPrepare with a string argument should resolve from the config directory\n\nonPrepare can take a string, which is a filename containing a script to load adn execute\nbefore any tests run. This fixes the string to resolve the filename relative to the\nconfig file, instead of relative to the current working directory where protractor\nis called.\n\n\n\n# 0.11.0\n\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Features\n\n- ([02cb819](https://github.com/angular/protractor/commit/02cb8199d89c6645d0bc9dbb39e5cb27517bfaf3)) feat(cli): allow passing params directly to your test\n\nAdds a config object 'params' which is passed directly\nto instances of protractor. 'params' may contain nested\nobjects, and can be changed via the command line as:\n\n  --params.login.user 'Joe' --params.login.password 'abc'\n\nThis change also switches to using optimist to parse\ncommand line flags for more flexibility and better usage\ndocumentation. Closes #32.\n\n- ([c025ddb](https://github.com/angular/protractor/commit/c025ddbe617b977908db509f365cc882924b196f)) feat(findElements): $ & $$ shortcuts.\n\nIntroducing the $ shortcut method for finding a single element by css\nwithout having to call protractor.By.css.  Additionally $$ for finding\nall elements by css.\n\nExamples:\n- ptor.$('.some .selector')\n- ptor.$$('.some .selector')\n\n- ([7d74184](https://github.com/angular/protractor/commit/7d7418411ea4a9d696855f755899161ecb36818d)) feat(explorer): add an interactive element explorer\n\nWhen debugging or first writing test suites, you may find it helpful to\ntry out Protractor commands without starting up the entire test suite. You can\ndo this with the element explorer. This change introduces a first version\nof the element explorer. Closes #107\n\n## Bug Fixes\n\n- ([e45ceaa](https://github.com/angular/protractor/commit/e45ceaae825cce0ec69580b8f6e93d102d4d61f1)) fix(repeaters): allow finding all rows of a repeater\n\nNow, finding an element with the strategy 'protractor.By.repeater()' returns\na promise which will resolve to an array of WebElements, where each WebElement\nis a row in the repeater. Closes #149.\n\n- ([b501ceb](https://github.com/angular/protractor/commit/b501ceb7b776a5d9f1c2659326577601d0fbce5a)) fix(findElements): Consistently include evaluate.\n\nWhen using findElements with a css locator, wrap the returned list of\nelements with protractor specific functionality.\n\n- ([c17ac12](https://github.com/angular/protractor/commit/c17ac12c2a213a7b6f8c236e81ba5cb2db542fd0)) fix(cli): allow running from command line without a config file\n\nIf all necessary fields are specified (e.g. seleniumAddress and at least\none spec), a config file shouldn't be necessary.\n\n## Breaking Changes\n\n- ([421d623](https://github.com/angular/protractor/commit/421d6232fe0b45ca1758afd634997da644f2e1db)) fix(repeat): use 0-based indexing for repeater rows\n\nBREAKING CHANGE: Finding rows with protractor.By.repeater now\nindexes from 0 instead of 1. This should be more familiar to most\nmodern programmers. You will need to edit existing tests. Closes #90.\n\nBefore:\n```\n// The fourth foo\nptor.findElement(protractor.By.repeater('foo in foos').row(4));\n```\nAfter:\n```\n// The fourth foo\nptor.findElement(protractor.By.repeater('foo in foos').row(3));\n```\n\n# 0.10.0\n\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Features\n\n- ([881759e](https://github.com/angular/protractor/commit/881759e77462dc8e1001eb77008668ae6dc552cd)) feat(timeouts): add a unique error message when waitForAngular times out\n\nTo improve the readability of error messages, when waitForAngular times out\nit now produces a custom message. This should help clarify confusion\nfor pages that continually poll using $interval. This change also adds more\ndocumentation on timeouts. See issue #109.\n\n- ([37e0f1a](https://github.com/angular/protractor/commit/37e0f1af196c3c0bf54dcddf0088a8c16602e5f2)) feat(install selenium): better communication in the install script\n\nAdds better messages in the selenium server install script, and also\nmakes the script output a 'start' executable which can be used to quickly\nstart up the selenium standalone. *not yet windows friendly*. Closes #108.\n\n- ([b32f5a5](https://github.com/angular/protractor/commit/b32f5a59169f1324271bd5abc09c17fcd9c4f249)) feat(config): add examples for dealing with log-in\n\nAdds examples for how to log in when the login page is not written\nin Angular. New examples are in spec/login.\n\n- ([1b7675a](https://github.com/angular/protractor/commit/1b7675aab7846bee54117876887bfec07ce31745)) feat(cli): add an onPrepare callback to the config\n\nThis onPrepare callback is useful when you want to do something with\nprotractor before running the specs. For example, you might want\nto monkey-patch protractor with custom functions used by all the\nspecs, or add the protractor instance to the globals.\nAn example usage is shown in the spec/onPrepareConf.js file and its\nassociated spec.\n\n## Bug fixes\n\n- ([256b21c](https://github.com/angular/protractor/commit/256b21cf8c744a200892e6b7f9172150b2f4fe8d)) fix(cli): allow passing the config file before the options\n\nThe cli usage says:\n> USAGE: protractor configFile [options]\nHowever, the options passed as argument are merged into the default\nconfiguration as soon as the configFile is met in the args parsing\nloop.\nThis fix merges the options in the default configuration only after\nthe loop, allowing to pass the options to the cli before or after,\nor around the config file.\n\n- ([6223825](https://github.com/angular/protractor/commit/62238252c7fc68c6a5941883f6a272e95997a8ff)) fix(jasminewd): allow use of custom matchers\n\nUsing jasmine.Matchers.prototype to generate the chained methods for\nexpect() calls is flawed because it does not pick up custom matchers\ndefined using addMatcher.  Instead, use either the matchersClass for\nthe current spec or from the environment.\n\n- ([c22fc38](https://github.com/angular/protractor/commit/c22fc387bc0ef7a07371e023d39a6ce58dfa56c9)) fix(sync): getCurrentUrl and friends should sync with Angular first\n\ngetCurrentUrl, getPageSource, and getTitle should sync with Angular\nbefore executing. Closes #92.\n\n- ([dd06756](https://github.com/angular/protractor/commit/dd067561cf9fe0a765e98605b9ebdd8fbfef04d3)) fix(clientsidescripts): findElements and isElementPresent for protractor.By.select\n\n- ([c607459](https://github.com/angular/protractor/commit/c60745945c6514e25403783eab3de5873e15758b)) fix (navigation): The defer label should appear before other window names,\nnot after.\n\n- ([806f381](https://github.com/angular/protractor/commit/806f38113c675a26171776a559a20bf3899aa2cc)) Fix: findElements() and isElementPresent() now work for protractor.By.input.\nCloses #79.\n\n## Breaking changes\n\n- ([881759e](https://github.com/angular/protractor/commit/881759e77462dc8e1001eb77008668ae6dc552cd)) feat(timeouts): add a unique error message when waitForAngular times out\n\nThis changes the default script timeout from 100 seconds down to 11. Tests\nwhich relied on extremely long timeouts will need to change the default script\ntimeout with `driver.manage().timeouts().setScriptTimeout(<bigNumber>)`.\n\n# 0.9.0\n\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Features\n\n- ([0e8de99](https://github.com/angular/protractor/commit/0e8de99eb0d8a0db4f6d3538dd051c94f35775f5)) Wrap WebElements with Protractor specific features. This change allows\nchained findElement calls to work with Protractor locators. It also\nadds a function, evaluate, to evaluate an angular expression in the context\nof a WebElement's scope.\n\n- ([9f53118](https://github.com/angular/protractor/commit/9f5311812cbae5122ce2c6ebe522132273b0ebcc)) Improving the command line interface (adding more options). This allows\nthe --spec option to be passed with test files that will be resolved\nrelative to the current directory. Smarter merging of default config\nvalues. Closes #65.\n\n- ([73821fb](https://github.com/angular/protractor/commit/73821fb6b6d252a93cc15ce990b4ec4738b87b95)) Adding an 'ignoreSynchronization' property to turn off Protractor's attempt to\nwait for Angular to be ready on a page. This can be used to test pages that\npoll with $timeout or $http.\n\n## Bug fixes\n\n- ([cfc6438](https://github.com/angular/protractor/commit/cfc6438e80e77387afae776f289cd55813e9b2d9)) Adding support for isElementPresent with Protractor locators.\nCloses #11.\n\n- ([8329b01](https://github.com/angular/protractor/commit/8329b01865074c32f7a261fe9bbf2c151b704a34)) Adding waitForAngular calls before WebElement functions. Closes #37.\n\n# 0.8.0\n\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Docs\n- Added documentation to the [docs folder](https://github.com/angular/protractor/tree/master/docs).\n\n- ([08ef244](https://github.com/angular/protractor/commit/08ef244217fb83206c818c84cbe8f07999116ee3)) Adding debugging tests showing different types of timeouts, and fixing\na bug where scheduled tasks from a previous it block would run into\nthe next in case of a timeout.\n\n## Features\n\n- ([1c7eae0](https://github.com/angular/protractor/commit/1c7eae0c09f13b7068f81324f24967709e264241)) Updating the binary script to understand debug, so that\nprotractor debug conf.js works.\n\n- ([7a59479](https://github.com/angular/protractor/commit/7a594791b5ac6616de9c98dcd7d44ecaffb0e8a3)) Adding a 'debug' function to protractor. This schedules a debugger pause\nwithin the webdriver control flow.\n\n- ([679c82d](https://github.com/angular/protractor/commit/679c82d510ea016690fba259db50b4afa36154cc)) Mixing in all webdriver exports to protractor. This means that webdriver\nclasses such as ActionSequence and Keys are accessible on the global\nprotractor.\n\n- ([3c76246](https://github.com/angular/protractor/commit/3c76246a01e584bc30da645a36e75920b5397251)) Added nested angular app (ng-app on an element other than `<html>` or `<body>`) capability via conf file.\n\n## Bug fixes\n\n- ([1c9b98d](https://github.com/angular/protractor/commit/1c9b98d0464bbe57194cf340c6e5942cbe7c8385)) Fixed Sauce issues: low timeouts, shutdown and init order.\n\n## Breaking Changes\n\n- Now running selenium 2.25. Requires updating WebDriverJS and the selenium standalone binary and chromedriver binary.\n\n- ([a54abfb](https://github.com/angular/protractor/commit/a54abfbbfd3b13be5144e64e52a267c73d409a81)) Spec paths in configuration files are now resolved from the location of the spec file instead of the current working directory when the command line is run.\n\n\n\n# 0.7.0\n\n_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._\n\n## Features\n\n- ([7966912](https://github.com/angular/protractor/commit/796691205795d93fe12c998d20a58c8220ac6fb7)) Updating to Selenium 2.24.\n\n- ([90f0a94](https://github.com/angular/protractor/commit/90f0a942b09faff5924674a20ce7705b6d685eba)) Instead of having tests run with the protractor runner need to require()\nthe protractor library, publish it to the global namespace. This insures\nthe instance of protractor used within the tests is the same as the\none used on the command line. Closes #36. Version bump for incompatible\nAPI changes.\n\n- ([cb373c9](https://github.com/angular/protractor/commit/cb373c99a7e33c5514bf1d2728a64f631ec8784c)) Adding glob matching to the spec files from the config. Closes #29.\n\n\n## Breaking changes\n\n- Now running on selenium 2.24. Requires updating WebDriverJS and the selenium standalone binary.\n\n- The protractor runner now publishes `protractor` to the global namespace and sets up the Jasmine-WebDriver adapter. Tests run with this should no longer include\n\n````javascript\n// var protractor = require('protractor'); // No longer needed!\n// require('protractor/jasminewd'); // No longer needed!\n\nvar ptor = protractor.getInstance(); // This should just work now.\n````\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "Contributing\n============\n\nQuestions\n---------\n\nPlease first read through the [FAQ](https://github.com/angular/protractor/blob/master/docs/faq.md).\n\nPlease ask questions on [StackOverflow](http://stackoverflow.com/questions/tagged/protractor), [Google Group discussion list](https://groups.google.com/forum/?fromgroups#!forum/angular), or [Gitter](https://gitter.im/angular/protractor).\n\nAny questions posted to Protractor's Github Issues will be closed with this note:\n\nPlease direct general support questions like this one to an appropriate support channel, see https://github.com/angular/protractor/blob/master/CONTRIBUTING.md#questions\n\nThank you!\n\n\nIssues\n======\n\nIf you have a bug or feature request, please file an issue.\nBefore submitting an issue, please search the issue archive to help reduce duplicates, and read the\n[FAQ](https://github.com/angular/protractor/blob/master/docs/faq.md).\n\nTry running with troubleshooting messages (`protractor --troubleshoot`) against your configuration to make sure that there is not an error with your setup.\n\nWhen submitting an issue, please include a reproducible case that we can actually run. Protractor has a test Angular application available at `http://www.protractortest.org/testapp` which you can use for the reproducible test case. If there's an error, please include the error text.\n\nPlease format code and markup in your issue using [github markdown](https://help.github.com/articles/github-flavored-markdown).\n\n\nContributing to Source Code (Pull Requests)\n===========================================\n\nLoosely, follow the [Angular contribution rules](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md).\n\n * If your PR changes any behavior or fixes an issue, it should have an associated test.\n * New features should be general and as simple as possible.\n * Breaking changes should be avoided if possible.\n * All pull requests require review. No PR will be merged without a comment from a team member stating LGTM (Looks good to me).\n\nProtractor specific rules\n-------------------------\n\n * JavaScript style should generally follow the [Google JS style guide](https://google.github.io/styleguide/javascriptguide.xml).\n * Wrap code at 80 chars.\n * Document public methods with jsdoc.\n * Be consistent with the code around you!\n\nCommit Messages\n---------------\n\nPlease write meaningful commit messages - they are used to generate the changelog, so the commit message should tell a user everything they need to know about a commit. Protractor follows AngularJS's [commit message format](https://docs.google.com/a/google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#heading=h.z8a3t6ehl060).\n\nIn summary, this style is\n\n    <type>(<scope>): <subject>\n    <BLANK LINE>\n    <body>\n\nWhere `<type>` is one of [feat, fix, docs, refactor, test, chore, deps] and\n`<scope>` is a quick descriptor of the location of the change, such as cli, clientSideScripts, element.\n\nTesting your changes\n--------------------\n\nTest your changes on your machine by running `npm start` to start up the test application,\nthen `npm test` to run the test suite. This assumes you have a Selenium Server running\nat localhost:4444.\n\nWhen you submit a PR, tests will also be run on the Continuous Integration environment\nthrough Travis. If your tests fail on Travis, take a look at the logs - if the failures\nare known flakes in Internet Explorer or Safari you can ignore them, but otherwise\nTravis should pass.\n"
  },
  {
    "path": "DEVELOPER.md",
    "content": "# Building and Testing Protractor\n\nThis document describes building, testing, releasing Protractor and provides an overview of\nthe repository layout.\n\n## Prerequisite software\n\nThe prerequisite software (Node.js, npm, git, jdk) are the same as for angular. See\nhttps://github.com/angular/angular/blob/master/docs/DEVELOPER.md#prerequisite-software\n\n## Getting the sources\n\nFork Protractor from github, then clone your fork with:\n\n```shell\ngit clone git@github.com:<github username>/protractor.git\n\n# Go to the Protractor directory:\ncd protractor/\n\n# Add the main protractor repository as an upstream remote to your repository:\ngit remote add upstream https://github.com/angular/protractor.git\n```\n\n## Installing and Building\n\nAll Protractor dependencies come from npm. Install with:\n\n```shell\nnpm install\n```\n\nThis will also trigger our build step. The build step runs the TypeScript compiler\nand copies necessary files into the output `built` directory. To run the build step\nindependently, run:\n\n```shell\nnpm run prepublish\n```\n\nYou can see the other available npm scripts in `package.json`. Note that most of these\nscripts just call our `gulp` commands, which can be seen in `gulpfile.js`.\n\n## Formatting\n\nProtractor uses clang-format to format the source code. If the source code is not properly formatted,\nthe CI will fail and the PR can not be merged.\n\nYou can automatically format your code by running:\n\n```shell\nnpm run format\n```\n\nYou can check that you will pass lint tests with:\n\n```shell\ngulp lint\n\n# or if you don't have gulp installed globally:\n./node_modules/.bin/gulp lint\n```\n\n## Code layout\n\n`docs/` contains markdown documentation files.\n`lib/` contains the actual Protractor code.\n`scripts/` contains scripts used for CI setup and running tests.\n`spec/` contains e2e and unit tests and configuration files for tests.\n`testapp/` contains the code for the Angular applications that e2e tests run against.\n`website/` contains code for generating Protractor API documentation and the website at protractortest.org.\n\nMost of the code is written in TypeScript, with the exception of a few js files.\n\n`lib/driverProviders` controls how WebDriver instances are created.\n`lib/frameworks` contains adapters for test frameworks such as Jasmine and Mocha.\n`lib/selenium-webdriver` and `lib/webdriver-js-extender` are used ONLY for API documentation generation.\n\n## Lightning Code Walkthrough\n\nTBD.\n\n## Testing\n\nRun `npm test` to run the full test suite. This assumes that you have the testapp and a\nselenium server running. Start these as separate processes with:\n\n```shell\nwebdriver-manager update\nwebdriver-manager start\n```\n\nand\n\n```shell\nnpm start\n```\n\nThis suite is described in `scripts/test.js`. It uses some small helper functions to run commands\nas child processes and capture the results, so that we can run protractor commands which should\nresult in failures and verify that we get the expected number and type of failures.\n\nThe suite contains unit tests, end to end tests using the built binary, and interactive tests.\nInteractive tests are for testing `browser.pause` and element explorer.\n\nEnd to end tests all have configuration files which live in `spec/`. Many tests do not need\nan actual Selenium server backing them and use the `mockSelenium` configuration, which saves\ntime by not connecting to a real selenium server.\n\n## Important dependencies\n\nProtractor has very close dependencies with several other projects under the Angular umbrella:\n\n`jasminewd2` is an extension of the Jasmine test framework that adds utilities for\nworking with selenium-webdriver. [jasminewd](https://github.com/angular/jasminewd)\n\n`blocking-proxy` is a separate binary, which handles traffic between a test script and\nwebdriver. It can be turned on via a protractor configuration file, and in the future\nall logic to wait for Angular will be handled through the blocking proxy.\n[blocking-proxy](https://github.com/angular/blocking-proxy)\n\n`webdriver-manager` is a separate binary which manages installing and starting up\nthe various binaries necessary for running webdriver tests. These binaries include\nspecific drivers for various browsers (e.g. chromedriver) and the selenium standalone\nserver. [webdriver-manager](https://github.com/angular/webdriver-manager)\n\n`webdriver-js-extender` extends selenium-webdriver to add Appium commands.\n[webdriver-js-extender](https://github.com/angular/webdriver-js-extender)\n\n## Continuous Integration\n\nPRs or changes submitted to master will automatically trigger continuous integration on two\ndifferent services - Travis, and Circle CI. We use Travis for tests run with SauceLabs because\nwe have more vm time on Travis and their integration with SauceLabs is smooth. CircleCI gives us\ngreater control over the vm, which allows us to run tests against local browsers and get better\nlogs.\n\nTravis runs e2e tests via SauceLabs against a variety of browsers. The essential browsers run a\nmore complete test suite, `specified by spec/ciFullConf.js`. We also run a set of smoke tests\nagainst a larger set of browsers, which is allowed to fail - this is configured in\n`spec/ciSmokeConf.js`. This is due to flakiness in IE, Safari and older browser versions.\nWe also run a small set of tests using BrowserStack to verify that our integration with their\nSelenium farm works.\n\nCircle CI runs a slightly modified version of `npm test` in a single VM. It installs\nthe browsers it needs locally. Circle CI runs unit tests and a set of e2e tests against Chrome.\n\n## Releasing\n\nSee [release.md](https://github.com/angular/protractor/blob/master/release.md) for full instructions.\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License\n\nCopyright (c) 2010-2017 Google, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "Protractor [![Build Status](https://travis-ci.org/angular/protractor.svg?branch=master)](https://travis-ci.org/angular/protractor) [![CircleCI Status](https://circleci.com/gh/angular/protractor.svg?style=shield)](https://circleci.com/gh/angular/protractor) [![Join the chat at https://gitter.im/angular/protractor](https://badges.gitter.im/angular/protractor.svg)](https://gitter.im/angular/protractor)\n==========\n\n[Protractor](http://angular.github.io/protractor) is an end-to-end test framework for [Angular](http://angular.io/) and [AngularJS](http://angularjs.org) applications. Protractor is a [Node.js](http://nodejs.org/) program built on top of [WebDriverJS](https://github.com/SeleniumHQ/selenium/wiki/WebDriverJs). Protractor runs tests against your application running in a real browser, interacting with it as a user would.\n\nCompatibility\n-------------\n\nProtractor 5 is compatible with nodejs v6 and newer.\n\nProtractor works with AngularJS versions greater than 1.0.6/1.1.4, and is compatible with Angular applications. Note that for Angular apps, the `binding` and `model` locators are not supported. We recommend using `by.css`.\n\n\nGetting Started\n---------------\nSee the [Protractor Website](http://www.protractortest.org) for most documentation.\n\nTo get set up and running quickly:\n - Work through the [Tutorial](http://www.protractortest.org/#/tutorial)\n - See the [API](http://www.protractortest.org/#/api)\n\nOnce you are familiar with the tutorial, you’re ready to move on. To modify your environment, see the Protractor Setup docs. To start writing tests, see the Protractor Tests docs.\n\nTo better understand how Protractor works with the Selenium WebDriver and Selenium Server see the reference materials.\n\n\nGetting Help\n------------\n\nCheck the [Protractor FAQ](https://github.com/angular/protractor/blob/master/docs/faq.md) and read through the [Top 20 questions on StackOverflow](http://stackoverflow.com/questions/tagged/protractor?sort=votes&pageSize=20).\n\nPlease ask usage and debugging questions on [StackOverflow](http://stackoverflow.com/questions/tagged/protractor) (use the [\"protractor\"](http://stackoverflow.com/questions/ask?tags=protractor) tag), the [Gitter](https://gitter.im/angular/protractor) chat room, or in the [Angular discussion group](https://groups.google.com/forum/?fromgroups#!forum/angular). (Please do not ask support questions here on Github.)\n\n\nFor Contributors\n----------------\nSee [DEVELOPER.md](https://github.com/angular/protractor/blob/master/DEVELOPER.md)\n"
  },
  {
    "path": "bin/protractor",
    "content": "#!/usr/bin/env node\n\nprocess.env.NODE_ENV = process.env.NODE_ENV || 'test';\n\nrequire('../built/cli.js');\n"
  },
  {
    "path": "bin/webdriver-manager",
    "content": "#!/usr/bin/env node\n\nrequire('webdriver-manager/dist/lib/cli');\n"
  },
  {
    "path": "debugging/async_await.js",
    "content": "describe('angularjs homepage', function() {\n  it('should greet the named user', async function() {\n    debugger;\n    await browser.get('http://www.angularjs.org');\n\n    await element(by.model('yourName')).sendKeys('Julie');\n\n    var greeting = element(by.binding('yourName'));\n\n    expect(await greeting.getText()).toEqual('Hello Julie!');\n  });\n\n  describe('todo list', function() {\n    var todoList;\n\n    beforeEach(async function() {\n      await browser.get('http://www.angularjs.org');\n      todoList = element.all(by.repeater('todo in todoList.todos'));\n    });\n\n    it('should list todos', async function() {\n      expect(await todoList.count()).toEqual(2);\n      expect(await todoList.get(1).getText()).toEqual('build an AngularJS app');\n    });\n\n    it('should add a todo', async function() {\n      var addTodo = element(by.model('todoList.todoText'));\n      var addButton = element(by.css('[value=\"add\"]'));\n\n      await addTodo.sendKeys('write a protractor test');\n      await addButton.click();\n\n      expect(await todoList.count()).toEqual(3);\n      expect(await todoList.get(2).getText()).toEqual('write a protractor test');\n    });\n  });\n});\n"
  },
  {
    "path": "debugging/conf.js",
    "content": "// An example configuration file for debugging test using async/await.\nexports.config = {\n  // Capabilities to be passed to the webdriver instance.\n  capabilities: {\n    'browserName': 'chrome'\n  },\n\n  seleniumAddress: 'http://localhost:4444/wd/hub',\n\n  // Framework to use. Jasmine is recommended.\n  framework: 'jasmine',\n\n  // Spec patterns are relative to the current working directory when\n  // protractor is called.\n  specs: ['async_await.js'],\n\n  SELENIUM_PROMISE_MANAGER: false,\n\n  // Options to be passed to Jasmine.\n  jasmineNodeOpts: {\n    defaultTimeoutInterval: 30000\n  }\n};\n"
  },
  {
    "path": "debugging/failureConf.js",
    "content": "var env = require('../spec/environment.js');\n\n// Examples of tests to show how debugging works with Protractor. Tests\n// should be run against the testapp.\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n\n  // Spec patterns are relative to the current working directly when\n  // protractor is called.\n  specs: [\n    'failure_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl,\n\n  // ----- Options to be passed to jasmine.\n  jasmineNodeOpts: {\n    showColors: true\n  }\n};\n"
  },
  {
    "path": "debugging/failure_spec.js",
    "content": "var webdriver = require('selenium-webdriver');\n\n\ndescribe('modes of failure', function() {\n  it('should fail to find a non-existent element', function() {\n    browser.get('index.html#/form');\n\n    // Run this statement before the line which fails. If protractor is run\n    // with the debugger (protractor debug debugging/conf.js), the test\n    // will pause after loading the webpage but before trying to find the\n    // element.\n    browser.debugger();\n\n    // This element doesn't exist, so this fails.\n    var nonExistant = element(by.binding('nopenopenope')).getText();\n  });\n\n  it('should fail to click a hidden element', function() {\n    browser.get('index.html#/form');\n    element(by.id('hiddenbutton')).click();\n  });\n\n  it('should fail to use protractor on a non-Angular site', function() {\n    browser.get('http://www.google.com');\n  }, 20000);\n\n  it('should fail within a promise', function() {\n    browser.get('index.html#/form');\n    var greeting = element(by.binding('greeting'));\n\n    greeting.getText().then(function(text) {\n      expect(text).toEqual('This is not what it equals');\n    });\n  });\n\n  it('should fail an assertion', function() {\n    browser.get('index.html#/form');\n\n    var greeting = element(by.binding('greeting'));\n\n    expect(greeting.getText()).toEqual('This is not what it equals');\n  });\n\n  it('should fail comparing a promise to another promise', function() {\n    browser.get('index.html#/form');\n\n    var greeting = element(by.binding('greeting'));\n\n    expect(greeting.getText()).toEqual(greeting.getAttribute('value'));\n  });\n\n\n  it('should fail because it throws an error', function() {\n    function foo() {\n      throw new Error('bar!');\n    }\n    foo();\n  });\n});\n"
  },
  {
    "path": "debugging/timeoutConf.js",
    "content": "var env = require('../spec/environment.js');\n\n// Examples of tests to show how timeouts works with Protractor. Tests\n// should be run against the testapp.\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n\n  // Spec patterns are relative to the current working directly when\n  // protractor is called.\n  specs: [\n    'timeout_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl,\n\n  // ----- Options to be passed to jasmine.\n  jasmineNodeOpts: {\n    showColors: true\n  }\n};\n"
  },
  {
    "path": "debugging/timeout_spec.js",
    "content": "describe('timeout possibilities', function() {\n  jasmine.getEnv().defaultTimeoutInterval = 33;\n\n\n  it('shoud pass - first test should not timeout', function() {\n    expect(true).toEqual(true);\n  });\n\n  it('should timeout due to webdriver script timeout', function() {\n    browser.driver.manage().timeouts().setScriptTimeout(55);\n\n    browser.get('index.html#/form');\n\n    browser.driver.executeAsyncScript(function() {\n      var callback = arguments[arguments.length - 1];\n      setTimeout(callback, 500);\n    });\n\n    expect(element(by.binding('greeting')).getText()).\n        toEqual('Hiya');\n  }, 5000); // The 5000 here sets the Jasmine spec timeout.\n\n  it('should fail normally', function() {\n    expect(false).toEqual(true);\n  });\n\n  it('should pass - tests in the middle should be unaffected', function() {\n    expect(true).toEqual(true);\n  });\n\n  describe('waitForAngular', function() {\n    it('should timeout and give a reasonable message', function() {\n\n      browser.driver.manage().timeouts().setScriptTimeout(55);\n\n      browser.get('index.html#/async');\n\n\n      var status = element(by.binding('slowHttpStatus'));\n      var button = element(by.css('[ng-click=\"slowHttp()\"]'));\n\n      expect(status.getText()).toEqual('not started');\n\n      button.click();\n\n      expect(status.getText()).toEqual('done');\n    }, 5000); // The 5000 here sets the Jasmine spec timeout.\n  });\n\n  it('should timeout due to Jasmine spec timeout', function() {\n    browser.driver.sleep(1000);\n    expect(true).toBe(true);\n  });\n\n  it('should pass - previous timeouts should not affect this', function() {\n    expect(true).toEqual(true);\n  });\n});\n"
  },
  {
    "path": "docs/api-overview.md",
    "content": "Working with Spec and Config Files\n==================================\n\nProtractor needs two files to run: the test or spec file and the configuration file.\n\nSpec files\n==========\n\nProtractor tests  are written using the syntax of your test framework, for example [Jasmine](http://jasmine.github.io/), and the [Protractor API](/docs/api.md).\n\nExample Spec File\n-----------------\nThis simple script ([example_spec.js](/example/example_spec.js)) tests the 'The Basics' example on the [angularjs.org](http://www.angularjs.org) homepage:\n\n```js\ndescribe('angularjs homepage', function() {\n  it('should greet the named user', function() {\n    // Load the AngularJS homepage.\n    browser.get('http://www.angularjs.org');\n\n    // Find the element with ng-model matching 'yourName' - this will\n    // find the <input type=\"text\" ng-model=\"yourName\"/> element - and then\n    // type 'Julie' into it.\n    element(by.model('yourName')).sendKeys('Julie');\n\n    // Find the element with binding matching 'yourName' - this will\n    // find the <h1>Hello {{yourName}}!</h1> element.\n    var greeting = element(by.binding('yourName'));\n\n    // Assert that the text element has the expected value.\n    // Protractor patches 'expect' to understand promises.\n\n    expect(greeting.getText()).toEqual('Hello Julie!');\n  });\n});\n```\n\nGlobal Variables\n----------------\n\nProtractor exports these global variables to your spec (test) file:\n\n - `browser` - A wrapper around an instance of WebDriver, used for navigation and page-wide information. The `browser.get` method loads a page. Protractor expects Angular to be present on a page, so it will throw an error if the page it is attempting to load does not contain the Angular library. (If you need to interact with a non-Angular page, you may access the wrapped webdriver instance directly with `browser.driver`).\n\n - `element` - A helper function for finding and interacting with DOM elements on the page you are testing. The `element` function searches for an element on the page. It requires one parameter, a locator strategy for locating the element. See [Using Locators](/docs/locators.md) for more information. See Protractor's ElementFinder test suite ([elements_spec.js](/spec/basic/elements_spec.js)) for more examples.\n\n - `by` - A collection of element locator strategies. For example, elements can be found by CSS selector, by ID, or by the attribute they are bound to with ng-model. See [Using Locators](/docs/locators.md).\n\n - `protractor` - The Protractor namespace which wraps the WebDriver namespace. Contains static variables and classes, such as `protractor.Key` which enumerates the codes for special keyboard signals.\n\n\nConfig Files\n============\n\nThe configuration file tells Protractor how to set up the Selenium Server, which tests to run, how to set up the browsers, and which test framework to use. The configuration file can also include one or more global settings.\n\nExample Config File\n-------------------\n\nA simple configuration ([conf.js](/example/conf.js)) is shown below:\n\n```js\n// An example configuration file\nexports.config = {\n  // The address of a running selenium server.\n  seleniumAddress: 'http://localhost:4444/wd/hub',\n\n  // Capabilities to be passed to the webdriver instance.\n  capabilities: {\n    browserName: 'chrome'\n  },\n\n  // Spec patterns are relative to the configuration file location passed\n  // to protractor (in this example conf.js).\n  // They may include glob patterns.\n  specs: ['example-spec.js'],\n\n  // Options to be passed to Jasmine-node.\n  jasmineNodeOpts: {\n    showColors: true, // Use colors in the command line report.\n  }\n};\n```\n\nConfig File\n-----------\n\nThe [config file](/lib/config.ts) provides explanations for all of the Protractor configuration options. Default settings include the standalone Selenium Server, the Chrome browser, and the Jasmine test framework. Additional information about various configuration options is available here:\n\n - [Setting Up the Selenium Server](/docs/server-setup.md)\n - [Setting Up the Browser](/docs/browser-setup.md)\n - [Choosing a Framework](/docs/frameworks.md)\n - [Using Page Objects to Organize Tests](/docs/page-objects.md)\n"
  },
  {
    "path": "docs/api.md",
    "content": "Protractor API\n==============\n\nThe API documentation was moved to: http://angular.github.io/protractor/#/api\n"
  },
  {
    "path": "docs/async-await.md",
    "content": "`async`/`await`\n===============\n\n**Background**\n\n-   The Web Driver Control Flow is used to synchronize your commands so they reach\nthe browser in the correct order (see\n[/docs/control-flow.md](/docs/control-flow.md) for details).  In the future, the\ncontrol flow is being removed (see [SeleniumHQ's github issue](\nhttps://github.com/SeleniumHQ/selenium/issues/2969) for details). Instead of the\ncontrol flow, you can synchronize your commands with promise chaining or the\nupcoming ES7 feature `async`/`await`.\n\n-   Previously, we have Typescript support for `async`/`await`: Please see [TypeScript examples which use `async`/`await`](/exampleTypescript/asyncAwait/README.md).\n\n-   The latest [Node.js](https://nodejs.org/en/) provides native async/await,\n    which means we can get stable e2e test without using control flow in javascript test.\n\n    **Note**: To write and run native async/await test, the node.js version should be greater than or equal to 8.0, and Jasmine version should be greater than or equal to 2.7\n\n-   If we disable control flow and use async/await to write tests, we can get a\n    better debugging experience by using [chrome\n    inspector](/docs/debugging.md#disabled-control-flow)\n\n**How to use native async/await in test**\n\nWe have a simple example to show how to use async/await in test.\n\nYou can find the whole example in\n[here](/debugging/async_await.js)\n\n```javascript\ndescribe('angularjs homepage', function() {\n  it('should greet the named user', async function() {\n    await browser.get('http://www.angularjs.org');\n\n    await element(by.model('yourName')).sendKeys('Julie');\n\n    var greeting = element(by.binding('yourName'));\n\n    expect(await greeting.getText()).toEqual('Hello Julie!');\n  });\n```\n\nAs you can see, the syntax is almost the same with TypeScript async/await.\n\n1.  We need wrap our asynchronous function with “async”.\n1.  We can add “await” keyword to each operation that we want our program to\n    wait for.\n\n    **Note:** Never forget to add “await” keyword in an async function, it\n    may bring some unexpected problem (e.g. your test might fail silently and\n    always be reported as passed).\n1.  Don’t forget to turn off control_flow, you cannot use a mix of `async`/`await` and the control flow:\n`async`/`await` causes the control flow to become unreliable (see\n[github issue]( https://github.com/SeleniumHQ/selenium/issues/3037)).  So if you\n`async`/`await` anywhere in a spec, you should use the\n`SELENIUM_PROMISE_MANAGER: false`\n\n```javascript\n// An example configuration file for debugging test using async/await.\nexports.config = {\n  // Capabilities to be passed to the webdriver instance.\n  capabilities: {\n    'browserName': 'chrome'\n  },\n\n  seleniumAddress: 'http://localhost:4444/wd/hub',\n\n  // Framework to use. Jasmine is recommended.\n  framework: 'jasmine',\n\n  // Spec patterns are relative to the current working directory when\n  // protractor is called.\n  specs: ['async_await.js'],\n\n  SELENIUM_PROMISE_MANAGER: false,\n\n  // Options to be passed to Jasmine.\n  jasmineNodeOpts: {\n    defaultTimeoutInterval: 30000\n  }\n};\n```\n"
  },
  {
    "path": "docs/browser-setup.md",
    "content": "Setting Up the Browser\n=======================\n\nProtractor works with [Selenium WebDriver](http://docs.seleniumhq.org/docs/03_webdriver.jsp), a browser automation framework. Selenium WebDriver supports several browser implementations or [drivers](http://docs.seleniumhq.org/docs/03_webdriver.jsp#selenium-webdriver-s-drivers) which are discussed below.\n\nBrowser Support\n---------------\nProtractor supports the two latest major versions of Chrome, Firefox, Safari, and IE.\n\nPlease see [Browser Support](/docs/browser-support.md) for a full list of\nsupported browsers and known issues.\n\n\nConfiguring Browsers\n--------------------\n\nIn your Protractor config file (see [config.ts](/lib/config.ts)), all browser setup is done within the `capabilities` object. This object is passed directly to the WebDriver builder ([builder.js](https://code.google.com/p/selenium/source/browse/javascript/webdriver/builder.js)). \n\n\nSee [DesiredCapabilities](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities) for full information on which properties are available.\n\n\nUsing Mobile Browsers\n---------------------\n\nPlease see the [Mobile Setup](/docs/mobile-setup.md) documentation for information on mobile browsers.\n\n\nUsing Browsers Other Than Chrome\n--------------------------------\n\nTo use a browser other than Chrome, simply set a different browser name in the capabilities object.\n\n```javascript\ncapabilities: {\n  'browserName': 'firefox'\n}\n```\n\nYou may need to install a separate binary to run another browser, such as IE or Android. For more information, see [SeleniumHQ Downloads](http://docs.seleniumhq.org/download/).\n\n\nAdding Chrome-Specific Options\n------------------------------\n\nChrome options are nested in the `goog:chromeOptions` object. A full list of options is at the [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver/capabilities) site. For example, to show an FPS counter in the upper right, your configuration would look like this:\n\n```javascript\ncapabilities: {\n  'browserName': 'chrome',\n  'goog:chromeOptions': {\n    'args': ['show-fps-counter=true']\n  }\n},\n```\n\nAdding Firefox-Specific Options\n------------------------------\n\nFirefox options are nested in the `moz:firefoxOptions` object. A full list of options is at the [GeckoDriver](https://github.com/mozilla/geckodriver#firefox-capabilities) Github page. For example, to run in safe mode, your configuration would look like this:\n\n```javascript\ncapabilities: {\n  'browserName': 'firefox',\n  'moz:firefoxOptions': {\n    'args': ['--safe-mode']\n  }\n},\n```\n\nTesting Against Multiple Browsers\n---------------------------------\n\nIf you would like to test against multiple browsers, use the `multiCapabilities` configuration option.\n\n```javascript\nmultiCapabilities: [{\n  'browserName': 'firefox'\n}, {\n  'browserName': 'chrome'\n}]\n```\n\nProtractor will run tests in parallel against each set of capabilities. Please note that if `multiCapabilities` is defined, the runner will ignore the `capabilities` configuration.\n\n\nUsing Multiple Browsers in the Same Test\n----------------------------------------\nIf you are testing apps where two browsers need to interact with each other (e.g. chat systems), you can do that with protractor by dynamically creating browsers on the go in your test. Protractor exposes a function in the `browser` object to help you achieve this: `browser.forkNewDriverInstance(opt_useSameUrl, opt_copyMockModules)`. \nCalling this will return a new independent browser object. The first parameter in the function denotes whether you want the new browser to start with the same url as the browser you forked from. The second parameter denotes whether you want the new browser to copy the mock modules from the browser you forked from.\n\n```javascript\nbrowser.get('http://www.angularjs.org');\nbrowser.addMockModule('moduleA', \"angular.module('moduleA', []).value('version', '3');\");\n\n// To create a new browser.\nvar browser2 = browser.forkNewDriverInstance();\n\n// To create a new browser with url as 'http://www.angularjs.org':\nvar browser3 = browser.forkNewDriverInstance(true);\n\n// To create a new browser with mock modules injected:\nvar browser4 = browser.forkNewDriverInstance(false, true);\n\n// To create a new browser with url as 'http://www.angularjs.org' and mock modules injected:\nvar browser4 = browser.forkNewDriverInstance(true, true);\n```\n\nNow you can interact with the browsers. However, note that the globals `element`, `$`, `$$` and `browser` are all associated with the original browser. In order to interact with the new browsers, you must specifically tell protractor to do so like the following:\n\n```javascript\nvar element2 = browser2.element;\nvar $2 = browser2.$;\nvar $$2 = browser2.$$;\nelement2(by.model(...)).click();\n$2('.css').click();\n$$2('.css').click();\n```\n\nProtractor will ensure that commands will automatically run in sync. For example, in the following code, `element(by.model(...)).click()` will run before `browser2.$('.css').click()`:\n\n```javascript\nbrowser.get('http://www.angularjs.org');\nbrowser2.get('http://localhost:1234');\n\nbrowser.sleep(5000);\nelement(by.model(...)).click();\nbrowser2.$('.css').click();\n```\n\n\nSetting up PhantomJS\n--------------------\nPhantomJS is [no longer officially supported](https://groups.google.com/forum/#!topic/phantomjs/9aI5d-LDuNE). Instead, we recommend to use one of the following alternatives:\n1. Chrome with [headless mode](https://developers.google.com/web/updates/2017/04/headless-chrome). Available in Chrome 59+ on Linux/Mac OS X, and in Chrome 60+ on Windows.\n2. Firefox with [headless mode](https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#-headless). Available in Firefox 55+ on Linux, and in Firefox 56+ on Windows/Mac OS X.\n3. Chrome with [Xvfb](http://www.tothenew.com/blog/protractor-with-jenkins-and-headless-chrome-xvfb-setup/).\n\n\nUsing headless Chrome\n---------------------\nTo start Chrome in headless mode, start Chrome with the `--headless` flag.\n\nAs of Chrome 58 you also need to set `--disable-gpu`, though this may change in future versions. \nAlso, changing the window size during a test will not work in headless mode, but you can set it\non the commandline like this `--window-size=800,600`.\n\n```javascript\ncapabilities: {\n  browserName: 'chrome',\n\n  'goog:chromeOptions': {\n     args: [ \"--headless\", \"--disable-gpu\", \"--window-size=800,600\" ]\n   }\n}\n```\n\nUsing headless Firefox\n---------------------\nTo start Firefox in headless mode, start Firefox with the [`--headless` flag](https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#-headless).\n\n```javascript\ncapabilities: {\n  browserName: 'firefox',\n  \n  'moz:firefoxOptions': {\n     args: [ \"--headless\" ]\n   }\n}\n```\n"
  },
  {
    "path": "docs/browser-support.md",
    "content": "Browser Support\n===============\nProtractor supports the two latest major versions of Chrome, Firefox, Safari, and IE. These are used in Protractor's own suite of tests. You can view the current status [on Travis](https://travis-ci.org/angular/protractor).\n\nNote that because Protractor uses WebDriver to drive browsers, any issues with WebDriver implementations (such as FireFoxDriver, ChromeDriver, and IEDriver) will show up in Protractor. The chart below links to major known issues. You can search through all WebDriver issues at [the Selenium issue tracker](https://code.google.com/p/selenium/issues/list).\n\n\n| Driver                 | Support      | Known Issues    |\n|------------------------|--------------|-----------------|\n|ChromeDriver            |Yes           |[Link](https://github.com/angular/protractor/labels/browser%3A%20chrome) |\n|FirefoxDriver*          |Yes           |[Link](https://github.com/angular/protractor/labels/browser%3A%20firefox) |\n|SafariDriver            |Yes           |[Link](https://github.com/angular/protractor/labels/browser%3A%20safari) |\n|IEDriver                |Yes           |[Link](https://github.com/angular/protractor/labels/browser%3A%20IE) |\n|OperaDriver             |No            |                 |\n|ios-Driver              |No            |                 |\n|Appium - iOS/Safari     |Yes**         |[Link](https://github.com/angular/protractor/labels/browser%3A%20iOS%20safari) |\n|Appium - Android/Chrome |Yes**         |[Link](https://github.com/angular/protractor/labels/browser%3A%20android%20chrome) |\n|Selendroid              |Yes**         |                 |\n|PhantomJS / GhostDriver |***           |[Link](https://github.com/angular/protractor/labels/browser%3A%20phantomjs) | \n\n\n(*) WebDriver support fr Firefox has changed recently, and FireFox version 48 does not work properly with the current tools. For the moment, we recommend testing against FireFox 47. In future releases, support for WebDriver via Mozilla's Marionette project will be available.\n\n(**) These drivers are not yet in the Protractor smoke tests.\n\n(***) We recommend against using PhantomJS for tests with Protractor. There are many reported issues with PhantomJS crashing and behaving differently from real browsers.\n"
  },
  {
    "path": "docs/control-flow.md",
    "content": "The WebDriver Control Flow\n==========================\n\nThe [WebDriverJS API](https://github.com/SeleniumHQ/selenium/wiki/WebDriverJs#understanding_the_api) is based on [promises](https://github.com/SeleniumHQ/selenium/wiki/WebDriverJs#promises),\nwhich are managed by a [control flow](https://github.com/SeleniumHQ/selenium/wiki/WebDriverJs#control-flows)\nand adapted for [Jasmine](http://jasmine.github.io/2.3/introduction.html).\nA short summary about how Protractor interacts with the control flow is presented below.\n\nDisabling the Control Flow\n--------------------------\n\nIn the future, the control flow is being removed (see\n[SeleniumHQ's github issue](https://github.com/SeleniumHQ/selenium/issues/2969)\nfor details).  To disable the control flow in your tests, you can use the\n`SELENIUM_PROMISE_MANAGER: false` [config option](/lib/config.ts#L714).\n\nInstead of the control flow, you can synchronize your commands\nwith promise chaining or the upcoming ES7 feature `async`/`await`.  See\n[`/spec/ts/`](/spec/ts/) for examples of tests with the control flow disabled.\n\nBecause `async`/`await` uses native promises, it will make the Control Flow\nunreliable.  As such, if you're writing a library or plugin which needs to work\nwhether or not the Control Flow is enabled, you'll need to handle\nsynchronization using promise chaining.\n\nPromises and the Control Flow\n-----------------------------\n\nWebDriverJS (and thus, Protractor) APIs are entirely asynchronous. All functions\nreturn promises.\n\nWebDriverJS maintains a queue of pending promises, called the control flow,\nto keep execution organized. For example, consider this test:\n\n```javascript\n  it('should find an element by text input model', function() {\n    browser.get('app/index.html#/form');\n\n    var username = element(by.model('username'));\n    username.clear();\n    username.sendKeys('Jane Doe');\n\n    var name = element(by.binding('username'));\n\n    expect(name.getText()).toEqual('Jane Doe');\n\n    // Point A\n  });\n```\n\nAt Point A, none of the tasks have executed yet. The `browser.get` call is at\nthe front of the control flow queue, and the `name.getText()` call is at the\nback. The value of `name.getText()` at point A is an unresolved promise\nobject.\n\n\nProtractor Adaptations\n----------------------\n\nProtractor adapts Jasmine so that each spec automatically waits until the\ncontrol flow is empty before exiting.\n\nJasmine expectations are also adapted to understand promises. That's why this\nline works - the code actually adds an expectation task to the control flow,\nwhich will run after the other tasks:\n\n```javascript\n  expect(name.getText()).toEqual('Jane Doe');\n```\n\nMocha Users\n-----------\n\nIf you are using Mocha as your test framework, the control flow will still\nautomatically empty itself before each test completes. However, the `expect`\nfunction in Mocha is _not_ adapted to understand promises - that's why you'll\nneed to use an assertion framework such as Chai as Promised. See\n[Choosing a Framework](/docs/frameworks.md) for more information.\n"
  },
  {
    "path": "docs/debugging.md",
    "content": "Debugging Protractor Tests\n==========================\n\nEnd-to-end tests can be difficult to debug because they depend on an entire\nsystem, may depend on prior actions (such as log-in), and may change the\nstate of the application they're testing. WebDriver tests in particular\ncan be difficult to debug because of long error messages and the separation\nbetween the browser and the process running the test.\n\n## Types of Failure\n\nProtractor comes with examples of failing tests ([failure_spec.js](https://github.com/angular/protractor/blob/master/debugging/failure_spec.js)).\nTo run, start up the test application and a Selenium Server, and run the command below. Then look at all the stack traces.\n\n```\nprotractor debugging/failureConf.js\n```\n\nThis test suite shows various types of failure:\n\n-  WebDriver throws an error - When a command cannot be completed, for example\n   an element is not found.\n-  Protractor will fail when it cannot find the Angular library on a page.\n   If your test needs to interact with a non-angular page, access the WebDriver\n   instance directly with `browser.driver`.\n-  Expectation Failure - Shows what a normal expectation failure looks\n   like.\n\n\n## Timeouts\n\nThere are several ways that Protractor can time out. See the [Timeouts](/docs/timeouts.md)\nreference for full documentation.\n\n## Disabled Control Flow\n\n\nThe latest [Node.js](https://nodejs.org/en/) provides native async/await, which\nmeans we can get stable e2e test easily without using control flow. Furthermore,\nif we write our test by using async/await[(how to?)](/docs/async-await.md), we can\nuse chrome development tool and chrome inspector together to debug the new\ntests, which will give a nicer debugging experience.\n\n**Debuging tests in chrome inspector**\n\nWe can debug both javascript and TypeScript async/await tests in chrome\ninspector and the debugging process is almost the same.\n\nWe have a simple example to show how to debug async/await in test. You can find\nthe whole example in\n[here](/debugging/async_await.js)\n\n-   Add “debugger” keyword to the test case that we want to debug.\n\n    ```javascript\n    it('should greet the named user', async function() {\n        debugger;\n        await browser.get('http://www.angularjs.org');\n\n        await element(by.model('yourName')).sendKeys('Julie');\n\n        var greeting = element(by.binding('yourName'));\n\n        expect(await greeting.getText()).toEqual('Hello Julie!');\n    });\n    ```\n\n-   Start test process with a new argument \"inspect-brk\", which will enable\n    inspector agent, listen on default address and port (127.0.0.1:9229) and\n    break before user code starts\n\n    Use\n\n    ```\n    node --inspect-brk bin/protractor <config_file>\n    ```\n\n-   Open chrome inspector: Enter \"chrome://inspect/#devices\" in browser, find\n    the current running target and click “Inspect”\n\n    ![screenshot](/docs/inspector.png)\n\n-   The test will start and pause at the beginning.\n\n    ![screenshot](/docs/firstBreak.png)\n\n-   We can click F8 (resume script execution), and the test will pause at the\n    first line that has our “debugger” keyword. We can then add breakpoints and\n    debug tests.\n\n    ![screenshot](/docs/breakpoint.png)\n\n-   We can also open chrome development tool on the webdriver controlled browser\n    to check the html elements and do some queries while the test execution is\n    pausing.\n\n    ![screenshot](/docs/chromeDevTool.png)\n\n-   Known Issues\n\n1.  If we resume test execution after a long time pause, it will jump to next\n    test case even we have some other breaking points in current test case since\n    current test case has already been timeout. You can set\n    jasmine.DEFAULT_TIMEOUT_INTERVAL to an arbitrary high value so that your\n    test doesn't time out.\n\n2.  If we step into protractor lib code which was written in Typescript, we\n    cannot see the TypeScript code. In general, you can add breakpoints to each\n    line that you want it to pause, and use F8 (resume script execution) to\n    debug(To avoid step into protractor lib).\n\n\n**Setting Up VSCode for Debugging**\n\nVS Code has built-in [debugging](https://code.visualstudio.com/docs/editor/debugging) support for the Node.js runtime and can debug JavaScript, TypeScript, and any other language that gets transpiled to JavaScript.\n\nTo set up VSCode for Protractor, follow the below steps:\n\n1. Click on the Debugging icon in the View Bar on the side of VS Code.\n2. Click on the Configure gear icon on the Debug view top bar and choose nodejs environment.\n3. It will generate a `launch.json` file under your workspace's `.vscode` folder.\n4. Setup your launch.json file by configuring below two commands:\n```\n \"program\": \"${workspaceRoot}/node_modules/protractor/bin/protractor\",\n  \"args\": [\"${workspaceRoot}/protractorConfig.js\"],\n```\n5. Save your launch.json, put some breakpoints and start debugging.\n\n**Setting Up WebStorm for Debugging**\n\nTo set up WebStorm for Protractor, do the following:\n\n1. Open the Run/Debug Configurations dialog\n2. Add new Node.js configuration.\n3. On the Configuration tab set:\n - **Node Interpreter**: path to node executable\n - **Working directory**: your project base path\n - **JavaScript file**: path to Protractor cli.js file (e.g. *node_modules\\protractor\\built\\cli.js*)\n - **Application parameters**: path to your Protractor configuration file (e.g.\n *protractorConfig.js*)\n4. Click OK, place some breakpoints, and start debugging.\n\n\n## Enabled Control Flow\n\n**Note:** Protractor debugger and element explorer cannot be used for Node.js 8+\n\n**Pausing to Debug**\n\nProtractor supports two methods for pausing to debug - `browser.pause()` and\n`browser.debugger()`. You probably want to use `browser.pause()`, unless you\nwould like precise control over the node debugger.\n\n**Using pause**\n\nInsert `browser.pause()` into your test where you want to pause.\n\n```js\nit('should fail to find a non-existent element', function() {\n  browser.get('app/index.html#/form');\n\n  browser.pause();\n\n  // This element doesn't exist, so this fails.\n  var nonExistent = element(by.binding('nopenopenope')).getText();\n});\n```\n\nRun your tests normally.\n\n`protractor failureConf.js`\n\nThe test will pause execution after the scheduled navigation to `app/index.html#/form`\nbut before trying to get text from the nonnexistant element. The terminal will\nprint instructions for continuing or inspecting the application and a list of the\ncurrently pending tasks on the WebDriver control flow.\n\n```\n-- WebDriver control flow schedule\n |- waiting for debugger to attach\n |---    at [object Object].<anonymous> (failure_spec.js:13:13)\n |- Protractor.waitForAngular()\n |---    at [object Object].<anonymous> (failure_spec.js:16:59)\nwd-debug>\n```\n\nEnter `c` to move the test forward by one task.\n\nWhile the test is paused you may also interact with the browser. Note that\nif you open the Chrome Dev Tools, you must close them before continuing\nthe test because ChromeDriver cannot operate when the Dev Tools are open.\n\nWhen you finish debugging, exit by pressing `Ctrl-C`. Your tests will continue\nwhere they left off, using the same browser.\n\nYou can also use `browser.explore()` in your test script to pause and enter\nan interactive repl loop. In this interactive mode, you can send\nWebDriver commands to your browser. The resulting value or error will\nbe reported to the terminal.\n\n```\n> element(by.binding('nopenopenope')).getText()\nNoSuchElementError: No element found using locator: by.binding(\"nopenopenope\")\n>\n> element(by.binding('user')).getText()\n'Anon'\n```\n\n**Note:** Since these are asynchronous tasks, you would have to increase the default timeout of your specs else default timeout exception would be thrown!\n\n**Using debugger**\n\nInsert `browser.debugger();` into your test where you want to break:\n\n```javascript\nit('should fail to find a non-existent element', function() {\n  browser.get('app/index.html#/form');\n\n  // Run this statement before the line which fails. If protractor is run\n  // with the debugger (protractor debug <...>), the test\n  // will pause after loading the webpage but before trying to find the\n  // element.\n  browser.debugger();\n\n  // This element doesn't exist, so this fails.\n  var nonExistent = element(by.binding('nopenopenope')).getText();\n});\n```\n\nThen run the test _in debug mode_:\n\n```\nprotractor debug debugging/failureConf.js\n```\n\nThis uses the [node debugger](http://nodejs.org/api/debugger.html). Enter\n`c` to start execution and continue after the breakpoint or enter `next` command.The next command steps to the next line in control flow.\n\n`browser.debugger();` is different from node's `debugger;` statement because\nit adds a breakpoint task asynchronously in queue. This means the example above will\npause after the `get` statement has been executed, whereas `debugger;`\npauses the test after the get command is scheduled but has not yet\nbeen executed.\n\nProtractor's `debugger()` method works by scheduling a node debug breakpoint\non the control flow.\n\nWhen `debugger()` is called, it also inserts all the client side scripts\nfrom Protractor into the browser as `window.clientSideScripts`. They can be\nused from the browser's console.\n\n```javascript\n// In the browser console (e.g. from Chrome Dev Tools)\n> window.clientSideScripts.findInputs('username');\n// Should return the input element with model 'username'.\n\n// You can also limit the scope of the locator\n> window.clientSideScripts.findInputs('username', document.getElementById('#myEl'));\n```\n\n**Testing Out Protractor Interactively**\n\nWhen debugging or first writing test suites, you may find it helpful to\ntry out Protractor commands without starting up the entire test suite. You can\ndo this with the element explorer.\n\nTo run element explorer, simply run protractor as you normally would, but pass in\nthe flag --elementExplorer:\n\n    protractor --elementExplorer\n\nThis will load up the URL on WebDriver and put the terminal into a REPL loop.\nYou will see a > prompt. The `browser`, `element` and `protractor` variables will\nbe available. Enter a command such as:\n\n    > browser.get('http://www.angularjs.org')\n\nor\n\n    > element(by.id('foobar')).getText()\n\nTyping tab at a blank prompt will fill in a suggestion for finding\nelements. You can also use the `list(locator)` command to list all elements\nmatching a locator.\n\nElement explorer will start chrome by default. However, you can specify\nanother browser, change browser settings, or specify any other config that you\nnormally would with your protractor test. To do this, pass configs to\nprotractor like you normally would,\nbut with the `--elementExplorer` flag set:\n\n    protractor [configFile] [options] --elementExplorer\n\nFor example, to connect to ChromeDriver directly, use\n\n    protractor --directConnect --elementExplorer\n\nElement explore will ignore your specs, not set up your framework (e.g. jasmine,\nmocha, cucumber), and only allow you to pass in 1 capability, but will honor\nevery other parameter in your config.\n\nNote `baseUrl` is used here as the initial page, i.e. element explorer will try\nto navigate to `baseUrl` automatically on start.\n\n\n## Taking Screenshots\n\nWebDriver can snap a screenshot with `browser.takeScreenshot()`. This can be a\ngood way to help debug tests, especially for tests that run on a continuous integration\nserver. The method returns a promise which will resolve to the screenshot as a\nbase-64 encoded PNG.\n\nSample usage:\n``` javascript\n// at the top of the test spec:\nvar fs = require('fs');\n\n// ... other code\n\n// abstract writing screen shot to a file\nfunction writeScreenShot(data, filename) {\n    var stream = fs.createWriteStream(filename);\n\n    stream.write(new Buffer(data, 'base64'));\n    stream.end();\n}\n\n// ...\n\n// within a test:\nbrowser.takeScreenshot().then(function (png) {\n    writeScreenShot(png, 'exception.png');\n});\n```\n"
  },
  {
    "path": "docs/faq.md",
    "content": "FAQ\n===\n\nMy tests time out in Protractor, but everything's working fine when running manually. What's up?\n--------------------\n\nThere are several ways that Protractor can time out - see the [Timeouts](/docs/timeouts.md)\nreference for full documentation.\n\nWhat's the difference between Karma and Protractor? When do I use which?\n---------------------------------------------------\n\n[Karma](http://karma-runner.github.io) is a great tool for unit testing, and Protractor is intended for\nend-to-end or integration testing. This means that small tests for the logic\nof your individual controllers, directives, and services should be run using\nKarma. Big tests in which you have a running instance of your entire application\nshould be run using Protractor. Protractor is intended to run tests from a\nuser's point of view - if your test could be written down as instructions\nfor a human interacting with your application, it should be an end-to-end test\nwritten with Protractor.\n\nHere's a [great blog post](http://www.yearofmoo.com/2013/09/advanced-testing-and-debugging-in-angularjs.html)\nwith more info.\n\nAngular can't be found on my page\n---------------------------------\n\nProtractor supports Angular and AngularJS 1.0.6/1.1.4 and higher - check that your version of Angular is upgraded.\n\nFor AngularJS apps, the `angular` variable is expected to be available in the global context. Try opening chrome devtools or firefox and see if `angular` is defined.\n\nFor Angular apps, you should see a global method `getAllAngularTestabilities`.\n\nHow do I deal with my log-in page?\n----------------------------------\n\nIf your app needs log-in, there are a couple ways to deal with it. If your login\npage is not written with Angular, you'll need to interact with it via \nunwrapped webdriver, which can be accessed like `browser.driver.get()`. You can also use\n`browser.waitForAngularEnabled(false)` as explained [here](/docs/timeouts.md#how-to-disable-waiting-for-angular).\n\nAnother option is to put your log-in code into an `onPrepare` function, which will be run\nonce before any of your tests. See this example ([withLoginConf.js](https://github.com/angular/protractor/blob/master/spec/withLoginConf.js))\n\nWhich browsers are supported?\n-----------------------------\nThe last two major versions of Chrome, Firefox, IE, and Safari. See details at [Setting Up the Browser](/docs/browser-setup.md) and [Browser Support](/docs/browser-support.md).\n\nThe result of `getText` from an input element is always empty\n-------------------------------------------------------------\n\nThis is a [webdriver quirk](http://grokbase.com/t/gg/webdriver/12bcmvwhcm/extarcting-text-from-the-input-field).\n`<input>` and `<textarea>` elements always have\nempty `getText` values. Instead, try `element.getAttribute('value')`.\n\nHow can I drag and drop elements?\n---------------------------------\nYou can specify a sequence of [actions](http://www.protractortest.org/#/api?view=webdriver.WebDriver.prototype.actions)\nto drag an drop elements.  Note mouse actions do not work on Chrome with the HTML5 Drag and Drop API due to a known\n[Chromedriver issue](https://bugs.chromium.org/p/chromedriver/issues/detail?id=841)\n\n\nHow can I interact directly with the JavaScript running in my app?\n------------------------------------------------------------------\n\nIn general, the design of WebDriver tests is to interact with the page as a user would, so it gets a little tricky if you want to interact with the JavaScript directly. You should avoid it unless you have a good reason. However, there are ways of doing it.\n\nYou can use the `evaluate` function on a WebElement to get the value of an Angular expression in the scope of that element. e.g.\n```javascript\nelement(by.css('.foo')).evaluate('bar')\n```\nwould return whatever `{{bar}}` is in the scope of the element with class 'foo'.\n\nYou can also execute arbitrary JavaScript in the browser with:\n```javascript\nbrowser.executeScript('your script as a string');\n```\n\nYou can also pass a regular JavaScript function into `executeScript()`, for example:\n\n```javascript\nfunction getAngularVersion() {\n  return window.angular.version.full;\n}\n\nbrowser.executeScript(getAngularVersion).then(function (version) {\n  console.log(version);\n});\n```\n\nHow can I get hold of the browser's console?\n--------------------------------------------\nIn your test:\n```javascript\nbrowser.manage().logs().get('browser').then(function(browserLog) {\n  console.log('log: ' + require('util').inspect(browserLog));\n});\n```\n\nThis will output logs from the browser console. Note that logs below the set logging level will be ignored. The default level is warnings and above. To change, add a `loggingPrefs` object to your capabilities, as described [in the DesiredCapabilities docs](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities#loggingpreferences-json-object).\n\nSee an example ([spec.js](https://github.com/juliemr/protractor-demo/blob/master/howtos/browserlog/spec.js)) of using this API to fail tests if the console has errors.\n\nHow can I get screenshots of failures?\n-------------------------------------------- \nFirst, this is how you can take a screenshot:\n```javascript\nbrowser.takeScreenshot().then(function(png) {\n  var stream = fs.createWriteStream(\"/tmp/screenshot.png\");\n  stream.write(new Buffer(png, 'base64'));\n  stream.end();\n});\n```\n\nThe method to take a screenshot automatically on failure would depend on the type of failure.\n* For failures of entire specs (such as timeout or an expectation within the spec failed), you can add a reporter as below:\n\n```javascript\n// Note: this is using Jasmine 2 reporter syntax.\njasmine.getEnv().addReporter(new function() {\n  this.specDone = function(result) {\n    if (result.failedExpectations.length >0) {\n      // take screenshot\n    }\n  };\n});\n```\n\nNote, you can also choose to take a screenshot in `afterEach`. However, because Jasmine does not execute `afterEach` for timeouts, those would not produce screenshots\n* For failures of individual expectations, you can override jasmine's addMatcherResult/addExpectationResult function as such:\n\n```javascript\n// Jasmine 2\nvar originalAddExpectationResult = jasmine.Spec.prototype.addExpectationResult;\njasmine.Spec.prototype.addExpectationResult = function() {\n  if (!arguments[0]) {\n    // take screenshot\n    // this.description and arguments[1].message can be useful to constructing the filename.\n  }\n  return originalAddExpectationResult.apply(this, arguments);\n};\n```\n\n[See an example of taking screenshot on spec failures](https://github.com/juliemr/protractor-demo/blob/master/howtos/screenshot/screenshotReporter.js).\n\nHow do I produce an XML report of my test results?\n--------------------------------------------------\n\nYou can use the npm package jasmine-reporters@^2.0.0 and add a JUnit XML Reporter in the `onPrepare` block. This would look something like:\n\n```\nvar jasmineReporters = require('jasmine-reporters');\njasmine.getEnv().addReporter(new jasmineReporters.JUnitXmlReporter({\n    consolidateAll: true,\n    savePath: 'testresults',\n    filePrefix: 'reportXMLoutput'\n}));\n```\n\nHow can I catch errors such as ElementNotFound?\n-----------------------------------------------\nWebDriver throws errors when commands cannot be completed - e.g. not being able to click on an element which is obscured by another element. If you need to retry these actions, try using [webdriverjs-retry](https://github.com/juliemr/webdriverjs-retry). If you would just like to catch the error, do so like this\n```javascript\nelm.click().then(function() { /* passing case */}, function(err) { /* error handling here */})\n```\n\nHow can I test file uploads?\n----------------------------\nVia Webdriver, you can just send the absolute file path to the input with type=file. [See this example](http://stackoverflow.com/questions/21305298/how-to-upload-file-in-angularjs-e2e-protractor-testing/21314337#21314337).\n\nIf you need to test file upload on a remote server (such as Sauce Labs), [you need to set a remote file detector](https://saucelabs.com/resources/articles/selenium-file-upload). You can do this via `browser.setFileDetector()`, and you'll need access to the `selenium-webdriver` node module.\n\n```js\nvar remote = require('selenium-webdriver/remote');\nbrowser.setFileDetector(new remote.FileDetector());\n```\n\nI get an error: Page reload detected during async script. What does this mean?\n------------------------------------------------------------------------------\nThis means that there was a navigation or reload event while a command was pending\non the browser. Usually, this is because a click action or navigation resulted\nin a page load. Protractor is trying to wait for Angular to become stable,\nbut it's interrupted by the reload.\n\nYou may need to insert a `browser.wait` condition to make sure the load\nis complete before continuing.\n\nHow do I switch off an option in the CLI?\n-----------------------------------------\nThis has to do with the way `yargs` parses command line args. In order to pass a false value, do one of the following:\n\n1) `webdriver-manager update --chrome=0`\n\n2) `webdriver-manager update --chrome=false`\n\n3) `webdriver-manager update --no-chrome` (see https://github.com/yargs/yargs/blob/HEAD/docs/tricks.md#negate)\n\nWhy does Protractor fail when I decorate $timeout?\n--------------------------------------------------\nProtractor tracks outstanding $timeouts by default, and reports them in the error message if Protractor fails to synchronize with Angular in time.\n\nHowever, in order to do this Protractor needs to decorate $timeout. This means if your app decorates $timeout, you must turn off this behavior for Protractor. To do so pass in the 'untrackOutstandingTimeouts' flag. \n\nI still have a question\n-----------------------\n\nPlease see our [Contributing Guidelines](https://github.com/angular/protractor/blob/master/CONTRIBUTING.md#questions) for questions and issues.\n"
  },
  {
    "path": "docs/frameworks.md",
    "content": "Choosing a Framework\n====================\n\nProtractor supports two behavior driven development (BDD) test frameworks out of the box: Jasmine and Mocha. These frameworks are based on JavaScript and Node.js and provide the syntax, scaffolding, and reporting tools you will use to write and manage your tests.\n\n\nUsing Jasmine\n-------------\n\nCurrently, Jasmine Version 2.x is supported and the default test framework when you install Protractor. For more information about Jasmine, see the [Jasmine GitHub site](http://jasmine.github.io/). For more information regarding how to upgrade to Jasmine 2.x from 1.3, see the [Jasmine upgrade guide](/docs/jasmine-upgrade.md).\n\n\nUsing Mocha\n-----------\n\n_Note: Limited support for Mocha is available as of December 2013. For more information, see the [Mocha documentation site](http://mochajs.org/)._\n\nIf you would like to use the Mocha test framework, you'll need to use the BDD interface and Chai assertions with [Chai As Promised](http://chaijs.com/plugins/chai-as-promised).\n\nDownload the dependencies with npm. Mocha should be installed in the same place as Protractor - so if protractor was installed globally, install Mocha with -g.\n\n```\nnpm install -g mocha\nnpm install chai\nnpm install chai-as-promised\n```\n\nYou will need to require and set up Chai inside your test files:\n\n```js\nvar chai = require('chai');\nvar chaiAsPromised = require('chai-as-promised');\n\nchai.use(chaiAsPromised);\nvar expect = chai.expect;\n```\n\nYou can then use Chai As Promised as such:\n\n```js\nexpect(myElement.getText()).to.eventually.equal('some text');\n```\n\nFinally, set the 'framework' property to 'mocha', either by adding `framework: 'mocha'` to the config file or by adding `--framework=mocha` to the command line.\n\nOptions for Mocha such as 'reporter' and 'slow' can be given in the [config file](/spec/mochaConf.js) with mochaOpts:\n\n```js\nmochaOpts: {\n  reporter: \"spec\",\n  slow: 3000\n}\n```\n\nFor a full example, see Protractor’s own test: [/spec/mocha/lib_spec.js](/spec/mocha/lib_spec.js).\n\n\nUsing Cucumber\n--------------\n\n_Note: Cucumber is no longer included by default as of version `3.0`. You can integrate Cucumber with Protractor with the `custom` framework option. For more information, see the [Protractor Cucumber Framework site](https://github.com/mattfritz/protractor-cucumber-framework) or the [Cucumber GitHub site](https://github.com/cucumber/cucumber-js)._\n\n\nIf you would  like to use the Cucumber test framework, download the dependencies with npm. Cucumber should be installed in the same place as Protractor - so if protractor was installed globally, install Cucumber with -g.\n\n```\nnpm install -g cucumber\nnpm install --save-dev protractor-cucumber-framework\n```\n\nSet the 'framework' property to custom by adding `framework: 'custom'` and `frameworkPath: 'protractor-cucumber-framework'` to the `config file(cucumberConf.js)`\n\nOptions for Cucumber such as 'format' can be given in the config file with cucumberOpts, A basic cucumberConf.js file has been provided below:\n\n```js\n/*\nBasic configuration to run your cucumber\nfeature files and step definitions with protractor.\n**/\nexports.config = {\n\n  seleniumAddress: 'http://localhost:4444/wd/hub',\n\n  baseUrl: 'https://angularjs.org/',\n\n  capabilities: {\n      browserName:'chrome'\n  },\n\n  framework: 'custom',  // set to \"custom\" instead of cucumber.\n\n  frameworkPath: require.resolve('protractor-cucumber-framework'),  // path relative to the current config file\n\n  specs: [\n    './cucumber/*.feature'     // Specs here are the cucumber feature files\n  ],\n\n  // cucumber command line options\n  cucumberOpts: {\n    require: ['./cucumber/*.js'],  // require step definition files before executing features\n    tags: [],                      // <string[]> (expression) only execute the features or scenarios with tags matching the expression\n    strict: true,                  // <boolean> fail if there are any undefined or pending steps\n    format: [\"pretty\"],            // <string[]> (type[:path]) specify the output format, optionally supply PATH to redirect formatter output (repeatable)\n    'dry-run': false,              // <boolean> invoke formatters without executing steps\n    compiler: []                   // <string[]> (\"extension:module\") require files with the given EXTENSION after requiring MODULE (repeatable)\n  },\n\n onPrepare: function () {\n    browser.manage().window().maximize(); // maximize the browser before executing the feature files\n  }\n};\n```\n\nUsing Serenity/JS\n-----------------\n\n[Serenity/JS](http://serenity-js.org) is an acceptance testing library which can be integrated as a drop-in replacement of [Mocha](http://serenity-js.org/mocha/readme.html) or [Cucumber](http://serenity-js.org/cucumber/readme.html) framework adapters to provide advanced [scalability and reporting capabilities](http://serenity-js.org/overview/readme.html).\n\nTo use it, [install](http://serenity-js.org/overview/installation.html) and [configure](http://serenity-js.org/overview/configuration.html) your test framework of choice.\n\nNext, [install Serenity/JS](http://serenity-js.org/overview/installation.html):\n\n```\nnpm install serenity-js --save-dev\n```\n\nand instruct Protractor to use the Serenity/JS adapter:\n\n```js\nexports.config = {\n  framework: 'custom',\n  frameworkPath: require.resolve('serenity-js')\n\n  // ...\n};\n```\n\nSpecifying either `cucumberOpts` or `mochaOpts` in your Protractor configuration will make Serenity/JS infer that it should delegate the execution to [Cucumber](https://github.com/cucumber/cucumber-js) or [Mocha](https://mochajs.org/), respectively.\n\nTo learn more, [visit the project website](http://serenity-js.org/) or [follow the tutorial](http://serenity-js.org/from-scripts-to-serenity/readme.html).\n\nUsing a Custom Framework\n------------------------\n\nCheck section [Framework Adapters for Protractor](/lib/frameworks/README.md) specifically [Custom Frameworks](/lib/frameworks/README.md#custom-frameworks)\n"
  },
  {
    "path": "docs/getting-started.md",
    "content": "Getting Started\n===============\n\nTo get started quickly, begin with the [Tutorial](/docs/tutorial.md) which provides a step by step overview of how to install Protractor, create test files, set up config files, and run tests.\n\nProtractor needs two files to run, the test or spec file, and the configuration file. For additional information, see [Working with Spec and Config Files](/docs/api-overview.md).\n\nWhen writing tests, keep in mind that Protractor is a wrapper around WebDriverJS. You may want to skim through the [WebDriverJS Users Guide](https://github.com/SeleniumHQ/selenium/wiki/WebDriverJs) before writing any tests.\n\nThe WebDriverJS API is based on promises. To learn more, check out [The WebDriver Control Flow](/docs/control-flow.md).\n\nTo learn how Protractor, Selenium Server, and Selenium WebDriver work together, take a look at [How It Works](/docs/infrastructure.md). \n\nOnce you are familiar with Protractor, it is recommended that you start using Page Objects. For more information see [Using Page Objects to Organize Tests](/docs/page-objects.md).\n\nFor a complete list of the Protractor documentation, see the [Table of Contents](/docs/toc.md).\n"
  },
  {
    "path": "docs/infrastructure.md",
    "content": "How It Works\n============\n\n\nProtractor is an end-to-end test framework for AngularJS applications. Protractor is a Node.js program that supports the Jasmine and Mocha test frameworks.\n\nSelenium is a browser automation framework. Selenium includes the Selenium Server, the WebDriver APIs, and the WebDriver browser drivers.\n\nProtractor works in conjunction with Selenium to provide an automated test infrastructure that can simulate a user’s interaction with an Angular application running in a browser or mobile device.\n\n![Protractor Components Diagram](/docs/components.png)\n\nWhen working with Protractor, it’s important to keep the following in mind:\n - Protractor is a wrapper around WebDriverJS, the JavaScript bindings for the Selenium WebDriver API (before writing any tests, skim through the [WebDriverJS Users Guide](https://github.com/SeleniumHQ/selenium/wiki/WebDriverJs)).\n - WebDriver commands are asynchronus. They are scheduled on a control flow and return promises, not primitive values (see [The WebDriver Control Flow](/docs/control-flow.md)).\n - Your test scripts send commands to the Selenium Server, which in turn communicates with the browser driver. Read on for more details.\n\nProcess Communication\n---------------------\n\nA test using Selenium WebDriver involves three processes - the test script, the server, and the browser. The communication between these processes is shown in the diagram below.\n\n![WebDriver test Processes Diagram](/docs/processes.png)\n\nThe Selenium Server takes care of interpreting commands from the test and forwarding them to one or more browsers. Communication between the server and the browser uses the [WebDriver Wire Protocol](https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol), a JSON protocol. The command is interpreted by the Browser Driver.\n\nWith Protractor, the test script is run using Node.js. Protractor runs an extra command before performing any action on the browser to ensure that the application being tested has stabilized. For example, let's look at the following snippet of test code.\n\n    element(by.css('button.myclass')).click();\n\nThis will result in three commands being sent to the Browser Driver\n\n - [/session/:sessionId/execute_async](https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#sessionsessionidexecute_async) - First, Protractor tells the browser to run a snippet of JavaScript. This is a custom command which asks Angular to respond when the application is done with all timeouts and asynchronous requests, and ready for the test to resume.\n\n - [/session/:sessionId/element](https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#post-sessionsessionidelement) - Then, the command to find the element is sent.\n\n - [/session/:sessionId/element/:id/click](https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#post-sessionsessionidelementidclick) - Finally the command to perform a click action is sent.\n\n\n\n"
  },
  {
    "path": "docs/jasmine-upgrade.md",
    "content": "Upgrading from Jasmine 1.3 to 2.x\n=================================\n\nFirst, please read [Jasmine's official upgrade documentation](http://jasmine.github.io/2.1/upgrading.html).\n\n### In your conf file\n\nSpecify that you want to use jasmine2.x:\n\n```javascript\nexports.config = {\n  // Specify you want to use jasmine 2.x as you would with mocha. Note, 'jasmine' by default will use the latest jasmine framework.\n  framework: 'jasmine'\n};\n\n```\n\nSimilar to jasmine 1.3, you may include `jasmineNodeOpts` in the config file. However, because we changed the runner from \"https://github.com/juliemr/minijasminenode\" to \"https://github.com/jasmine/jasmine-npm\", the options have changed slightly. In particular, we will only support the options in the official \"jasmine-npm\":\n\n```javascript\njasmineNodeOpts: {\n  // If true, print colors to the terminal.\n  showColors: true,\n  // Default time to wait in ms before a test fails.\n  defaultTimeoutInterval: 30000,\n  // Function called to print jasmine results.\n  print: function() {},\n  // If set, only execute specs whose names match the pattern, which is\n  // internally compiled to a RegExp.\n  grep: 'pattern',\n  // Inverts 'grep' matches\n  invertGrep: false\n}\n```\n\nNotably options `print` and `grep` are new, but we will no longer support options `isVerbose` and `includeStackTrace` (unless, of course, \"jasmine-npm\" introduces these options).\n\n### In your specs\n\n#### Focused specs\n\nInstead of `iit`, please use `fit`. Instead of `ddescribe`, please use `fdescribe`.\n\n#### Timeouts\n\nHaving a custom timeout for an `it` block as a third parameter is not currently\nsupported in Jasmine2, but it will be supported in a release soon. See [this issue](https://github.com/angular/protractor/issues/1701).\n\n#### Custom matchers\n\nSee http://jasmine.github.io/2.0/upgrading.html#section-Custom_Matchers\n\nBefore:\n```javascript\ntoHaveText: function(expectedText) {\n  return this.actual.getText().then(function(actualText) {\n    return expectedText === actualText;\n  });\n}\n```\n\nNow:\n```javascript\ntoHaveText: function() {\n  return {\n    compare: function(actual, expectedText) {\n      return {\n        pass: actual.getText().then(function(actualText) {\n          return actualText === expectedText;\n        })\n      };\n    }\n  };\n}\n```\n\n#### Asynchronous specs\n\nNote: `minijasminenode` provided asynchronous support for jasmine1.3 before (i.e. via done callback). Jasmine 2.x now provides the support natively, but the change is mostly transparent to protractor users who are upgrading from jasmine1.3.\n\nYou can still pass in the done parameter as part of your asynchronous spec, but the syntax for failing it has changed.\n\nBefore:\n```javascript\nit('async spec', function(done) {\n  setTimeout(function() {\n    if (passed) {\n      done(); // When done\n    } else {\n      done('failure message'); // To fail spec\n    }\n  }, 5000);\n});\n```\n\nNow:\n```javascript\nit('async spec', function(done) {\n  setTimeout(function() {\n    if (passed) {\n      done(); // When done\n    } else {\n      done.fail('failure message'); // To fail spec\n    }\n  }, 5000);\n});\n```\n\n#### Reporters\n\nThe syntax for custom reporters has changed for Jasmine2. If you were previously\nadding reporters from a node module, such as the `jasmine-reporters` package on\nnpm, make sure you upgrade to a version which supports Jasmine2. If you are\nwriting your own reporter, see the [Jasmine docs on custom reporters](http://jasmine.github.io/2.1/custom_reporter.html).\n"
  },
  {
    "path": "docs/locators.md",
    "content": "Using Locators\n==============\n\nThe heart of end-to-end tests for webpages is finding DOM elements, interacting with them, and getting information about the current state of your application. This doc is an overview of how to locate and perform actions on DOM elements using Protractor.\n\nOverview\n--------\n\nProtractor exports a global function `element`, which takes a *Locator* and will return an *ElementFinder*. This function finds a single element - if you need to manipulate multiple elements, use the `element.all` function.\n\nThe *ElementFinder* has a set of *action methods*, such as `click()`, `getText()`, and `sendKeys`. These are the core way to interact with an element and get information back from it.\n\nWhen you find elements in Protractor all actions are asynchronous. Behind the scenes, all actions are sent to the browser being controlled using the JSON Webdriver Wire Protocol. The browser then performs the action as a user natively would.\n\nLocators\n--------\n\nA locator tells Protractor how to find a certain DOM element. Protractor exports locator factories on the global `by` object. The most common locators are:\n\n```js\n// Find an element using a css selector.\nby.css('.myclass')\n\n// Find an element with the given id.\nby.id('myid')\n\n// Find an element using an input name selector.\nby.name('field_name')\n\n// Find an element with a certain ng-model.\n// Note that at the moment, this is only supported for AngularJS apps.\nby.model('name')\n\n// Find an element bound to the given variable.\n// Note that at the moment, this is only supported for AngularJS apps.\nby.binding('bindingname')\n```\n\nFor a list of Protractor-specific locators, see the [Protractor API: ProtractorBy](http://angular.github.io/protractor/#/api?view=ProtractorBy).\n\nThe locators are passed to the `element` function, as below:\n\n```js\nelement(by.css('some-css'));\nelement(by.model('item.name'));\nelement(by.binding('item.name'));\n```\n\nWhen using CSS Selectors as a locator, you can use the shortcut $() notation:\n\n```js\n$('my-css');\n\n// Is the same as:\n\nelement(by.css('my-css'));\n```\n\nActions\n-------\n\nThe `element()` function returns an ElementFinder object. The ElementFinder knows how to locate the DOM element using the locator you passed in as a parameter, but it has not actually done so yet. It will not contact the browser until an *action* method has been called.\n\nThe most common action methods are:\n\n```js\nvar el = element(locator);\n\n// Click on the element.\nel.click();\n\n// Send keys to the element (usually an input).\nel.sendKeys('my text');\n\n// Clear the text in an element (usually an input).\nel.clear();\n\n// Get the value of an attribute, for example, get the value of an input.\nel.getAttribute('value');\n```\n\nSince all actions are asynchronous, all action methods return a [promise](https://github.com/SeleniumHQ/selenium/wiki/WebDriverJs#promises). So, to log the text of an element, you would do something like:\n```js\nvar el = element(locator);\nel.getText().then(function(text) {\n  console.log(text);\n});\n```\n\nAny action available in WebDriverJS on a WebElement is available on an ElementFinder. [See a full list](http://angular.github.io/protractor/#/api?view=webdriver.WebElement).\n\n\nFinding Multiple Elements\n-------------------------\n\nTo deal with multiple DOM elements, use the `element.all` function. This also takes a locator as its only parameter.\n\n```js\nelement.all(by.css('.selector')).then(function(elements) {\n  // elements is an array of ElementFinders.\n});\n```\n\n`element.all()` has several helper functions:\n\n```js\n// Number of elements.\nelement.all(locator).count();\n\n// Get by index (starting at 0).\nelement.all(locator).get(index);\n\n// First and last.\nelement.all(locator).first();\nelement.all(locator).last();\n```\n\nWhen using CSS Selectors as a locator, you can use the shortcut $$() notation:\n\n```js\n$$('.selector');\n\n// Is the same as:\n\nelement.all(by.css('.selector'));\n```\n\n\nFinding Sub-Elements\n--------------------\n\nTo find sub-elements, simply chain element and element.all functions together as shown below.\n\nUsing a single locator to find:\n\n - an element\n    ```js\n    element(by.css('some-css'));\n    ```\n\n - a list of elements:\n    ```js\n    element.all(by.css('some-css'));\n    ```\n\nUsing chained locators to find:\n\n - a sub-element:\n    ```js\n    element(by.css('some-css')).element(by.tagName('tag-within-css'));\n    ```\n\n - to find a list of sub-elements:\n    ```js\n    element(by.css('some-css')).all(by.tagName('tag-within-css'));\n    ```\n\nYou can chain with get/first/last as well like so:\n\n```js\nelement.all(by.css('some-css')).first().element(by.tagName('tag-within-css'));\nelement.all(by.css('some-css')).get(index).element(by.tagName('tag-within-css'));\nelement.all(by.css('some-css')).first().all(by.tagName('tag-within-css'));\n```\n\nBehind the Scenes: ElementFinders versus WebElements\n----------------------------------------------------\n\nIf you're familiar with WebDriver and WebElements, or you're just curious about the WebElements mentioned above, you may be wondering how they relate to ElementFinders.\n\nWhen you call `driver.findElement(locator)`, WebDriver immediately sends a command over to the browser asking it to locate the element. This isn't great for creating page objects, because we want to be able to do things in setup (before a page may have been loaded) like\n\n```js\nvar myButton = ??;\n```\n\nand re-use the variable `myButton` throughout your test. ElementFinders get around this by simply storing the locator information until an action is called.\n\n```js\nvar myButton = element(locator);\n// No command has been sent to the browser yet.\n```\n\nThe browser will not receive any commands until you call an action.\n\n```js\nmyButton.click();\n// Now two commands are sent to the browser - find the element, and then click it.\n```\n\nElementFinders also enable chaining to find subelements, such as `element(locator1).element(locator2)`.\n\nAll WebElement actions are wrapped in this way and available on the ElementFinder, in addition to a couple helper methods like `isPresent`. \n\nYou can always access the underlying WebElement using `element(locator).getWebElement()`, but you should not need to.\n\n"
  },
  {
    "path": "docs/mobile-setup.md",
    "content": "Mobile Setup\n============\n\nThere are many options for using WebDriver to test on mobile browsers. Protractor\ndoes not yet officially support or run its own tests against a particular configuration, but the following are some notes on various setup options.\n\nSetting Up Protractor with Appium - Android/Chrome\n-------------------------------------\n###### Setup\n\nUse `webdriver-manager` to install `appium` and the Android SDK.  See details\n[on the WebDriver Manager page](\nhttps://github.com/angular/webdriver-manager/blob/master/docs/mobile.md).\n\n###### Running Tests\n*   Ensure app is running if testing local app (Skip if testing public website):\n\n```shell\n> npm start # or `./scripts/web-server.js`\nStarting express web server in /workspace/protractor/testapp on port 8000\n```\n\n*   Start appium and the Android Emulators (details [on the WebDriver Manager\npage](https://github.com/angular/webdriver-manager/blob/master/docs/mobile.md)).\n\n```shell\n> webdriver-manager start --android\n```\n\n*   Configure protractor:\n\nConfig File:\n```javascript\nexports.config = {\n  seleniumAddress: 'http://localhost:4723/wd/hub',\n\n  specs: ['basic/*_spec.js'],\n\n  // Reference: https://github.com/appium/sample-code/blob/master/sample-code/examples/node/helpers/caps.js\n  capabilities: {\n    browserName: 'chrome',\n    platformName: 'Android',\n    platformVersion: '7.0',\n    deviceName: 'Android Emulator',\n  },\n\n  baseUrl: 'http://10.0.2.2:8000'\n};\n```\n*Note the following:*\n - baseUrl is 10.0.2.2 instead of localhost because it is used to access the localhost of the host machine in the android emulator\n - selenium address is using port 4723\n\nSetting Up Protractor with Appium - iOS/Safari\n-------------------------------------\n###### Setup\n\nUse `webdriver-manager` to install `appium` and the Android SDK.  See details\n[on the WebDriver Manager page](\nhttps://github.com/angular/webdriver-manager/blob/master/docs/mobile.md).\n\n###### Running Tests\n*   Ensure app is running if testing local app (Skip if testing public website):\n\n```shell\n> npm start # or `./scripts/web-server.js`\nStarting express web server in /workspace/protractor/testapp on port 8000\n```\n\n*   Start Appium:\n\n```shell\n> webdriver-manager start\n```\n*Note: Appium listens to port 4723 instead of 4444.*\n\n*   Configure protractor:\n\niPhone:\n```javascript\nexports.config = {\n  seleniumAddress: 'http://localhost:4723/wd/hub',\n\n  specs: [\n    'basic/*_spec.js'\n  ],\n\n  // Reference: https://github.com/appium/sample-code/blob/master/sample-code/examples/node/helpers/caps.js\n  capabilities: {\n    browserName: 'safari',\n    platformName: 'iOS',\n    platformVersion: '7.1',\n    deviceName: 'iPhone Simulator',\n  },\n\n  baseUrl: 'http://localhost:8000'\n};\n```\n\niPad:\n```javascript\nexports.config = {\n  seleniumAddress: 'http://localhost:4723/wd/hub',\n\n  specs: [\n    'basic/*_spec.js'\n  ],\n\n  // Reference: https://github.com/appium/sample-code/blob/master/sample-code/examples/node/helpers/caps.js\n  capabilities: {\n    browserName: 'safari',\n    platformName: 'iOS',\n    platformVersion: '7.1',\n    deviceName: 'IPad Simulator',\n  },\n\n  baseUrl: 'http://localhost:8000'\n};\n\n```\n*Note the following:*\n - note capabilities\n - baseUrl is localhost (not 10.0.2.2)\n - selenium address is using port 4723\n\nSetting Up Protractor with Selendroid\n-------------------------------------\n###### Setup\n*   Install Java SDK (>1.6) and configure JAVA_HOME (Important: make sure it's not pointing to JRE).\n*   Follow http://spring.io/guides/gs/android/ to install and set up Android developer environment. Do not set up Android Virtual Device as instructed here.\n*   From commandline, 'android avd' and then follow Selendroid's recommendation (http://selendroid.io/setup.html#androidDevices). Take note of the emulator accelerator. Here's an example:\n\n```shell\n> android list avd\nAvailable Android Virtual Devices:\n    Name: myAvd\n  Device: Nexus 5 (Google)\n    Path: /Users/hankduan/.android/avd/Hank.avd\n  Target: Android 4.4.2 (API level 19)\n Tag/ABI: default/x86\n    Skin: WVGA800\n```\n\n###### Running Tests\n*   Ensure app is running if testing local app (Skip if testing public website):\n\n```shell\n> npm start # or `./scripts/web-server.js`\nStarting express web server in /workspace/protractor/testapp on port 8000\n```\n\n*   Start emulator manually (at least the first time):\n\n```shell\n> emulator -avd myAvd\nHAX is working and emulator runs in fast virt mode\n```\n\n*Note: The last line that tells you the emulator accelerator is running.*\n*   Start selendroid:\n\n```shell\n> java -jar selendroid-standalone-0.9.0-with-dependencies.jar\n...\n```\n\n*   Once selendroid is started, you should be able to go to \"http://localhost:4444/wd/hub/status\" and see your device there:\n\n```javascript\n{\"value\":{\"os\":{\"name\":\"Mac OS X\",\"arch\":\"x86_64\",\"version\":\"10.9.2\"},\"build\":{\"browserName\":\"selendroid\",\"version\":\"0.9.0\"},\"supportedDevices\":[{\"emulator\":true,\"screenSize\":\"WVGA800\",\"avdName\":\"Hank\",\"androidTarget\":\"ANDROID19\"}],\"supportedApps\":[{\"mainActivity\":\"io.selendroid.androiddriver.WebViewActivity\",\"appId\":\"io.selendroid.androiddriver:0.9.0\",\"basePackage\":\"io.selendroid.androiddriver\"}]},\"status\":0}\n```\n\n*   Configure protractor:\n\n```javascript\nexports.config = {\n  seleniumAddress: 'http://localhost:4444/wd/hub',\n\n  specs: [\n    'basic/*_spec.js'\n  ],\n\n  capabilities: {\n    'browserName': 'android'\n  },\n\n  baseUrl: 'http://10.0.2.2:8000'\n};\n```\n\n*Note the following:*\n - browserName is 'android'\n - baseUrl is 10.0.2.2 instead of localhost because it is used to access the localhost of the host machine in the android emulator\n\nUsing `wd` and `wd-bridge`\n-------------------------------------\n\nAs of version 5.1.0, Protractor uses `webdriver-js-extender` to provide all the\nmobile commands you should need (see the API page for details).  However, if you\nprefer `wd`, you can access it via `wd-bridge`.  First, install both `wd` and\n`wd-bridge` as `devDependencies`:\n\n```shell\nnpm install --save-dev wd wd-bridge\n```\n\nThen, in your config file:\n\n```javascript\n  // configuring wd in onPrepare\n  // wdBridge helps to bridge wd driver with other selenium clients\n  // See https://github.com/sebv/wd-bridge/blob/master/README.md\n  onPrepare: function () {\n    var wd = require('wd'),\n      protractor = require('protractor'),\n      wdBridge = require('wd-bridge')(protractor, wd);\n    wdBridge.initFromProtractor(exports.config);\n  }\n```\n\n"
  },
  {
    "path": "docs/page-objects.md",
    "content": "Using Page Objects to Organize Tests\n====================================\n\nWhen writing end-to-end tests, a common pattern is to use [Page Objects](https://github.com/SeleniumHQ/selenium/wiki/PageObjects). Page Objects help you write cleaner tests by encapsulating information about the elements on your application page. A Page Object can be reused across multiple tests, and if the template of your application changes, you only need to update the Page Object.\n\nWithout Page Objects\n--------------------\n\nHere’s a simple test script ([example_spec.js](/example/example_spec.js)) for ‘The Basics’ example on the [angularjs.org](http://www.angularjs.org) homepage.\n\n```js\ndescribe('angularjs homepage', function() {\n  it('should greet the named user', function() {\n    browser.get('http://www.angularjs.org');\n    element(by.model('yourName')).sendKeys('Julie');\n    var greeting = element(by.binding('yourName'));\n    expect(greeting.getText()).toEqual('Hello Julie!');\n  });\n});\n```\n\nWith Page Objects\n----------------\n\nTo switch to Page Objects, the first thing you need to do is create a Page Object. A Page Object for ‘The Basics’ example on the angularjs.org homepage could look like this:\n\n```js\nvar AngularHomepage = function() {\n  var nameInput = element(by.model('yourName'));\n  var greeting = element(by.binding('yourName'));\n\n  this.get = function() {\n    browser.get('http://www.angularjs.org');\n  };\n\n  this.setName = function(name) {\n    nameInput.sendKeys(name);\n  };\n\n  this.getGreetingText = function() {\n    return greeting.getText();\n  };\n};\nmodule.exports = new AngularHomepage();\n```\n\nOr, if using `async / await`, something like this: (Note that functions\nthat don't use `await` shouldn't have the `async` prefix.)\n\n```js\nvar AngularHomepage = function() {\n  var nameInput = element(by.model('yourName'));\n  var greeting = element(by.binding('yourName'));\n\n  this.get = async function() {\n    await browser.get('http://www.angularjs.org');\n  };\n\n  this.setName = async function(name) {\n    await nameInput.sendKeys(name);\n  };\n\n  this.getGreetingText = function() {\n    return greeting.getText();\n  };\n\n  // Not async, returns the element\n  this.getGreeting = function() {\n    return greeting;\n  };\n};\nmodule.exports = new AngularHomepage();\n```\n\nThe next thing you need to do is modify the test script to use the Page Object and its properties. Note that the _functionality_ of the test script itself does not change (nothing is added or deleted).\n\nIn the test script, you'll `require` the Page Object as presented above. The path to the Page Object _will be relative_ to your spec, so adjust accordingly.\n\n```js\nvar angularHomepage = require('./AngularHomepage');\ndescribe('angularjs homepage', function() {\n  it('should greet the named user', function() {\n    angularHomepage.get();\n\n    angularHomepage.setName('Julie');\n\n    expect(angularHomepage.getGreetingText()).toEqual('Hello Julie!');\n  });\n});\n```\n\nIf using `async / await`, that would turn into something like:\n\n```js\nvar angularHomepage = require('./AngularHomepage');\ndescribe('angularjs homepage', function() {\n  it('should greet the named user', async function() {\n    await angularHomepage.get();\n\n    await angularHomepage.setName('Julie');\n\n    expect(await angularHomepage.getGreetingText()).toEqual('Hello Julie!');\n  });\n});\n```\n\nConfiguring Test Suites\n-----------------------\n\nIt is possible to separate your tests into various test suites. In your config file, you could setup the suites option as shown below:\n\n```js\nexports.config = {\n  // The address of a running selenium server.\n  seleniumAddress: 'http://localhost:4444/wd/hub',\n\n  // Capabilities to be passed to the webdriver instance.\n  capabilities: {\n    'browserName': 'chrome'\n  },\n\n  // Spec patterns are relative to the location of the spec file. They may\n  // include glob patterns.\n  suites: {\n    homepage: 'tests/e2e/homepage/**/*Spec.js',\n    search: ['tests/e2e/contact_search/**/*Spec.js',\n      'tests/e2e/venue_search/**/*Spec.js']\n  },\n\n  // Options to be passed to Jasmine-node.\n  jasmineNodeOpts: {\n    showColors: true, // Use colors in the command line report.\n  }\n};\n```\n\nFrom the command line, you can then easily switch between running one or the other suite of tests. This command will run only the homepage section of the tests:\n\n    protractor protractor.conf.js --suite homepage\n\nAdditionally, you can run specific suites of tests with the command:\n\n    protractor protractor.conf.js --suite homepage,search\n"
  },
  {
    "path": "docs/plugins.md",
    "content": "Protractor Plugins\n=================\n\nPlugins extend Protractor's base features by using hooks during test\nexecution to gather more data and potentially modify the test output.\n\nThe Protractor API and available plugins are *BETA* and may change\nwithout a major version bump.\n\n## Table of contents\n* [Using Plugins](/docs/plugins.md#using-plugins)\n* [Writing Plugins](/docs/plugins.md#writing-plugins)\n* [First Party Plugins](/docs/plugins.md#first-party-plugins)\n* [Community Plugins](/docs/plugins.md#community-plugins)\n\nUsing Plugins\n-------------\n\nPlugins are enabled via your config file.\n\n```javascript\n// protractor.conf.js\nexports.config = {\n\n  // ... the rest of your config\n\n  plugins: [{\n    // The only required field for each plugin is the path to that\n    // plugin's entry script.\n    // Paths are relative to location of the config file.\n    path: 'path/to/plugin/index.js',\n\n    // Plugins may use additional options specified here. See the\n    // individual plugin docs for more information.\n    option1: 'foo',\n    option2: 'bar'\n  }]\n};\n```\n\nIf your plugin is a node module, you may use it with the `package` option. For\nexample, if you did `npm install example-protractor-plugin` your config would\nlook like:\n\n```javascript\n  plugins: [{\n    package: 'example-protractor-plugin',\n  }]\n```\n\nIf you are writing a small plugin which will only be used by one config file,\nyou can write the plugin inline into the config:\n\n```javascript\n  plugins: [{\n    inline: {\n      setup: function() { ... },\n      teardown: function() { ... },\n      ...\n    }\n  }]\n```\n\nWhen using plugins, you should specify exactly one of `path`, `package`, or\n`inline`.\n\nWriting Plugins\n---------------\n\nPlugins are designed to work with any test framework (Jasmine, Mocha, etc),\nso they use generic hooks which Protractor provides. Plugins may change\nthe output of Protractor by returning a results object.\n\nPlugins are node modules that export an object implementing the\n`ProtractorPlugin` interface.  Please see [`/lib/plugins.ts`](\n/lib/plugins.ts#L25) for a list of hooks that are available to plugins.\n\n##### Provided properties and functions\n\nExtra properties are added to your `module.exports` when Protractor loads your\nplugin.  These allow your plugin to do things like access its configuration\nblock or add test results.  See `/lib/plugins.ts` for the full list.\n\n### Writing Plugins in TypeScript\n\nThe simplest way to write plugins in TypeScript is to mirror the javascript\nsyntax:\n\n```typescript\nexport function onPageLoad(): void {\n  this.addSuccess({specName: 'Hello, World!'});\n};\n```\n\nIf you want your code more heavily typed, you can write your plugin with\nthe `ProtractorPlugin` interface:\n\n```typescript\nimport {ProtractorPlugin} from 'protractor';\n\n// creating a \"var module: any\" will allow use of module.exports\ndeclare var module: any;\n\nlet myPlugin: ProtractorPlugin = {\n  addSuccess(info: {specName: string}) {\n    console.log('on success: ' + info.specName);\n  },\n  onPageLoad() {\n    this.addSuccess({specName: 'Hello, World!'});\n  }\n};\n\nmodule.exports = myPlugin;\n\n```\n\n\nFirst Party Plugins\n-------------------\n\n* Accessibility Plugin\n\n  The accessibility plugin runs a set of accessibility audits on your webapp.\n  It is published at the npm module [`protractor-accessibility-plugin`](https://www.npmjs.com/package/protractor-accessibility-plugin) and stored at\n  the github repo [angular/protractor-accessibility-plugin](https://github.com/angular/protractor-accessibility-plugin).\n\n* Timeline Plugin\n\n  The timeline plugin gathers test timeline information from various sources and\n  presents the output visually.  This improves understanding of where latency\n  issues are in tests.  It is published at the npm module\n  [`protractor-timeline-plugin`](https://www.npmjs.com/package/protractor-timeline-plugin) and stored at the\n  github repo [angular/protractor-timeline-plugin](https://github.com/angular/protractor-timeline-plugin).\n\n* Console Plugin (Chrome Only)\n\n  The console plugin checks the browser log after each test for warnings and\n  errors.  It is published at the npm module [`protractor-console-plugin`](https://www.npmjs.com/package/protractor-console-plugin) and stored at the\n  github repo [angular/protractor-console-plugin](https://github.com/angular/protractor-console-plugin).\n\n* ngHint Plugin (NOT MAINTAINED)\n\n  The ngHint plugin uses [Angular Hint](https://github.com/angular/angular-hint)\n  to generate run-time hinting and then turns these hints into Protractor tests.\n  It is published at the npm module [`protractor-ng-hint-plugin`](https://www.npmjs.com/package/protractor-ng-hint-plugin) and stored at the\n  github repo [angular/protractor-ng-hint-plugin](https://github.com/angular/protractor-ng-hint-plugin).\n\nCommunity Plugins\n-----------------\n\nThis list is here for reference and the plugins included are not developed or\nmantained by protractor's team by any means. If you find any issues with this\nplugins please report them to the corresponding plugin developer.\n\n* [Protractor testability plugin](https://github.com/alfonso-presa/protractor-testability-plugin): this plugin enables synchronous testing with protractor for features that are not developed using the services provided by AngularJS, preventing the need of additional waits coded in the tests. This happens for example if you have WebSockets communication with the server or for web applications built with frameworks different than AngularJS.\n\n* [protractor-fail-fast](https://github.com/Updater/protractor-fail-fast): Allows Protractor to \"fail-fast\", forcing all test runners to exit if one of them encounters a failing test. For scenarios where a failure means the entire build has failed (e.g. CI), failing fast can save a tremendous amount of time.\n\n* [protractor-numerator](https://github.com/Marketionist/protractor-numerator): This plugin gives you readable functions for getting elements by their numbers inside Protractor tests. Adds functions like `.second()`, `.third()`, etc. instead of `.get(1)`, `.get(2)`, etc.\n\n* [Ng-apimock](https://github.com/mdasberg/ng-apimock): this plugin adds the ability to use scenario based api mocking for local development and protractor testing for both AngularJS and Angular applications.\n\n* [protractor-cucumber-steps](https://github.com/Marketionist/protractor-cucumber-steps): This plugin provides Cucumber steps (step definitions) written with Protractor for end-to-end tests.\n"
  },
  {
    "path": "docs/protractor-setup.md",
    "content": "Setting Up Protractor\n=====================\n\nPrerequisites\n-------------\n\n**Node.js**\n\nProtractor is a [Node.js](http://nodejs.org/) program. To run Protractor, you will need to have Node.js installed. Check the version of node you have by running `node --version`. It should be greater than v0.10.0. \n\nNode.js comes with the Protractor [npm](https://www.npmjs.org/) package, which you can use to install Protractor.\n\n\nInstalling Protractor\n---------------------\n\nUse npm to install Protractor globally (omit the -g if you’d prefer not to install globally):\n\n    npm install -g protractor\n\nCheck that Protractor is working by running `protractor --version`.\n\nThe Protractor install includes the following:\n - `protractor` command line tool\n - `webdriver-manager` command line tool\n - Protractor API (library)\n"
  },
  {
    "path": "docs/referenceConf.js",
    "content": "/**\n * The reference configuration has moved. Please refer to /lib/config.ts\n */\n"
  },
  {
    "path": "docs/server-setup.md",
    "content": "Setting Up the Selenium Server\n==============================\n\nWhen working with Protractor, you need to specify how to connect to the browser drivers which will start up and control the browsers you are testing on. You will most likely use the Selenium Server. The server acts as proxy between your test script (written with the WebDriver API) and the browser driver (controlled by the WebDriver protocols).\n\nThe server forwards commands from your script to the driver and returns responses from the driver to your script. The server can handle multiple scripts in different languages. The server can startup and manage multiple browsers in different versions and implementations.\n\n         [Test Scripts] < ------------ > [Selenium Server] < ------------ > [Browser Drivers]\n\nThe [config file](/lib/config.ts) includes several options for the Selenium Server, which are explained in the sections below.\n\n\nStandalone Selenium Server\n--------------------------\n\nTo run the Selenium Server on your local machine, use the standalone Selenium Server. \n\n**JDK**\n\nTo run a local Selenium Server, you will need to have the [Java Development Kit (JDK)](http://www.oracle.com/technetwork/java/javase/downloads/index.html) installed.  Check this by running `java -version` from the command line.\n\n\n**Installing and Starting the Server**\n\nTo install and start the standalone Selenium Server manually, use the webdriver-manager command line tool, which comes with Protractor.\n\n1. Run the update command:\n    `webdriver-manager update`\n     This will install the server and ChromeDriver.\n\n2. Run the start command:\n   `webdriver-manager start`\n    This will start the server. You will see a lot of output logs, starting with INFO. The last \n    line will  be 'Info - Started org.openqa.jetty.jetty.Server'.\n\n3. Leave the server running while you conduct your test sessions.\n\n4. In your config file, set `seleniumAddress` to the address of the running server. This defaults to\n   `http://localhost:4444/wd/hub`.\n\n\n**Starting the Server from a Test Script**\n\nTo start the standalone Selenium Server from within your test script, set these options in your config file:\n\n - `seleniumServerJar` -  The location of the jar file for the standalone Selenium Server. Specify a file location.\n\n - `seleniumPort` - The port to use to start the standalone Selenium Server. If not specified, defaults to 4444.\n\n - `seleniumArgs` -  Array of command line options to pass to the server. For a full list, start the server with the `-help` flag.\n\n**Connecting to a Running Server**\n\nTo connect to a running instance of a standalone Selenium Server, set this option:\n\n - `seleniumAddress` -  Connect to a running instance of a standalone Selenium Server. The address will be a URL.\n\nPlease note that if you set seleniumAddress, the settings for `seleniumServerJar`, `seleniumPort`, `seleniumArgs`, `browserstackUser`, `browserstackKey`, `sauceUser` and `sauceKey` will be ignored.\n\n\nRemote Selenium Server\n----------------------\n\nTo run your tests against a remote Selenium Server, you will need an account with a service that hosts the server (and the browser drivers). Protractor has built in support for [BrowserStack](https://www.browserstack.com) , [Sauce Labs](http://www.saucelabs.com) and [TestObject](https://www.testobject.com).\n\n**Using TestObject as remote Selenium Server**\n\nIn your config file, set these options:\n - `testobjectUser` -  The username for your TestObject account.\n - `testobjectKey` -  The key for your TestObject account.\n\nPlease note that if you set `testobjectUser` and `testobjectKey`, the settings for `kobitonUser`, `kobitonKey`, `browserstackUser`, `browserstackKey`, `seleniumServerJar`, `seleniumPort`, `seleniumArgs`, `sauceUser` and `sauceKey` will be ignored.\n\n**Using Kobiton as remote Selenium Server**\n\nIn your config file, set these options:\n - `kobitonUser` -  The username for your Kobiton account.\n - `kobitonKey` -  The API key from your Kobiton account.\n\nPlease note that if you set `kobitonUser` and `kobitonKey`, the settings for `browserstackUser`, `browserstackKey`, `seleniumServerJar`, `seleniumPort`, `seleniumArgs`, `sauceUser` and `sauceKey` will be ignored.\n\n**Using BrowserStack as remote Selenium Server**\n\nIn your config file, set these options:\n - `browserstackUser` -  The username for your BrowserStack account.\n - `browserstackKey` -  The key for your BrowserStack account.\n\nPlease note that if you set `browserstackUser` and `browserstackKey`, the settings for `seleniumServerJar`, `seleniumPort`, `seleniumArgs`, `sauceUser` and `sauceKey` will be ignored.\n\nYou can optionally set the `name` property in a capability in order to give the jobs a name on the server.  Otherwise they will just be allotted a random hash.\n\n**Using Sauce Labs as remote Selenium Server**\n\nIn your config file, set these options:\n - `sauceUser` -  The username for your Sauce Labs account.\n - `sauceKey` -  The key for your Sauce Labs account.\n\nPlease note that if you set `sauceUser` and `sauceKey`, the settings for `seleniumServerJar`, `seleniumPort`, `seleniumArgs`, `browserstackUser` and `browserstackKey` will be ignored.\n\nYou can optionally set the `name` property in a capability in order to give the jobs a name on the server.  Otherwise they will just be called `Unnamed Job`.\n\n\nConnecting Directly to Browser Drivers\n--------------------------------------\n\nProtractor can test directly against Chrome and Firefox without using a Selenium Server. To use this, in your config file set `directConnect: true`.\n\n - `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.\n\nThe advantage of directly connecting to browser drivers is that your test scripts may start up and run faster.\n"
  },
  {
    "path": "docs/style-guide.md",
    "content": "Protractor style guide\n======================\n\nThis style guide was originally created by\n[Carmen Popoviciu](https://github.com/CarmenPopoviciu) and \n[Andres Dominguez](https://github.com/andresdominguez). It is based on Carmen's \nProtractor \n[style guide](https://github.com/CarmenPopoviciu/protractor-styleguide) and \nGoogle's Protractor style guide.\n\n## Video\n\nCarmen and Andres gave a talk about this style guide at \n[AngularConnect](http://angularconnect.com/) in London. Here's the video in \ncase you want to watch it.\n\n<a href=\"http://www.youtube.com/watch?feature=player_embedded&v=-lTGnYwnEuM\" \n  target=\"_blank\"><img src=\"http://img.youtube.com/vi/-lTGnYwnEuM/0.jpg\" \n  alt=\"Protractor styleguide @AngularConnect\" width=\"240\" height=\"180\" \n  border=\"10\"/></a>\n\n## Table of contents\n\n* [Test suites](#test-suites)\n* [Locator strategies](#locator-strategies)\n* [Page objects](#page-objects)\n* [Project structure](#project-structure)\n\n# Test suites\n\n### Don't e2e test what’s been unit tested\n\n**Why?**\n* Unit tests are much faster than e2e tests\n* Avoid duplicate tests\n\n### Make your tests independent at least at the file level\n\n* Protractor can run your tests in parallel when you enable sharding. The files\n  are executed across different browsers as they become available.\n* Make your tests independent at the file level because the order in which\n  they run is not guaranteed and it's easier to run a test in isolation.\n\n### Do not add logic to your test\n\n* Avoid using if statements and for loops. When you add logic your test may\n  pass without testing anything, or may run very slowly.\n\n### Don't mock unless you need to\n\nThis rule is a bit controversial, in the sense that opinions are very divided \nwhen it comes to what the best practice is. Some developers argue that e2e \ntests should use mocks for everything in order to avoid external network calls \nand have a second set of integration tests to test the APIs and database. Other \ndevelopers argue that e2e tests should operate on the entire system and be as \nclose to the 'real deal' as possible.\n\n**Why?**\n* Using the real application with all the dependencies gives you high confidence\n* Helps you spot some corner cases you might have overlooked\n\n### Use Jasmine2\n\n**Why?**\n* Jasmine is well documented\n* It is supported by Protractor out of the box\n* You can use `beforeAll` and `afterAll`\n\n### Make your tests independent from each other\n\nThis rule holds true unless the operations performed to initialize the state of \nthe tests are too expensive. For example, if your e2e tests would require that \nyou create a new user before each spec is executed, you might end up with too \nhigh test run times. However, this does not mean you should make tests directly \ndepend on one another. So, instead of creating a user in one of your tests and \nexpect that record to be there for all other subsequent tests, you could harvest \nthe power of jasmine's beforeAll (since Jasmine 2.1) to create the user.\n\n```javascript\n/* avoid */\n\nit('should create user', function() {\n   browser.get('#/user-list');\n   userList.newButton.click();\n\n   userProperties.name.sendKeys('Teddy B');\n   userProperties.saveButton.click();\n\n   browser.get('#/user-list');\n   userList.search('Teddy B');\n   expect(userList.getNames()).toEqual(['Teddy B']);\n});\n\nit('should update user', function() {\n   browser.get('#/user-list');\n   userList.clickOn('Teddy B');\n\n   userProperties.name.clear().sendKeys('Teddy C');\n   userProperties.saveButton.click();\n\n   browser.get('#/user-list');\n   userList.search('Teddy C');\n   expect(userList.getNames()).toEqual(['Teddy C']);\n});\n```\n\n```javascript\n/* recommended */\n\ndescribe('when the user Teddy B is created', function(){\n\n  beforeAll(function() { \n    browser.get('#/user-list'); \n    userList.newButton.click(); \n    \n    userProperties.name.sendKeys('Teddy B'); \n    userProperties.saveButton.click(); \n    browser.get('#/user-list'); \n  });\n\n  it('should exist', function() { \n    userList.search('Teddy B'); \n    expect(userList.getNames()).toEqual(['Teddy B']); \n    userList.clear(); \n  });\n\n  describe('and gets updated to Teddy C', function() {\n    beforeAll(function() { \n      userList.clickOn('Teddy B'); \n      userProperties.name.clear().sendKeys('Teddy C'); \n      userProperties.saveButton.click(); \n      \n      browser.get('#/user-list'); \n    }); \n    \n    it('should be Teddy C', function() { \n      userList.search('Teddy C'); \n      expect(userList.getNames()).toEqual(['Teddy C']); \n      userList.clear(); \n    }); \n  });\n});\n```\n\n**Why?**\n* You can run tests in parallel with sharding\n* The execution order is not guaranteed\n* You can run suites in isolation\n* You can debug your tests (ddescribe/fdescribe/xdescribe/iit/fit/xit)\n\n### Navigate to the page under test before each test\n\n**Why?**\n* Assures you that the page under test is in a clean state\n\n### Have a suite that navigates through the major routes of the app\n\n**Why?**\n* Makes sure the major parts of the application are correctly connected\n* Users usually don’t navigate by manually entering urls \n* Gives confidence about permissions\n\n# Locator strategies\n\n### NEVER use xpath\n\n**Why?**\n* It's the slowest and most brittle locator strategy of all\n* Markup is very easily subject to change and therefore xpath locators require\n  a lot of maintenance\n* xpath expressions are unreadable and very hard to debug\n\n```javascript\n/* avoid */\n\nvar elem = element(by.xpath('/*/p[2]/b[2]/following-sibling::node()' +\n '[count(.|/*/p[2]/b[2]/following-sibling::br[1]/preceding-sibling::node())' +\n '=' +\n ' count((/*/p[2]/b[2]/following-sibling::br[1]/preceding-sibling::node()))' +\n ']'));\n```\n\n### Prefer protractor locator strategies when possible\n\n* Prefer protractor-specific locators such as `by.model` and `by.binding`.\n\n```html\n<ul class=\"red\">\n  <li>{{color.name}}</li>\n  <li>{{color.shade}}</li>\n  <li>{{color.code}}</li>\n</ul>\n\n<div class=\"details\">\n  <div class=\"personal\">\n    <input ng-model=\"person.name\">\n  </div>\n</div>\n```\n\n```js\n/* avoid */\n\nvar nameElement = element.all(by.css('.red li')).get(0);\nvar personName = element(by.css('.details .personal input'));\n\n/* recommended */\n\nvar nameElement = element(by.binding('color.name'));\nvar personName = element(by.model('person.name'));\n```\n\n**Why?**\n* These locators are usually specific, short, and easy to read.\n* It is easier to write your locator\n* The code is less likely to change than other markup\n\n### Prefer by.id and by.css when no Protractor locators are available\n\n**Why?**\n* Both are very performant and readable locators\n* Access elements easier\n\n### Avoid text locators for text that changes frequently\n\n* Try to avoid text-based locators such as `by.linkText`, `by.buttonText`,\n  `by.cssContainingText`.\n\n**Why?**\n* Text for buttons, links, and labels tends to change over time\n* Your tests should not break when you make minor text changes\n\n# Page objects\n\nPage Objects help you write cleaner tests by encapsulating information about\nthe elements on your application page. A page object can be reused across\nmultiple tests, and if the template of your application changes, you only need\nto update the page object.\n\n### Use Page Objects to interact with page under test\n\n**Why?**\n* Encapsulate information about the elements on the page under test\n* They can be reused across multiple tests\n* Decouple the test logic from implementation details\n\n```javascript\n/* avoid */\n\n/* question-spec.js */\ndescribe('Question page', function() {\n  it('should answer any question', function() {\n    var question = element(by.model('question.text'));\n    var answer = element(by.binding('answer'));\n    var button = element(by.css('.question-button'));\n\n    question.sendKeys('What is the purpose of life?');\n    button.click();\n    expect(answer.getText()).toEqual(\"Chocolate!\");\n  });\n});\n```\n\n```javascript\n/* recommended */\n\n/* question-spec.js */\nvar QuestionPage = require('./question-page');\n\ndescribe('Question page', function() {\n  var question = new QuestionPage();\n\n  it('should ask any question', function() {\n    question.ask('What is the purpose of meaning?');\n    expect(question.answer.getText()).toEqual('Chocolate');\n  });\n});\n\n/* recommended */\n\n/* question-page.js */\nvar QuestionPage = function() {\n  this.question = element(by.model('question.text'));\n  this.answer = element(by.binding('answer'));\n  this.button = element(by.className('question-button'));\n\n  this.ask = function(question) {\n    this.question.sendKeys(question);\n    this.button.click();\n  };\n};\n\nmodule.exports = QuestionPage;\n```\n\n### Declare one page object per file\n\n**Why?**\n* Each page object should be defined in its own file.\n* Why? Keeps code clean and makes things easy to find.\n\n### Use a single module.exports at the end of the page object file\n\n**Why?**\n* Each page object should declare a single class. You only need to export one\n  class.\n\n```js\n/* avoid */\n\nvar UserProfilePage = function() {};\nvar UserSettingsPage = function() {};\n\nmodule.exports = UserPropertiesPage;\nmodule.exports = UserSettingsPage;\n```\n\n```javascript\n/* recommended */\n\n/** @constructor */\nvar UserPropertiesPage = function() {};\n\nmodule.exports = UserPropertiesPage;\n```\n\n* Why? One page object per file means there's only one class to export.\n\n### Require all the modules at the top\n\n* You should declare all the required modules at the top of your page object,\n  test, or helper module.\n\n```js\nvar UserPage = require('./user-properties-page');\nvar MenuPage = require('./menu-page');\nvar FooterPage = require('./footer-page');\n\ndescribe('User properties page', function() {\n    ...\n});\n```\n\n**Why?**\n* The module dependencies should be clear and easy to find.\n\n### Instantiate all page objects at the beginning of the test suite\n\n* Create new instances of the page object at the top of your top-level describe.\n* Use upper case for the constructor name; lowercase for the instance name.\n\n```js\nvar UserPropertiesPage = require('./user-properties-page');\nvar MenuPage = require('./menu-page');\nvar FooterPage = require('./footer-page');\n\ndescribe('User properties page', function() {\n  var userProperties = new UserPropertiesPage();\n  var menu = new MenuPage();\n  var footer = new FooterPage();\n\n  // specs\n});\n```\n\n**Why?**\n* Separates dependencies from the test code.\n* Makes the dependencies available to all specs of the suite.\n\n\n### Declare all the page object public elements in the constructor\n\n* All the elements that will be visible to the test should be declared in the\n  constructor.\n\n```html\n<form>\n  Name: <input type=\"text\" ng-model=\"ctrl.user.name\">\n  E-mail: <input type=\"text\" ng-model=\"ctrl.user.email\">\n  <button id=\"save-button\">Save</button>\n</form>\n```\n\n```javascript\n/** @constructor */\nvar UserPropertiesPage = function() {\n  // List all public elements here.\n  this.name = element(by.model('ctrl.user.name'));\n  this.email = element(by.model('ctrl.user.email'));\n  this.saveButton = $('#save-button');\n};\n```\n\n**Why?**\n* The user of the page object should have quick access to the available\n  elements on a page\n\n\n### Declare page object functions for operations that require more than one step\n\n```javascript\n/**\n * Page object for the user properties view.\n * @constructor\n */\nvar UserPropertiesPage = function() {\n  this.newPhoneButton = $('button.new-phone');\n\n  /**\n   * Encapsulate complex operations in a function.\n   * @param {string} phone Phone number.\n   * @param {string} contactType Phone type (work, home, etc.).\n   */\n  this.addContactPhone = function(phone, contactType) {\n    this.newPhoneButton.click();\n    $$('#phone-list .phone-row').first().then(function(row) {\n      row.element(by.model('item.phoneNumber')).sendKeys(phone);\n      row.element(by.model('item.contactType')).sendKeys(contactType);\n    });\n  };\n};\n```\n\n**Why?**\n* Most elements are already exposed by the page object and can be used\n  directly in the test.\n* Doing otherwise will not have any added value\n\n### Avoid using expect() in page objects\n\n* Don't make any assertions in your page objects.\n\n**Why?**\n* It is the responsibility of the test to do all the assertions.\n* A reader of the test should be able to understand the behavior of the\n  application by looking at the test only\n\n### Add page object wrappers for directives, dialogs, and common elements\n\n* Some directives render complex HTML or they change frequently. Avoid code\n  duplication by writing wrappers to interact with complex directives.\n* Dialogs or modals are frequently used across multiple views.\n* When the directive changes you only need to change the wrapper once.\n\nFor example, the Protractor website has navigation bar with multiple dropdown\nmenus. Each menu has multiple options. A page object for the menu would look\nlike this:\n\n```js\n/**\n * Page object for Protractor website menu.\n * @constructor\n */\nvar MenuPage = function() {\n  this.dropdown = function(dropdownName) {\n    /**\n     * Dropdown api. Used to click on an element under a dropdown.\n     * @param {string} dropdownName\n     * @return {{option: Function}}\n     */\n    var openDropdown = function() {\n      element(by.css('.navbar-nav'))\n          .element(by.linkText(dropdownName))\n          .click();\n    };\n\n    return {\n      /**\n       * Get an option element under a dropdown.\n       * @param {string} optionName\n       * @return {ElementFinder}\n       */\n      option: function(optionName) {\n        openDropdown();\n        return element(by.css('.dropdown.open'))\n            .element(by.linkText(optionName));\n      }\n    }\n  };\n};\n\nmodule.exports = MenuPage;\n```\n\n```js\nvar Menu = require('./menu');\n\ndescribe('protractor website', function() {\n\n  var menu = new Menu();\n\n  it('should navigate to API view', function() {\n    browser.get('http://www.protractortest.org/#/');\n\n    menu.dropdown('Reference').option('Protractor API').click();\n\n    expect(browser.getCurrentUrl())\n        .toBe('http://www.protractortest.org/#/api');\n  });\n});\n```\n\n**Why?**\n* When you have a large team and multiple e2e tests people tend to write\n  their own custom locators for the same directives.\n\n# Project structure\n\n### Group your e2e tests in a structure that makes sense to the structure of your project\n\n**Why?**\n* Finding your e2e related files should be intuitive and easy\n* Makes the folder structure more readable\n* Clearly separates e2e tests from unit tests\n\n```\n/* avoid */\n\n|-- project-folder\n  |-- app\n    |-- css\n    |-- img\n    |-- partials\n        home.html\n        profile.html\n        contacts.html\n    |-- js\n      |-- controllers\n      |-- directives\n      |-- services\n      app.js\n      ...\n    index.html\n  |-- test\n    |-- unit\n    |-- e2e\n        home-page.js\n        home-spec.js\n        profile-page.js\n        profile-spec.js\n        contacts-page.js\n        contacts-spec.js\n\n/* recommended */\n\n|-- project-folder\n  |-- app\n    |-- css\n    |-- img\n    |-- partials\n        home.html\n        profile.html\n        contacts.html\n    |-- js\n      |-- controllers\n      |-- directives\n      |-- services\n      app.js\n      ...\n    index.html\n  |-- test\n    |-- unit\n    |-- e2e\n      |-- page-objects\n          home-page.js\n          profile-page.js\n          contacts-page.js\n      home-spec.js\n      profile-spec.js\n      contacts-spec.js\n```\n"
  },
  {
    "path": "docs/system-setup.md",
    "content": "Setting Up the System Under Test\n================================\n\nProtractor uses real browsers to run its tests, so it can connect to anything that your browser can connect to. This means you have great flexibility in deciding what you are actually testing. It could be a development server on localhost, a staging server up on your local network, or even production servers on the general internet. All Protractor needs is the URL.\nThere are a couple of things to watch out for!\n\n**If your page does manual bootstrap** Protractor will not be able to load your page using browser.get. Instead, use the base webdriver instance - `browser.driver.get`. This means that Protractor does not know when your page is fully loaded, and you may need to add a wait statement to make sure your tests avoid race conditions.\n\n**If your page uses `$timeout` for polling** Protractor will not be able to tell when your page is ready. Consider using `$interval` instead of `$timeout`.\n\n**If you need to do global preparation for your tests** (for example, logging in), you can put this into the config in the `onPrepare` property. This property can be either a function or a filename. If a filename, Protractor will load that file with Node.js and run its contents.\n\n`onPrepare` can optionally return a promise, which Protractor will wait for before continuing execution. This can be used if the preparation involves any asynchronous calls, e.g. interacting with the browser. Otherwise Protractor cannot guarantee order of execution and may start the tests before preparation finishes.\n\nSee the [login tests](https://github.com/angular/protractor/blob/master/spec/withLoginConf.js) for an example.\n"
  },
  {
    "path": "docs/timeouts.md",
    "content": "Timeouts\n========\n\nBecause WebDriver tests are asynchronous and involve many components, there are several reasons why a timeout could occur in a Protractor test.\n\nTimeouts from Protractor\n------------------------\n\n### Waiting for Page to Load\n\nWhen navigating to a new page using `browser.get`, Protractor waits for the page to\nbe loaded and the new URL to appear before continuing.\n\n - Looks like: an error in your test results - `Error: Timed out waiting for page to load after 10000ms`\n\n - Default timeout: 10 seconds\n\n - How to change: To change globally, add `getPageTimeout: timeout_in_millis` to your Protractor configuration file. For an individual call to `get`, pass an additional parameter: `browser.get(address, timeout_in_millis)`\n\n### Waiting for Angular\n\nBefore performing any action, Protractor waits until there are no pending asynchronous tasks in your Angular application. This means that all timeouts and http requests are finished. \n\n - Looks like: an error in your test results - `Timed out waiting for asynchronous Angular tasks to finish after 11 seconds.`\n\n - Default timeout: 11 seconds\n\n - How to change: Add `allScriptsTimeout: timeout_in_millis` to your Protractor configuration file.\n\nYou may also need to fix this problem with a change to your application. \n\n#### AngularJS\n\nIf your AngularJS application continuously polls $timeout or $http, Protractor will wait indefinitely and time out. You should use the\n[$interval](https://github.com/angular/angular.js/blob/master/src/ng/interval.js) for anything that polls continuously (introduced in Angular 1.2rc3).\n\n#### Angular\n\nFor Angular apps, Protractor will wait until the [Angular Zone](https://medium.com/@MertzAlertz/what-the-hell-is-zone-js-and-why-is-it-in-my-angular-2-6ff28bcf943e) stabilizes. This means long running async operations will block your test from continuing. To work around this, run these tasks outside the Angular zone. For example:\n\n```ts\nthis.ngZone.runOutsideAngular(() => {\n  setTimeout(() => {\n    // Changes here will not propagate into your view.\n    this.ngZone.run(() => {\n      // Run inside the ngZone to trigger change detection.\n    });\n  }, REALLY_LONG_DELAY);\n});\n```\n\nAs an alternative to either of these options, you could disable waiting for Angular, [see below](#how-to-disable-waiting-for-angular).\n\n### Waiting for Angular on Page Load\n\nProtractor waits for the `angular` variable to be present when loading a new page.\n\n - Looks like: an error in your test results - `Angular could not be found on the page: retries looking for angular exceeded`\n\n - Default timeout: 10 seconds\n\n - How to change: To change globally, add `getPageTimeout: timeout_in_millis` to your Protractor configuration file. For an individual call to `get`, pass an additional parameter: `browser.get(address, timeout_in_millis)`\n\n### _How to disable waiting for Angular_\n\nIf you need to navigate to a page which does not use Angular, you can turn off waiting for Angular by setting\n`browser.waitForAngularEnabled(false)`. For example:\n\n```js\nbrowser.waitForAngularEnabled(false);\nbrowser.get('/non-angular-login-page.html');\n\nelement(by.id('username')).sendKeys('Jane');\nelement(by.id('password')).sendKeys('1234');\nelement(by.id('clickme')).click();\n\nbrowser.waitForAngularEnabled(true);\nbrowser.get('/page-containing-angular.html');\n```\n\n\nTimeouts from WebDriver\n-----------------------\n\n### Asynchronous Script Timeout\n\nSets the amount of time to wait for an asynchronous script to finish execution before throwing an error.\n\n - Looks like: an error in your test results - `ScriptTimeoutError: asynchronous script timeout: result was not received in 11 seconds`\n\n - Default timeout: 11 seconds\n\n - How to change: Add `allScriptsTimeout: timeout_in_millis` to your Protractor configuration file.\n\n\nTimeouts from Jasmine\n---------------------\n\n### Spec Timeout\n\nIf a spec (an 'it' block) takes longer than the Jasmine timeout for any reason, it will fail.\n\n - Looks like: a failure in your test results - `timeout: timed out after 30000 msec waiting for spec to complete`\n\n - Default timeout: 30 seconds\n\n - How to change: To change for all specs, add `jasmineNodeOpts: {defaultTimeoutInterval: timeout_in_millis}` to your Protractor configuration file. To change for one individual spec, pass a third parameter to `it`: `it(description, testFn, timeout_in_millis)`.\n\n\nTimeouts from Sauce Labs\n------------------------\nIf you are using Sauce Labs, there are a couple additional ways your test can time out. See [Sauce Labs Timeouts Documentation](https://docs.saucelabs.com/reference/test-configuration/#timeouts) for more information.\n\n### Maximum Test Duration\n\nSauce Labs limits the maximum total duration for a test.\n\n - Looks like: `Test exceeded maximum duration after 1800 seconds`\n\n - Default timeout: 30 minutes\n\n - How to change: Edit the \"max-duration\" key in the capabilities object.\n\n### Command Timeout\n\nAs a safety measure to prevent Selenium crashes from making your tests run indefinitely, Sauce limits how long Selenium can take to run a command in browsers. This is set to 300 seconds by default.\n\n - Looks like: `Selenium took too long to run your command`\n\n - Default timeout: 300 seconds\n\n - How to change: Edit the \"command-timeout\" key in the capabilities object.\n\n### Idle Timeout\n\nAs a safety measure to prevent tests from running too long after something has gone wrong, Sauce limits how long a browser can wait for a test to send a new command. This is set to 90 seconds by default. You can adjust this limit on a per-job basis.\n\n - Looks like: `Test did not see a new command for 90 seconds. Timing out.`\n\n - Default timeout: 90 seconds\n\n - How to change: Edit the \"idle-duration\" key in the capabilities object.\n"
  },
  {
    "path": "docs/toc.md",
    "content": "Table of Contents\n=================\n\nProtractor Setup\n - [Setting Up Protractor](/docs/protractor-setup.md)\n - [Setting Up the Selenium Server](/docs/server-setup.md)\n - [Setting Up the Browser](/docs/browser-setup.md)\n - [Choosing a Framework](/docs/frameworks.md)\n\nProtractor Tests\n - [Getting Started](/docs/getting-started.md)\n - [Tutorial](/docs/tutorial.md)\n - [Working with Spec and Config Files](/docs/api-overview.md)\n - [Setting Up the System Under Test](/docs/system-setup.md)\n - [Using Locators](/docs/locators.md)\n - [Using Page Objects to Organize Tests](/docs/page-objects.md)\n - [Debugging Protractor Tests](/docs/debugging.md)\n\nReference\n - [Config File](/lib/config.ts)\n - [Protractor API](/docs/api.md)\n - [Style Guide](style-guide.md)\n - [Protractor Syntax vs WebDriverJS Syntax](webdriver-vs-protractor.md)\n - [Browser Support](/docs/browser-support.md)\n - [Plugins](plugins.md)\n - [Timeouts](/docs/timeouts.md)\n - [The WebDriver Control Flow](/docs/control-flow.md)\n - [Using TypeScript](typescript.md)\n - [Using `async`/`await`](async-await.md)\n - [How It Works](/docs/infrastructure.md)\n - [Upgrading to Jasmine 2.x](/docs/jasmine-upgrade.md)\n - [Mobile Setup](/docs/mobile-setup.md)\n - [FAQ](/docs/faq.md)\n"
  },
  {
    "path": "docs/tutorial.md",
    "content": "Tutorial\n========\n\nThis is a simple tutorial that shows you how to set up Protractor and start running tests.\n\nPrerequisites\n-------------\n\nProtractor is a [Node.js](http://nodejs.org/) program. To run, you will need to have Node.js installed. You will download Protractor package using [npm](https://www.npmjs.org/), which comes with Node.js. Check the version of Node.js you have by running `node --version`. Then, check the [compatibility notes](https://github.com/angular/protractor#compatibility) in the Protractor README to make sure your version of Node.js is compatible with Protractor. \n\nBy default, Protractor uses the [Jasmine](http://jasmine.github.io/) test framework for its testing interface. This tutorial assumes some familiarity with Jasmine, and we will use version 2.4.\n\nThis tutorial will set up a test using a local standalone Selenium Server to control browsers. You will need to have the [Java Development Kit (JDK)](http://www.oracle.com/technetwork/java/javase/downloads/index.html) installed to run the standalone Selenium Server. Check this by running `java -version` from the command line.\n\nSetup\n-----\n\nUse npm to install Protractor globally with:\n\n    npm install -g protractor\n\nThis will install two command line tools, `protractor` and `webdriver-manager`. Try running `protractor --version` to make sure it's working.\n\nThe `webdriver-manager` is a helper tool to easily get an instance of a Selenium Server running. Use it to download the necessary binaries with:\n\n    webdriver-manager update\n\nNow start up a server with:\n\n    webdriver-manager start\n\nThis will start up a Selenium Server and will output a bunch of info logs. Your Protractor test will send requests to this server to control a local browser. Leave this server running throughout the tutorial. You can see information about the status of the server at `http://localhost:4444/wd/hub`.\n\nStep 0 - write a test\n---------------------\n\nOpen a new command line or terminal window and create a clean folder for testing.\n\nProtractor needs two files to run, a **spec file** and a **configuration file**. \n\nLet's start with a simple test that navigates to an example AngularJS application and checks its title. We’ll use the Super Calculator application at [http://juliemr.github.io/protractor-demo/](http://juliemr.github.io/protractor-demo/).\n\nCopy the following into spec.js:\n\n```javascript\n// spec.js\ndescribe('Protractor Demo App', function() {\n  it('should have a title', function() {\n    browser.get('http://juliemr.github.io/protractor-demo/');\n\n    expect(browser.getTitle()).toEqual('Super Calculator');\n  });\n});\n```\n\nThe `describe` and `it` syntax is from the Jasmine framework. `browser` is a global created by Protractor, which is used for browser-level commands such as navigation with `browser.get`.\n\nNow create the configuration file. Copy the following into conf.js:\n\n```js\n// conf.js\nexports.config = {\n  framework: 'jasmine',\n  seleniumAddress: 'http://localhost:4444/wd/hub',\n  specs: ['spec.js']\n}\n```\n\nThis configuration tells Protractor where your test files (`specs`) are, and where to talk to your Selenium Server (`seleniumAddress`). It specifies that we will be using Jasmine for the test framework. It will use the defaults for all other configuration. Chrome is the default browser.\n\nNow run the test with\n\n    protractor conf.js\n\nYou should see a Chrome browser window open up and navigate to the Calculator, then close itself (this should be very fast!). The test output should be `1 tests, 1 assertion, 0 failures`. Congratulations, you've run your first Protractor test!\n\nStep 1 - interacting with elements\n----------------------------------\n\nNow let's modify the test to interact with elements on the page. Change spec.js to the following:\n\n```js\n// spec.js\ndescribe('Protractor Demo App', function() {\n  it('should add one and two', function() {\n    browser.get('http://juliemr.github.io/protractor-demo/');\n    element(by.model('first')).sendKeys(1);\n    element(by.model('second')).sendKeys(2);\n\n    element(by.id('gobutton')).click();\n\n    expect(element(by.binding('latest')).getText()).\n        toEqual('5'); // This is wrong!\n  });\n});\n```\nThis uses the globals `element` and `by`, which are also created by Protractor. The `element` function is used for finding HTML elements on your webpage. It returns an ElementFinder object, which can be used to interact with the element or get information from it. In this test, we use `sendKeys` to type into `<input>`s, `click` to click a button, and `getText` to return the content of an element.\n\n`element` takes one parameter, a Locator, which describes how to find the element. The `by` object creates Locators. Here, we're using three types of Locators:\n\n  - `by.model('first')` to find the element with `ng-model=\"first\"`. If you inspect the Calculator page source, you will see this is `<input type=\"text\" ng-model=\"first\">`.\n  - `by.id('gobutton')` to find the element with the given id. This finds `<button id=\"gobutton\">`.\n  - `by.binding('latest')` to find the element bound to the variable `latest`. This finds the span containing `{{latest}}`\n\n  [Learn more about locators and ElementFinders](/docs/locators.md).\n\nRun the tests with\n\n    protractor conf.js\n\nYou should see the page enter two numbers and wait for the result to be displayed. Because the result is 3, not 5, our test fails. Fix the test and try running it again.\n\nStep 2 - writing multiple scenarios\n-----------------------------------\n\nLet's put these two tests together and clean them up a bit. Change spec.js to the following:\n\n```js\n// spec.js\ndescribe('Protractor Demo App', function() {\n  var firstNumber = element(by.model('first'));\n  var secondNumber = element(by.model('second'));\n  var goButton = element(by.id('gobutton'));\n  var latestResult = element(by.binding('latest'));\n\n  beforeEach(function() {\n    browser.get('http://juliemr.github.io/protractor-demo/');\n  });\n\n  it('should have a title', function() {\n    expect(browser.getTitle()).toEqual('Super Calculator');\n  });\n\n  it('should add one and two', function() {\n    firstNumber.sendKeys(1);\n    secondNumber.sendKeys(2);\n\n    goButton.click();\n\n    expect(latestResult.getText()).toEqual('3');\n  });\n\n  it('should add four and six', function() {\n    // Fill this in.\n    expect(latestResult.getText()).toEqual('10');\n  });\n  \n  it('should read the value from an input', function() {\n    firstNumber.sendKeys(1);\n    expect(firstNumber.getAttribute('value')).toEqual('1');\n  });\n});\n```\n\nHere, we've pulled the navigation out into a `beforeEach` function which is run before every `it` block. We've also stored the ElementFinders for the first and second input in nice variables that can be reused. Fill out the second test using those variables, and run the tests again to ensure they pass.\n\nIn the last assertion we read the value from the input field with `firstNumber.getAttribute('value')` and compare it with the value we have set before.\n\nStep 3 - changing the configuration\n-----------------------------------\n\nNow that we've written some basic tests, let's take a look at the configuration file. The configuration file lets you change things like which browsers are used and how to connect to the Selenium Server. Let's change the browser. Change conf.js to the following:\n\n```js\n// conf.js\nexports.config = {\n  framework: 'jasmine',\n  seleniumAddress: 'http://localhost:4444/wd/hub',\n  specs: ['spec.js'],\n  capabilities: {\n    browserName: 'firefox'\n  }\n}\n```\n\nTry running the tests again. You should see the tests running on Firefox instead of Chrome. The `capabilities` object describes the browser to be tested against. For a full list of options, see [the config file](/lib/config.ts).\n\nYou can also run tests on more than one browser at once. Change conf.js to:\n\n```js\n// conf.js\nexports.config = {\n  framework: 'jasmine',\n  seleniumAddress: 'http://localhost:4444/wd/hub',\n  specs: ['spec.js'],\n  multiCapabilities: [{\n    browserName: 'firefox'\n  }, {\n    browserName: 'chrome'\n  }]\n}\n```\n\nTry running once again. You should see the tests running on Chrome and Firefox simultaneously, and the results reported separately on the command line.\n\nStep 4 - lists of elements\n--------------------------\n\nLet's go back to the test files. Feel free to change the configuration back to using only one browser.\n\nSometimes, you will want to deal with a list of multiple elements. You can do this with `element.all`, which returns an ElementArrayFinder. In our calculator application, every operation is logged in the history, which is implemented on the site as a table with `ng-repeat`. Let's do a couple of operations, then test that they're in the history. Change spec.js to:\n\n```js\n// spec.js\ndescribe('Protractor Demo App', function() {\n  var firstNumber = element(by.model('first'));\n  var secondNumber = element(by.model('second'));\n  var goButton = element(by.id('gobutton'));\n  var latestResult = element(by.binding('latest'));\n  var history = element.all(by.repeater('result in memory'));\n\n  function add(a, b) {\n    firstNumber.sendKeys(a);\n    secondNumber.sendKeys(b);\n    goButton.click();\n  }\n\n  beforeEach(function() {\n    browser.get('http://juliemr.github.io/protractor-demo/');\n  });\n\n  it('should have a history', function() {\n    add(1, 2);\n    add(3, 4);\n\n    expect(history.count()).toEqual(2);\n\n    add(5, 6);\n\n    expect(history.count()).toEqual(0); // This is wrong!\n  });\n});\n```\n\nWe've done a couple things here - first, we created a helper function, `add`. We've added the variable `history`. We use `element.all` with the `by.repeater` Locator to get an ElementArrayFinder. In our spec, we assert that the history has the expected length using the `count` method. Fix the test so that the second expectation passes.\n\n`ElementArrayFinder` has many methods in addition to `count`. Let's use `last` to get an ElementFinder that matches the last element found by the Locator. Change the test to:\n```js\n  it('should have a history', function() {\n    add(1, 2);\n    add(3, 4);\n\n    expect(history.last().getText()).toContain('1 + 2');\n    expect(history.first().getText()).toContain('foo'); // This is wrong!\n  });\n```\n\nSince the Calculator reports the oldest result at the bottom, the oldest addition (1 + 2) be the last history entry. We're using the `toContain` Jasmine matcher to assert that the element text contains \"1 + 2\". The full element text will also contain the timestamp and the result. \n\nFix the test so that it correctly expects the first history entry to contain the text \"3 + 4\".\n\nElementArrayFinder also has methods `each`, `map`, `filter`, and `reduce` which are analogous to JavaScript Array methods. [Read the API for more details](http://angular.github.io/protractor/#/api?view=ElementArrayFinder).\n\nWhere to go next\n----------------\n\nThis should get you started writing tests. To learn more, see the documentation [Table of Contents](/docs/toc.md).\n"
  },
  {
    "path": "docs/typescript.md",
    "content": "TypeScript\n==========\n\nPlease see [our TypeScript examples](/exampleTypescript/).\n"
  },
  {
    "path": "docs/webdriver-vs-protractor.md",
    "content": "Protractor Syntax vs WebDriverJS Syntax\n=======================================\n\nIn vanilla [WebDriverJS](https://code.google.com/p/selenium/wiki/WebDriverJs)\ncode, you might start your tests with \n```js\nvar webdriver = require('selenium-webdriver');\nvar browser = new webdriver.Builder().usingServer().withCapabilities(c).build();\n```\nIn Protractor, you are provided with global `protractor` and `browser` objects,\nwhich more or less match the `webdriver` and `browser` objects, respectively.\nSo if you are already familiar with writing WebDriver code, the most basic way\nto start writing Protractor code is just to replace `webdriver` with\n`protractor`.\n\nHowever, Protractor also provides some syntactic sugar to help you write your\ntests.\n\n\n| WebDriver Syntax                              | Protractor Syntax            |\n|-----------------------------------------------|------------------------------|\n| `webdriver.By`                                | `by`                         |\n| `browser.findElement(...)`                    | `element(...)`               |\n| `browser.findElements(...)`                   | `element.all(...)`           |\n| `browser.findElement(webdriver.By.css(...))`  | `$(...)`                     |\n| `browser.findElements(webdriver.By.css(...))` | `$$(...)`                    |\n\nIf you need the vanilla WebDriver `browser` object, you can access it via\n`browser.driver`\n\nUsing ElementFinders\n--------------------\n\nIn Protractor, you use the `element` function to find and interact with elements\nthrough an `ElementFinder` object. This extends a WebDriver `WebElement` by\nadding chaining and utilities for dealing with lists. See\n[locators#actions](/docs/locators.md#actions) for details.\n\nJasmine Integration\n-------------------\n\nProtractor uses [`jasminewd2`](https://github.com/angular/jasminewd), which\nwraps around jasmine's `expect` so that you can write:\n```js\nexpect(el.getText()).toBe('Hello, World!')\n```\nInstead of:\n```js\nel.getText().then(function(text) {\n  expect(text).toBe('Hello, World!');\n});\n```\n"
  },
  {
    "path": "example/angular_material/conf.js",
    "content": "// An example configuration file.\nexports.config = {\n    directConnect: true,\n  \n    // Capabilities to be passed to the webdriver instance.\n    capabilities: {\n      'browserName': 'chrome'\n    },\n  \n    // Framework to use. Jasmine is recommended.\n    framework: 'jasmine',\n  \n    // Spec patterns are relative to the current working directory when\n    // protractor is called.\n    specs: [\n        'input_spec.js',\n        'mat_paginator_spec.js'\n    ],\n\n    // Disable promise manager because we are going to use async/await\n    SELENIUM_PROMISE_MANAGER: false,\n  \n    // Options to be passed to Jasmine.\n    jasmineNodeOpts: {\n      defaultTimeoutInterval: 30000\n    }\n};\n"
  },
  {
    "path": "example/angular_material/input_spec.js",
    "content": "describe('angular-material input component page', () => {\n  const EC = protractor.ExpectedConditions;\n\n  it('Should change input component value', async () => {\n    await browser.get('https://material.angular.io/components/input/examples');\n\n    await browser.wait(\n      EC.elementToBeClickable($('.mat-button-wrapper>.mat-icon')), 5000);\n\n    const emailInputField = $$('.mat-form-field-infix>input').get(1);\n    await emailInputField.sendKeys('invalid');\n    expect(await $('mat-error').isPresent()).toBe(true);\n  });\n});"
  },
  {
    "path": "example/angular_material/mat_paginator_spec.js",
    "content": "describe('angular-material paginator component page', () => {\n  const EC = protractor.ExpectedConditions;\n\n  beforeAll(async () => {\n    await browser.get(\n      'https://material.angular.io/components/paginator/examples');\n\n    await browser.wait(\n      EC.elementToBeClickable($('.mat-button-wrapper>.mat-icon')), 5000);\n  });\n\n  it('Should navigate to next page', async () => {\n    await $('button[aria-label=\\'Next page\\']').click();\n    console.log(await $('.mat-paginator-range-label').getAttribute('innerText'));\n    expect(await $('.mat-paginator-range-label').getAttribute('innerText'))\n      .toEqual('11 - 20 of 100');\n  });\n\n  it('Should navigate to previous page', async () => {\n    await $('button[aria-label=\\'Previous page\\']').click();\n    expect(await $('.mat-paginator-range-label').getAttribute('innerText'))\n      .toEqual('1 - 10 of 100');\n  });\n\n  it('Should change list length to 5 items per page', async () => {\n    await $('mat-select>div').click();\n    const fiveItemsOption = $$('mat-option>.mat-option-text').first();\n    await fiveItemsOption.click();\n    expect(await $('.mat-paginator-range-label').getAttribute('innerText'))\n      .toEqual('1 - 5 of 100');\n  });\n});"
  },
  {
    "path": "example/conf.js",
    "content": "// An example configuration file.\nexports.config = {\n  directConnect: true,\n\n  // Capabilities to be passed to the webdriver instance.\n  capabilities: {\n    'browserName': 'chrome'\n  },\n\n  // Framework to use. Jasmine is recommended.\n  framework: 'jasmine',\n\n  // Spec patterns are relative to the current working directory when\n  // protractor is called.\n  specs: [\n    'example_spec.js',\n    'angular_material/input_spec.js',\n    'angular_material/mat_paginator_spec.js'\n\n  ],\n\n  // Options to be passed to Jasmine.\n  jasmineNodeOpts: {\n    defaultTimeoutInterval: 30000\n  }\n};\n"
  },
  {
    "path": "example/example_spec.js",
    "content": "describe('angularjs homepage', () => {\n  it('should greet the named user', async () => {\n    await browser.get('http://www.angularjs.org');\n\n    await element(by.model('yourName')).sendKeys('Julie');\n    const greeting = element(by.binding('yourName'));\n    expect(await greeting.getText()).toEqual('Hello Julie!');\n  });\n\n  describe('todo list', () => {\n    let todoList;\n\n    beforeEach(async () => {\n      await browser.get('http://www.angularjs.org');\n      todoList = element.all(by.repeater('todo in todoList.todos'));\n    });\n\n    it('should list todos', async () => {\n      expect(await todoList.count()).toEqual(2);\n      expect(await todoList.get(1).getText()).toEqual('build an AngularJS app');\n    });\n\n    it('should add a todo', async () => {\n      const addTodo = element(by.model('todoList.todoText'));\n      const addButton = element(by.css('[value=\"add\"]'));\n\n      await addTodo.sendKeys('write a protractor test');\n      await addButton.click();\n\n      expect(await todoList.count()).toEqual(3);\n      expect(await todoList.get(2).getText())\n        .toEqual('write a protractor test');\n    });\n  });\n});\n"
  },
  {
    "path": "example/package.json",
    "content": "{\n  \"name\": \"example-javascript\",\n  \"version\": \"1.0.0\",\n  \"description\": \"a javascript example\",\n  \"author\": \"\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"debug\": \"node --inspect-brk ./node_modules/.bin/protractor conf.js\",\n    \"pretest\": \"npm run webdriver-update\",\n    \"test\": \"protractor conf.js\",\n    \"webdriver-update\": \"webdriver-manager update --standalone=false --gecko=false\"\n  },\n  \"dependencies\": {\n    \"jasmine\": \"^3.3.1\",\n    \"protractor\": \"file:../\"\n  },\n  \"devDependencies\": {}\n}\n"
  },
  {
    "path": "exampleTypescript/.gitignore",
    "content": "plugins.js\nspec.js\nspecPageObjects.js\nangularPage.js\n*.d.ts\nnode_modules\npackage-lock.json"
  },
  {
    "path": "exampleTypescript/README.md",
    "content": "# Protractor with Typescript\n\nTypescript provides code auto completion and helpful hints with a text editor like Microsoft's Visual Studio Code or another text editor with Typescript support.\n\nNote that this example uses TypeScript 2.0.\n\n## Examples\n\nThere are two examples in this directory:\n\n* Simple Protractor example\n  * Similar to the [github protractor example](https://github.com/angular/protractor/tree/master/example)\n  * Files `conf.js` and `spec.ts`\n* Page objects example\n  * Follows the [protractortest.org page objects example](http://www.protractortest.org/#/page-objects)\n  * Files `conf.js`, `specPageObjects.ts`, and `angularPage.ts`\n\n## File organization\n\n```\nexampleTypescript/\n|- node_modules/       // downloaded node modules\n|\n|- .gitignore          // since typescript produces javascript, we should not\n|                      // commit javascript to the repo\n|- angularPage.ts      // page object example\n|- conf.js             // configuration file\n|- package.json        // node dependencies for the project\n|- README.md           // this file\n|- spec.ts             // spec for the simple protractor example\n|- specPageObjects.ts  // spec for the page objects example\n|- tsconfig.json       // typescript transpile configuration\n```\n\n\n## Getting started\n\nThis package.json references the local protractor directory with `\"protractor\": \"file: ../\"`. For the type declarations to work, from the protractor directory run an `npm install` to generate the declarations file.\n\nNext, install the exampleTypescript node_modules with:\n\n```\nnpm install\n```\n\n\n## Protractor typings\n\nTo use Protractor types, you'll need to import `protractor`. After this is imported, you should have autocompletion hints when typing.\n\n```\nimport {browser, element, by, By, $, $$, ExpectedConditions} from 'protractor';\n```\n\nAlthough the Protractor configuration file can be written in javascript, creating it in typescript will have some hints. These hints and the reference configuration can be found in `lib/config.ts`. Below we are importing the Config interface and applying that interface to the config variable:\n\n```\nimport {Config} from 'protractor';\n\nexport let config: Config = {\n  ...\n}\n```\n\n## Ambient typings\n\nProtractor also uses ambient types including jasmine, jasminewd2, and node. These are brought in via the `tsconfig.json` file, which uses npm module resolution to get types from `node_modules/@types`.\n\nIf you are using the jasmine framework for your tests, make sure to do:\n\n```\nnpm install --save-dev @types/jasmine @types/jasminewd2\n```\n\n## Compiling your code\n\nTo convert your typescript to javascript (transpiling), you'll use the Typescript compiler (tsc). If you install typescript globally, the command is `tsc`. If it is not installed globally, the typescript compiler can be executed with `npm run tsc`.\n\n## Running Protractor\n\nAfter transpiling your code to javascript, you'll run Protractor like before: `protractor conf.js`\n\n## Helpful links\n\n* [TypescriptLang.org tutorial](http://www.typescriptlang.org/docs/tutorial.html)\n* [TypescriptLang.org tsconfig.json](http://www.typescriptlang.org/docs/handbook/tsconfig-json.html)\n* [Typescript gitter](https://gitter.im/Microsoft/TypeScript)\n* [Typescript stackoverflow](http://stackoverflow.com/questions/tagged/typescript)\n"
  },
  {
    "path": "exampleTypescript/angularPage.ts",
    "content": "// Because this file references protractor, you'll need to have it as a project\n// dependency to use 'protractor'. Here is the full list of imports:\n//\n// import {browser, element, by, By, $, $$, ExpectedConditions}\n//   from 'protractor';\n//\nimport {browser, element, by} from 'protractor';\n\nexport class AngularHomepage {\n  nameInput = element(by.model('yourName'));\n  greeting = element(by.binding('yourName'));\n\n  async get(): Promise<void> {\n    await browser.get('http://www.angularjs.org');\n  }\n\n  async setName(name: string): Promise<void> {\n    await this.nameInput.sendKeys(name);\n  }\n\n  // getGreeting returns a native Promise<string>\n  async getGreeting(): Promise<string> {\n    return this.greeting.getText();\n  }\n}\n"
  },
  {
    "path": "exampleTypescript/conf.js",
    "content": "// Because this file imports from  protractor, you'll need to have it as a\n// project dependency. Please see the reference config: lib/config.ts for more\n// information.\n//\n// Why you might want to create your config with typescript:\n// Editors like Microsoft Visual Studio Code will have autocomplete and\n// description hints.\n//\n// To run this example, run `protractor conf.js`.\nexports.config = {\n  framework: 'jasmine',\n  capabilities: {\n    browserName: 'chrome'\n  },\n  specs: [\n    'spec.js',\n    'specPageObjects.js'\n  ],\n  directConnect: true,\n\n  // You could set no globals to true to avoid jQuery '$' and protractor '$'\n  // collisions on the global namespace.\n  noGlobals: true\n};\n"
  },
  {
    "path": "exampleTypescript/package.json",
    "content": "{\n  \"name\": \"example-typescript\",\n  \"version\": \"1.0.0\",\n  \"description\": \"a typescript example\",\n  \"author\": \"\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"debug\": \"node --inspect-brk ./node_modules/.bin/protractor conf.js\",\n    \"pretest\": \"npm run tsc && npm run webdriver-update\",\n    \"test\": \"protractor conf.js\",\n    \"tsc\": \"tsc\",\n    \"webdriver-update\": \"webdriver-manager update --standalone=false --gecko=false\"\n  },\n  \"dependencies\": {\n    \"@types/jasmine\": \"^3.3.12\",\n    \"jasmine\": \"^3.3.1\",\n    \"protractor\": \"file:../\",\n    \"ts-node\": \"^8.0.3\",\n    \"typescript\": \"^3.4.1\"\n  },\n  \"devDependencies\": {\n    \"@types/selenium-webdriver\": \"^4.0.0\"\n  }\n}\n"
  },
  {
    "path": "exampleTypescript/plugins.ts",
    "content": "//  a plugin example with the protractor plugin interface\nimport { ProtractorPlugin } from 'protractor';\n\n// creating a \"var module: any\" will allow use of module.exports\ndeclare var module: any;\n\nlet myPlugin: ProtractorPlugin = {\n  addSuccess(info: {specName: string}) {\n    console.log('on success: ' + info.specName);\n  },\n  onPageLoad() {\n    this.addSuccess({specName: 'Hello, World!'});\n  }\n};\n\nmodule.exports = myPlugin;\n"
  },
  {
    "path": "exampleTypescript/spec.ts",
    "content": "// Because this file references protractor, you'll need to have it as a project\n// dependency to use 'protractor/globals'. Here is the full list of imports:\n//\n// import {browser, element, by, By, $, $$, ExpectedConditions}\n//   from 'protractor';\n//\n// The jasmine typings are brought in via DefinitelyTyped ambient typings.\nimport {browser, element, by, By, $, $$, ExpectedConditions} from 'protractor';\n\ndescribe('protractor with typescript typings', () => {\n  beforeEach(async () => {\n    await browser.get('http://www.angularjs.org');\n  });\n\n  it('should greet the named user', async () => {\n    await element(by.model('yourName')).sendKeys('Julie');\n    const greeting = element(by.binding('yourName'));\n    expect(await greeting.getText()).toEqual('Hello Julie!');\n  });\n\n  it('should list todos', async () => {\n    const todoList = element.all(by.repeater('todo in todoList.todos'));\n    expect(await todoList.count()).toEqual(2);\n    expect(await todoList.get(1).getText()).toEqual('build an AngularJS app');\n  });\n});\n"
  },
  {
    "path": "exampleTypescript/specPageObjects.ts",
    "content": "// local import of the exported AngularPage class\nimport {AngularHomepage} from './angularPage';\n\n// The jasmine typings are brought in via DefinitelyTyped ambient typings.\ndescribe('angularjs homepage', () => {\n  it('should greet the named user', async () => {\n    const angularHomepage = new AngularHomepage();\n    await angularHomepage.get();\n    await angularHomepage.setName('Julie');\n    expect(await angularHomepage.getGreeting()).toEqual('Hello Julie!');\n  });\n});\n"
  },
  {
    "path": "exampleTypescript/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es6\",\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\",\n    \"inlineSourceMap\": true,\n    \"declaration\": false,\n    \"noImplicitAny\": false\n  },\n  \"exclude\": [\n    \"node_modules\",\n    \"plugins.ts\"\n  ]\n}\n"
  },
  {
    "path": "gulpfile.js",
    "content": "'use strict';\n\nconst gulp = require('gulp');\nconst format = require('gulp-clang-format');\nconst clangFormat = require('clang-format');\nconst spawn = require('child_process').spawn;\nconst tslint = require('gulp-tslint');\nconst fs = require('fs');\nconst path = require('path');\nconst semver = require('semver');\n\nconst runSpawn = (done, task, opt_arg, opt_io) => {\n  opt_arg = typeof opt_arg !== 'undefined' ? opt_arg : [];\n  const stdio = 'inherit';\n  if (opt_io === 'ignore') {\n    stdio = 'ignore';\n  }\n  const child = spawn(task, opt_arg, {stdio: stdio});\n  let running = false;\n  child.on('close', () => {\n    if (!running) {\n      running = true;\n      done();\n    }\n  });\n  child.on('error', () => {\n    if (!running) {\n      console.error('gulp encountered a child error');\n      running = true;\n      done();\n    }\n  });\n};\n\ngulp.task('tslint', () => {\n  return gulp.src(['lib/**/*.ts', 'spec/**/*.ts', '!spec/install/**/*.ts'])\n      .pipe(tslint())\n      .pipe(tslint.report());\n});\n\ngulp.task('format:enforce', () => {\n  return gulp.src(['lib/**/*.ts'])\n      .pipe(format.checkFormat('file', clangFormat,\n      {verbose: true, fail: true}));\n});\n\ngulp.task('lint', gulp.series('tslint', 'format:enforce'));\n\n// prevent contributors from using the wrong version of node\ngulp.task('checkVersion', (done) => {\n  // read minimum node on package.json\n  const packageJson = JSON.parse(fs.readFileSync(path.resolve('package.json')));\n  const protractorVersion = packageJson.version;\n  const nodeVersion = packageJson.engines.node;\n\n  if (semver.satisfies(process.version, nodeVersion)) {\n    done();\n  } else {\n    throw new Error('minimum node version for Protractor ' +\n        protractorVersion + ' is node ' + nodeVersion);\n  }\n});\n\ngulp.task('built:copy', () => {\n  return gulp.src(['lib/**/*.js'])\n      .pipe(gulp.dest('built/'));\n});\n\ngulp.task('built:copy:typings', () => {\n  return gulp.src(['lib/selenium-webdriver/**/*.d.ts'])\n      .pipe(gulp.dest('built/selenium-webdriver/'));\n});\n\ngulp.task('webdriver:update', (done) => {\n  runSpawn(done, 'node', ['bin/webdriver-manager', 'update',\n  '--versions.chrome=2.44']);\n});\n\ngulp.task('format', () => {\n  return gulp.src(['lib/**/*.ts'], { base: '.' })\n      .pipe(format.format('file', clangFormat))\n      .pipe(gulp.dest('.'));\n});\n\ngulp.task('tsc', (done) => {\n  runSpawn(done, 'node', ['node_modules/typescript/bin/tsc']);\n});\n\ngulp.task('tsc:spec', (done) => {\n  runSpawn(done, 'node', ['node_modules/typescript/bin/tsc', '-p', 'ts_spec_config.json']);\n});\n\ngulp.task('prepublish', gulp.series('checkVersion', 'tsc', 'built:copy'));\n\ngulp.task('pretest', gulp.series(\n  'checkVersion',\n  gulp.parallel('webdriver:update', 'tslint', 'format'),\n  'tsc', 'built:copy', 'built:copy:typings', 'tsc:spec'));\n\ngulp.task('default', gulp.series('prepublish'));\n\n"
  },
  {
    "path": "lib/bpRunner.ts",
    "content": "import {ChildProcess, fork} from 'child_process';\n\nimport {Config} from './config';\nimport {Logger} from './logger';\n\nconst BP_PATH = require.resolve('blocking-proxy/built/lib/bin.js');\n\nlet logger = new Logger('BlockingProxy');\n\nexport class BlockingProxyRunner {\n  bpProcess: ChildProcess;\n  public port: number;\n\n  constructor(private config: Config) {}\n\n  start() {\n    return new Promise((resolve, reject) => {\n      this.checkSupportedConfig();\n\n      let args = [\n        '--fork',\n        '--seleniumAddress',\n        this.config.seleniumAddress,\n      ];\n      if (this.config.webDriverLogDir) {\n        args.push('--logDir', this.config.webDriverLogDir);\n      }\n      if (this.config.highlightDelay) {\n        args.push('--highlightDelay', this.config.highlightDelay.toString());\n      }\n      this.bpProcess = fork(BP_PATH, args, {silent: true});\n      logger.info('Starting BlockingProxy with args: ' + args.toString());\n      this.bpProcess\n          .on('message',\n              (data) => {\n                this.port = data['port'];\n                resolve(data['port']);\n              })\n          .on('error',\n              (err) => {\n                reject(new Error('Unable to start BlockingProxy ' + err));\n              })\n          .on('exit', (code: number, signal: number) => {\n            reject(new Error('BP exited with ' + code));\n            logger.error('Exited with ' + code);\n            logger.error('signal ' + signal);\n          });\n\n      this.bpProcess.stdout.on('data', (msg: Buffer) => {\n        logger.debug(msg.toString().trim());\n      });\n\n      this.bpProcess.stderr.on('data', (msg: Buffer) => {\n        logger.error(msg.toString().trim());\n      });\n\n      process.on('exit', () => {\n        this.bpProcess.kill();\n      });\n    });\n  }\n\n  checkSupportedConfig() {\n    if (this.config.directConnect) {\n      throw new Error('BlockingProxy not yet supported with directConnect!');\n    }\n  }\n}\n"
  },
  {
    "path": "lib/breakpointhook.js",
    "content": "module.exports = function() {\n  return true;\n};\n\n/**\n * The reason this file exists is so that we can set a breakpoint via\n * script name, and then control when that breakpoint is set in\n * our library code by importing and calling this function. The\n * breakpoint will always be on line 2.\n */"
  },
  {
    "path": "lib/browser.ts",
    "content": "import {BPClient} from 'blocking-proxy';\nimport {By, Navigation, WebDriver, WebElement, WebElementPromise} from 'selenium-webdriver';\nimport {Command, ICommandName} from 'selenium-webdriver/lib/command';\nimport * as url from 'url';\n\nconst CommandName = require('selenium-webdriver/lib/command').Name as ICommandName;\n\nimport {build$, build$$, ElementArrayFinder, ElementFinder} from './element';\nimport {IError} from './exitCodes';\nimport {ProtractorExpectedConditions} from './expectedConditions';\nimport {Locator, ProtractorBy} from './locators';\nimport {Logger} from './logger';\nimport {Plugins} from './plugins';\n\nconst clientSideScripts = require('./clientsidescripts');\n\n// jshint browser: true\n\nconst DEFER_LABEL = 'NG_DEFER_BOOTSTRAP!';\nconst DEFAULT_RESET_URL = 'data:text/html,<html></html>';\nconst DEFAULT_GET_PAGE_TIMEOUT = 10000;\n\nlet logger = new Logger('browser');\n\n// TODO(cnishina): either remove for loop entirely since this does not export anything\n// the user might need since everything is composed (with caveat that this could be a\n// potential breaking change) or export the types with `export * from 'selenium-webdriver'`;\n/*\n * Mix in other webdriver functionality to be accessible via protractor.\n */\nfor (let foo in require('selenium-webdriver')) {\n  exports[foo] = require('selenium-webdriver')[foo];\n}\n\n/**\n * Mix a function from one object onto another. The function will still be\n * called in the context of the original object.  Any arguments of type\n * `ElementFinder` will be unwrapped to their underlying `WebElement` instance\n *\n * @private\n * @param {Object} to\n * @param {Object} from\n * @param {string} fnName\n * @param {function=} setupFn\n */\nfunction ptorMixin(to: any, from: any, fnName: string, setupFn?: Function) {\n  to[fnName] = function() {\n    const args = arguments;\n    for (let i = 0; i < args.length; i++) {\n      if (args[i] instanceof ElementFinder) {\n        args[i] = args[i].getWebElement();\n      }\n    }\n    const run = () => {\n      return from[fnName].apply(from, args);\n    };\n    if (setupFn) {\n      const setupResult = setupFn();\n      if (setupResult && (typeof setupResult.then === 'function')) {\n        return setupResult.then(run);\n      }\n    }\n    return run();\n  };\n}\n\nexport interface ElementHelper extends Function {\n  (locator: Locator): ElementFinder;\n  all: (locator: Locator) => ElementArrayFinder;\n}\n\n/**\n * Build the helper 'element' function for a given instance of Browser.\n *\n * @private\n * @param {Browser} browser A browser instance.\n * @returns {function(webdriver.Locator): ElementFinder}\n */\nfunction buildElementHelper(browser: ProtractorBrowser): ElementHelper {\n  let element = ((locator: Locator) => {\n                  return new ElementArrayFinder(browser).all(locator).toElementFinder_();\n                }) as ElementHelper;\n\n  element.all = (locator: Locator) => {\n    return new ElementArrayFinder(browser).all(locator);\n  };\n\n  return element;\n}\n\n/**\n * @alias browser\n * @constructor\n * @extends {webdriver_extensions.ExtendedWebDriver}\n * @param {webdriver.WebDriver} webdriver\n * @param {string=} opt_baseUrl A base URL to run get requests against.\n * @param {string|Promise<string>=} opt_rootElement  Selector element that has an\n *     ng-app in scope.\n * @param {boolean=} opt_untrackOutstandingTimeouts Whether Protractor should\n *     stop tracking outstanding $timeouts.\n */\nexport class ProtractorBrowser {\n  /**\n   * @type {ProtractorBy}\n   */\n  static By = new ProtractorBy();\n\n  /**\n   * @type {ExpectedConditions}\n   */\n  ExpectedConditions: ProtractorExpectedConditions;\n\n  /**\n   * The browser's WebDriver instance\n   *\n   * @type {webdriver.WebDriver}\n   */\n  driver: WebDriver;\n\n  /**\n   * The client used to control the BlockingProxy. If unset, BlockingProxy is\n   * not being used and Protractor will handle client-side synchronization.\n   */\n  bpClient: BPClient;\n\n  /**\n   * Helper function for finding elements.\n   *\n   * @type {function(webdriver.Locator): ElementFinder}\n   */\n  element: ElementHelper;\n\n  /**\n   * Shorthand function for finding elements by css.\n   *\n   * @type {function(string): ElementFinder}\n   */\n  $: (query: string) => ElementFinder;\n\n  /**\n   * Shorthand function for finding arrays of elements by css.\n   *\n   * @type {function(string): ElementArrayFinder}\n   */\n  $$: (query: string) => ElementArrayFinder;\n\n  /**\n   * All get methods will be resolved against this base URL. Relative URLs are =\n   * resolved the way anchor tags resolve.\n   *\n   * @type {string}\n   */\n  baseUrl: string;\n\n  /**\n   * The css selector for an element on which to find Angular. This is usually\n   * 'body' but if your ng-app is on a subsection of the page it may be\n   * a subelement.\n   *\n   * This property is deprecated - please use angularAppRoot() instead.\n   *\n   * @deprecated\n   * @type {string}\n   */\n  set rootEl(value: string) {\n    this.angularAppRoot(value);\n  }\n\n  get rootEl() {\n    return this.internalRootEl;\n  }\n\n  private internalRootEl: string;\n\n  /**\n   * Set the css selector for an element on which to find Angular. This is usually\n   * 'body' but if your ng-app is on a subsection of the page it may be\n   * a subelement.\n   *\n   * @param {string|Promise<string>} valuePromise The new selector.\n   * @returns A promise that resolves with the value of the selector.\n   */\n  async angularAppRoot(valuePromise: string|Promise<string> = null): Promise<string> {\n    if (valuePromise != null) {\n      const value = await valuePromise;\n      this.internalRootEl = value;\n      if (this.bpClient) {\n        await this.bpClient.setWaitParams(value);\n      }\n    }\n    return this.internalRootEl;\n  }\n\n  /**\n   * If true, Protractor will not attempt to synchronize with the page before\n   * performing actions. This can be harmful because Protractor will not wait\n   * until $timeouts and $http calls have been processed, which can cause\n   * tests to become flaky. This should be used only when necessary, such as\n   * when a page continuously polls an API using $timeout.\n   *\n   * Initialized to `false` by the runner.\n   *\n   * ignoreSynchornization is deprecated.\n   *\n   * Please use waitForAngularEnabled instead.\n   *\n   * @deprecated\n   * @type {boolean}\n   */\n  private internalIgnoreSynchronization: boolean;\n\n  /**\n   * Timeout in milliseconds to wait for pages to load when calling `get`.\n   *\n   * @type {number}\n   */\n  getPageTimeout: number;\n\n  /**\n   * An object that holds custom test parameters.\n   *\n   * @type {Object}\n   */\n  params: any;\n\n  /**\n   * Resolved when the browser is ready for use.  Resolves to the browser, so\n   * you can do:\n   *\n   *   forkedBrowser = await browser.forkNewDriverInstance();\n   *\n   * Set by the runner.\n   *\n   * @type {Promise<ProtractorBrowser>}\n   */\n  ready: Promise<ProtractorBrowser>;\n\n  /*\n   * Set by the runner.\n   *\n   * @type {Plugins} Object containing plugin funtions from config.\n   */\n  plugins_: Plugins;\n\n  /**\n   * The reset URL to use between page loads.\n   *\n   * @type {string}\n   */\n  resetUrl: string;\n\n  /**\n   * If true, Protractor will track outstanding $timeouts and report them in the\n   * error message if Protractor fails to synchronize with Angular in time.\n   * @private {boolean}\n   */\n  trackOutstandingTimeouts_: boolean;\n\n  /*\n   * Copy of `config.allScriptsTimeout`.  Used for plugins and nothing else.\n   *\n   * Set by the runner.\n   */\n  allScriptsTimeout: number;\n\n  /**\n   * Information about mock modules that will be installed during every\n   * get().\n   *\n   * @type {Array<{name: string, script: function|string, args: Array.<string>}>}\n   */\n  mockModules_: {name: string, script: string|Function, args: any[]}[];\n\n  /**\n   * If specified, start a debugger server at specified port instead of repl\n   * when running element explorer.\n   * @public {number}\n   */\n  public debuggerServerPort: number;\n\n  /**\n   * If true, Protractor will interpret any angular apps it comes across as\n   * hybrid angular1/angular2 apps.\n   *\n   * @type {boolean}\n   */\n  ng12Hybrid: boolean;\n\n  // This index type allows looking up methods by name so we can do mixins.\n  [key: string]: any;\n\n  constructor(\n      webdriverInstance: WebDriver, opt_baseUrl?: string, opt_rootElement?: string|Promise<string>,\n      opt_untrackOutstandingTimeouts?: boolean, opt_blockingProxyUrl?: string) {\n    // These functions should delegate to the webdriver instance, but should\n    // wait for Angular to sync up before performing the action. This does not\n    // include functions which are overridden by protractor below.\n    let methodsToSync = ['getCurrentUrl', 'getPageSource', 'getTitle'];\n\n    // Mix all other driver functionality into Protractor.\n    Object.getOwnPropertyNames(WebDriver.prototype).forEach(method => {\n      if (!this[method] && typeof(webdriverInstance as any)[method] === 'function') {\n        if (methodsToSync.indexOf(method) !== -1) {\n          ptorMixin(this, webdriverInstance, method, this.waitForAngular.bind(this));\n        } else {\n          ptorMixin(this, webdriverInstance, method);\n        }\n      }\n    });\n\n    this.driver = webdriverInstance;\n    if (opt_blockingProxyUrl) {\n      logger.info('Starting BP client for ' + opt_blockingProxyUrl);\n      this.bpClient = new BPClient(opt_blockingProxyUrl);\n    }\n    this.element = buildElementHelper(this);\n    this.$ = build$(this.element, By);\n    this.$$ = build$$(this.element, By);\n    this.baseUrl = opt_baseUrl || '';\n    this.getPageTimeout = DEFAULT_GET_PAGE_TIMEOUT;\n    this.params = {};\n    this.resetUrl = DEFAULT_RESET_URL;\n\n    let ng12Hybrid_ = false;\n    Object.defineProperty(this, 'ng12Hybrid', {\n      get: function() {\n        return ng12Hybrid_;\n      },\n      set: function(ng12Hybrid) {\n        if (ng12Hybrid) {\n          logger.warn(\n              'You have set ng12Hybrid.  As of Protractor 4.1.0, ' +\n              'Protractor can automatically infer if you are using an ' +\n              'ngUpgrade app (as long as ng1 is loaded before you call ' +\n              'platformBrowserDynamic()), and this flag is no longer needed ' +\n              'for most users');\n        }\n        ng12Hybrid_ = ng12Hybrid;\n      }\n    });\n\n    this.trackOutstandingTimeouts_ = !opt_untrackOutstandingTimeouts;\n    this.mockModules_ = [];\n    this.addBaseMockModules_();\n\n    // set up expected conditions\n    this.ExpectedConditions = new ProtractorExpectedConditions(this);\n  }\n\n  /**\n   * If set to false, Protractor will not wait for Angular $http and $timeout\n   * tasks to complete before interacting with the browser. This can cause\n   * flaky tests, but should be used if, for instance, your app continuously\n   * polls an API with $timeout.\n   *\n   * Call waitForAngularEnabled() without passing a value to read the current\n   * state without changing it.\n   */\n  async waitForAngularEnabled(enabledPromise: boolean|Promise<boolean> = null): Promise<boolean> {\n    if (enabledPromise != null) {\n      const enabled = await enabledPromise;\n      if (this.bpClient) {\n        logger.debug('Setting waitForAngular' + !enabled);\n        await this.bpClient.setWaitEnabled(enabled);\n      }\n      this.internalIgnoreSynchronization = !enabled;\n    }\n    return !this.internalIgnoreSynchronization;\n  }\n\n  /**\n   * Get the processed configuration object that is currently being run. This\n   * will contain the specs and capabilities properties of the current runner\n   * instance.\n   *\n   * Set by the runner.\n   *\n   * @returns {Promise} A promise which resolves to the\n   * capabilities object.\n   */\n  getProcessedConfig(): Promise<any> {\n    return null;\n  }\n\n  /**\n   * Fork another instance of browser for use in interactive tests.\n   *\n   * @example\n   * var forked = await browser.forkNewDriverInstance();\n   * await forked.get('page1'); // 'page1' gotten by forked browser\n   *\n   * @param {boolean=} useSameUrl Whether to navigate to current url on creation\n   * @param {boolean=} copyMockModules Whether to apply same mock modules on creation\n   * @param {boolean=} copyConfigUpdates Whether to copy over changes to `baseUrl` and similar\n   *   properties initialized to values in the the config.  Defaults to `true`\n   *\n   * @returns {ProtractorBrowser} A browser instance.\n   */\n  async forkNewDriverInstance(\n      useSameUrl?: boolean, copyMockModules?: boolean,\n      copyConfigUpdates = true): Promise<ProtractorBrowser> {\n    return null;\n  }\n\n  /**\n   * Restart the browser.  This is done by closing this browser instance and creating a new one.\n   * A promise resolving to the new instance is returned, and if this function was called on the\n   * global `browser` instance then Protractor will automatically overwrite the global `browser`\n   * variable.\n   *\n   * When restarting a forked browser, it is the caller's job to overwrite references to the old\n   * instance.\n   *\n   * Set by the runner.\n   *\n   * @example\n   * // Running against global browser\n   * await browser.get('page1');\n   * await browser.restart();\n   * await browser.get('page2'); // 'page2' gotten by restarted browser\n   *\n   * // Running against forked browsers\n   * var forked = await browser.forkNewDriverInstance();\n   * await fork.get('page1');\n   * fork = await fork.restart();\n   * await fork.get('page2'); // 'page2' gotten by restarted fork\n   *\n   * // Unexpected behavior can occur if you save references to the global `browser`\n   * var savedBrowser = browser;\n   * browser.get('foo').then(function() {\n   *   console.log(browser === savedBrowser); // false\n   * });\n   * browser.restart();\n   *\n   * @returns {Promise<ProtractorBrowser>} A promise resolving to the restarted\n   *   browser\n   */\n  restart(): Promise<ProtractorBrowser> {\n    return;\n  }\n\n  /**\n   * Instead of using a single root element, search through all angular apps\n   * available on the page when finding elements or waiting for stability.\n   * Only compatible with Angular2.\n   */\n  useAllAngular2AppRoots() {\n    // The empty string is an invalid css selector, so we use it to easily\n    // signal to scripts to not find a root element.\n    this.angularAppRoot('');\n  }\n\n  /**\n   * The same as {@code webdriver.WebDriver.prototype.executeScript},\n   * but with a customized description for debugging.\n   *\n   * @private\n   * @param {!(string|Function)} script The script to execute.\n   * @param {string} description A description of the command for debugging.\n   * @param {...*} var_args The arguments to pass to the script.\n   * @returns {!Promise<T>} A promise that will resolve to\n   * the scripts return value.\n   * @template T\n   */\n  public executeScriptWithDescription(\n      script: string|Function, description: string, ...scriptArgs: any[]): Promise<any> {\n    if (typeof script === 'function') {\n      script = 'return (' + script + ').apply(null, arguments);';\n    }\n\n    // TODO(selenium4): schedule does not exist on driver. Should use execute instead.\n    return this.driver.execute((new Command(CommandName.EXECUTE_SCRIPT) as Command)\n                                   .setParameter('script', script)\n                                   .setParameter('args', scriptArgs));\n  }\n\n  /**\n   * The same as {@code webdriver.WebDriver.prototype.executeAsyncScript},\n   * but with a customized description for debugging.\n   *\n   * @private\n   * @param {!(string|Function)} script The script to execute.\n   * @param {string} description A description for debugging purposes.\n   * @param {...*} var_args The arguments to pass to the script.\n   * @returns {!Promise<T>} A promise that will resolve to\n   * the\n   *    scripts return value.\n   * @template T\n   */\n  private executeAsyncScript_(script: string|Function, description: string, ...scriptArgs: any[]):\n      Promise<any> {\n    // TODO(selenium4): decide what to do with description.\n    if (typeof script === 'function') {\n      script = 'return (' + script + ').apply(null, arguments);';\n    }\n    // TODO(selenium4): fix typings. driver.execute should exist\n    return (this.driver as any)\n        .execute(new Command(CommandName.EXECUTE_ASYNC_SCRIPT)\n                     .setParameter('script', script)\n                     .setParameter('args', scriptArgs));\n  }\n\n  /**\n   * Instruct webdriver to wait until Angular has finished rendering and has\n   * no outstanding $http or $timeout calls before continuing.\n   * Note that Protractor automatically applies this command before every\n   * WebDriver action.\n   *\n   * @param {string=} opt_description An optional description to be added\n   *     to webdriver logs.\n   * @returns {!Promise} A promise that will resolve to the\n   *    scripts return value.\n   */\n  async waitForAngular(opt_description?: string): Promise<any> {\n    let description = opt_description ? ' - ' + opt_description : '';\n    if (!await this.waitForAngularEnabled()) {\n      return true;\n    }\n\n    let runWaitForAngularScript = async(): Promise<any> => {\n      if (this.plugins_.skipAngularStability() || this.bpClient) {\n        return null;\n      } else {\n        let rootEl = await this.angularAppRoot();\n        return this.executeAsyncScript_(\n            clientSideScripts.waitForAngular, `Protractor.waitForAngular() ${description}`, rootEl);\n      }\n    };\n\n    try {\n      let browserErr = await runWaitForAngularScript();\n      if (browserErr) {\n        throw new Error(\n            'Error while waiting for Protractor to ' +\n            'sync with the page: ' + JSON.stringify(browserErr));\n      }\n      await this.plugins_.waitForPromise(this);\n\n      await this.driver.wait(async () => {\n        let results = await this.plugins_.waitForCondition(this);\n        return results.reduce((x, y) => x && y, true);\n      }, this.allScriptsTimeout, 'Plugins.waitForCondition()');\n    } catch (err) {\n      let timeout: RegExpExecArray;\n      if (/asynchronous script timeout/.test(err.message)) {\n        // Timeout on Chrome\n        timeout = /-?[\\d\\.]*\\ seconds/.exec(err.message);\n      } else if (/Timed out waiting for async script/.test(err.message)) {\n        // Timeout on Firefox\n        timeout = /-?[\\d\\.]*ms/.exec(err.message);\n      } else if (/Timed out waiting for an asynchronous script/.test(err.message)) {\n        // Timeout on Safari\n        timeout = /-?[\\d\\.]*\\ ms/.exec(err.message);\n      }\n      if (timeout) {\n        let errMsg = `Timed out waiting for asynchronous Angular tasks to finish after ` +\n            `${timeout}. This may be because the current page is not an Angular ` +\n            `application. Please see the FAQ for more details: ` +\n            `https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular`;\n        if (description.indexOf(' - Locator: ') == 0) {\n          errMsg += '\\nWhile waiting for element with locator' + description;\n        }\n        let pendingTimeoutsPromise: Promise<any>;\n        if (this.trackOutstandingTimeouts_) {\n          pendingTimeoutsPromise = this.executeScriptWithDescription(\n              'return window.NG_PENDING_TIMEOUTS',\n              'Protractor.waitForAngular() - getting pending timeouts' + description);\n        } else {\n          pendingTimeoutsPromise = Promise.resolve();\n        }\n        let pendingHttpsPromise = this.executeScriptWithDescription(\n            clientSideScripts.getPendingHttpRequests,\n            'Protractor.waitForAngular() - getting pending https' + description,\n            this.internalRootEl);\n\n        let arr = await Promise.all([pendingTimeoutsPromise, pendingHttpsPromise]);\n\n        let pendingTimeouts = arr[0] || [];\n        let pendingHttps = arr[1] || [];\n\n        let key: string, pendingTasks: string[] = [];\n        for (key in pendingTimeouts) {\n          if (pendingTimeouts.hasOwnProperty(key)) {\n            pendingTasks.push(' - $timeout: ' + pendingTimeouts[key]);\n          }\n        }\n        for (key in pendingHttps) {\n          pendingTasks.push(' - $http: ' + pendingHttps[key].url);\n        }\n        if (pendingTasks.length) {\n          errMsg += '. \\nThe following tasks were pending:\\n';\n          errMsg += pendingTasks.join('\\n');\n        }\n        err.message = errMsg;\n      }\n      throw err;\n    }\n  }\n\n  /**\n   * Waits for Angular to finish renderActionSequenceing before searching for elements.\n   * @see webdriver.WebDriver.findElement\n   * @returns {!webdriver.WebElementPromise} A promise that will be resolved to\n   *      the located {@link webdriver.WebElement}.\n   */\n  findElement(locator: Locator): WebElementPromise {\n    return this.element(locator).getWebElement();\n  }\n\n  /**\n   * Waits for Angular to finish rendering before searching for elements.\n   * @see webdriver.WebDriver.findElements\n   * @returns {!Promise} A promise that will be resolved to an\n   *     array of the located {@link webdriver.WebElement}s.\n   */\n  findElements(locator: Locator): Promise<WebElement[]> {\n    return this.element.all(locator).getWebElements();\n  }\n\n  /**\n   * Tests if an element is present on the page.\n   * @see webdriver.WebDriver.isElementPresent\n   * @returns {!Promise} A promise that will resolve to whether\n   *     the element is present on the page.\n   */\n  isElementPresent(locatorOrElement: Locator|WebElement|ElementFinder): Promise<any> {\n    let element: ElementFinder;\n    if (locatorOrElement instanceof ElementFinder) {\n      element = locatorOrElement;\n    } else if (locatorOrElement instanceof WebElement) {\n      element = ElementFinder.fromWebElement_(this, locatorOrElement);\n    } else {\n      element = this.element(locatorOrElement);\n    }\n    return element.isPresent();\n  }\n\n  /**\n   * Add a module to load before Angular whenever Protractor.get is called.\n   * Modules will be registered after existing modules already on the page,\n   * so any module registered here will override preexisting modules with the\n   * same name.\n   *\n   * @example\n   * browser.addMockModule('modName', function() {\n   *   angular.module('modName', []).value('foo', 'bar');\n   * });\n   *\n   * @param {!string} name The name of the module to load or override.\n   * @param {!string|Function} script The JavaScript to load the module.\n   *     Note that this will be executed in the browser context, so it cannot\n   *     access variables from outside its scope.\n   * @param {...*} varArgs Any additional arguments will be provided to\n   *     the script and may be referenced using the `arguments` object.\n   */\n  addMockModule(name: string, script: string|Function, ...moduleArgs: any[]) {\n    this.mockModules_.push({name: name, script: script, args: moduleArgs});\n  }\n\n  /**\n   * Clear the list of registered mock modules.\n   */\n  clearMockModules() {\n    this.mockModules_ = [];\n    this.addBaseMockModules_();\n  }\n\n  /**\n   * Remove a registered mock module.\n   *\n   * @example\n   * browser.removeMockModule('modName');\n   *\n   * @param {!string} name The name of the module to remove.\n   */\n  removeMockModule(name: string) {\n    for (let i = 0; i < this.mockModules_.length; ++i) {\n      if (this.mockModules_[i].name == name) {\n        this.mockModules_.splice(i--, 1);\n      }\n    }\n  }\n\n  /**\n   * Get a list of the current mock modules.\n   *\n   * @returns {Array.<!string|Function>} The list of mock modules.\n   */\n  getRegisteredMockModules(): Array<string|Function> {\n    return this.mockModules_.map(module => module.script);\n  }\n\n  /**\n   * Add the base mock modules used for all Protractor tests.\n   *\n   * @private\n   */\n  private addBaseMockModules_() {\n    this.addMockModule(\n        'protractorBaseModule_', clientSideScripts.protractorBaseModuleFn,\n        this.trackOutstandingTimeouts_);\n  }\n\n  /**\n   * @see webdriver.WebDriver.get\n   *\n   * Navigate to the given destination and loads mock modules before\n   * Angular. Assumes that the page being loaded uses Angular.\n   * If you need to access a page which does not have Angular on load, use\n   * the wrapped webdriver directly.\n   *\n   * @example\n   * await browser.get('https://angularjs.org/');\n   * expect(await browser.getCurrentUrl()).toBe('https://angularjs.org/');\n   *\n   * @param {string} destination Destination URL.\n   * @param {number=} opt_timeout Number of milliseconds to wait for Angular to\n   *     start.\n   */\n  async get(destination: string, timeout = this.getPageTimeout) {\n    destination = this.baseUrl.indexOf('file://') === 0 ? this.baseUrl + destination :\n                                                          url.resolve(this.baseUrl, destination);\n    if (!await this.waitForAngularEnabled()) {\n      await this.driver.get(destination);\n      await this.plugins_.onPageLoad(this);\n      return;\n    }\n\n    let msg = (str: string) => {\n      return 'Protractor.get(' + destination + ') - ' + str;\n    };\n\n    if (this.bpClient) {\n      await this.bpClient.setWaitEnabled(false);\n    }\n    // Go to reset url\n    await this.driver.get(this.resetUrl);\n\n    // Set defer label and navigate\n    await this.executeScriptWithDescription(\n        'window.name = \"' + DEFER_LABEL + '\" + window.name;' +\n            'window.location.replace(\"' + destination + '\");',\n        msg('reset url'));\n\n    // We need to make sure the new url has loaded before\n    // we try to execute any asynchronous scripts.\n    await this.driver.wait(() => {\n      return this.executeScriptWithDescription('return window.location.href;', msg('get url'))\n          .then(\n              (url: any) => {\n                return url !== this.resetUrl;\n              },\n              (err: IError) => {\n                if (err.code == 13 || err.name === 'JavascriptError') {\n                  // Ignore the error, and continue trying. This is\n                  // because IE driver sometimes (~1%) will throw an\n                  // unknown error from this execution. See\n                  // https://github.com/angular/protractor/issues/841\n                  // This shouldn't mask errors because it will fail\n                  // with the timeout anyway.\n                  return false;\n                } else {\n                  throw err;\n                }\n              });\n    }, timeout, 'waiting for page to load for ' + timeout + 'ms');\n\n    // Run Plugins\n    await this.plugins_.onPageLoad(this);\n\n    let angularVersion: number;\n    try {\n      // Make sure the page is an Angular page.\n      const angularTestResult: {ver: number, message: string} = await this.executeAsyncScript_(\n          clientSideScripts.testForAngular, msg('test for angular'), Math.floor(timeout / 1000),\n          this.ng12Hybrid);\n      angularVersion = angularTestResult.ver;\n\n      if (!angularVersion) {\n        let message = angularTestResult.message;\n        logger.error(`Could not find Angular on page ${destination} : ${message}`);\n        throw new Error(\n            `Angular could not be found on the page ${destination}. ` +\n            `If this is not an Angular application, you may need to turn off waiting for Angular.\n            Please see\n            https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular-on-page-load`);\n      }\n    } catch (err) {\n      throw new Error('Error while running testForAngular: ' + err.message);\n    }\n\n    // Load Angular Mocks\n    if (angularVersion === 1) {\n      // At this point, Angular will pause for us until angular.resumeBootstrap is called.\n      let moduleNames: string[] = [];\n      for (const {name, script, args} of this.mockModules_) {\n        moduleNames.push(name);\n        let executeScriptArgs = [script, msg('add mock module ' + name), ...args];\n        await this.executeScriptWithDescription.apply(this, executeScriptArgs)\n            .then(null, (err: Error) => {\n              throw new Error('Error while running module script ' + name + ': ' + err.message);\n            });\n      }\n\n      await this.executeScriptWithDescription(\n          'window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__ = ' +\n              'angular.resumeBootstrap(arguments[0]);',\n          msg('resume bootstrap'), moduleNames);\n    } else {\n      // TODO: support mock modules in Angular2. For now, error if someone\n      // has tried to use one.\n      if (this.mockModules_.length > 1) {\n        throw 'Trying to load mock modules on an Angular v2+ app is not yet supported.';\n      }\n    }\n\n    // Reset bpClient sync\n    if (this.bpClient) {\n      await this.bpClient.setWaitEnabled(!this.internalIgnoreSynchronization);\n    }\n\n    // Run Plugins\n    await this.plugins_.onPageStable(this);\n  }\n\n  /**\n   * @see webdriver.WebDriver.refresh\n   *\n   * Makes a full reload of the current page and loads mock modules before\n   * Angular. Assumes that the page being loaded uses Angular.\n   * If you need to access a page which does not have Angular on load, use\n   * the wrapped webdriver directly.\n   *\n   * @param {number=} opt_timeout Number of milliseconds to wait for Angular to start.\n   */\n  async refresh(opt_timeout?: number) {\n    if (!await this.waitForAngularEnabled()) {\n      return this.driver.navigate().refresh();\n    }\n\n    const href = await this.executeScriptWithDescription(\n        'return window.location.href', 'Protractor.refresh() - getUrl');\n    return this.get(href, opt_timeout);\n  }\n\n  /**\n   * Mixin navigation methods back into the navigation object so that\n   * they are invoked as before, i.e. driver.navigate().refresh()\n   */\n  navigate(): Navigation {\n    let nav = this.driver.navigate();\n    ptorMixin(nav, this, 'refresh');\n    return nav;\n  }\n\n  /**\n   * Browse to another page using in-page navigation.\n   *\n   * @example\n   * await browser.get('http://angular.github.io/protractor/#/tutorial');\n   * await browser.setLocation('api');\n   * expect(await browser.getCurrentUrl())\n   *     .toBe('http://angular.g../../ithub.io/protractor/#/api');\n   *\n   * @param {string} url In page URL using the same syntax as $location.url()\n   * @returns {!Promise} A promise that will resolve once\n   *    page has been changed.\n   */\n  async setLocation(url: string): Promise<any> {\n    await this.waitForAngular();\n    const rootEl = await this.angularAppRoot();\n    const browserErr = await this.executeScriptWithDescription(\n        clientSideScripts.setLocation, 'Protractor.setLocation()', rootEl, url);\n    if (browserErr) {\n      throw 'Error while navigating to \\'' + url + '\\' : ' + JSON.stringify(browserErr);\n    }\n  }\n\n  /**\n   * Deprecated, use `browser.getCurrentUrl()` instead.\n   *\n   * Despite its name, this function will generally return `$location.url()`, though in some\n   * cases it will return `$location.absUrl()` instead.  This function is only here for legacy\n   * users, and will probably be removed in Protractor 6.0.\n   *\n   * @deprecated Please use `browser.getCurrentUrl()`\n   * @example\n   * await browser.get('http://angular.github.io/protractor/#/api');\n   * expect(await browser.getLocationAbsUrl())\n   *     .toBe('http://angular.github.io/protractor/#/api');\n   * @returns {Promise<string>} The current absolute url from\n   * AngularJS.\n   */\n  async getLocationAbsUrl(): Promise<any> {\n    logger.warn(\n        '`browser.getLocationAbsUrl()` is deprecated, please use `browser.getCurrentUrl` instead.');\n    await this.waitForAngular();\n    const rootEl = await this.angularAppRoot();\n    return await this.executeScriptWithDescription(\n        clientSideScripts.getLocationAbsUrl, 'Protractor.getLocationAbsUrl()', rootEl);\n  }\n}\n"
  },
  {
    "path": "lib/cli.ts",
    "content": "import * as fs from 'fs';\nimport * as path from 'path';\nimport * as yargs from 'yargs';\n\n/**\n * The command line interface for interacting with the Protractor runner.\n * It takes care of parsing command line options.\n *\n * Values from command line options override values from the config.\n */\n\nlet args: Array<string> = [];\n\nprocess.argv.slice(2).forEach(function(arg: string) {\n  let flag: string = arg.split('=')[0];\n\n  switch (flag) {\n    case 'debug':\n      args.push('--nodeDebug');\n      args.push('true');\n      break;\n    case '-d':\n    case '--debug':\n    case '--debug-brk':\n      args.push('--v8Debug');\n      args.push('true');\n      break;\n    default:\n      args.push(arg);\n      break;\n  }\n});\n\n// TODO(cnishina): Make cli checks better.\nlet allowedNames = [\n  'seleniumServerJar',\n  'seleniumServerStartTimeout',\n  'localSeleniumStandaloneOpts',\n  'chromeDriver',\n  'seleniumAddress',\n  'seleniumSessionId',\n  'webDriverProxy',\n  'useBlockingProxy',\n  'blockingProxyUrl',\n  'sauceUser',\n  'sauceKey',\n  'sauceAgent',\n  'sauceBuild',\n  'sauceSeleniumUseHttp',\n  'sauceSeleniumAddress',\n  'browserstackUser',\n  'browserstackKey',\n  'browserstackProxy',\n  'kobitonUser',\n  'kobitonKey',\n  'testobjectUser',\n  'testobjectKey',\n  'directConnect',\n  'firefoxPath',\n  'noGlobals',\n  'specs',\n  'exclude',\n  'suites',\n  'suite',\n  'capabilities',\n  'multiCapabilities',\n  'getMultiCapabilities',\n  'maxSessions',\n  'verbose',\n  'verboseMultiSessions',\n  'baseUrl',\n  'rootElement',\n  'allScriptsTimeout',\n  'getPageTimeout',\n  'beforeLaunch',\n  'onPrepare',\n  'onComplete',\n  'onCleanUp',\n  'afterLaunch',\n  'params',\n  'resultJsonOutputFile',\n  'restartBrowserBetweenTests',\n  'untrackOutstandingTimeouts',\n  'ignoreUncaughtExceptions',\n  'framework',\n  'jasmineNodeOpts',\n  'mochaOpts',\n  'plugins',\n  'skipSourceMapSupport',\n  'disableEnvironmentOverrides',\n  'ng12Hybrid',\n  'seleniumArgs',\n  'jvmArgs',\n  'configDir',\n  'troubleshoot',\n  'seleniumPort',\n  'mockSelenium',\n  'v8Debug',\n  'nodeDebug',\n  'debuggerServerPort',\n  'frameworkPath',\n  'elementExplorer',\n  'debug',\n  'logLevel',\n  'disableChecks',\n  'browser',\n  'name',\n  'platform',\n  'platform-version',\n  'tags',\n  'build',\n  'grep',\n  'invert-grep',\n  'explorer',\n  'stackTrace'\n];\n\nlet yargsOptions: any = {\n  describes: {\n    help: 'Print Protractor help menu',\n    version: 'Print Protractor version',\n    browser: 'Browsername, e.g. chrome or firefox',\n    seleniumAddress: 'A running selenium address to use',\n    seleniumSessionId: 'Attaching an existing session id',\n    seleniumServerJar: 'Location of the standalone selenium jar file',\n    seleniumPort: 'Optional port for the selenium standalone server',\n    baseUrl: 'URL to prepend to all relative paths',\n    rootElement: 'Element housing ng-app, if not html or body',\n    specs: 'Comma-separated list of files to test',\n    exclude: 'Comma-separated list of files to exclude',\n    verbose: 'Print full spec names',\n    stackTrace: 'Print stack trace on error',\n    params: 'Param object to be passed to the tests',\n    framework: 'Test framework to use: jasmine, mocha, or custom',\n    resultJsonOutputFile: 'Path to save JSON test result',\n    troubleshoot: 'Turn on troubleshooting output',\n    elementExplorer: 'Interactively test Protractor commands',\n    debuggerServerPort: 'Start a debugger server at specified port instead of repl',\n    disableChecks: 'Disable cli checks',\n    logLevel: 'Define Protractor log level [ERROR, WARN, INFO, DEBUG]'\n  },\n  aliases: {\n    browser: 'capabilities.browserName',\n    name: 'capabilities.name',\n    platform: 'capabilities.platform',\n    'platform-version': 'capabilities.version',\n    tags: 'capabilities.tags',\n    build: 'capabilities.build',\n    grep: 'jasmineNodeOpts.grep',\n    'invert-grep': 'jasmineNodeOpts.invertGrep',\n    explorer: 'elementExplorer'\n  },\n  strings: {'capabilities.tunnel-identifier': ''}\n};\n\nyargs.usage(\n    'Usage: protractor [configFile] [options]\\n' +\n    'configFile defaults to protractor.conf.js\\n' +\n    'The [options] object will override values from the config file.\\n' +\n    'See the reference config for a full list of options.');\nfor (let key of Object.keys(yargsOptions.describes)) {\n  yargs.describe(key, yargsOptions.describes[key]);\n}\nfor (let key of Object.keys(yargsOptions.aliases)) {\n  yargs.alias(key, yargsOptions.aliases[key]);\n}\nfor (let key of Object.keys(yargsOptions.strings)) {\n  yargs.string(key);\n}\n\nyargs.check(function(arg: any) {\n  if (arg._.length > 1) {\n    throw new Error('Error: more than one config file specified');\n  }\n\n  return true;\n});\n\nlet argv: any = yargs.parse(args);\n\nif (argv.help) {\n  yargs.showHelp();\n  process.exit(0);\n}\n\nif (argv.version) {\n  console.log('Version ' + require(path.resolve(__dirname, '../package.json')).version);\n  process.exit(0);\n}\n\n// Check to see if additional flags were used.\nargv.unknownFlags_ = Object.keys(argv).filter((element: string) => {\n  return element !== '$0' && element !== '_' && allowedNames.indexOf(element) === -1;\n});\n\n/**\n * Helper to resolve comma separated lists of file pattern strings relative to\n * the cwd.\n *\n * @private\n * @param {Array} list\n */\nfunction processFilePatterns_(list: string): Array<string> {\n  return list.split(',').map(function(spec) {\n    return path.resolve(process.cwd(), spec);\n  });\n}\n\nif (argv.specs) {\n  argv.specs = processFilePatterns_(<string>argv.specs);\n}\nif (argv.exclude) {\n  argv.exclude = processFilePatterns_(<string>argv.exclude);\n}\n\nif (argv.capabilities && argv.capabilities.chromeOptions) {\n  // ensure that single options (which optimist parses as a string)\n  // are passed in an array in chromeOptions when required:\n  // https://sites.google.com/a/chromium.org/chromedriver/capabilities#TOC-chromeOptions-object\n  ['args', 'extensions', 'excludeSwitches', 'windowTypes'].forEach((key) => {\n    if (typeof argv.capabilities.chromeOptions[key] === 'string') {\n      argv.capabilities.chromeOptions[key] = [argv.capabilities.chromeOptions[key]];\n    }\n  });\n}\n\n// Use default configuration, if it exists.\nlet configFile: string = argv._[0];\nif (!configFile) {\n  if (fs.existsSync('./protractor.conf.js')) {\n    configFile = './protractor.conf.js';\n  }\n}\n\nif (!configFile && !argv.elementExplorer && args.length < 3) {\n  console.log(\n      '**you must either specify a configuration file ' +\n      'or at least 3 options. See below for the options:\\n');\n  yargs.showHelp();\n  process.exit(1);\n}\n\n// Run the launcher\nimport * as launcher from './launcher';\nlauncher.init(configFile, argv);\n"
  },
  {
    "path": "lib/clientsidescripts.js",
    "content": "/**\n * All scripts to be run on the client via executeAsyncScript or\n * executeScript should be put here.\n *\n * NOTE: These scripts are transmitted over the wire as JavaScript text\n * constructed using their toString representation, and *cannot*\n * reference external variables.\n *\n * Some implementations seem to have issues with // comments, so use star-style\n * inside scripts.  (TODO: add issue number / example implementations\n * that caused the switch to avoid the // comments.)\n */\n\n// jshint browser: true\n// jshint shadow: true\n/* global angular */\nvar functions = {};\n\n///////////////////////////////////////////////////////\n////                                               ////\n////                    HELPERS                    ////\n////                                               ////\n///////////////////////////////////////////////////////\n\n\n/* Wraps a function up into a string with its helper functions so that it can\n * call those helper functions client side\n *\n * @param {function} fun The function to wrap up with its helpers\n * @param {...function} The helper functions.  Each function must be named\n *\n * @return {string} The string which, when executed, will invoke fun in such a\n *   way that it has access to its helper functions\n */\nfunction wrapWithHelpers(fun) {\n  var helpers = Array.prototype.slice.call(arguments, 1);\n  if (!helpers.length) {\n    return fun;\n  }\n  var FunClass = Function; // Get the linter to allow this eval\n  return new FunClass(\n      helpers.join(';') + String.fromCharCode(59) +\n      '  return (' + fun.toString() + ').apply(this, arguments);');\n}\n\n/* Tests if an ngRepeat matches a repeater\n *\n * @param {string} ngRepeat The ngRepeat to test\n * @param {string} repeater The repeater to test against\n * @param {boolean} exact If the ngRepeat expression needs to match the whole\n *   repeater (not counting any `track by ...` modifier) or if it just needs to\n *   match a substring\n * @return {boolean} If the ngRepeat matched the repeater\n */\nfunction repeaterMatch(ngRepeat, repeater, exact) {\n  if (exact) {\n    return ngRepeat.split(' track by ')[0].split(' as ')[0].split('|')[0].\n        split('=')[0].trim() == repeater;\n  } else {\n    return ngRepeat.indexOf(repeater) != -1;\n  }\n}\n\n/* Tries to find $$testability and possibly $injector for an ng1 app\n *\n * By default, doesn't care about $injector if it finds $$testability.  However,\n * these priorities can be reversed.\n *\n * @param {string=} selector The selector for the element with the injector.  If\n *   falsy, tries a variety of methods to find an injector\n * @param {boolean=} injectorPlease Prioritize finding an injector\n * @return {$$testability?: Testability, $injector?: Injector} Returns whatever\n *   ng1 app hooks it finds\n */\nfunction getNg1Hooks(selector, injectorPlease) {\n  function tryEl(el) {\n    try {\n      if (!injectorPlease && angular.getTestability) {\n        var $$testability = angular.getTestability(el);\n        if ($$testability) {\n          return {$$testability: $$testability};\n        }\n      } else {\n        var $injector = angular.element(el).injector();\n        if ($injector) {\n          return {$injector: $injector};\n        }\n      }\n    } catch(err) {}\n  }\n  function trySelector(selector) {\n    var els = document.querySelectorAll(selector);\n    for (var i = 0; i < els.length; i++) {\n      var elHooks = tryEl(els[i]);\n      if (elHooks) {\n        return elHooks;\n      }\n    }\n  }\n\n  if (selector) {\n    return trySelector(selector);\n  } else if (window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__) {\n    var $injector = window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__;\n    var $$testability = null;\n    try {\n      $$testability = $injector.get('$$testability');\n    } catch (e) {}\n    return {$injector: $injector, $$testability: $$testability};\n  } else {\n    return tryEl(document.body) ||\n        trySelector('[ng-app]') || trySelector('[ng\\\\:app]') ||\n        trySelector('[ng-controller]') || trySelector('[ng\\\\:controller]');\n  }\n}\n\n///////////////////////////////////////////////////////\n////                                               ////\n////                    SCRIPTS                    ////\n////                                               ////\n///////////////////////////////////////////////////////\n\n\n/**\n * Wait until Angular has finished rendering and has\n * no outstanding $http calls before continuing. The specific Angular app\n * is determined by the rootSelector.\n *\n * Asynchronous.\n *\n * @param {string} rootSelector The selector housing an ng-app\n * @param {function(string)} callback callback. If a failure occurs, it will\n *     be passed as a parameter.\n */\nfunctions.waitForAngular = function(rootSelector, callback) {\n\n  try {\n    // Wait for both angular1 testability and angular2 testability.\n\n    var testCallback = callback;\n\n    // Wait for angular1 testability first and run waitForAngular2 as a callback\n    var waitForAngular1 = function(callback) {\n\n      if (window.angular) {\n        var hooks = getNg1Hooks(rootSelector);\n        if (!hooks){\n          callback();  // not an angular1 app\n        }\n        else{\n          if (hooks.$$testability) {\n            hooks.$$testability.whenStable(callback);\n          } else if (hooks.$injector) {\n            hooks.$injector.get('$browser')\n                .notifyWhenNoOutstandingRequests(callback);\n          } else if (!rootSelector) {\n            throw new Error(\n                'Could not automatically find injector on page: \"' +\n                window.location.toString() + '\".  Consider using config.rootEl');\n          } else {\n            throw new Error(\n                'root element (' + rootSelector + ') has no injector.' +\n                ' this may mean it is not inside ng-app.');\n          }\n        }\n      }\n      else {callback();}  // not an angular1 app\n    };\n\n    // Wait for Angular2 testability and then run test callback\n    var waitForAngular2 = function() {\n      if (window.getAngularTestability) {\n        if (rootSelector) {\n          var testability = null;\n          var el = document.querySelector(rootSelector);\n          try{\n            testability = window.getAngularTestability(el);\n          }\n          catch(e){}\n          if (testability) {\n            testability.whenStable(testCallback);\n            return;\n          }\n        }\n\n        // Didn't specify root element or testability could not be found\n        // by rootSelector. This may happen in a hybrid app, which could have\n        // more than one root.\n        var testabilities = window.getAllAngularTestabilities();\n        var count = testabilities.length;\n\n        // No angular2 testability, this happens when\n        // going to a hybrid page and going back to a pure angular1 page\n        if (count === 0) {\n          testCallback();\n          return;\n        }\n\n        var decrement = function() {\n          count--;\n          if (count === 0) {\n            testCallback();\n          }\n        };\n        testabilities.forEach(function(testability) {\n          testability.whenStable(decrement);\n        });\n\n      }\n      else {testCallback();}  // not an angular2 app\n    };\n\n    if (!(window.angular) && !(window.getAngularTestability)) {\n      // no testability hook\n      throw new Error(\n          'both angularJS testability and angular testability are undefined.' +\n          '  This could be either ' +\n          'because this is a non-angular page or because your test involves ' +\n          'client-side navigation, which can interfere with Protractor\\'s ' +\n          'bootstrapping.  See http://git.io/v4gXM for details');\n    } else {waitForAngular1(waitForAngular2);}  // Wait for angular1 and angular2\n                                                // Testability hooks sequentially\n\n  } catch (err) {\n    callback(err.message);\n  }\n\n};\n\n/**\n * Find a list of elements in the page by their angular binding.\n *\n * @param {string} binding The binding, e.g. {{cat.name}}.\n * @param {boolean} exactMatch Whether the binding needs to be matched exactly\n * @param {Element} using The scope of the search.\n * @param {string} rootSelector The selector to use for the root app element.\n *\n * @return {Array.<Element>} The elements containing the binding.\n */\nfunctions.findBindings = function(binding, exactMatch, using, rootSelector) {\n  using = using || document;\n  if (angular.getTestability) {\n    return getNg1Hooks(rootSelector).$$testability.\n        findBindings(using, binding, exactMatch);\n  }\n  var bindings = using.getElementsByClassName('ng-binding');\n  var matches = [];\n  for (var i = 0; i < bindings.length; ++i) {\n    var dataBinding = angular.element(bindings[i]).data('$binding');\n    if (dataBinding) {\n      var bindingName = dataBinding.exp || dataBinding[0].exp || dataBinding;\n      if (exactMatch) {\n        var matcher = new RegExp('({|\\\\s|^|\\\\|)' +\n            /* See http://stackoverflow.com/q/3561711 */\n            binding.replace(/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g, '\\\\$&') +\n            '(}|\\\\s|$|\\\\|)');\n        if (matcher.test(bindingName)) {\n          matches.push(bindings[i]);\n        }\n      } else {\n        if (bindingName.indexOf(binding) != -1) {\n          matches.push(bindings[i]);\n        }\n      }\n\n    }\n  }\n  return matches; /* Return the whole array for webdriver.findElements. */\n};\n\n/**\n * Find an array of elements matching a row within an ng-repeat.\n * Always returns an array of only one element for plain old ng-repeat.\n * Returns an array of all the elements in one segment for ng-repeat-start.\n *\n * @param {string} repeater The text of the repeater, e.g. 'cat in cats'.\n * @param {boolean} exact Whether the repeater needs to be matched exactly\n * @param {number} index The row index.\n * @param {Element} using The scope of the search.\n *\n * @return {Array.<Element>} The row of the repeater, or an array of elements\n *     in the first row in the case of ng-repeat-start.\n */\nfunction findRepeaterRows(repeater, exact, index, using) {\n  using = using || document;\n\n  var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\\\:'];\n  var rows = [];\n  for (var p = 0; p < prefixes.length; ++p) {\n    var attr = prefixes[p] + 'repeat';\n    var repeatElems = using.querySelectorAll('[' + attr + ']');\n    attr = attr.replace(/\\\\/g, '');\n    for (var i = 0; i < repeatElems.length; ++i) {\n      if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {\n        rows.push(repeatElems[i]);\n      }\n    }\n  }\n  /* multiRows is an array of arrays, where each inner array contains\n     one row of elements. */\n  var multiRows = [];\n  for (var p = 0; p < prefixes.length; ++p) {\n    var attr = prefixes[p] + 'repeat-start';\n    var repeatElems = using.querySelectorAll('[' + attr + ']');\n    attr = attr.replace(/\\\\/g, '');\n    for (var i = 0; i < repeatElems.length; ++i) {\n      if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {\n        var elem = repeatElems[i];\n        var row = [];\n        while (elem.nodeType != 8 ||\n            !repeaterMatch(elem.nodeValue, repeater)) {\n          if (elem.nodeType == 1) {\n            row.push(elem);\n          }\n          elem = elem.nextSibling;\n        }\n        multiRows.push(row);\n      }\n    }\n  }\n  var row = rows[index] || [], multiRow = multiRows[index] || [];\n  return [].concat(row, multiRow);\n}\nfunctions.findRepeaterRows = wrapWithHelpers(findRepeaterRows, repeaterMatch);\n\n /**\n * Find all rows of an ng-repeat.\n *\n * @param {string} repeater The text of the repeater, e.g. 'cat in cats'.\n * @param {boolean} exact Whether the repeater needs to be matched exactly\n * @param {Element} using The scope of the search.\n *\n * @return {Array.<Element>} All rows of the repeater.\n */\nfunction findAllRepeaterRows(repeater, exact, using) {\n  using = using || document;\n\n  var rows = [];\n  var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\\\:'];\n  for (var p = 0; p < prefixes.length; ++p) {\n    var attr = prefixes[p] + 'repeat';\n    var repeatElems = using.querySelectorAll('[' + attr + ']');\n    attr = attr.replace(/\\\\/g, '');\n    for (var i = 0; i < repeatElems.length; ++i) {\n      if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {\n        rows.push(repeatElems[i]);\n      }\n    }\n  }\n  for (var p = 0; p < prefixes.length; ++p) {\n    var attr = prefixes[p] + 'repeat-start';\n    var repeatElems = using.querySelectorAll('[' + attr + ']');\n    attr = attr.replace(/\\\\/g, '');\n    for (var i = 0; i < repeatElems.length; ++i) {\n      if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {\n        var elem = repeatElems[i];\n        while (elem.nodeType != 8 ||\n            !repeaterMatch(elem.nodeValue, repeater)) {\n          if (elem.nodeType == 1) {\n            rows.push(elem);\n          }\n          elem = elem.nextSibling;\n        }\n      }\n    }\n  }\n  return rows;\n}\nfunctions.findAllRepeaterRows = wrapWithHelpers(findAllRepeaterRows, repeaterMatch);\n\n/**\n * Find an element within an ng-repeat by its row and column.\n *\n * @param {string} repeater The text of the repeater, e.g. 'cat in cats'.\n * @param {boolean} exact Whether the repeater needs to be matched exactly\n * @param {number} index The row index.\n * @param {string} binding The column binding, e.g. '{{cat.name}}'.\n * @param {Element} using The scope of the search.\n * @param {string} rootSelector The selector to use for the root app element.\n *\n * @return {Array.<Element>} The element in an array.\n */\nfunction findRepeaterElement(repeater, exact, index, binding, using, rootSelector) {\n  var matches = [];\n  using = using || document;\n\n  var rows = [];\n  var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\\\:'];\n  for (var p = 0; p < prefixes.length; ++p) {\n    var attr = prefixes[p] + 'repeat';\n    var repeatElems = using.querySelectorAll('[' + attr + ']');\n    attr = attr.replace(/\\\\/g, '');\n    for (var i = 0; i < repeatElems.length; ++i) {\n      if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {\n        rows.push(repeatElems[i]);\n      }\n    }\n  }\n  /* multiRows is an array of arrays, where each inner array contains\n     one row of elements. */\n  var multiRows = [];\n  for (var p = 0; p < prefixes.length; ++p) {\n    var attr = prefixes[p] + 'repeat-start';\n    var repeatElems = using.querySelectorAll('[' + attr + ']');\n    attr = attr.replace(/\\\\/g, '');\n    for (var i = 0; i < repeatElems.length; ++i) {\n      if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {\n        var elem = repeatElems[i];\n        var row = [];\n        while (elem.nodeType != 8 || (elem.nodeValue &&\n            !repeaterMatch(elem.nodeValue, repeater))) {\n          if (elem.nodeType == 1) {\n            row.push(elem);\n          }\n          elem = elem.nextSibling;\n        }\n        multiRows.push(row);\n      }\n    }\n  }\n  var row = rows[index];\n  var multiRow = multiRows[index];\n  var bindings = [];\n  if (row) {\n    if (angular.getTestability) {\n      matches.push.apply(\n          matches,\n          getNg1Hooks(rootSelector).$$testability.findBindings(row, binding));\n    } else {\n      if (row.className.indexOf('ng-binding') != -1) {\n        bindings.push(row);\n      }\n      var childBindings = row.getElementsByClassName('ng-binding');\n      for (var i = 0; i < childBindings.length; ++i) {\n        bindings.push(childBindings[i]);\n      }\n    }\n  }\n  if (multiRow) {\n    for (var i = 0; i < multiRow.length; ++i) {\n      var rowElem = multiRow[i];\n      if (angular.getTestability) {\n        matches.push.apply(\n            matches,\n            getNg1Hooks(rootSelector).$$testability.findBindings(rowElem,\n                binding));\n      } else {\n        if (rowElem.className.indexOf('ng-binding') != -1) {\n          bindings.push(rowElem);\n        }\n        var childBindings = rowElem.getElementsByClassName('ng-binding');\n        for (var j = 0; j < childBindings.length; ++j) {\n          bindings.push(childBindings[j]);\n        }\n      }\n    }\n  }\n  for (var i = 0; i < bindings.length; ++i) {\n    var dataBinding = angular.element(bindings[i]).data('$binding');\n    if (dataBinding) {\n      var bindingName = dataBinding.exp || dataBinding[0].exp || dataBinding;\n      if (bindingName.indexOf(binding) != -1) {\n        matches.push(bindings[i]);\n      }\n    }\n  }\n  return matches;\n}\nfunctions.findRepeaterElement =\n    wrapWithHelpers(findRepeaterElement, repeaterMatch, getNg1Hooks);\n\n/**\n * Find the elements in a column of an ng-repeat.\n *\n * @param {string} repeater The text of the repeater, e.g. 'cat in cats'.\n * @param {boolean} exact Whether the repeater needs to be matched exactly\n * @param {string} binding The column binding, e.g. '{{cat.name}}'.\n * @param {Element} using The scope of the search.\n * @param {string} rootSelector The selector to use for the root app element.\n *\n * @return {Array.<Element>} The elements in the column.\n */\nfunction findRepeaterColumn(repeater, exact, binding, using, rootSelector) {\n  var matches = [];\n  using = using || document;\n\n  var rows = [];\n  var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\\\:'];\n  for (var p = 0; p < prefixes.length; ++p) {\n    var attr = prefixes[p] + 'repeat';\n    var repeatElems = using.querySelectorAll('[' + attr + ']');\n    attr = attr.replace(/\\\\/g, '');\n    for (var i = 0; i < repeatElems.length; ++i) {\n      if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {\n        rows.push(repeatElems[i]);\n      }\n    }\n  }\n  /* multiRows is an array of arrays, where each inner array contains\n     one row of elements. */\n  var multiRows = [];\n  for (var p = 0; p < prefixes.length; ++p) {\n    var attr = prefixes[p] + 'repeat-start';\n    var repeatElems = using.querySelectorAll('[' + attr + ']');\n    attr = attr.replace(/\\\\/g, '');\n    for (var i = 0; i < repeatElems.length; ++i) {\n      if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {\n        var elem = repeatElems[i];\n        var row = [];\n        while (elem.nodeType != 8 || (elem.nodeValue &&\n            !repeaterMatch(elem.nodeValue, repeater))) {\n          if (elem.nodeType == 1) {\n            row.push(elem);\n          }\n          elem = elem.nextSibling;\n        }\n        multiRows.push(row);\n      }\n    }\n  }\n  var bindings = [];\n  for (var i = 0; i < rows.length; ++i) {\n    if (angular.getTestability) {\n      matches.push.apply(\n          matches,\n          getNg1Hooks(rootSelector).$$testability.findBindings(rows[i],\n              binding));\n    } else {\n      if (rows[i].className.indexOf('ng-binding') != -1) {\n        bindings.push(rows[i]);\n      }\n      var childBindings = rows[i].getElementsByClassName('ng-binding');\n      for (var k = 0; k < childBindings.length; ++k) {\n        bindings.push(childBindings[k]);\n      }\n    }\n  }\n  for (var i = 0; i < multiRows.length; ++i) {\n    for (var j = 0; j < multiRows[i].length; ++j) {\n      if (angular.getTestability) {\n        matches.push.apply(\n            matches,\n            getNg1Hooks(rootSelector).$$testability.findBindings(\n                multiRows[i][j], binding));\n      } else {\n        var elem = multiRows[i][j];\n        if (elem.className.indexOf('ng-binding') != -1) {\n          bindings.push(elem);\n        }\n        var childBindings = elem.getElementsByClassName('ng-binding');\n        for (var k = 0; k < childBindings.length; ++k) {\n          bindings.push(childBindings[k]);\n        }\n      }\n    }\n  }\n  for (var j = 0; j < bindings.length; ++j) {\n    var dataBinding = angular.element(bindings[j]).data('$binding');\n    if (dataBinding) {\n      var bindingName = dataBinding.exp || dataBinding[0].exp || dataBinding;\n      if (bindingName.indexOf(binding) != -1) {\n        matches.push(bindings[j]);\n      }\n    }\n  }\n  return matches;\n}\nfunctions.findRepeaterColumn =\n    wrapWithHelpers(findRepeaterColumn, repeaterMatch, getNg1Hooks);\n\n/**\n * Find elements by model name.\n *\n * @param {string} model The model name.\n * @param {Element} using The scope of the search.\n * @param {string} rootSelector The selector to use for the root app element.\n *\n * @return {Array.<Element>} The matching elements.\n */\nfunctions.findByModel = function(model, using, rootSelector) {\n  using = using || document;\n\n  if (angular.getTestability) {\n    return getNg1Hooks(rootSelector).$$testability.\n        findModels(using, model, true);\n  }\n  var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\\\:'];\n  for (var p = 0; p < prefixes.length; ++p) {\n    var selector = '[' + prefixes[p] + 'model=\"' + model + '\"]';\n    var elements = using.querySelectorAll(selector);\n    if (elements.length) {\n      return elements;\n    }\n  }\n};\n\n/**\n * Find elements by options.\n *\n * @param {string} optionsDescriptor The descriptor for the option\n *     (i.e. fruit for fruit in fruits).\n * @param {Element} using The scope of the search.\n *\n * @return {Array.<Element>} The matching elements.\n */\nfunctions.findByOptions = function(optionsDescriptor, using) {\n  using = using || document;\n\n  var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\\\:'];\n  for (var p = 0; p < prefixes.length; ++p) {\n    var selector = '[' + prefixes[p] + 'options=\"' + optionsDescriptor + '\"] option';\n    var elements = using.querySelectorAll(selector);\n    if (elements.length) {\n      return elements;\n    }\n  }\n};\n\n/**\n * Find buttons by textual content.\n *\n * @param {string} searchText The exact text to match.\n * @param {Element} using The scope of the search.\n *\n * @return {Array.<Element>} The matching elements.\n */\nfunctions.findByButtonText = function(searchText, using) {\n  using = using || document;\n\n  var elements = using.querySelectorAll('button, input[type=\"button\"], input[type=\"submit\"]');\n  var matches = [];\n  for (var i = 0; i < elements.length; ++i) {\n    var element = elements[i];\n    var elementText;\n    if (element.tagName.toLowerCase() == 'button') {\n      elementText = element.textContent || element.innerText || '';\n    } else {\n      elementText = element.value;\n    }\n    if (elementText.trim() === searchText) {\n      matches.push(element);\n    }\n  }\n\n  return matches;\n};\n\n/**\n * Find buttons by textual content.\n *\n * @param {string} searchText The exact text to match.\n * @param {Element} using The scope of the search.\n *\n * @return {Array.<Element>} The matching elements.\n */\nfunctions.findByPartialButtonText = function(searchText, using) {\n  using = using || document;\n\n  var elements = using.querySelectorAll('button, input[type=\"button\"], input[type=\"submit\"]');\n  var matches = [];\n  for (var i = 0; i < elements.length; ++i) {\n    var element = elements[i];\n    var elementText;\n    if (element.tagName.toLowerCase() == 'button') {\n      elementText = element.textContent || element.innerText || '';\n    } else {\n      elementText = element.value;\n    }\n    if (elementText.indexOf(searchText) > -1) {\n      matches.push(element);\n    }\n  }\n\n  return matches;\n};\n\n/**\n * Find elements by css selector and textual content.\n *\n * @param {string} cssSelector The css selector to match.\n * @param {string} searchText The exact text to match or a serialized regex.\n * @param {Element} using The scope of the search.\n *\n * @return {Array.<Element>} An array of matching elements.\n */\nfunctions.findByCssContainingText = function(cssSelector, searchText, using) {\n  using = using || document;\n\n  if (searchText.indexOf('__REGEXP__') === 0) {\n    var match = searchText.split('__REGEXP__')[1].match(/\\/(.*)\\/(.*)?/);\n    searchText = new RegExp(match[1], match[2] || '');\n  }\n  var elements = using.querySelectorAll(cssSelector);\n  var matches = [];\n  for (var i = 0; i < elements.length; ++i) {\n    var element = elements[i];\n    var elementText = element.textContent || element.innerText || '';\n    var elementMatches = searchText instanceof RegExp ?\n        searchText.test(elementText) :\n        elementText.indexOf(searchText) > -1;\n\n    if (elementMatches) {\n      matches.push(element);\n    }\n  }\n  return matches;\n};\n\n/**\n * Tests whether the angular global variable is present on a page. Retries\n * in case the page is just loading slowly.\n *\n * Asynchronous.\n *\n * @param {number} attempts Number of times to retry.\n * @param {boolean} ng12Hybrid Flag set if app is a hybrid of angular 1 and 2\n * @param {function({version: ?number, message: ?string})} asyncCallback callback\n *\n */\nfunctions.testForAngular = function(attempts, ng12Hybrid, asyncCallback) {\n  var callback = function(args) {\n    setTimeout(function() {\n      asyncCallback(args);\n    }, 0);\n  };\n  var definitelyNg1 = !!ng12Hybrid;\n  var definitelyNg2OrNewer = false;\n  var check = function(n) {\n    try {\n      /* Figure out which version of angular we're waiting on */\n      if (!definitelyNg1 && !definitelyNg2OrNewer) {\n        if (window.angular && !(window.angular.version && window.angular.version.major > 1)) {\n          definitelyNg1 = true;\n        } else if (window.getAllAngularTestabilities) {\n          definitelyNg2OrNewer = true;\n        }\n      }\n      /* See if our version of angular is ready */\n      if (definitelyNg1) {\n        if (window.angular && window.angular.resumeBootstrap) {\n          return callback({ver: 1});\n        }\n      } else if (definitelyNg2OrNewer) {\n        if (true /* ng2 has no resumeBootstrap() */) {\n          return callback({ver: 2});\n        }\n      }\n      /* Try again (or fail) */\n      if (n < 1) {\n        if (definitelyNg1 && window.angular) {\n          callback({message: 'angular never provided resumeBootstrap'});\n        } else if (ng12Hybrid && !window.angular) {\n          callback({message: 'angular 1 never loaded' +\n              window.getAllAngularTestabilities ? ' (are you sure this app ' +\n              'uses ngUpgrade?  Try un-setting ng12Hybrid)' : ''});\n        } else {\n          callback({message: 'retries looking for angular exceeded'});\n        }\n      } else {\n        window.setTimeout(function() {check(n - 1);}, 1000);\n      }\n    } catch (e) {\n      callback({message: e});\n    }\n  };\n  check(attempts);\n};\n\n/**\n * Evalute an Angular expression in the context of a given element.\n *\n * @param {Element} element The element in whose scope to evaluate.\n * @param {string} expression The expression to evaluate.\n *\n * @return {?Object} The result of the evaluation.\n */\nfunctions.evaluate = function(element, expression) {\n  return angular.element(element).scope().$eval(expression);\n};\n\nfunctions.allowAnimations = function(element, value) {\n  var ngElement = angular.element(element);\n  if (ngElement.allowAnimations) {\n    // AngularDart: $testability API.\n    return ngElement.allowAnimations(value);\n  } else {\n    // AngularJS\n    var enabledFn = ngElement.injector().get('$animate').enabled;\n    return (value == null) ? enabledFn() : enabledFn(value);\n  }\n};\n\n/**\n * Return the current url using $location.absUrl().\n *\n * @param {string} selector The selector housing an ng-app\n */\nfunctions.getLocationAbsUrl = function(selector) {\n  var hooks = getNg1Hooks(selector);\n  if (angular.getTestability) {\n    return hooks.$$testability.getLocation();\n  }\n  return hooks.$injector.get('$location').absUrl();\n};\n\n/**\n * Browse to another page using in-page navigation.\n *\n * @param {string} selector The selector housing an ng-app\n * @param {string} url In page URL using the same syntax as $location.url(),\n *     /path?search=a&b=c#hash\n */\nfunctions.setLocation = function(selector, url) {\n  var hooks = getNg1Hooks(selector);\n  if (angular.getTestability) {\n    return hooks.$$testability.setLocation(url);\n  }\n  var $injector = hooks.$injector;\n  var $location = $injector.get('$location');\n  var $rootScope = $injector.get('$rootScope');\n\n  if (url !== $location.url()) {\n    $location.url(url);\n    $rootScope.$digest();\n  }\n};\n\n/**\n * Retrieve the pending $http requests.\n *\n * @param {string} selector The selector housing an ng-app\n * @return {!Array<!Object>} An array of pending http requests.\n */\nfunctions.getPendingHttpRequests = function(selector) {\n  var hooks = getNg1Hooks(selector, true);\n  var $http = hooks.$injector.get('$http');\n  return $http.pendingRequests;\n};\n\n['waitForAngular', 'findBindings', 'findByModel', 'getLocationAbsUrl',\n  'setLocation', 'getPendingHttpRequests'].forEach(function(funName) {\n    functions[funName] = wrapWithHelpers(functions[funName], getNg1Hooks);\n});\n\n/* Publish all the functions as strings to pass to WebDriver's\n * exec[Async]Script.  In addition, also include a script that will\n * install all the functions on window (for debugging.)\n *\n * We also wrap any exceptions thrown by a clientSideScripts function\n * that is not an instance of the Error type into an Error type.  If we\n * don't do so, then the resulting stack trace is completely unhelpful\n * and the exception message is just \"unknown error.\"  These types of\n * exceptions are the common case for dart2js code.  This wrapping gives\n * us the Dart stack trace and exception message.\n */\nvar util = require('util');\nvar scriptsList = [];\nvar scriptFmt = (\n    'try { return (%s).apply(this, arguments); }\\n' +\n    'catch(e) { throw (e instanceof Error) ? e : new Error(e); }');\nfor (var fnName in functions) {\n  if (functions.hasOwnProperty(fnName)) {\n    exports[fnName] = util.format(scriptFmt, functions[fnName]);\n    scriptsList.push(util.format('%s: %s', fnName, functions[fnName]));\n  }\n}\n\nexports.installInBrowser = (util.format(\n    'window.clientSideScripts = {%s};', scriptsList.join(', ')));\n\n/**\n * Automatically installed by Protractor when a page is loaded, this\n * default mock module decorates $timeout to keep track of any\n * outstanding timeouts.\n *\n * @param {boolean} trackOutstandingTimeouts\n */\nexports.protractorBaseModuleFn = function(trackOutstandingTimeouts) {\n  var ngMod = angular.module('protractorBaseModule_', []).config([\n    '$compileProvider',\n    function($compileProvider) {\n      if ($compileProvider.debugInfoEnabled) {\n        $compileProvider.debugInfoEnabled(true);\n      }\n    }\n  ]);\n  if (trackOutstandingTimeouts) {\n    ngMod.config([\n      '$provide',\n      function ($provide) {\n        $provide.decorator('$timeout', [\n          '$delegate',\n          function ($delegate) {\n            var $timeout = $delegate;\n\n            var taskId = 0;\n\n            if (!window['NG_PENDING_TIMEOUTS']) {\n              window['NG_PENDING_TIMEOUTS'] = {};\n            }\n\n            var extendedTimeout= function() {\n              var args = Array.prototype.slice.call(arguments);\n              if (typeof(args[0]) !== 'function') {\n                return $timeout.apply(null, args);\n              }\n\n              taskId++;\n              var fn = args[0];\n              window['NG_PENDING_TIMEOUTS'][taskId] =\n                  fn.toString();\n              var wrappedFn = (function(taskId_) {\n                return function() {\n                  delete window['NG_PENDING_TIMEOUTS'][taskId_];\n                  return fn.apply(null, arguments);\n                };\n              })(taskId);\n              args[0] = wrappedFn;\n\n              var promise = $timeout.apply(null, args);\n              promise.ptorTaskId_ = taskId;\n              return promise;\n            };\n\n            extendedTimeout.cancel = function() {\n              var taskId_ = arguments[0] && arguments[0].ptorTaskId_;\n              if (taskId_) {\n                delete window['NG_PENDING_TIMEOUTS'][taskId_];\n              }\n              return $timeout.cancel.apply($timeout, arguments);\n            };\n\n            return extendedTimeout;\n          }\n        ]);\n      }\n    ]);\n  }\n};\n"
  },
  {
    "path": "lib/config.ts",
    "content": "import {PluginConfig} from './plugins';\n\nexport interface Config {\n  [key: string]: any;\n\n  // ---------------------------------------------------------------------------\n  // ----- How to connect to Browser Drivers -----------------------------------\n  // ---------------------------------------------------------------------------\n  //\n  // Protractor needs to know how to connect to Drivers for the browsers\n  // it is testing on. This is usually done through a Selenium Server.\n  // There are five options - specify one of the following:\n  //\n  // 1. seleniumServerJar - to start a standalone Selenium Server locally.\n  // 2. seleniumAddress - to connect to a Selenium Server which is already\n  //    running.\n  // 3. sauceUser/sauceKey - to use remote Selenium Servers via Sauce Labs.\n  // 4. browserstackUser/browserstackKey - to use remote Selenium Servers via\n  // BrowserStack.\n  // 5. directConnect - to connect directly to the browser Drivers.\n  //    This option is only available for Firefox and Chrome.\n  //\n  // ---- 1. To start a standalone Selenium Server locally ---------------------\n\n  /**\n   * The location of the standalone Selenium Server jar file, relative\n   * to the location of webdriver-manager. If no other method of starting\n   * Selenium Server is found, this will default to\n   * node_modules/protractor/node_modules/webdriver-manager/selenium/<jar file>\n   */\n  seleniumServerJar?: string;\n\n  /**\n   * The timeout milliseconds waiting for a local standalone Selenium Server to start.\n   *\n   * default: 30000ms\n   */\n  seleniumServerStartTimeout?: number;\n\n  /**\n   * Can be an object which will be passed to the SeleniumServer class as args.\n   * See a full list of options at\n   * https://github.com/SeleniumHQ/selenium/blob/master/javascript/node/selenium-webdriver/remote/index.js\n   * If you specify `args` or `port` in this object, it will overwrite the\n   * values set via the deprecated config values `seleniumPort` and\n   * `seleniumArgs`.\n   */\n  localSeleniumStandaloneOpts?: {\n    /**\n     * The port to start the Selenium Server on, or null if the server should\n     * find its own unused port.\n     */\n    port?: any;\n    /**\n     * Additional command line options to pass to selenium. For example,\n     * if you need to change the browser timeout, use\n     * seleniumArgs: ['-browserTimeout=60']\n     */\n    args?: any;\n\n    /**\n     * Additional command line jvm options to pass to selenium. For example,\n     * if you need to change the browser driver, use\n     * jvmArgs: ['-Dwebdriver.ie.driver=IEDriverServer_Win32_2.53.1.exe']\n     */\n    jvmArgs?: string[];\n  };\n  /**\n   * ChromeDriver location is used to help find the chromedriver binary. This will be passed to the\n   * Selenium jar as the system property webdriver.chrome.driver. If the value is not set when\n   * launching locally, it will use the default values downloaded from webdriver-manager.\n   *\n   * example:\n   * chromeDriver: './node_modules/webdriver-manager/selenium/chromedriver_2.20'\n   */\n  chromeDriver?: string;\n\n  /**\n   * geckoDriver location is used to help find the gecko binary. This will be passed to the Selenium\n   * jar as the system property webdriver.gecko.driver. If the value is not set when launching\n   * locally, it will use the default values downloaded from webdriver-manager.\n   */\n  geckoDriver?: string;\n\n  // ---- 2. To connect to a Selenium Server which is already running ----------\n\n  /**\n   * The address of a running Selenium Server. If specified, Protractor will\n   * connect to an already running instance of Selenium. This usually looks like\n   * seleniumAddress: 'http://localhost:4444/wd/hub'\n   */\n  seleniumAddress?: string;\n  /**\n   * The selenium session id allows Protractor to attach to an existing selenium\n   * browser session. The selenium session is maintained after the test has\n   * completed. Ignored if seleniumAddress is null.\n   */\n  seleniumSessionId?: string;\n  /**\n   * The address of a proxy server to use for communicating to Sauce Labs REST APIs via the\n   * saucelabs node module. For example, the Sauce Labs Proxy can be setup with: sauceProxy:\n   * 'http://localhost:3128'\n   */\n  sauceProxy?: string;\n\n  /**\n   * The proxy address that WebDriver (e.g. Selenium commands) traffic will go through\n   * which is tied to the browser session.\n   */\n  webDriverProxy?: string;\n\n  /**\n   * If specified, connect to webdriver through a proxy that manages client-side\n   * synchronization. Blocking Proxy is an experimental feature and may change\n   * without notice.\n   */\n  useBlockingProxy?: boolean;\n\n  /**\n   * If specified, Protractor will connect to the Blocking Proxy at the given\n   * url instead of starting it's own.\n   */\n  blockingProxyUrl?: string;\n\n  // ---- 3. To use remote browsers via Sauce Labs -----------------------------\n\n  /**\n   * If the sauceUser and sauceKey are specified, seleniumServerJar will be\n   * ignored. The tests will be run remotely using Sauce Labs.\n   */\n  sauceUser?: string;\n  /**\n   * If the sauceUser and sauceKey are specified, seleniumServerJar will be\n   * ignored. The tests will be run remotely using Sauce Labs.\n   */\n  sauceKey?: string;\n  /**\n   * If you run your tests on SauceLabs you can specify the region you want to run your tests\n   * in via the `sauceRegion` property. Available short handles for regions are:\n   * us: us-west-1 (default)\n   * eu: eu-central-1\n   */\n  sauceRegion?: string;\n  /**\n   * Use sauceAgent if you need custom HTTP agent to connect to saucelabs.com APIs.\n   * This is needed if your computer is behind a corporate proxy.\n   *\n   * To match sauce agent implementation, use\n   * [HttpProxyAgent](https://github.com/TooTallNate/node-http-proxy-agent)\n   * to generate the agent or use sauceProxy as an alternative. If a\n   * sauceProxy is provided, the sauceAgent will be overridden.\n   */\n  sauceAgent?: any;\n  /**\n   * Use sauceBuild if you want to group test capabilites by a build ID\n   */\n  sauceBuild?: string;\n  /**\n   * If true, Protractor will use http:// protocol instead of https:// to\n   * connect to Sauce Labs defined by sauceSeleniumAddress.\n   *\n   * default: false\n   */\n  sauceSeleniumUseHttp?: boolean;\n  /**\n   * Use sauceSeleniumAddress if you need to customize the URL Protractor\n   * uses to connect to sauce labs (for example, if you are tunneling selenium\n   * traffic through a sauce connect tunnel). Default is\n   * ondemand.saucelabs.com:443/wd/hub\n   */\n  sauceSeleniumAddress?: string;\n\n  // ---- 4. To use remote browsers via TestObject ---------------------------\n\n  /**\n   * If testobjectUser and testobjectKey are specified, kobitonUser, kobitonKey, browserstackUser,\n   * browserStackKey and seleniumServerJar will be ignored. The tests will be run remotely using\n   * TestObject.\n   */\n  testobjectUser?: string;\n  /**\n   * If testobjectUser and testobjectKey are specified, kobitonUser, kobitonKey, browserStackUser,\n   * browserStackKey and seleniumServerJar will be ignored. The tests will be run remotely using\n   * TestObject.\n   */\n  testobjectKey?: string;\n\n  // ---- 5. To use remote browsers via Kobiton ---------------------------\n\n  /**\n   * If kobitonUser and kobitonKey are specified, testobjectUser, testojbectKey, browserstackUser,\n   * browserStackKey and seleniumServerJar will be ignored. The tests will be run remotely using\n   * TestObject.\n   */\n  kobitonUser?: string;\n  /**\n   * If kobitonUser and kobitonKey are specified, testobjectUser, testojbectKey, browserStackUser,\n   * browserStackKey and seleniumServerJar will be ignored. The tests will be run remotely using\n   * TestObject.\n   */\n  kobitonKey?: string;\n\n  // ---- 6. To use remote browsers via BrowserStack ---------------------------\n\n  /**\n   * If browserstackUser and browserstackKey are specified, seleniumServerJar\n   * will be ignored. The tests will be run remotely using BrowserStack.\n   */\n  browserstackUser?: string;\n  /**\n   * If browserstackUser and browserstackKey are specified, seleniumServerJar\n   * will be ignored. The tests will be run remotely using BrowserStack.\n   */\n  browserstackKey?: string;\n\n  /**\n   * Proxy server to be used for connecting to BrowserStack APIs\n   * e.g. \"http://proxy.example.com:1234\".\n   * This should be used when you are behind a proxy server.\n   */\n  browserstackProxy?: string;\n\n  // ---- 7. To connect directly to Drivers ------------------------------------\n\n  /**\n   * If true, Protractor will connect directly to the browser Drivers\n   * at the locations specified by chromeDriver and firefoxPath. Only Chrome\n   * and Firefox are supported for direct connect.\n   *\n   * default: false\n   */\n  directConnect?: boolean;\n  /**\n   * Path to the firefox application binary. If null, will attempt to find\n   * firefox in the default locations.\n   */\n  firefoxPath?: string;\n\n  // ---------------------------------------------------------------------------\n  // ----- What tests to run ---------------------------------------------------\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Use default globals: 'protractor', 'browser', '$', '$$', 'element', 'by'.\n   * These also exist as properties of the protractor namespace:\n   * 'protractor.browser', 'protractor.$', 'protractor.$$',\n   * 'protractor.element', 'protractor.by', and 'protractor.By'.\n   *\n   * When no globals is set to true, the only available global variable will be\n   * 'protractor'.\n   */\n  noGlobals?: boolean;\n\n  /**\n   * Required. Spec patterns are relative to the location of this config.\n   *\n   * Example:\n   * specs: [\n   *   'spec/*_spec.js'\n   * ]\n   */\n  specs?: Array<string>;\n\n  /**\n   * Patterns to exclude specs.\n   */\n  exclude?: Array<string>|string;\n\n  /**\n   * Alternatively, suites may be used. When run without a command line\n   * parameter, all suites will run. If run with --suite=smoke or\n   * --suite=smoke,full only the patterns matched by the specified suites will\n   * run.\n   *\n   * Example:\n   * suites: {\n   *   smoke: 'spec/smoketests/*.js',\n   *   full: 'spec/*.js'\n   * }\n   */\n  suites?: any;\n  /**\n   * If you would like protractor to use a specific suite by default instead of\n   * all suites, you can put that in the config file as well.\n   */\n  suite?: string;\n\n  // ---------------------------------------------------------------------------\n  // ----- How to set up browsers ----------------------------------------------\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Protractor can launch your tests on one or more browsers. If you are\n   * testing on a single browser, use the capabilities option. If you are\n   * testing on multiple browsers, use the multiCapabilities array.\n   *\n   * For a list of available capabilities, see\n   * https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities\n   * In addition, you may specify count, shardTestFiles, and maxInstances.\n   *\n   * Example:\n   * capabilities: {\n   *   browserName: 'chrome',\n   *   name: 'Unnamed Job',\n   *   logName: 'Chrome - English',\n   *   count: 1,\n   *   shardTestFiles: false,\n   *   maxInstances: 1,\n   *   specs: ['spec/chromeOnlySpec.js'],\n   *   exclude: ['spec/doNotRunInChromeSpec.js'],\n   *   seleniumAddress: 'http://localhost:4444/wd/hub'\n   * }\n   */\n  capabilities?: {\n\n    [key: string]: any;\n\n    browserName?: string;\n\n    /**\n     * Name of the process executing this capability.  Not used directly by\n     * protractor or the browser, but instead pass directly to third parties\n     * like BrowserStack and SauceLabs as the name of the job running this\n     * test\n     */\n    name?: string;\n\n    /**\n     * User defined name for the capability that will display in the results\n     * log. Defaults to the browser name\n     */\n    logName?: string;\n\n    /**\n     * Number of times to run this set of capabilities (in parallel, unless\n     * limited by maxSessions). Default is 1.\n     */\n    count?: number;\n\n    /**\n     * If this is set to be true, specs will be sharded by file (i.e. all\n     * files to be run by this set of capabilities will run in parallel).\n     * Default is false.\n     */\n    shardTestFiles?: boolean;\n\n    /**\n     * Maximum number of browser instances that can run in parallel for this\n     * set of capabilities. This is only needed if shardTestFiles is true.\n     * Default is 1.\n     */\n    maxInstances?: number;\n\n    /**\n     * Additional spec files to be run on this capability only.\n     */\n    specs?: string[];\n\n    /**\n     * Spec files to be excluded on this capability only.\n     */\n    exclude?: string[];\n\n    /**\n     * Optional: override global seleniumAddress on this capability only.\n     */\n    seleniumAddress?: string;\n\n    // Optional: Additional third-party specific capabilities can be\n    // specified here.\n    // For a list of BrowserStack specific capabilities, visit\n    // https://www.browserstack.com/automate/capabilities\n  };\n\n  /**\n   * If you would like to run more than one instance of WebDriver on the same\n   * tests, use multiCapabilities, which takes an array of capabilities.\n   * If this is specified, capabilities will be ignored.\n   */\n  multiCapabilities?: Array<any>;\n\n  /**\n   * If you need to resolve multiCapabilities asynchronously (i.e. wait for\n   * server/proxy, set firefox profile, etc), you can specify a function here\n   * which will return either `multiCapabilities` or a promise to\n   * `multiCapabilities`.\n   *\n   * If this returns a promise, it is resolved immediately after\n   * `beforeLaunch` is run, and before any driver is set up. If this is\n   * specified, both capabilities and multiCapabilities will be ignored.\n   */\n  getMultiCapabilities?: any;\n\n  /**\n   * Maximum number of total browser sessions to run. Tests are queued in\n   * sequence if number of browser sessions is limited by this parameter.\n   * Use a number less than 1 to denote unlimited. Default is unlimited.\n   */\n  maxSessions?: number;\n\n  /**\n   * Whether or not to buffer output when running tests on multiple browsers\n   * in parallel. By default, when running multiple browser sessions, the\n   * results are buffered and not logged until the test run finishes. If true,\n   * when running multiple sessions in parallel results will be logged when\n   * each test finishes.\n   */\n  verboseMultiSessions?: boolean;\n\n  // ---------------------------------------------------------------------------\n  // ----- Global test information\n  // ---------------------------------------------\n  // ---------------------------------------------------------------------------\n\n  /**\n   * A base URL for your application under test. Calls to protractor.get()\n   * with relative paths will be resolved against this URL (via url.resolve)\n   */\n  baseUrl?: string;\n\n  /**\n   * A CSS Selector for a DOM element within your Angular application.\n   * Protractor will attempt to automatically find your application, but it is\n   * necessary to set rootElement in certain cases.\n   *\n   * In Angular 1, Protractor will use the element your app bootstrapped to by\n   * default.  If that doesn't work, it will then search for hooks in `body` or\n   * `ng-app` elements (details here: https://git.io/v1b2r).\n   *\n   * In later versions of Angular, Protractor will try to hook into all angular\n   * apps on the page. Use rootElement to limit the scope of which apps\n   * Protractor waits for and searches within.\n   */\n  rootElement?: string;\n\n  /**\n   * The timeout in milliseconds for each script run on the browser. This\n   * should be longer than the maximum time your application needs to\n   * stabilize between tasks.\n   */\n  allScriptsTimeout?: number;\n\n  /**\n   * How long to wait for a page to load.\n   */\n  getPageTimeout?: number;\n\n  /**\n   * A callback function called once configs are read but before any\n   * environment setup. This will only run once, and before onPrepare.\n   *\n   * You can specify a file containing code to run by setting beforeLaunch to\n   * the filename string.\n   *\n   * At this point, global variable 'protractor' object will NOT be set up,\n   * and globals from the test framework will NOT be available. The main\n   * purpose of this function should be to bring up test dependencies.\n   */\n  beforeLaunch?: () => void;\n\n  /**\n   * A callback function called once protractor is ready and available, and\n   * before the specs are executed. If multiple capabilities are being run,\n   * this will run once per capability.\n   *\n   * You can specify a file containing code to run by setting onPrepare to\n   * the filename string. onPrepare can optionally return a promise, which\n   * Protractor will wait for before continuing execution. This can be used if\n   * the preparation involves any asynchronous calls, e.g. interacting with\n   * the browser. Otherwise Protractor cannot guarantee order of execution\n   * and may start the tests before preparation finishes.\n   *\n   * At this point, global variable 'protractor' object will be set up, and\n   * globals from the test framework will be available. For example, if you\n   * are using Jasmine, you can add a reporter with:\n   *\n   *    jasmine.getEnv().addReporter(new jasmine.JUnitXmlReporter(\n   *      'outputdir/', true, true));\n   *\n   * If you need access back to the current configuration object,\n   * use a pattern like the following:\n   *\n   *    return browser.getProcessedConfig().then(function(config) {\n   *      // config.capabilities is the CURRENT capability being run, if\n   *      // you are using multiCapabilities.\n   *      console.log('Executing capability', config.capabilities);\n   *    });\n   */\n  onPrepare?: () => void;\n\n  /**\n   * A callback function called once tests are finished. onComplete can\n   * optionally return a promise, which Protractor will wait for before\n   * shutting down webdriver.\n   *\n   * At this point, tests will be done but global objects will still be\n   * available.\n   */\n  onComplete?: () => void;\n\n  /**\n   * A callback function called once the tests have finished running and\n   * the WebDriver instance has been shut down. It is passed the exit code\n   * (0 if the tests passed). This is called once per capability.\n   */\n  onCleanUp?: (exitCode: number) => void;\n\n  /**\n   * A callback function called once all tests have finished running and\n   * the WebDriver instance has been shut down. It is passed the exit code\n   * (0 if the tests passed). afterLaunch must return a promise if you want\n   * asynchronous code to be executed before the program exits.\n   * This is called only once before the program exits (after onCleanUp).\n   */\n  afterLaunch?: (exitCode: number) => void;\n\n  /**\n   * The params object will be passed directly to the Protractor instance,\n   * and can be accessed from your test as browser.params. It is an arbitrary\n   * object and can contain anything you may need in your test.\n   * This can be changed via the command line as:\n   *   --params.login.user \"Joe\"\n   *\n   * Example:\n   * params: {\n   *   login: {\n   *     user: 'Jane',\n   *     password: '1234'\n   *   }\n   * }\n   */\n  params?: any;\n\n  /**\n   * If set, protractor will save the test output in json format at this path.\n   * The path is relative to the location of this config.\n   */\n  resultJsonOutputFile?: any;\n\n  /**\n   * If true, protractor will restart the browser between each test. Default\n   * value is false.\n   *\n   * CAUTION: This will cause your tests to slow down drastically.\n   */\n  restartBrowserBetweenTests?: boolean;\n\n  /**\n   * Protractor will track outstanding $timeouts by default, and report them\n   * in the error message if Protractor fails to synchronize with Angular in\n   * time. In order to do this Protractor needs to decorate $timeout.\n   *\n   * CAUTION: If your app decorates $timeout, you must turn on this flag. This\n   * is false by default.\n   */\n  untrackOutstandingTimeouts?: boolean;\n\n  /**\n   * If set, Protractor will ignore uncaught exceptions instead of exiting\n   * without an error code. The exceptions will still be logged as warnings.\n   */\n  ignoreUncaughtExceptions?: boolean;\n\n  /**\n   * If set, will create a log file in the given directory with a readable log of\n   * the webdriver commands it executes.\n   *\n   * This is an experimental feature. Enabling this will also turn on Blocking Proxy\n   * synchronization, which is also experimental.\n   */\n  webDriverLogDir?: string;\n\n  /**\n   * If set, Protractor will pause the specified amount of time (in milliseconds)\n   * before interactions with browser elements (ie, sending keys, clicking). It will\n   * also highlight the element it's about to interact with.\n   *\n   * This is an experimental feature. Enabling this will also turn on Blocking Proxy\n   * synchronization, which is also experimental.\n   */\n  highlightDelay?: number;\n\n  /**\n   * Protractor log level\n   *\n   * default: INFO\n   */\n  logLevel?: 'ERROR'|'WARN'|'INFO'|'DEBUG';\n\n  // ---------------------------------------------------------------------------\n  // ----- The test framework\n  // --------------------------------------------------\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Test framework to use. This may be one of: jasmine, mocha or custom.\n   * Default value is 'jasmine'\n   *\n   * When the framework is set to \"custom\" you'll need to additionally\n   * set frameworkPath with the path relative to the config file or absolute:\n   *\n   *   framework: 'custom',\n   *   frameworkPath: './frameworks/my_custom_jasmine.js',\n   *\n   * See github.com/angular/protractor/blob/master/lib/frameworks/README.md\n   * to comply with the interface details of your custom implementation.\n   *\n   * Jasmine is fully supported as test and assertion frameworks.\n   * Mocha has limited support. You will need to include your\n   * own assertion framework (such as Chai) if working with Mocha.\n   */\n  framework?: string;\n\n  /**\n   * Options to be passed to jasmine.\n   *\n   * See https://github.com/jasmine/jasmine-npm/blob/master/lib/jasmine.js\n   * for the exact options available.\n   */\n  jasmineNodeOpts?: {\n    [key: string]: any;\n    /**\n     * If true, print colors to the terminal.\n     */\n    showColors?: boolean;\n    /**\n     * Default time to wait in ms before a test fails.\n     */\n    defaultTimeoutInterval?: number;\n    /**\n     * Function called to print jasmine results.\n     */\n    print?: () => void;\n    /**\n     * If set, only execute specs whose names match the pattern, which is\n     * internally compiled to a RegExp.\n     */\n    grep?: string;\n    /**\n     * Inverts 'grep' matches\n     */\n    invertGrep?: boolean;\n    /**\n     * If true, run specs in semi-random order\n     */\n    random?: boolean,\n    /**\n     * Set the randomization seed if randomization is turned on\n     */\n    seed?: string,\n  };\n\n  /**\n   * Options to be passed to Mocha.\n   *\n   * See the full list at http://mochajs.org/\n   */\n  mochaOpts?: {[key: string]: any; ui?: string; reporter?: string;};\n\n  /**\n   * See docs/plugins.md\n   */\n  plugins?: PluginConfig[];\n\n  /**\n   * Turns off source map support.  Stops protractor from registering global\n   * variable `source-map-support`.  Defaults to `false`\n   */\n  skipSourceMapSupport?: boolean;\n\n  /**\n   * Turns off WebDriver's environment variables overrides to ignore any\n   * environment variable and to only use the configuration in this file.\n   * Defaults to `false`\n   */\n  disableEnvironmentOverrides?: boolean;\n\n  /**\n   * Tells Protractor to interpret any angular apps it comes across as hybrid\n   * angular1/angular2 apps (i.e. apps using ngUpgrade)\n   * Defaults to `false`\n   *\n   * @type {boolean}\n   */\n  ng12Hybrid?: boolean;\n\n  /**\n   * Protractor will exit with an error if it sees any command line flags it doesn't\n   * recognize. Set disableChecks true to disable this check.\n   */\n  disableChecks?: boolean;\n\n  /**\n   * Enable/disable the WebDriver Control Flow.\n   *\n   * WebDriverJS (and by extention, Protractor) uses a Control Flow to manage the order in which\n   * commands are executed and promises are resolved (see docs/control-flow.md for details).\n   * However, as syntax like `async`/`await` are being introduced, WebDriverJS has decided to\n   * deprecate the control flow, and have users manage the asynchronous activity themselves\n   * (details here: https://github.com/SeleniumHQ/selenium/issues/2969).\n   *\n   * At the moment, the WebDriver Control Flow is still enabled by default. You can disable it by\n   * setting the environment variable `SELENIUM_PROMISE_MANAGER` to `0`.  In a webdriver release in\n   * Q4 2017, the Control Flow will be disabled by default, but you will be able to re-enable it by\n   * setting `SELENIUM_PROMISE_MANAGER` to `1`.  At a later point, the control flow will be removed\n   * for good.\n   *\n   * If you don't like managing environment variables, you can set this option in your config file,\n   * and Protractor will handle enabling/disabling the control flow for you.  Setting this option\n   * is higher priority than the `SELENIUM_PROMISE_MANAGER` environment variable.\n   *\n   * @type {boolean=}\n   */\n  SELENIUM_PROMISE_MANAGER?: boolean;\n\n  seleniumArgs?: any[];\n  jvmArgs?: string[];\n  configDir?: string;\n  troubleshoot?: boolean;\n  seleniumPort?: number;\n  mockSelenium?: boolean;\n  v8Debug?: any;\n  nodeDebug?: boolean;\n  debuggerServerPort?: number;\n  frameworkPath?: string;\n\n  /**\n   * Deprecated: Element explorer depends on the WebDriver control flow, and\n   * thus is no longer supported.\n   */\n  elementExplorer?: any;\n  debug?: boolean;\n  unknownFlags_?: string[];\n}\n"
  },
  {
    "path": "lib/configParser.ts",
    "content": "import * as glob from 'glob';\nimport * as path from 'path';\n\nimport {Config} from './config';\nimport {ConfigError} from './exitCodes';\nimport {Logger} from './logger';\n\nlet logger = new Logger('configParser');\n\n// Coffee is required here to enable config files written in coffee-script.\ntry {\n  require('coffee-script').register();\n} catch (e) {\n  // Intentionally blank - ignore if coffee-script is not available.\n}\n\n// CoffeeScript lost the hyphen in the module name a long time ago, all new version are named this:\ntry {\n  require('coffeescript').register();\n} catch (e) {\n  // Intentionally blank - ignore if coffeescript is not available.\n}\n\n// LiveScript is required here to enable config files written in LiveScript.\ntry {\n  require('LiveScript');\n} catch (e) {\n  // Intentionally blank - ignore if LiveScript is not available.\n}\n\nexport class ConfigParser {\n  private config_: Config;\n  constructor() {\n    // Default configuration.\n    this.config_ = {\n      specs: [],\n      multiCapabilities: [],\n      verboseMultiSessions: false,\n      rootElement: '',\n      allScriptsTimeout: 11000,\n      getPageTimeout: 10000,\n      params: {},\n      framework: 'jasmine',\n      jasmineNodeOpts: {showColors: true, defaultTimeoutInterval: (30 * 1000)},\n      seleniumArgs: [],\n      mochaOpts: {ui: 'bdd', reporter: 'list'},\n      configDir: './',\n      noGlobals: false,\n      plugins: [],\n      skipSourceMapSupport: false,\n      ng12Hybrid: false\n    };\n  }\n\n  /**\n   * Resolve a list of file patterns into a list of individual file paths.\n   *\n   * @param {Array.<string> | string} patterns\n   * @param {=boolean} opt_omitWarnings Whether to omit did not match warnings\n   * @param {=string} opt_relativeTo Path to resolve patterns against\n   *\n   * @return {Array} The resolved file paths.\n   */\n  public static resolveFilePatterns(\n      patterns: string[]|string, opt_omitWarnings?: boolean, opt_relativeTo?: string): string[] {\n    let resolvedFiles: string[] = [];\n    let cwd = opt_relativeTo || process.cwd();\n\n    patterns = (typeof patterns === 'string') ? [patterns] : patterns;\n\n    if (patterns) {\n      for (let fileName of patterns) {\n        let matches = glob.hasMagic(fileName) ? glob.sync(fileName, {cwd}) : [fileName];\n        if (!matches.length && !opt_omitWarnings) {\n          logger.warn('pattern ' + fileName + ' did not match any files.');\n        }\n        for (let match of matches) {\n          let resolvedPath = path.resolve(cwd, match);\n          resolvedFiles.push(resolvedPath);\n        }\n      }\n    }\n    return resolvedFiles;\n  }\n\n  /**\n   * Returns only the specs that should run currently based on `config.suite`\n   *\n   * @return {Array} An array of globs locating the spec files\n   */\n  static getSpecs(config: Config): string[] {\n    let specs: string[] = [];\n    if (config.suite) {\n      config.suite.split(',').forEach((suite) => {\n        let suiteList = config.suites ? config.suites[suite] : null;\n        if (suiteList == null) {\n          throw new ConfigError(logger, 'Unknown test suite: ' + suite);\n        }\n        union(specs, makeArray(suiteList));\n      });\n      return specs;\n    }\n\n    if (config.specs.length > 0) {\n      return config.specs;\n    }\n\n    Object.keys(config.suites || {}).forEach((suite) => {\n      union(specs, makeArray(config.suites[suite]));\n    });\n    return specs;\n  }\n\n  /**\n   * Add the options in the parameter config to this runner instance.\n   *\n   * @private\n   * @param {Object} additionalConfig\n   * @param {string} relativeTo the file path to resolve paths against\n   */\n  private addConfig_(additionalConfig: any, relativeTo: string): void {\n    // All filepaths should be kept relative to the current config location.\n    // This will not affect absolute paths.\n    ['seleniumServerJar', 'chromeDriver', 'firefoxPath', 'frameworkPath', 'geckoDriver',\n     'onPrepare']\n        .forEach((name: string) => {\n          if (additionalConfig[name] && typeof additionalConfig[name] === 'string') {\n            additionalConfig[name] = path.resolve(relativeTo, additionalConfig[name]);\n          }\n        });\n\n    merge_(this.config_, additionalConfig);\n  }\n\n  /**\n   * Public function specialized towards merging in a file's config\n   *\n   * @public\n   * @param {String} filename\n   */\n  public addFileConfig(filename: string): ConfigParser {\n    if (!filename) {\n      return this;\n    }\n    let filePath = path.resolve(process.cwd(), filename);\n    let fileConfig: any;\n    try {\n      fileConfig = require(filePath).config;\n    } catch (e) {\n      throw new ConfigError(logger, 'failed loading configuration file ' + filename, e);\n    }\n    if (!fileConfig) {\n      throw new ConfigError(\n          logger, 'configuration file ' + filename + ' did not export a config object');\n    }\n    fileConfig.configDir = path.dirname(filePath);\n    this.addConfig_(fileConfig, fileConfig.configDir);\n    return this;\n  }\n\n  /**\n   * Public function specialized towards merging in config from argv\n   *\n   * @public\n   * @param {Object} argv\n   */\n  public addConfig(argv: any): ConfigParser {\n    this.addConfig_(argv, process.cwd());\n    return this;\n  }\n\n  /**\n   * Public getter for the final, computed config object\n   *\n   * @public\n   * @return {Object} config\n   */\n  public getConfig(): Config {\n    return this.config_;\n  }\n}\n\n/**\n * Merge config objects together.\n *\n * @private\n * @param {Object} into\n * @param {Object} from\n *\n * @return {Object} The 'into' config.\n */\nlet merge_ = function(into: any, from: any): any {\n  for (let key in from) {\n    if (into[key] instanceof Object && !(into[key] instanceof Array) &&\n        !(into[key] instanceof Function)) {\n      merge_(into[key], from[key]);\n    } else {\n      into[key] = from[key];\n    }\n  }\n  return into;\n};\n\n/**\n * Returns the item if it's an array or puts the item in an array\n * if it was not one already.\n */\nlet makeArray = function(item: any): any {\n  return Array.isArray(item) ? item : [item];\n};\n\n/**\n * Adds to an array all the elements in another array without adding any\n * duplicates\n *\n * @param {string[]} dest The array to add to\n * @param {string[]} src The array to copy from\n */\nlet union = function(dest: string[], src: string[]): void {\n  let elems: any = {};\n  for (let key in dest) {\n    elems[dest[key]] = true;\n  }\n  for (let key in src) {\n    if (!elems[src[key]]) {\n      dest.push(src[key]);\n      elems[src[key]] = true;\n    }\n  }\n};\n"
  },
  {
    "path": "lib/driverProviders/README.md",
    "content": "WebDriver Providers for Protractor\n==================================\n\nDriverProviders define ways that the Protractor runner can connect to\nWebDriver.\n\nEach file exports a function which takes in the configuration as a parameter and returns a new DriverProvider, which has the following interface:\n\n```js\n/**\n * @return {q.promise} A promise which will resolve when the environment is\n *     ready to test.\n */\nDriverProvider.prototype.setupDriverEnv\n\n/**\n * @return {Array.<webdriver.WebDriver>} Array of existing webdriver instances.\n */\nDriverProvider.prototype.getExistingDrivers\n\n/**\n * @return {webdriver.WebDriver} A new setup driver instance.\n */\nDriverProvider.prototype.getNewDriver\n\n/**\n * @param {webdriver.WebDriver} The driver instance to quit.\n * @return {webdriver.promise.Promise<void>} A promise which resolves when the instance has quit\n */\nDriverProvider.prototype.quitDriver\n\n/**\n * @return {q.Promise<any>} A promise which will resolve when the environment is down.\n */\nDriverProvider.prototype.teardownEnv\n\n/**\n * This is an optional function. If defined, it will be called with the final\n * status of the test suite (pass/fail) once it is completed.\n *\n * @param {{passed: boolean}}\n * @return {q.promise} A promise that will resolve when the update is complete.\n */\nDriverProvider.prototype.updateJob\n```\n\nRequirements\n------------\n\n - `setupDriverEnv` will be called before the test framework is loaded, so any\n pre-work which might cause timeouts on the first test should be done there. \n `getNewDriver` will be called once right after `setupDriverEnv` to generate the\n initial driver, and possibly during the middle of the test if users request\n additional browsers.\n\n - `teardownEnv` should call the driver's `quit` method.\n"
  },
  {
    "path": "lib/driverProviders/attachSession.ts",
    "content": "/*\n *  This is an implementation of the Attach Session Driver Provider.\n *  It is responsible for setting up the account object, tearing\n *  it down, and setting up the driver correctly.\n */\nimport {Session, WebDriver} from 'selenium-webdriver';\nimport {Executor, HttpClient} from 'selenium-webdriver/http';\n\nimport {Config} from '../config';\nimport {Logger} from '../logger';\nimport {DriverProvider} from './driverProvider';\n\nlet logger = new Logger('attachSession');\n\nexport class AttachSession extends DriverProvider {\n  constructor(config: Config) {\n    super(config);\n  }\n\n  /**\n   * Configure and launch (if applicable) the object's environment.\n   * @return {Promise} A promise which will resolve when the environment is\n   *     ready to test.\n   */\n  protected async setupDriverEnv(): Promise<any> {\n    logger.info('Using the selenium server at ' + this.config_.seleniumAddress);\n    logger.info('Using session id - ' + this.config_.seleniumSessionId);\n  }\n\n  /**\n   * Getting a new driver by attaching an existing session.\n   *\n   * @public\n   * @return {WebDriver} webdriver instance\n   */\n  async getNewDriver(): Promise<WebDriver> {\n    const httpClient: HttpClient = new HttpClient(this.config_.seleniumAddress);\n    const executor: Executor = new Executor(httpClient);\n    const session: Session = new Session(this.config_.seleniumSessionId, null);\n\n    const newDriver = new WebDriver(session, executor);\n    this.drivers_.push(newDriver);\n    return newDriver;\n  }\n\n  /**\n   * Maintains the existing session and does not quit the driver.\n   *\n   * @public\n   */\n  async quitDriver(): Promise<void> {}\n}\n"
  },
  {
    "path": "lib/driverProviders/browserStack.ts",
    "content": "/*\n * This is an implementation of the Browserstack Driver Provider.\n * It is responsible for setting up the account object, tearing\n * it down, and setting up the driver correctly.\n */\nimport {WebDriver} from 'selenium-webdriver';\nimport * as util from 'util';\n\nimport {Config} from '../config';\nimport {BrowserError} from '../exitCodes';\nimport {Logger} from '../logger';\n\nimport {DriverProvider} from './driverProvider';\n\nconst BrowserstackClient = require('browserstack');\n\nlet logger = new Logger('browserstack');\n\nexport class BrowserStack extends DriverProvider {\n  browserstackClient: any;\n\n  constructor(config: Config) {\n    super(config);\n  }\n\n  /**\n   * Hook to update the BrowserStack job status.\n   * @public\n   * @param {Object} update\n   * @return {Promise} A promise that will resolve when the update is complete.\n   */\n  async updateJob(update: any): Promise<any> {\n    let mappedDrivers = this.drivers_.map(async (driver: WebDriver) => {\n      let session = await driver.getSession();\n\n      // Fetching BrowserStack session details.\n      this.browserstackClient.getSession(\n          session.getId(), function(error: Error, automate_session: any) {\n            if (error) {\n              logger.info(\n                  'BrowserStack results available at ' +\n                  'https://www.browserstack.com/automate');\n            } else {\n              if (automate_session && automate_session.browser_url) {\n                logger.info('BrowserStack results available at ' + automate_session.browser_url);\n              } else {\n                logger.info(\n                    'BrowserStack results available at ' +\n                    'https://www.browserstack.com/automate');\n              }\n            }\n          });\n\n      let jobStatus = update.passed ? 'completed' : 'error';\n      let statusObj = {status: jobStatus};\n\n      // Updating status of BrowserStack session.\n      this.browserstackClient.updateSession(\n          session.getId(), statusObj, function(error: Error, automate_session: any) {\n            if (error) {\n              throw new BrowserError(\n                  logger, 'Error updating BrowserStack pass/fail status: ' + util.inspect(error));\n            } else {\n              logger.info(automate_session);\n            }\n          });\n    });\n\n    return Promise.all(mappedDrivers);\n  }\n\n  /**\n   * Configure and launch (if applicable) the object's environment.\n   * @return {promise} A promise which will resolve when the environment is\n   *     ready to test.\n   */\n  protected async setupDriverEnv(): Promise<any> {\n    this.config_.capabilities['browserstack.user'] = this.config_.browserstackUser;\n    this.config_.capabilities['browserstack.key'] = this.config_.browserstackKey;\n    this.config_.seleniumAddress = 'http://hub.browserstack.com/wd/hub';\n\n    this.browserstackClient = BrowserstackClient.createAutomateClient({\n      username: this.config_.browserstackUser,\n      password: this.config_.browserstackKey,\n      proxy: this.config_.browserstackProxy\n    });\n\n    // Append filename to capabilities.name so that it's easier to identify\n    // tests.\n    if (this.config_.capabilities.name && this.config_.capabilities.shardTestFiles) {\n      this.config_.capabilities.name +=\n          (':' + this.config_.specs.toString().replace(/^.*[\\\\\\/]/, ''));\n    }\n\n    logger.info(`Using BrowserStack selenium server at ${this.config_.seleniumAddress}`);\n  }\n}\n"
  },
  {
    "path": "lib/driverProviders/direct.ts",
    "content": "/*\n *  This is an implementation of the Direct Driver Provider.\n *  It is responsible for setting up the account object, tearing\n *  it down, and setting up the driver correctly.\n */\nimport * as fs from 'fs';\nimport {Capabilities, WebDriver} from 'selenium-webdriver';\nimport {Driver as DriverForChrome, ServiceBuilder as ServiceBuilderForChrome} from 'selenium-webdriver/chrome';\nimport {Driver as DriverForFirefox, ServiceBuilder as SerivceBuilderForFirefox} from 'selenium-webdriver/firefox';\n\nimport {ChromeDriver, GeckoDriver} from 'webdriver-manager';\n\nimport {Config} from '../config';\nimport {BrowserError} from '../exitCodes';\nimport {Logger} from '../logger';\n\nimport {DriverProvider} from './driverProvider';\n\nlet logger = new Logger('direct');\nexport class Direct extends DriverProvider {\n  constructor(config: Config) {\n    super(config);\n  }\n\n  /**\n   * Configure and launch (if applicable) the object's environment.\n   * @return {Promise} A promise which will resolve when the environment is\n   *     ready to test.\n   */\n  protected async setupDriverEnv(): Promise<any> {\n    switch (this.config_.capabilities.browserName) {\n      case 'chrome':\n        logger.info('Using ChromeDriver directly...');\n        break;\n      case 'firefox':\n        logger.info('Using FirefoxDriver directly...');\n        break;\n      default:\n        throw new BrowserError(\n            logger,\n            'browserName ' + this.config_.capabilities.browserName +\n                ' is not supported with directConnect.');\n    }\n  }\n\n  /**\n   * Create a new driver.\n   *\n   * @public\n   * @override\n   * @return webdriver instance\n   */\n  async getNewDriver(): Promise<WebDriver> {\n    let driver: WebDriver;\n\n    switch (this.config_.capabilities.browserName) {\n      case 'chrome':\n        let chromeDriverFile: string;\n        if (this.config_.chromeDriver) {\n          chromeDriverFile = this.config_.chromeDriver;\n        } else {\n          try {\n            chromeDriverFile = new ChromeDriver().getBinaryPath();\n          } catch (e) {\n            throw new BrowserError(\n                logger, 'Run \\'webdriver-manager update\\' to download binaries.');\n          }\n        }\n\n        if (!fs.existsSync(chromeDriverFile)) {\n          throw new BrowserError(\n              logger,\n              'Could not find chromedriver at ' + chromeDriverFile +\n                  '. Run \\'webdriver-manager update\\' to download binaries.');\n        }\n\n        const chromeService =\n            (new ServiceBuilderForChrome(chromeDriverFile) as ServiceBuilderForChrome).build();\n        driver = await DriverForChrome.createSession(\n            new Capabilities(this.config_.capabilities), chromeService);\n        break;\n      case 'firefox':\n        let geckoDriverFile: string;\n        if (this.config_.geckoDriver) {\n          geckoDriverFile = this.config_.geckoDriver;\n        } else {\n          try {\n            geckoDriverFile = new GeckoDriver().getBinaryPath();\n          } catch (e) {\n            throw new BrowserError(\n                logger, 'Run \\'webdriver-manager update\\' to download binaries.');\n          }\n        }\n\n        if (!fs.existsSync(geckoDriverFile)) {\n          throw new BrowserError(\n              logger,\n              'Could not find geckodriver at ' + geckoDriverFile +\n                  '. Run \\'webdriver-manager update\\' to download binaries.');\n        }\n\n        let firefoxService = new SerivceBuilderForFirefox(geckoDriverFile).build();\n        driver = await DriverForFirefox.createSession(\n            new Capabilities(this.config_.capabilities), firefoxService);\n        break;\n      default:\n        throw new BrowserError(\n            logger,\n            'browserName ' + this.config_.capabilities.browserName +\n                ' is not supported with directConnect.');\n    }\n    this.drivers_.push(driver);\n    return driver;\n  }\n}\n"
  },
  {
    "path": "lib/driverProviders/driverProvider.ts",
    "content": "/**\n *  This is a base driver provider class.\n *  It is responsible for setting up the account object, tearing\n *  it down, and setting up the driver correctly.\n */\nimport {Builder, WebDriver} from 'selenium-webdriver';\nimport {BlockingProxyRunner} from '../bpRunner';\nimport {Config} from '../config';\nimport {BrowserError} from '../exitCodes';\nimport {Logger} from '../logger';\n\nlet logger = new Logger('driverProvider');\n\nexport abstract class DriverProvider {\n  drivers_: WebDriver[];\n  config_: Config;\n  private bpRunner: BlockingProxyRunner;\n\n  constructor(config: Config) {\n    this.config_ = config;\n    this.drivers_ = [];\n    this.bpRunner = new BlockingProxyRunner(config);\n  }\n\n  /**\n   * Get all existing drivers.\n   *\n   * @public\n   * @return array of webdriver instances\n   */\n  getExistingDrivers() {\n    return this.drivers_.slice();  // Create a shallow copy\n  }\n\n  getBPUrl() {\n    if (this.config_.blockingProxyUrl) {\n      return this.config_.blockingProxyUrl;\n    }\n    return `http://localhost:${this.bpRunner.port}`;\n  }\n\n  /**\n   * Create a new driver.\n   *\n   * @public\n   * @return a promise to a webdriver instance\n   */\n  async getNewDriver(): Promise<WebDriver> {\n    let builder: Builder;\n    if (this.config_.useBlockingProxy) {\n      builder =\n          new Builder().usingServer(this.getBPUrl()).withCapabilities(this.config_.capabilities);\n    } else {\n      builder = new Builder()\n                    .usingServer(this.config_.seleniumAddress)\n                    .usingWebDriverProxy(this.config_.webDriverProxy)\n                    .withCapabilities(this.config_.capabilities);\n    }\n    if (this.config_.disableEnvironmentOverrides === true) {\n      builder.disableEnvironmentOverrides();\n    }\n    let newDriver: WebDriver;\n    try {\n      newDriver = await builder.build();\n    } catch (e) {\n      throw new BrowserError(logger, (e as Error).message);\n    }\n    this.drivers_.push(newDriver);\n    return newDriver;\n  }\n\n  /**\n   * Quit a driver.\n   *\n   * @public\n   * @param webdriver instance\n   */\n  async quitDriver(driver: WebDriver): Promise<void> {\n    let driverIndex = this.drivers_.indexOf(driver);\n    if (driverIndex >= 0) {\n      this.drivers_.splice(driverIndex, 1);\n      try {\n        await driver.close();\n        await driver.quit();\n      } catch (err) {\n        // This happens when Protractor keeps track of all the webdrivers\n        // created and calls quit. If a user calls driver.quit, then this will\n        // throw an error. This catch will swallow the error.\n      }\n    }\n  }\n\n  /**\n   * Quits an array of drivers and returns a q promise instead of a webdriver one\n   *\n   * @param drivers {webdriver.WebDriver[]} The webdriver instances\n   */\n  static async quitDrivers(provider: DriverProvider, drivers: WebDriver[]): Promise<void> {\n    await Promise.all(drivers.map((driver: WebDriver) => {\n      return provider.quitDriver(driver);\n    }));\n  }\n\n  /**\n   * Default update job method.\n   * @return a promise\n   */\n  async updateJob(update: any): Promise<any> {}\n\n  /**\n   * Default setup environment method, common to all driver providers.\n   */\n  async setupEnv(): Promise<any> {\n    await this.setupDriverEnv();\n    if (this.config_.useBlockingProxy && !this.config_.blockingProxyUrl) {\n      await this.bpRunner.start();\n    }\n  }\n\n  /**\n   * Set up environment specific to a particular driver provider. Overridden\n   * by each driver provider.\n   */\n  protected async abstract setupDriverEnv(): Promise<any>;\n\n  /**\n   * Teardown and destroy the environment and do any associated cleanup.\n   * Shuts down the drivers.\n   *\n   * @public\n   * @return {Promise<any>} A promise which will resolve when the environment is down.\n   */\n  async teardownEnv(): Promise<any> {\n    await DriverProvider.quitDrivers(this, this.drivers_);\n  }\n}\n"
  },
  {
    "path": "lib/driverProviders/hosted.ts",
    "content": "/*\n *  This is an implementation of the Hosted Driver Provider.\n *  It is responsible for setting up the account object, tearing\n *  it down, and setting up the driver correctly.\n */\nimport {Config} from '../config';\nimport {Logger} from '../logger';\n\nimport {DriverProvider} from './driverProvider';\n\nlet logger = new Logger('hosted');\nexport class Hosted extends DriverProvider {\n  constructor(config: Config) {\n    super(config);\n  }\n\n  /**\n   * Configure and launch (if applicable) the object's environment.\n   * @public\n   * @return {Promise} A promise which will resolve when the environment is\n   *     ready to test.\n   */\n  protected async setupDriverEnv(): Promise<any> {\n    logger.info('Using the selenium server at ' + this.config_.seleniumAddress);\n  }\n}\n"
  },
  {
    "path": "lib/driverProviders/index.ts",
    "content": "export * from './attachSession';\nexport * from './browserStack';\nexport * from './direct';\nexport * from './driverProvider';\nexport * from './hosted';\nexport * from './local';\nexport * from './mock';\nexport * from './sauce';\nexport * from './testObject';\nexport * from './kobiton';\n\n\nimport {AttachSession} from './attachSession';\nimport {BrowserStack} from './browserStack';\nimport {DriverProvider} from './driverProvider';\nimport {Direct} from './direct';\nimport {Hosted} from './hosted';\nimport {Local} from './local';\nimport {Mock} from './mock';\nimport {Sauce} from './sauce';\nimport {TestObject} from './testObject';\nimport {Kobiton} from './kobiton';\n\nimport {Config} from '../config';\nimport {Logger} from '../logger';\n\nlet logger = new Logger('driverProviders');\n\nexport let buildDriverProvider = (config: Config): DriverProvider => {\n  let driverProvider: DriverProvider;\n\n  if (config.directConnect) {\n    driverProvider = new Direct(config);\n    logWarnings('directConnect', config);\n  } else if (config.seleniumAddress) {\n    if (config.seleniumSessionId) {\n      driverProvider = new AttachSession(config);\n      logWarnings('attachSession', config);\n    } else {\n      driverProvider = new Hosted(config);\n      logWarnings('hosted', config);\n    }\n  } else if (config.testobjectUser && config.testobjectKey) {\n    driverProvider = new TestObject(config);\n    logWarnings('testObject', config);\n  } else if (config.kobitonUser && config.kobitonKey) {\n    driverProvider = new Kobiton(config);\n    logWarnings('kobiton', config);\n  } else if (config.browserstackUser && config.browserstackKey) {\n    driverProvider = new BrowserStack(config);\n    logWarnings('browserStack', config);\n  } else if (config.sauceUser && config.sauceKey) {\n    driverProvider = new Sauce(config);\n    logWarnings('sauce', config);\n  } else if (config.seleniumServerJar) {\n    driverProvider = new Local(config);\n    logWarnings('local', config);\n  } else if (config.mockSelenium) {\n    driverProvider = new Mock(config);\n    logWarnings('mock', config);\n  } else {\n    driverProvider = new Local(config);\n    logWarnings('local', config);\n  }\n  return driverProvider;\n};\n\nexport let logWarnings = (providerType: string, config: Config): void => {\n\n  let warnInto = 'Using driver provider ' + providerType +\n      ', but also found extra driver provider parameter(s): ';\n  let warnList: string[] = [];\n  if ('directConnect' !== providerType && config.directConnect) {\n    warnList.push('directConnect');\n  }\n  if ('attachSession' !== providerType && 'hosted' !== providerType && config.seleniumAddress) {\n    warnList.push('seleniumAddress');\n  }\n  if ('attachSession' !== providerType && config.seleniumSessionId) {\n    warnList.push('seleniumSessionId');\n  }\n  if ('testObject' !== providerType && config.testObjectUser) {\n    warnList.push('testobjectUser');\n  }\n  if ('testObject' !== providerType && config.testObjectKey) {\n    warnList.push('testobjectKey');\n  }\n  if ('kobitonUser' !== providerType && config.kobitonUser) {\n    warnList.push('kobitonUser');\n  }\n  if ('kobitonKey' !== providerType && config.kobitonKey) {\n    warnList.push('kobitonKey');\n  }\n  if ('browserStack' !== providerType && config.browserstackUser) {\n    warnList.push('browserstackUser');\n  }\n  if ('browserStack' !== providerType && config.browserstackKey) {\n    warnList.push('browserstackKey');\n  }\n  if ('sauce' !== providerType && config.sauceUser) {\n    warnList.push('sauceUser');\n  }\n  if ('sauce' !== providerType && config.sauceKey) {\n    warnList.push('sauceKey');\n  }\n  if ('local' !== providerType && config.seleniumServerJar) {\n    warnList.push('seleniumServerJar');\n  }\n  if ('mock' !== providerType && config.mockSelenium) {\n    warnList.push('mockSelenium');\n  }\n  if (warnList.length !== 0) {\n    logger.warn(warnInto + warnList.join(', '));\n  }\n};\n"
  },
  {
    "path": "lib/driverProviders/kobiton.ts",
    "content": "/*\n * This is an implementation of the Kobiton Driver Provider.\n * It is responsible for setting up the account object, tearing\n * it down, and setting up the driver correctly.\n */\nimport {Config} from '../config';\nimport {Logger} from '../logger';\nimport {DriverProvider} from './driverProvider';\n\nlet logger = new Logger('kobiton');\n\nexport class Kobiton extends DriverProvider {\n  constructor(config: Config) {\n    super(config);\n  }\n\n  /**\n   * Configure and launch (if applicable) the object's environment.\n   * @return {Promise} A promise which will resolve when the environment is\n   *      ready to test.\n   */\n  protected async setupDriverEnv(): Promise<any> {\n    this.config_.capabilities['kobitonUser'] = this.config_.kobitonUser;\n    this.config_.capabilities['kobitonKey'] = this.config_.kobitonKey;\n    this.config_.seleniumAddress = 'https://' + this.config_.kobitonUser + ':' +\n        this.config_.kobitonKey + '@api.kobiton.com/wd/hub';\n\n    logger.info('Using Kobiton selenium server at ' + this.config_.seleniumAddress);\n  }\n}\n"
  },
  {
    "path": "lib/driverProviders/local.ts",
    "content": "/*\n * This is an implementation of the Local Driver Provider.\n * It is responsible for setting up the account object, tearing\n * it down, and setting up the driver correctly.\n *\n * TODO - it would be nice to do this in the launcher phase,\n * so that we only start the local selenium once per entire launch.\n */\nimport * as fs from 'fs';\nimport {SeleniumServer} from 'selenium-webdriver/remote';\nimport {ChromeDriver, GeckoDriver, SeleniumServer as WdmSeleniumServer} from 'webdriver-manager';\n\nimport {Config} from '../config';\nimport {BrowserError, ConfigError} from '../exitCodes';\nimport {Logger} from '../logger';\n\nimport {DriverProvider} from './driverProvider';\n\nlet logger = new Logger('local');\n\nexport class Local extends DriverProvider {\n  server_: SeleniumServer;\n  constructor(config: Config) {\n    super(config);\n    this.server_ = null;\n  }\n\n  /**\n   * Helper to locate the default jar path if none is provided by the user.\n   * @private\n   */\n  addDefaultBinaryLocs_(): void {\n    if (!this.config_.seleniumServerJar) {\n      logger.debug(\n          'Attempting to find the SeleniumServerJar in the default ' +\n          'location used by webdriver-manager');\n      try {\n        this.config_.seleniumServerJar = new WdmSeleniumServer().getBinaryPath();\n      } catch (err) {\n        throw new BrowserError(logger, 'Run \\'webdriver-manager update\\' to download binaries.');\n      }\n    }\n    if (!fs.existsSync(this.config_.seleniumServerJar)) {\n      throw new BrowserError(\n          logger,\n          'No selenium server jar found at ' + this.config_.seleniumServerJar +\n              '. Run \\'webdriver-manager update\\' to download binaries.');\n    }\n    if (this.config_.capabilities.browserName === 'chrome') {\n      if (!this.config_.chromeDriver) {\n        logger.debug(\n            'Attempting to find the chromedriver binary in the default ' +\n            'location used by webdriver-manager');\n\n        try {\n          this.config_.chromeDriver = new ChromeDriver().getBinaryPath();\n        } catch (err) {\n          throw new BrowserError(logger, 'Run \\'webdriver-manager update\\' to download binaries.');\n        }\n      }\n\n      // Check if file exists, if not try .exe or fail accordingly\n      if (!fs.existsSync(this.config_.chromeDriver)) {\n        if (fs.existsSync(this.config_.chromeDriver + '.exe')) {\n          this.config_.chromeDriver += '.exe';\n        } else {\n          throw new BrowserError(\n              logger,\n              'Could not find chromedriver at ' + this.config_.chromeDriver +\n                  '. Run \\'webdriver-manager update\\' to download binaries.');\n        }\n      }\n    }\n\n    if (this.config_.capabilities.browserName === 'firefox') {\n      if (!this.config_.geckoDriver) {\n        logger.debug(\n            'Attempting to find the gecko driver binary in the default ' +\n            'location used by webdriver-manager');\n\n        try {\n          this.config_.geckoDriver = new GeckoDriver().getBinaryPath();\n        } catch (err) {\n          throw new BrowserError(logger, 'Run \\'webdriver-manager update\\' to download binaries.');\n        }\n      }\n\n      // Check if file exists, if not try .exe or fail accordingly\n      if (!fs.existsSync(this.config_.geckoDriver)) {\n        if (fs.existsSync(this.config_.geckoDriver + '.exe')) {\n          this.config_.geckoDriver += '.exe';\n        } else {\n          throw new BrowserError(\n              logger,\n              'Could not find gecko driver at ' + this.config_.geckoDriver +\n                  '. Run \\'webdriver-manager update\\' to download binaries.');\n        }\n      }\n    }\n  }\n\n  /**\n   * Configure and launch (if applicable) the object's environment.\n   * @public\n   * @return {Promise} A promise which will resolve when the environment is\n   *     ready to test.\n   */\n  async setupDriverEnv(): Promise<any> {\n    this.addDefaultBinaryLocs_();\n    logger.info('Starting selenium standalone server...');\n\n    let serverConf = this.config_.localSeleniumStandaloneOpts || {};\n\n    // If args or port is not set use seleniumArgs and seleniumPort\n    // for backward compatibility\n    if (serverConf.args === undefined) {\n      serverConf.args = this.config_.seleniumArgs || [];\n    }\n    if (serverConf.jvmArgs === undefined) {\n      serverConf.jvmArgs = this.config_.jvmArgs || [];\n    } else {\n      if (!Array.isArray(serverConf.jvmArgs)) {\n        throw new ConfigError(logger, 'jvmArgs should be an array.');\n      }\n    }\n    if (serverConf.port === undefined) {\n      serverConf.port = this.config_.seleniumPort;\n    }\n\n    // configure server\n    if (this.config_.chromeDriver) {\n      serverConf.jvmArgs.push('-Dwebdriver.chrome.driver=' + this.config_.chromeDriver);\n    }\n    if (this.config_.geckoDriver) {\n      serverConf.jvmArgs.push('-Dwebdriver.gecko.driver=' + this.config_.geckoDriver);\n    }\n\n    this.server_ = new SeleniumServer(this.config_.seleniumServerJar, serverConf);\n\n    // start local server, grab hosted address, and resolve promise\n    const url = await this.server_.start(this.config_.seleniumServerStartTimeout);\n\n    logger.info('Selenium standalone server started at ' + url);\n    const address = await this.server_.address();\n    this.config_.seleniumAddress = address;\n  }\n}\n"
  },
  {
    "path": "lib/driverProviders/mock.ts",
    "content": "/*\n * This is an mock implementation of the Driver Provider.\n * It returns a fake webdriver and never actually contacts a selenium\n * server.\n */\nimport {Session, WebDriver} from 'selenium-webdriver';\n\nimport {Config} from '../config';\nimport {DriverProvider} from './driverProvider';\n\nexport class MockExecutor {\n  execute(_: any): any {}\n}\n\nexport class Mock extends DriverProvider {\n  constructor(config?: Config) {\n    super(config);\n  }\n\n  /**\n   * An execute function that returns a promise with a test value.\n   */\n  async execute(): Promise<any> {\n    return {value: 'test_response'};\n  }\n\n  /**\n   * Configure and launch (if applicable) the object's environment.\n   * @public\n   * @return {Promise} A promise which will resolve immediately.\n   */\n  protected async setupDriverEnv(): Promise<any> {}\n\n  /**\n   * Create a new driver.\n   *\n   * @public\n   * @override\n   * @return webdriver instance\n   */\n  async getNewDriver(): Promise<WebDriver> {\n    const mockSession: Session = new Session('test_session_id', {});\n    const newDriver: WebDriver = new WebDriver(mockSession, new MockExecutor());\n    this.drivers_.push(newDriver);\n    return newDriver;\n  }\n}\n"
  },
  {
    "path": "lib/driverProviders/sauce.ts",
    "content": "/*\n * This is an implementation of the SauceLabs Driver Provider.\n * It is responsible for setting up the account object, tearing\n * it down, and setting up the driver correctly.\n */\n\nimport {WebDriver} from 'selenium-webdriver';\nimport * as util from 'util';\n\nimport {Config} from '../config';\nimport {Logger} from '../logger';\n\nimport {DriverProvider} from './driverProvider';\n\nconst SauceLabs = require('saucelabs');\nconst SAUCE_REGIONS: {[key: string]: string} = {\n  'us': '',  // default endpoint\n  'eu': 'eu-central-1.'\n};\n\nlet logger = new Logger('sauce');\nexport class Sauce extends DriverProvider {\n  sauceServer_: any;\n\n  constructor(config: Config) {\n    super(config);\n  }\n\n  /**\n   * Hook to update the sauce job.\n   * @public\n   * @param {Object} update\n   * @return {Promise} A promise that will resolve when the update is complete.\n   */\n  updateJob(update: any): Promise<any> {\n    let mappedDrivers = this.drivers_.map(async (driver: WebDriver) => {\n      const session = await driver.getSession();\n      logger.info('SauceLabs results available at http://saucelabs.com/jobs/' + session.getId());\n      this.sauceServer_.updateJob(session.getId(), update, (err: Error) => {\n        if (err) {\n          throw new Error('Error updating Sauce pass/fail status: ' + util.inspect(err));\n        }\n      });\n    });\n    return Promise.all(mappedDrivers);\n  }\n\n  /**\n   * Configure and launch (if applicable) the object's environment.\n   * @public\n   * @return {Promise} A promise which will resolve when the environment is\n   *     ready to test.\n   */\n  protected async setupDriverEnv(): Promise<any> {\n    this.sauceServer_ = new SauceLabs({\n      hostname: this.getSauceEndpoint(this.config_.sauceRegion),\n      username: this.config_.sauceUser,\n      password: this.config_.sauceKey,\n      agent: this.config_.sauceAgent,\n      proxy: this.config_.sauceProxy\n    });\n    this.config_.capabilities['username'] = this.config_.sauceUser;\n    this.config_.capabilities['accessKey'] = this.config_.sauceKey;\n    this.config_.capabilities['build'] = this.config_.sauceBuild;\n    let protocol = this.config_.sauceSeleniumUseHttp ? 'http://' : 'https://';\n    let auth = protocol + this.config_.sauceUser + ':' + this.config_.sauceKey + '@';\n    this.config_.seleniumAddress = auth +\n        (this.config_.sauceSeleniumAddress ?\n             this.config_.sauceSeleniumAddress :\n             `ondemand.${this.getSauceEndpoint(this.config_.sauceRegion)}:443/wd/hub`);\n\n    // Append filename to capabilities.name so that it's easier to identify\n    // tests.\n    if (this.config_.capabilities.name && this.config_.capabilities.shardTestFiles) {\n      this.config_.capabilities.name +=\n          (':' + this.config_.specs.toString().replace(/^.*[\\\\\\/]/, ''));\n    }\n\n    logger.info(\n        'Using SauceLabs selenium server at ' +\n        this.config_.seleniumAddress.replace(/\\/\\/.+@/, '//'));\n  }\n\n  /**\n   * Get the Sauce Labs endpoint\n   * @private\n   * @param {string} region\n   * @return {string} The endpoint that needs to be used\n   */\n  private getSauceEndpoint(region: string): string {\n    const dc = region ?\n        typeof SAUCE_REGIONS[region] !== 'undefined' ? SAUCE_REGIONS[region] : (region + '.') :\n        '';\n    return `${dc}saucelabs.com`;\n  }\n}\n"
  },
  {
    "path": "lib/driverProviders/testObject.ts",
    "content": "/*\n * This is an implementation of the TestObject Driver Provider.\n * It is responsible for setting up the account object, tearing\n * it down, and setting up the driver correctly.\n */\nimport {Config} from '../config';\nimport {Logger} from '../logger';\nimport {DriverProvider} from './driverProvider';\n\nlet logger = new Logger('testobject');\n\nexport class TestObject extends DriverProvider {\n  constructor(config: Config) {\n    super(config);\n  }\n\n  /**\n   * Configure and launch (if applicable) the object's environment.\n   * @return {Promise} A promise which will resolve when the environment is\n   *      ready to test.\n   */\n  protected async setupDriverEnv(): Promise<any> {\n    this.config_.capabilities['testobject.user'] = this.config_.testobjectUser;\n    this.config_.capabilities['testobject_api_key'] = this.config_.testobjectKey;\n    this.config_.seleniumAddress = 'https://us1.appium.testobject.com/wd/hub';\n\n    logger.info('Using TestObject selenium server at ' + this.config_.seleniumAddress);\n  }\n}\n"
  },
  {
    "path": "lib/element.ts",
    "content": "import {By, error as wderror, WebElement, WebElementPromise} from 'selenium-webdriver';\n\nimport {ElementHelper, ProtractorBrowser} from './browser';\nimport {isProtractorLocator, Locator} from './locators';\nimport {Logger} from './logger';\nimport {falseIfMissing} from './util';\n\nlet clientSideScripts = require('./clientsidescripts');\n\nlet logger = new Logger('element');\n\nexport class WebdriverWebElement {}\nexport interface WebdriverWebElement extends WebElement { [key: string]: any; }\n\nlet WEB_ELEMENT_FUNCTIONS = [\n  'click', 'sendKeys', 'getTagName', 'getCssValue', 'getAttribute', 'getText', 'getRect',\n  'isEnabled', 'isSelected', 'submit', 'clear', 'isDisplayed', 'getId', 'takeScreenshot'\n];\n\n/**\n * ElementArrayFinder is used for operations on an array of elements (as opposed\n * to a single element).\n *\n * The ElementArrayFinder is used to set up a chain of conditions that identify\n * an array of elements. In particular, you can call all(locator) and\n * filter(filterFn) to return a new ElementArrayFinder modified by the\n * conditions, and you can call get(index) to return a single ElementFinder at\n * position 'index'.\n *\n * Similar to jquery, ElementArrayFinder will search all branches of the DOM\n * to find the elements that satisfy the conditions (i.e. all, filter, get).\n * However, an ElementArrayFinder will not actually retrieve the elements until\n * an action is called, which means it can be set up in helper files (i.e.\n * page objects) before the page is available, and reused as the page changes.\n *\n * You can treat an ElementArrayFinder as an array of WebElements for most\n * purposes, in particular, you may perform actions (i.e. click, getText) on\n * them as you would an array of WebElements. The action will apply to\n * every element identified by the ElementArrayFinder. ElementArrayFinder\n * extends Promise, and once an action is performed on an ElementArrayFinder,\n * the latest result can be accessed using then, and will be returned as an\n * array of the results; the array has length equal to the length of the\n * elements found by the ElementArrayFinder and each result represents the\n * result of performing the action on the element. Unlike a WebElement, an\n * ElementArrayFinder will wait for the angular app to settle before\n * performing finds or actions.\n *\n * @alias element.all(locator)\n * @view\n * <ul class=\"items\">\n *   <li>First</li>\n *   <li>Second</li>\n *   <li>Third</li>\n * </ul>\n *\n * @example\n * const items = await element.all(by.css('.items li'));\n * expect(items.length).toBe(3);\n * expect(await items[0].getText()).toBe('First');\n *\n * // Or using the shortcut $$() notation instead of element.all(by.css()):\n *\n * const items = await $$('.items li');\n * expect(items.length).toBe(3);\n * expect(await items[0].getText()).toBe('First');\n *\n * @constructor\n * @param {ProtractorBrowser} browser A browser instance.\n * @param {function(): Array.<webdriver.WebElement>} getWebElements A function\n *    that returns a list of the underlying Web Elements.\n * @param {Locator} locator The most relevant locator. It is only\n *    used for error reporting and ElementArrayFinder.locator.\n * @param {Array<Promise>} opt_actionResults An array\n *    of promises which will be retrieved with then. Resolves to the latest\n *    action result, or null if no action has been called.\n * @returns {ElementArrayFinder}\n */\nexport class ElementArrayFinder extends WebdriverWebElement {\n  constructor(\n      public browser_: ProtractorBrowser, public getWebElements: () => Promise<WebElement[]> = null,\n      public locator_?: any, public actionResults_: Promise<any> = null) {\n    super();\n\n    // TODO(juliemr): might it be easier to combine this with our docs and just\n    // wrap each one explicity with its own documentation?\n    WEB_ELEMENT_FUNCTIONS.forEach((fnName: string) => {\n      this[fnName] = (...args: any[]) => {\n        let actionFn = (webElem: any) => {\n          return webElem[fnName].apply(webElem, args);\n        };\n        return this.applyAction_(actionFn);\n      };\n    });\n  }\n\n  /**\n   * Create a shallow copy of ElementArrayFinder.\n   *\n   * @returns {!ElementArrayFinder} A shallow copy of this.\n   */\n  clone(): ElementArrayFinder {\n    // A shallow copy is all we need since the underlying fields can never be\n    // modified. (Locator can be modified by the user, but that should\n    // rarely/never happen and it doesn't affect functionalities).\n    return new ElementArrayFinder(\n        this.browser_, this.getWebElements, this.locator_, this.actionResults_);\n  }\n\n  /**\n   * Calls to ElementArrayFinder may be chained to find an array of elements\n   * using the current elements in this ElementArrayFinder as the starting\n   * point. This function returns a new ElementArrayFinder which would contain\n   * the children elements found (and could also be empty).\n   *\n   * @alias element.all(locator).all(locator)\n   * @view\n   * <div id='id1' class=\"parent\">\n   *   <ul>\n   *     <li class=\"foo\">1a</li>\n   *     <li class=\"baz\">1b</li>\n   *   </ul>\n   * </div>\n   * <div id='id2' class=\"parent\">\n   *   <ul>\n   *     <li class=\"foo\">2a</li>\n   *     <li class=\"bar\">2b</li>\n   *   </ul>\n   * </div>\n   *\n   * @example\n   * let foo = element.all(by.css('.parent')).all(by.css('.foo'));\n   * expect(await foo.getText()).toEqual(['1a', '2a']);\n   * let baz = element.all(by.css('.parent')).all(by.css('.baz'));\n   * expect(await baz.getText()).toEqual(['1b']);\n   * let nonexistent = element.all(by.css('.parent'))\n   *   .all(by.css('.NONEXISTENT'));\n   * expect(await nonexistent.getText()).toEqual(['']);\n   *\n   * // Or using the shortcut $$() notation instead of element.all(by.css()):\n   *\n   * let foo = $$('.parent').$$('.foo');\n   * expect(await foo.getText()).toEqual(['1a', '2a']);\n   * let baz = $$('.parent').$$('.baz');\n   * expect(await baz.getText()).toEqual(['1b']);\n   * let nonexistent = $$('.parent').$$('.NONEXISTENT');\n   * expect(await nonexistent.getText()).toEqual(['']);\n   *\n   * @param {Locator} locator\n   * @returns {ElementArrayFinder}\n   */\n  all(locator: Locator): ElementArrayFinder {\n    const ptor = this.browser_;\n    const getWebElements = async(): Promise<WebElement[]> => {\n      if (this.getWebElements === null) {\n        // This is the first time we are looking for an element\n        await ptor.waitForAngular('Locator: ' + locator);\n\n        if (isProtractorLocator(locator)) {\n          return locator.findElementsOverride(ptor.driver, null, ptor.rootEl);\n        } else {\n          return ptor.driver.findElements(locator);\n        }\n      } else {\n        const parentWebElements = await this.getWebElements();\n        // For each parent web element, find their children and construct\n        // a list of Promise<List<child_web_element>>\n        const childrenPromiseList = parentWebElements.map((parentWebElement: WebElement) => {\n          return isProtractorLocator(locator) ?\n              locator.findElementsOverride(ptor.driver, parentWebElement, ptor.rootEl) :\n              parentWebElement.findElements(locator);\n        });\n\n        // Resolve the list of Promise<List<child_web_elements>> and merge\n        // into a single list\n        const resolved = await Promise.all(childrenPromiseList);\n        return resolved.reduce((childrenList, resolvedE) => {\n          return childrenList.concat(resolvedE);\n        }, []);\n      }\n    };\n    return new ElementArrayFinder(this.browser_, getWebElements, locator);\n  }\n\n  /**\n   * Apply a filter function to each element within the ElementArrayFinder.\n   * Returns a new ElementArrayFinder with all elements that pass the filter\n   * function. The filter function receives the ElementFinder as the first\n   * argument and the index as a second arg. This does not actually retrieve\n   * the underlying list of elements, so it can be used in page objects.\n   *\n   * @alias element.all(locator).filter(filterFn)\n   * @view\n   * <ul class=\"items\">\n   *   <li class=\"one\">First</li>\n   *   <li class=\"two\">Second</li>\n   *   <li class=\"three\">Third</li>\n   * </ul>\n   *\n   * @example\n   * await element.all(by.css('.items li'))\n   *   .filter(async (elem, index) => await elem.getText() === 'Third')\n   *   .first()\n   *   .click();\n   *\n   * // Or using the shortcut $$() notation instead of element.all(by.css()):\n   *\n   * await $$('.items li')\n   *   .filter(async (elem, index) => await elem.getText() === 'Third')\n   *   .first()\n   *   .click();\n   *\n   * @param {function(ElementFinder, number): boolean|Promise<boolean>} filterFn\n   *     Filter function that will test if an element should be returned.\n   *     filterFn can either return a boolean or a promise that resolves to a\n   *     boolean.\n   * @returns {!ElementArrayFinder} A ElementArrayFinder that represents an\n   * array\n   *     of element that satisfy the filter function.\n   */\n  filter(filterFn: (element: ElementFinder, index?: number) => boolean | Promise<boolean>):\n      ElementArrayFinder {\n    const getWebElements = async(): Promise<WebElement[]> => {\n      const parentWebElements = await this.getWebElements();\n      const list = parentWebElements.map((parentWebElement: WebElement, index: number) => {\n        let elementFinder =\n            ElementFinder.fromWebElement_(this.browser_, parentWebElement, this.locator_);\n        return filterFn(elementFinder, index);\n      });\n      const resolvedList = await Promise.all(list);\n      return parentWebElements.filter((_: WebElement, index: number) => {\n        return resolvedList[index];\n      });\n    };\n    return new ElementArrayFinder(this.browser_, getWebElements, this.locator_);\n  }\n\n  /**\n   * Get an element within the ElementArrayFinder by index. The index starts at\n   * 0. Negative indices are wrapped (i.e. -i means ith element from last)\n   * This does not actually retrieve the underlying element.\n   *\n   * @alias element.all(locator).get(index)\n   * @view\n   * <ul class=\"items\">\n   *   <li>First</li>\n   *   <li>Second</li>\n   *   <li>Third</li>\n   * </ul>\n   *\n   * @example\n   * let list = element.all(by.css('.items li'));\n   * expect(await list.get(0).getText()).toBe('First');\n   * expect(await list.get(1).getText()).toBe('Second');\n   *\n   * // Or using the shortcut $$() notation instead of element.all(by.css()):\n   *\n   * let list = $$('.items li');\n   * expect(await list.get(0).getText()).toBe('First');\n   * expect(await list.get(1).getText()).toBe('Second');\n   *\n   * @param {number|Promise} indexPromise Element index.\n   * @returns {ElementFinder} finder representing element at the given index.\n   */\n  get(indexPromise: number|Promise<number>): ElementFinder {\n    const getWebElements = async(): Promise<WebElement[]> => {\n      let index = await indexPromise;\n      const parentWebElements = await this.getWebElements();\n      if (index < 0) {\n        index += parentWebElements.length;\n      }\n      if (index < 0 || index >= parentWebElements.length) {\n        throw new wderror.NoSuchElementError(\n            `Index out of bound. Trying to access element at index: ` +\n            `${index}, but there are only ${parentWebElements.length} ` +\n            `elements that match locator ${this.locator_.toString()}`);\n      }\n      return [parentWebElements[index]];\n    };\n    return new ElementArrayFinder(this.browser_, getWebElements, this.locator_).toElementFinder_();\n  }\n\n  /**\n   * Get the first matching element for the ElementArrayFinder. This does not\n   * actually retrieve the underlying element.\n   *\n   * @alias element.all(locator).first()\n   * @view\n   * <ul class=\"items\">\n   *   <li>First</li>\n   *   <li>Second</li>\n   *   <li>Third</li>\n   * </ul>\n   *\n   * @example\n   * let first = element.all(by.css('.items li')).first();\n   * expect(await first.getText()).toBe('First');\n   *\n   * // Or using the shortcut $$() notation instead of element.all(by.css()):\n   *\n   * let first = $$('.items li').first();\n   * expect(await first.getText()).toBe('First');\n   *\n   * @returns {ElementFinder} finder representing the first matching element\n   */\n  first(): ElementFinder {\n    return this.get(0);\n  }\n\n  /**\n   * Get the last matching element for the ElementArrayFinder. This does not\n   * actually retrieve the underlying element.\n   *\n   * @alias element.all(locator).last()\n   * @view\n   * <ul class=\"items\">\n   *   <li>First</li>\n   *   <li>Second</li>\n   *   <li>Third</li>\n   * </ul>\n   *\n   * @example\n   * let last = element.all(by.css('.items li')).last();\n   * expect(await last.getText()).toBe('Third');\n   *\n   * // Or using the shortcut $$() notation instead of element.all(by.css()):\n   *\n   * let last = $$('.items li').last();\n   * expect(await last.getText()).toBe('Third');\n   *\n   * @returns {ElementFinder} finder representing the last matching element\n   */\n  last(): ElementFinder {\n    return this.get(-1);\n  }\n\n  /**\n   * Shorthand function for finding arrays of elements by css.\n   * `element.all(by.css('.abc'))` is equivalent to `$$('.abc')`\n   *\n   * @alias $$(cssSelector)\n   * @view\n   * <div class=\"count\">\n   *   <span class=\"one\">First</span>\n   *   <span class=\"two\">Second</span>\n   * </div>\n   *\n   * @example\n   * // The following two blocks of code are equivalent.\n   * let list = element.all(by.css('.count span'));\n   * expect(await list.count()).toBe(2);\n   * expect(await list.get(0).getText()).toBe('First');\n   * expect(await list.get(1).getText()).toBe('Second');\n   *\n   * // Or using the shortcut $$() notation instead of element.all(by.css()):\n   *\n   * let list = $$('.count span');\n   * expect(await list.count()).toBe(2);\n   * expect(await list.get(0).getText()).toBe('First');\n   * expect(await list.get(1).getText()).toBe('Second');\n   *\n   * @param {string} selector a css selector\n   * @returns {ElementArrayFinder} which identifies the\n   *     array of the located {@link webdriver.WebElement}s.\n   */\n  $$(selector: string): ElementArrayFinder {\n    return this.all(By.css(selector));\n  }\n\n  /**\n   * Returns an ElementFinder representation of ElementArrayFinder. It ensures\n   * that the ElementArrayFinder resolves to one and only one underlying\n   * element.\n   *\n   * @returns {ElementFinder} An ElementFinder representation\n   * @private\n   */\n  toElementFinder_(): ElementFinder {\n    return new ElementFinder(this.browser_, this);\n  }\n\n  /**\n   * Count the number of elements represented by the ElementArrayFinder.\n   *\n   * @alias element.all(locator).count()\n   * @view\n   * <ul class=\"items\">\n   *   <li>First</li>\n   *   <li>Second</li>\n   *   <li>Third</li>\n   * </ul>\n   *\n   * @example\n   * let list = element.all(by.css('.items li'));\n   * expect(await list.count()).toBe(3);\n   *\n   * // Or using the shortcut $$() notation instead of element.all(by.css()):\n   *\n   * let list = $$('.items li');\n   * expect(await list.count()).toBe(3);\n   *\n   * @returns {!Promise} A promise which resolves to the\n   *     number of elements matching the locator.\n   */\n  async count(): Promise<number> {\n    try {\n      const arr = await this.getWebElements();\n      return arr.length;\n    } catch (err) {\n      if (err instanceof wderror.NoSuchElementError) {\n        return 0;\n      } else {\n        throw err;\n      }\n    }\n  }\n\n  /**\n   * Returns true if there are any elements present that match the finder.\n   *\n   * @alias element.all(locator).isPresent()\n   *\n   * @example\n   * expect(await $('.item').isPresent()).toBeTruthy();\n   *\n   * @returns {Promise<boolean>}\n   */\n  async isPresent(): Promise<boolean> {\n    const count = await this.count();\n    return count > 0;\n  }\n\n  /**\n   * Returns the most relevant locator.\n   *\n   * @example\n   * // returns by.css('#ID1')\n   * $('#ID1').locator();\n   *\n   * // returns by.css('#ID2')\n   * $('#ID1').$('#ID2').locator();\n   *\n   * // returns by.css('#ID1')\n   * $$('#ID1').filter(filterFn).get(0).click().locator();\n   *\n   * @returns {Locator}\n   */\n  locator(): Locator {\n    return this.locator_;\n  }\n\n  /**\n   * Apply an action function to every element in the ElementArrayFinder,\n   * and return a new ElementArrayFinder that contains the results of the\n   * actions.\n   *\n   * @param {function(ElementFinder)} actionFn\n   *\n   * @returns {ElementArrayFinder}\n   * @private\n   */\n  // map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];\n  private applyAction_(actionFn: (value: WebElement, index: number, array: WebElement[]) => any):\n      ElementArrayFinder {\n    const callerError = new Error();\n    let actionResults = this.getWebElements()\n                            .then((arr: any) => Promise.all(arr.map(actionFn)))\n                            .then(\n                                (value: any) => {\n                                  return {passed: true, value};\n                                },\n                                (error: any) => {\n                                  return {passed: false, value: error};\n                                });\n    const getWebElements = () => actionResults.then(() => this.getWebElements());\n    actionResults = actionResults.then((result: {passed: boolean, value: any}) => {\n      if (result.passed) {\n        return result.value;\n      } else {\n        let noSuchErr: any;\n        if (result.value instanceof Error) {\n          noSuchErr = result.value;\n          noSuchErr.stack = noSuchErr.stack + callerError.stack;\n        } else {\n          noSuchErr = new Error(result.value as string);\n          noSuchErr.stack = callerError.stack;\n        }\n        throw noSuchErr;\n      }\n    });\n    return new ElementArrayFinder(this.browser_, getWebElements, this.locator_, actionResults);\n  }\n\n  /**\n   * Represents the ElementArrayFinder as an array of ElementFinders.\n   *\n   * @returns {Promise<ElementFinder[]>} Return a promise, which resolves to a\n   *   list of ElementFinders specified by the locator.\n   */\n  async asElementFinders_(): Promise<ElementFinder[]> {\n    const arr = await this.getWebElements();\n    return arr.map((webElem: WebElement) => {\n      return ElementFinder.fromWebElement_(this.browser_, webElem, this.locator_);\n    });\n  }\n\n  /**\n   * Retrieve the elements represented by the ElementArrayFinder. The input\n   * function is passed to the resulting promise, which resolves to an\n   * array of ElementFinders.\n   *\n   * @alias element.all(locator).then(thenFunction)\n   * @view\n   * <ul class=\"items\">\n   *   <li>First</li>\n   *   <li>Second</li>\n   *   <li>Third</li>\n   * </ul>\n   *\n   * @example\n   * const arr = await element.all(by.css('.items li'));\n   * expect(arr.length).toEqual(3);\n   *\n   * // Or using the shortcut $$() notation instead of element.all(by.css()):\n   *\n   * const arr = $$('.items li');\n   * expect(arr.length).toEqual(3);\n   *\n   * @param {function(Array.<ElementFinder>)} fn\n   * @param {function(Error)} errorFn\n   *\n   * @returns {!Promise} A promise which will resolve to\n   *     an array of ElementFinders represented by the ElementArrayFinder.\n   */\n  then<T>(fn?: (value: ElementFinder[]|any[]) => T | Promise<T>, errorFn?: (error: any) => any):\n      Promise<T> {\n    if (this.actionResults_) {\n      return this.actionResults_.then(fn, errorFn) as Promise<T>;\n    } else {\n      return this.asElementFinders_().then(fn, errorFn);\n    }\n  }\n\n  /**\n   * Calls the input function on each ElementFinder represented by the\n   * ElementArrayFinder.\n   *\n   * @alias element.all(locator).each(eachFunction)\n   * @view\n   * <ul class=\"items\">\n   *   <li>First</li>\n   *   <li>Second</li>\n   *   <li>Third</li>\n   * </ul>\n   *\n   * @example\n   * await element.all(by.css('.items li')).each(async (element, index) => {\n   *   // Will print 0 First, 1 Second, 2 Third.\n   *   console.log(index, await element.getText());\n   * });\n   *\n   * // Or using the shortcut $$() notation instead of element.all(by.css()):\n   *\n   * $$('.items li').each(async (element, index) => {\n   *   // Will print 0 First, 1 Second, 2 Third.\n   *   console.log(index, await element.getText());\n   * });\n   *\n   * @param {function(ElementFinder)} fn Input function\n   *\n   * @returns {!Promise} A promise that will resolve when the\n   *     function has been called on all the ElementFinders. The promise will\n   *     resolve to null.\n   */\n  async each(fn: (elementFinder?: ElementFinder, index?: number) => any): Promise<any> {\n    return await this.map(fn);\n  }\n\n  /**\n   * Apply a map function to each element within the ElementArrayFinder. The\n   * callback receives the ElementFinder as the first argument and the index as\n   * a second arg.\n   *\n   * @alias element.all(locator).map(mapFunction)\n   * @view\n   * <ul class=\"items\">\n   *   <li class=\"one\">First</li>\n   *   <li class=\"two\">Second</li>\n   *   <li class=\"three\">Third</li>\n   * </ul>\n   *\n   * @example\n   * let items = await element.all(by.css('.items li'))\n   *   .map(async (elm, index) => {\n   *     return {\n   *       index: index,\n   *       text: await elm.getText(),\n   *       class: await elm.getAttribute('class')\n   *     };\n   *   });\n   * expect(items).toEqual([\n   *   {index: 0, text: 'First', class: 'one'},\n   *   {index: 1, text: 'Second', class: 'two'},\n   *   {index: 2, text: 'Third', class: 'three'}\n   * ]);\n   *\n   * // Or using the shortcut $$() notation instead of element.all(by.css()):\n   *\n   * let items = await $$('.items li').map(async (elm, index) => {\n   *   return {\n   *     index: index,\n   *     text: await elm.getText(),\n   *     class: await elm.getAttribute('class')\n   *   };\n   * });\n   * expect(items).toEqual([\n   *   {index: 0, text: 'First', class: 'one'},\n   *   {index: 1, text: 'Second', class: 'two'},\n   *   {index: 2, text: 'Third', class: 'three'}\n   * ]);\n   *\n   * @param {function(ElementFinder, number)} mapFn Map function that\n   *     will be applied to each element.\n   * @returns {!Promise} A promise that resolves to an array\n   *     of values returned by the map function.\n   */\n  async map<T>(mapFn: (elementFinder?: ElementFinder, index?: number) => T | any): Promise<T[]> {\n    const arr = await this.asElementFinders_();\n\n    const list = arr.map(async (elementFinder?: ElementFinder, index?: number) => {\n      let mapResult = mapFn(elementFinder, index);\n      // All nested arrays and objects will also be fully resolved.\n      return await mapResult;\n    });\n    return Promise.all(list);\n  }\n\n  /**\n   * Apply a reduce function against an accumulator and every element found\n   * using the locator (from left-to-right). The reduce function has to reduce\n   * every element into a single value (the accumulator). Returns promise of\n   * the accumulator. The reduce function receives the accumulator, current\n   * ElementFinder, the index, and the entire array of ElementFinders,\n   * respectively.\n   *\n   * @alias element.all(locator).reduce(reduceFn)\n   * @view\n   * <ul class=\"items\">\n   *   <li class=\"one\">First</li>\n   *   <li class=\"two\">Second</li>\n   *   <li class=\"three\">Third</li>\n   * </ul>\n   *\n   * @example\n   * let value = await element.all(by.css('.items li'))\n   *   .reduce(async (acc, elem) => acc + (await elem.getText()) + ' ', '');\n   *\n   * expect(value).toEqual('First Second Third ');\n   *\n   * // Or using the shortcut $$() notation instead of element.all(by.css()):\n   *\n   * let value = await $$('.items li')\n   *   .reduce(async (acc, elem) => acc + (await elem.getText()) + ' ', '');\n   *\n   * expect(value).toEqual('First Second Third ');\n   *\n   * @param {function(number, ElementFinder, number, Array.<ElementFinder>)}\n   *     reduceFn Reduce function that reduces every element into a single\n   * value.\n   * @param {*} initialValue Initial value of the accumulator.\n   * @returns {!Promise} A promise that resolves to the final\n   *     value of the accumulator.\n   */\n  async reduce(reduceFn: Function, initialValue: any): Promise<any> {\n    const valuePromise = await initialValue;\n    const arr = await this.asElementFinders_();\n    return arr.reduce(async (valuePromise: any, elementFinder: ElementFinder, index: number) => {\n      return reduceFn(await valuePromise, elementFinder, index, arr);\n    }, valuePromise);\n  }\n\n  /**\n   * Evaluates the input as if it were on the scope of the current underlying\n   * elements.\n   *\n   * @view\n   * <span class=\"foo\">{{letiableInScope}}</span>\n   *\n   * @example\n   * let value = element.all(by.css('.foo')).evaluate('letiableInScope');\n   *\n   * // Or using the shortcut $$() notation instead of element.all(by.css()):\n   *\n   * let value = $$('.foo').evaluate('letiableInScope');\n   *\n   * @param {string} expression\n   *\n   * @returns {ElementArrayFinder} which resolves to the\n   *     evaluated expression for each underlying element.\n   *     The result will be resolved as in\n   *     {@link webdriver.WebDriver.executeScript}. In summary - primitives will\n   *     be resolved as is, functions will be converted to string, and elements\n   *     will be returned as a WebElement.\n   */\n  evaluate(expression: string): ElementArrayFinder {\n    const evaluationFn = (webElem: WebElement) => {\n      return webElem.getDriver().executeScript(clientSideScripts.evaluate, webElem, expression);\n    };\n    return this.applyAction_(evaluationFn);\n  }\n\n  /**\n   * Determine if animation is allowed on the current underlying elements.\n   * @param {string} value\n   *\n   * @example\n   * // Turns off ng-animate animations for all elements in the <body>\n   * element(by.css('body')).allowAnimations(false);\n   *\n   * // Or using the shortcut $() notation instead of element(by.css()):\n   *\n   * $('body').allowAnimations(false);\n   *\n   * @returns {ElementArrayFinder} which resolves to whether animation is\n   * allowed.\n   */\n  allowAnimations(value: boolean): ElementArrayFinder {\n    const allowAnimationsTestFn = (webElem: WebElement) => {\n      return webElem.getDriver().executeScript(clientSideScripts.allowAnimations, webElem, value);\n    };\n    return this.applyAction_(allowAnimationsTestFn);\n  }\n}\n\n/**\n * The ElementFinder simply represents a single element of an\n * ElementArrayFinder (and is more like a convenience object). As a result,\n * anything that can be done with an ElementFinder, can also be done using\n * an ElementArrayFinder.\n *\n * The ElementFinder can be treated as a WebElement for most purposes, in\n * particular, you may perform actions (i.e. click, getText) on them as you\n * would a WebElement. Once an action is performed on an ElementFinder, the\n * latest result from the chain can be accessed using the then method.\n * Unlike a WebElement, an ElementFinder will wait for angular to settle before\n * performing finds or actions.\n *\n * ElementFinder can be used to build a chain of locators that is used to find\n * an element. An ElementFinder does not actually attempt to find the element\n * until an action is called, which means they can be set up in helper files\n * before the page is available.\n *\n * @alias element(locator)\n * @view\n * <span>{{person.name}}</span>\n * <span ng-bind=\"person.email\"></span>\n * <input type=\"text\" ng-model=\"person.name\"/>\n *\n * @example\n * // Find element with {{scopelet}} syntax.\n * const name = await element(by.binding('person.name')).getText();\n * expect(name).toBe('Foo');\n *\n * // Find element with ng-bind=\"scopelet\" syntax.\n * const email = await element(by.binding('person.email')).getText();\n * expect(email).toBe('foo@bar.com');\n *\n * // Find by model.\n * let input = element(by.model('person.name'));\n * await input.sendKeys('123');\n * expect(await input.getAttribute('value')).toBe('Foo123');\n *\n * @constructor\n * @extends {webdriver.WebElement}\n * @param {ProtractorBrowser} browser_ A browser instance.\n * @param {ElementArrayFinder} elementArrayFinder The ElementArrayFinder\n *     that this is branched from.\n * @returns {ElementFinder}\n */\nexport class ElementFinder extends WebdriverWebElement {\n  parentElementArrayFinder: ElementArrayFinder;\n  elementArrayFinder_: ElementArrayFinder;\n  then?:\n      (fn: (value: any) => any | Promise<any>,\n       errorFn?: (error: any) => any) => Promise<any> = null;\n\n  constructor(public browser_: ProtractorBrowser, elementArrayFinder: ElementArrayFinder) {\n    super();\n    if (!elementArrayFinder) {\n      throw new Error('BUG: elementArrayFinder cannot be empty');\n    }\n    this.parentElementArrayFinder = elementArrayFinder;\n\n    // Only have a `then` method if the parent element array finder\n    // has action results.\n    if (this.parentElementArrayFinder.actionResults_) {\n      // Access the underlying actionResult of ElementFinder.\n      this.then = (fn: (value: any) => any | Promise<any>, errorFn?: (error: any) => any) => {\n        return this.elementArrayFinder_.then((actionResults: any) => {\n          if (!fn) {\n            return actionResults[0];\n          }\n          return fn(actionResults[0]);\n        }, errorFn);\n      };\n    }\n\n    // This filter verifies that there is only 1 element returned by the\n    // elementArrayFinder. It will warn if there are more than 1 element and\n    // throw an error if there are no elements.\n    const getWebElements = async(): Promise<WebElement[]> => {\n      const webElements = await elementArrayFinder.getWebElements();\n      if (webElements.length === 0) {\n        throw new wderror.NoSuchElementError(\n            'No element found using locator: ' + elementArrayFinder.locator().toString());\n      } else {\n        if (webElements.length > 1) {\n          logger.warn(\n              'more than one element found for locator ' + elementArrayFinder.locator().toString() +\n              ' - the first result will be used');\n        }\n        return [webElements[0]];\n      }\n    };\n\n    // Store a copy of the underlying elementArrayFinder, but with the more\n    // restrictive getWebElements (which checks that there is only 1 element).\n    this.elementArrayFinder_ = new ElementArrayFinder(\n        this.browser_, getWebElements, elementArrayFinder.locator(),\n        elementArrayFinder.actionResults_);\n\n    WEB_ELEMENT_FUNCTIONS.forEach((fnName: string) => {\n      (this)[fnName] = (...args: any[]) => {\n        return (this.elementArrayFinder_)[fnName]\n            .apply(this.elementArrayFinder_, args)\n            .toElementFinder_();\n      };\n    });\n  }\n\n  static fromWebElement_(browser: ProtractorBrowser, webElem: WebElement, locator?: Locator):\n      ElementFinder {\n    const getWebElements = () => {\n      return Promise.resolve([webElem]);\n    };\n    return new ElementArrayFinder(browser, getWebElements, locator).toElementFinder_();\n  }\n\n  /**\n   * Create a shallow copy of ElementFinder.\n   *\n   * @returns {!ElementFinder} A shallow copy of this.\n   */\n  clone(): ElementFinder {\n    // A shallow copy is all we need since the underlying fields can never be\n    // modified\n    return new ElementFinder(this.browser_, this.parentElementArrayFinder);\n  }\n\n  /**\n   * @see ElementArrayFinder.prototype.locator\n   *\n   * @returns {Locator}\n   */\n  locator(): any {\n    return this.elementArrayFinder_.locator();\n  }\n\n  /**\n   * Returns the WebElement represented by this ElementFinder.\n   * Throws the WebDriver error if the element doesn't exist.\n   *\n   * @alias element(locator).getWebElement()\n   * @view\n   * <div class=\"parent\">\n   *   some text\n   * </div>\n   *\n   * @example\n   * // The following four expressions are equivalent.\n   * $('.parent').getWebElement();\n   * element(by.css('.parent')).getWebElement();\n   * browser.driver.findElement(by.css('.parent'));\n   * browser.findElement(by.css('.parent'));\n   *\n   * @returns {webdriver.WebElement}\n   */\n  getWebElement(): WebElementPromise {\n    const id = this.elementArrayFinder_.getWebElements().then((parentWebElements: WebElement[]) => {\n      return parentWebElements[0];\n    });\n    return new WebElementPromise(this.browser_.driver, id) as WebElementPromise;\n  }\n\n  /**\n   * Calls to {@code all} may be chained to find an array of elements within a\n   * parent.\n   *\n   * @alias element(locator).all(locator)\n   * @view\n   * <div class=\"parent\">\n   *   <ul>\n   *     <li class=\"one\">First</li>\n   *     <li class=\"two\">Second</li>\n   *     <li class=\"three\">Third</li>\n   *   </ul>\n   * </div>\n   *\n   * @example\n   * let items = element(by.css('.parent')).all(by.tagName('li'));\n   *\n   * // Or using the shortcut $() notation instead of element(by.css()):\n   *\n   * let items = $('.parent').all(by.tagName('li'));\n   *\n   * @param {Locator} subLocator\n   * @returns {ElementArrayFinder}\n   */\n  all(subLocator: Locator): ElementArrayFinder {\n    return this.elementArrayFinder_.all(subLocator);\n  }\n\n  /**\n   * Calls to {@code element} may be chained to find elements within a parent.\n   *\n   * @alias element(locator).element(locator)\n   * @view\n   * <div class=\"parent\">\n   *   <div class=\"child\">\n   *     Child text\n   *     <div>{{person.phone}}</div>\n   *   </div>\n   * </div>\n   *\n   * @example\n   * // Chain 2 element calls.\n   * let child = element(by.css('.parent')).\n   *     element(by.css('.child'));\n   * expect(await child.getText()).toBe('Child text\\n555-123-4567');\n   *\n   * // Chain 3 element calls.\n   * let triple = element(by.css('.parent')).\n   *     element(by.css('.child')).\n   *     element(by.binding('person.phone'));\n   * expect(await triple.getText()).toBe('555-123-4567');\n   *\n   * // Or using the shortcut $() notation instead of element(by.css()):\n   *\n   * // Chain 2 element calls.\n   * let child = $('.parent').$('.child');\n   * expect(await child.getText()).toBe('Child text\\n555-123-4567');\n   *\n   * // Chain 3 element calls.\n   * let triple = $('.parent').$('.child').\n   *     element(by.binding('person.phone'));\n   * expect(await triple.getText()).toBe('555-123-4567');\n   *\n   * @param {Locator} subLocator\n   * @returns {ElementFinder}\n   */\n  element(subLocator: Locator): ElementFinder {\n    return this.all(subLocator).toElementFinder_();\n  }\n\n  /**\n   * Calls to {@code $$} may be chained to find an array of elements within a\n   * parent.\n   *\n   * @alias element(locator).all(selector)\n   * @view\n   * <div class=\"parent\">\n   *   <ul>\n   *     <li class=\"one\">First</li>\n   *     <li class=\"two\">Second</li>\n   *     <li class=\"three\">Third</li>\n   *   </ul>\n   * </div>\n   *\n   * @example\n   * let items = element(by.css('.parent')).$$('li');\n   *\n   * // Or using the shortcut $() notation instead of element(by.css()):\n   *\n   * let items = $('.parent').$$('li');\n   *\n   * @param {string} selector a css selector\n   * @returns {ElementArrayFinder}\n   */\n  $$(selector: string): ElementArrayFinder {\n    return this.all(By.css(selector));\n  }\n\n  /**\n   * Calls to {@code $} may be chained to find elements within a parent.\n   *\n   * @alias element(locator).$(selector)\n   * @view\n   * <div class=\"parent\">\n   *   <div class=\"child\">\n   *     Child text\n   *     <div>{{person.phone}}</div>\n   *   </div>\n   * </div>\n   *\n   * @example\n   * // Chain 2 element calls.\n   * let child = element(by.css('.parent')).\n   *     $('.child');\n   * expect(await child.getText()).toBe('Child text\\n555-123-4567');\n   *\n   * // Chain 3 element calls.\n   * let triple = element(by.css('.parent')).\n   *     $('.child').\n   *     element(by.binding('person.phone'));\n   * expect(await triple.getText()).toBe('555-123-4567');\n   *\n   * // Or using the shortcut $() notation instead of element(by.css()):\n   *\n   * // Chain 2 element calls.\n   * let child = $('.parent').$('.child');\n   * expect(await child.getText()).toBe('Child text\\n555-123-4567');\n   *\n   * // Chain 3 element calls.\n   * let triple = $('.parent').$('.child').\n   *     element(by.binding('person.phone'));\n   * expect(await triple.getText()).toBe('555-123-4567');\n   *\n   * @param {string} selector A css selector\n   * @returns {ElementFinder}\n   */\n  $(selector: string): ElementFinder {\n    return this.element(By.css(selector));\n  }\n\n  /**\n   * Determine whether the element is present on the page.\n   *\n   * @view\n   * <span>{{person.name}}</span>\n   *\n   * @example\n   * // Element exists.\n   * expect(await element(by.binding('person.name')).isPresent()).toBe(true);\n   *\n   * // Element not present.\n   * expect(await element(by.binding('notPresent')).isPresent()).toBe(false);\n   *\n   * @returns {Promise<boolean>} which resolves to whether\n   *     the element is present on the page.\n   */\n  async isPresent(): Promise<boolean> {\n    try {\n      const arr = await this.parentElementArrayFinder.getWebElements();\n      if (arr.length === 0) {\n        return false;\n      }\n      // is present, whether it is enabled or not\n      return await arr[0].isEnabled();\n    } catch (err) {\n      return falseIfMissing(err);\n    }\n  }\n\n  /**\n   * Same as ElementFinder.isPresent(), except this checks whether the element\n   * identified by the subLocator is present, rather than the current element\n   * finder, i.e.: `element(by.css('#abc')).element(by.css('#def')).isPresent()`\n   * is identical to `element(by.css('#abc')).isElementPresent(by.css('#def'))`.\n   *\n   * // Or using the shortcut $() notation instead of element(by.css()):\n   *\n   * `$('#abc').$('#def').isPresent()` is identical to\n   * `$('#abc').isElementPresent($('#def'))`.\n   *\n   * @see ElementFinder.isPresent\n   *\n   * @param {Locator} subLocator Locator for element to look for.\n   * @returns {Promise<boolean>} which resolves to whether\n   *     the subelement is present on the page.\n   */\n  isElementPresent(subLocator: Locator): Promise<boolean> {\n    if (!subLocator) {\n      throw new Error(\n          'SubLocator is not supplied as a parameter to ' +\n          '`isElementPresent(subLocator)`. You are probably looking for the ' +\n          'function `isPresent()`.');\n    }\n    return this.element(subLocator).isPresent();\n  }\n\n  /**\n   * Evaluates the input as if it were on the scope of the current element.\n   * @see ElementArrayFinder.prototype.evaluate\n   *\n   * @view\n   * <span id=\"foo\">{{letiableInScope}}</span>\n   *\n   * @example\n   * let value = element(by.id('foo')).evaluate('letiableInScope');\n   *\n   * @param {string} expression\n   *\n   * @returns {ElementFinder} which resolves to the evaluated expression.\n   */\n  evaluate(expression: string): ElementFinder {\n    return this.elementArrayFinder_.evaluate(expression).toElementFinder_();\n  }\n\n  /**\n   * @see ElementArrayFinder.prototype.allowAnimations.\n   * @param {string} value\n   *\n   * @returns {ElementFinder} which resolves to whether animation is allowed.\n   */\n  allowAnimations(value: boolean): ElementFinder {\n    return this.elementArrayFinder_.allowAnimations(value).toElementFinder_();\n  }\n\n  /**\n   * Compares an element to this one for equality.\n   *\n   * @param {!ElementFinder|!webdriver.WebElement} element The element to compare to.\n   *\n   * @returns {!Promise<boolean>} A promise that will be\n   *     resolved to whether the two WebElements are equal.\n   */\n  async equals(element: ElementFinder|WebElement): Promise<boolean> {\n    const a = await this.getWebElement();\n    const b = (element as any).getWebElement ? await(element as ElementFinder).getWebElement() :\n                                               element as WebElement;\n    // TODO(selenium4): Use `return WebElement.equals(a, b);` when\n    // https://github.com/SeleniumHQ/selenium/pull/6749 is fixed.\n    return a.getDriver().executeScript('return arguments[0] === arguments[1]', a, b);\n  }\n}\n\n/**\n * Shortcut for querying the document directly with css.\n * `element(by.css('.abc'))` is equivalent to `$('.abc')`\n *\n * @alias $(cssSelector)\n * @view\n * <div class=\"count\">\n *   <span class=\"one\">First</span>\n *   <span class=\"two\">Second</span>\n * </div>\n *\n * @example\n * let item = $('.count .two');\n * expect(await item.getText()).toBe('Second');\n *\n * @param {string} selector A css selector\n * @returns {ElementFinder} which identifies the located\n *     {@link webdriver.WebElement}\n */\nexport const build$ = (element: ElementHelper, by: typeof By) => {\n  return (selector: string) => {\n    return element(by.css(selector));\n  };\n};\n\n/**\n * Shortcut for querying the document directly with css.\n * `element.all(by.css('.abc'))` is equivalent to `$$('.abc')`\n *\n * @alias $$(cssSelector)\n * @view\n * <div class=\"count\">\n *   <span class=\"one\">First</span>\n *   <span class=\"two\">Second</span>\n * </div>\n *\n * @example\n * // The following protractor expressions are equivalent.\n * let list = element.all(by.css('.count span'));\n * expect(await list.count()).toBe(2);\n *\n * list = $$('.count span');\n * expect(await list.count()).toBe(2);\n * expect(await list.get(0).getText()).toBe('First');\n * expect(await list.get(1).getText()).toBe('Second');\n *\n * @param {string} selector a css selector\n * @returns {ElementArrayFinder} which identifies the\n *     array of the located {@link webdriver.WebElement}s.\n */\nexport const build$$ = (element: ElementHelper, by: typeof By) => {\n  return (selector: string) => {\n    return element.all(by.css(selector));\n  };\n};\n"
  },
  {
    "path": "lib/exitCodes.ts",
    "content": "import {Logger} from './logger';\n\nconst CONFIG_ERROR_CODE = 105;\nconst BROWSER_CONNECT_ERROR_CODE = 135;\nconst KITCHEN_SINK_CODE = 199;\n\nexport class IError extends Error {\n  code?: number;\n  stack?: string;\n}\n\nexport class ProtractorError extends IError {\n  static ERR_MSGS: string[];\n  static CODE = KITCHEN_SINK_CODE;\n  static SUPRESS_EXIT_CODE = false;\n\n  message: string;  // a one line message\n\n  constructor(logger: Logger, message: string, code: number, error?: Error) {\n    super(message);\n    this.message = message;\n    this.code = code;\n\n    // replacing the stack trace with the thrown error stack trace.\n    if (error) {\n      let protractorError = error as ProtractorError;\n      this.stack = protractorError.stack;\n    }\n    ProtractorError.log(logger, this.code, this.message, this.stack);\n\n    if (!ProtractorError.SUPRESS_EXIT_CODE) {\n      process.exit(this.code);\n    }\n  }\n\n  static log(logger: Logger, code: number, message: string, stack: string) {\n    let messages = message.split('\\n');\n    if (messages.length > 1) {\n      message = messages[0];\n    }\n    logger.error('Error code: ' + code);\n    logger.error('Error message: ' + message);\n    logger.error(stack);\n  }\n}\n\n/**\n * Configuration file error\n */\nexport class ConfigError extends ProtractorError {\n  static CODE = CONFIG_ERROR_CODE;\n  constructor(logger: Logger, message: string, error?: Error) {\n    super(logger, message, ConfigError.CODE, error);\n  }\n}\n\n/**\n * Browser errors including getting a driver session, direct connect, etc.\n */\nexport class BrowserError extends ProtractorError {\n  static CODE = BROWSER_CONNECT_ERROR_CODE;\n  static ERR_MSGS = [\n    'ECONNREFUSED connect ECONNREFUSED', 'Sauce Labs Authentication Error',\n    'Invalid username or password'\n  ];\n  constructor(logger: Logger, message: string) {\n    super(logger, message, BrowserError.CODE);\n  }\n}\n\nexport class ErrorHandler {\n  static isError(errMsgs: string[], e: Error): boolean {\n    if (errMsgs && errMsgs.length > 0) {\n      for (let errPos in errMsgs) {\n        let errMsg = errMsgs[errPos];\n        if (e.message && e.message.indexOf(errMsg) !== -1) {\n          return true;\n        }\n      }\n    }\n    return false;\n  }\n\n  static parseError(e: Error): number {\n    if (ErrorHandler.isError(ConfigError.ERR_MSGS, e)) {\n      return ConfigError.CODE;\n    }\n    if (ErrorHandler.isError(BrowserError.ERR_MSGS, e)) {\n      return BrowserError.CODE;\n    }\n    return null;\n  }\n}\n"
  },
  {
    "path": "lib/expectedConditions.ts",
    "content": "import {error as wderror} from 'selenium-webdriver';\nimport {ProtractorBrowser} from './browser';\nimport {ElementFinder} from './element';\nimport {falseIfMissing, passBoolean} from './util';\n\n/**\n * Represents a library of canned expected conditions that are useful for\n * protractor, especially when dealing with non-angular apps.\n *\n * Each condition returns a function that evaluates to a promise. You may mix\n * multiple conditions using `and`, `or`, and/or `not`. You may also\n * mix these conditions with any other conditions that you write.\n *\n * See ExpectedCondition Class in Selenium WebDriver codebase.\n * http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/ExpectedConditions.html\n *\n *\n * @example\n * const EC = protractor.ExpectedConditions;\n * const button = $('#xyz');\n * const isClickable = EC.elementToBeClickable(button);\n *\n * await browser.get(URL);\n * await browser.wait(isClickable, 5000); //wait for an element to become clickable\n * await button.click();\n *\n * // You can define your own expected condition, which is a function that\n * // takes no parameter and evaluates to a promise of a boolean.\n * const urlChanged = async () => {\n *   return await browser.getCurrentUrl() === 'http://www.angularjs.org';\n * }\n *\n * // You can customize the conditions with EC.and, EC.or, and EC.not.\n * // Here's a condition to wait for url to change, $('abc') element to contain\n * // text 'bar', and button becomes clickable.\n * const condition = EC.and(urlChanged, EC.textToBePresentInElement($('abc'),\n * 'bar'), isClickable);\n * await browser.get(URL);\n * await browser.wait(condition, 5000); //wait for condition to be true.\n * await button.click();\n *\n * @alias ExpectedConditions\n * @constructor\n */\nexport class ProtractorExpectedConditions {\n  constructor(public browser: ProtractorBrowser) {}\n\n  /**\n   * Negates the result of a promise.\n   *\n   * @example\n   * const EC = protractor.ExpectedConditions;\n   * const titleIsNotFoo = EC.not(EC.titleIs('Foo'));\n   * // Waits for title to become something besides 'foo'.\n   * await browser.wait(titleIsNotFoo, 5000);\n   *\n   * @alias ExpectedConditions.not\n   * @param {!function} expectedCondition\n   *\n   * @returns {!function} An expected condition that returns the negated value.\n   */\n  not(expectedCondition: Function): (() => Promise<boolean>) {\n    return async(): Promise<boolean> => {\n      const bool = await expectedCondition();\n      return !bool;\n    };\n  }\n\n  /**\n   * Helper function that is equivalent to the logical_and if defaultRet==true,\n   * or logical_or if defaultRet==false\n   *\n   * @private\n   * @param {boolean} defaultRet\n   * @param {Array.<Function>} fns An array of expected conditions to chain.\n   *\n   * @returns {!function} An expected condition that returns a promise which\n   *     evaluates to the result of the logical chain.\n   */\n  logicalChain_(defaultRet: boolean, fns: Array<Function>): (() => Promise<boolean>) {\n    let self = this;\n    return async(): Promise<boolean> => {\n      if (fns.length === 0) {\n        return defaultRet;\n      }\n      const fn = fns[0];\n      const bool = await fn();\n      if (bool === defaultRet) {\n        return self.logicalChain_(defaultRet, fns.slice(1))();\n      } else {\n        return !defaultRet;\n      }\n    };\n  }\n\n  /**\n   * Chain a number of expected conditions using logical_and, short circuiting\n   * at the first expected condition that evaluates to false.\n   *\n   * @example\n   * const EC = protractor.ExpectedConditions;\n   * const titleContainsFoo = EC.titleContains('Foo');\n   * const titleIsNotFooBar = EC.not(EC.titleIs('FooBar'));\n   * // Waits for title to contain 'Foo', but is not 'FooBar'\n   * await browser.wait(EC.and(titleContainsFoo, titleIsNotFooBar), 5000);\n   *\n   * @alias ExpectedConditions.and\n   * @param {Array.<Function>} args An array of expected conditions to 'and'\n   * together.\n   *\n   * @returns {!function} An expected condition that returns a promise which\n   *     evaluates to the result of the logical and.\n   */\n  and(...args: Function[]): (() => Promise<boolean>) {\n    return this.logicalChain_(true, args);\n  }\n\n  /**\n   * Chain a number of expected conditions using logical_or, short circuiting\n   * at the first expected condition that evaluates to true.\n   *\n   * @alias ExpectedConditions.or\n   * @example\n   * const EC = protractor.ExpectedConditions;\n   * const titleContainsFoo = EC.titleContains('Foo');\n   * const titleContainsBar = EC.titleContains('Bar');\n   * // Waits for title to contain either 'Foo' or 'Bar'\n   * await browser.wait(EC.or(titleContainsFoo, titleContainsBar), 5000);\n   *\n   * @param {Array.<Function>} args An array of expected conditions to 'or'\n   * together.\n   *\n   * @returns {!function} An expected condition that returns a promise which\n   *     evaluates to the result of the logical or.\n   */\n  or(...args: Function[]): (() => Promise<boolean>) {\n    return this.logicalChain_(false, args);\n  }\n\n  /**\n   * Expect an alert to be present.\n   *\n   * @example\n   * const EC = protractor.ExpectedConditions;\n   * // Waits for an alert pops up.\n   * await browser.wait(EC.alertIsPresent(), 5000);\n   *\n   * @alias ExpectedConditions.alertIsPresent\n   * @returns {!function} An expected condition that returns a promise\n   *     representing whether an alert is present.\n   */\n  alertIsPresent(): (() => Promise<boolean>) {\n    return async(): Promise<boolean> => {\n      try {\n        await this.browser.driver.switchTo().alert();\n        return true;\n      } catch (e) {\n        if (e instanceof wderror.NoSuchAlertError) {\n          return false;\n        } else {\n          throw e;\n        }\n      }\n    };\n  }\n\n  /**\n   * An Expectation for checking an element is visible and enabled such that you\n   * can click it.\n   *\n   * @example\n   * const EC = protractor.ExpectedConditions;\n   * // Waits for the element with id 'abc' to be clickable.\n   * await browser.wait(EC.elementToBeClickable($('#abc')), 5000);\n   *\n   * @alias ExpectedConditions.elementToBeClickable\n   * @param {!ElementFinder} elementFinder The element to check\n   *\n   * @returns {!function} An expected condition that returns a promise\n   *     representing whether the element is clickable.\n   */\n  elementToBeClickable(elementFinder: ElementFinder): (() => Promise<boolean>) {\n    return this.and(this.visibilityOf(elementFinder), () => {\n      return elementFinder.isEnabled().then(passBoolean, falseIfMissing);\n    });\n  }\n\n  /**\n   * An expectation for checking if the given text is present in the\n   * element. Returns false if the elementFinder does not find an element.\n   *\n   * @example\n   * const EC = protractor.ExpectedConditions;\n   * // Waits for the element with id 'abc' to contain the text 'foo'.\n   * await browser.wait(EC.textToBePresentInElement($('#abc'), 'foo'), 5000);\n   *\n   * @alias ExpectedConditions.textToBePresentInElement\n   * @param {!ElementFinder} elementFinder The element to check\n   * @param {!string} text The text to verify against\n   *\n   * @returns {!function} An expected condition that returns a promise\n   *     representing whether the text is present in the element.\n   */\n  textToBePresentInElement(elementFinder: ElementFinder, text: string): (() => Promise<boolean>) {\n    let hasText = async () => {\n      try {\n        const actualText = await elementFinder.getText();\n        // MSEdge does not properly remove newlines, which causes false\n        // negatives\n        return actualText.replace(/\\r?\\n|\\r/g, '').indexOf(text) > -1;\n      } catch (e) {\n        return falseIfMissing(e);\n      }\n    };\n    return this.and(this.presenceOf(elementFinder), hasText);\n  }\n\n  /**\n   * An expectation for checking if the given text is present in the element’s\n   * value. Returns false if the elementFinder does not find an element.\n   *\n   * @example\n   * const EC = protractor.ExpectedConditions;\n   * // Waits for the element with id 'myInput' to contain the input 'foo'.\n   * await browser.wait(EC.textToBePresentInElementValue($('#myInput'), 'foo'), 5000);\n   *\n   * @alias ExpectedConditions.textToBePresentInElementValue\n   * @param {!ElementFinder} elementFinder The element to check\n   * @param {!string} text The text to verify against\n   *\n   * @returns {!function} An expected condition that returns a promise\n   *     representing whether the text is present in the element's value.\n   */\n  textToBePresentInElementValue(elementFinder: ElementFinder, text: string):\n      (() => Promise<boolean>) {\n    let hasText = async () => {\n      try {\n        const actualText = await elementFinder.getAttribute('value');\n        return actualText.indexOf(text) > -1;\n      } catch (e) {\n        return falseIfMissing(e);\n      }\n    };\n    return this.and(this.presenceOf(elementFinder), hasText);\n  }\n\n  /**\n   * An expectation for checking that the title contains a case-sensitive\n   * substring.\n   *\n   * @example\n   * const EC = protractor.ExpectedConditions;\n   * // Waits for the title to contain 'foo'.\n   * await browser.wait(EC.titleContains('foo'), 5000);\n   *\n   * @alias ExpectedConditions.titleContains\n   * @param {!string} title The fragment of title expected\n   *\n   * @returns {!function} An expected condition that returns a promise\n   *     representing whether the title contains the string.\n   */\n  titleContains(title: string): (() => Promise<boolean>) {\n    return async(): Promise<boolean> => {\n      const actualTitle = await this.browser.driver.getTitle();\n      return actualTitle.indexOf(title) > -1;\n    };\n  }\n\n  /**\n   * An expectation for checking the title of a page.\n   *\n   * @example\n   * const EC = protractor.ExpectedConditions;\n   * // Waits for the title to be 'foo'.\n   * await browser.wait(EC.titleIs('foo'), 5000);\n   *\n   * @alias ExpectedConditions.titleIs\n   * @param {!string} title The expected title, which must be an exact match.\n   *\n   * @returns {!function} An expected condition that returns a promise\n   *     representing whether the title equals the string.\n   */\n  titleIs(title: string): (() => Promise<boolean>) {\n    return async(): Promise<boolean> => {\n      const actualTitle = await this.browser.driver.getTitle();\n      return actualTitle === title;\n    };\n  }\n\n  /**\n   * An expectation for checking that the URL contains a case-sensitive\n   * substring.\n   *\n   * @example\n   * const EC = protractor.ExpectedConditions;\n   * // Waits for the URL to contain 'foo'.\n   * await browser.wait(EC.urlContains('foo'), 5000);\n   *\n   * @alias ExpectedConditions.urlContains\n   * @param {!string} url The fragment of URL expected\n   *\n   * @returns {!function} An expected condition that returns a promise\n   *     representing whether the URL contains the string.\n   */\n  urlContains(url: string): (() => Promise<boolean>) {\n    return async(): Promise<boolean> => {\n      const actualUrl = await this.browser.driver.getCurrentUrl();\n      return actualUrl.indexOf(url) > -1;\n    };\n  }\n\n  /**\n   * An expectation for checking the URL of a page.\n   *\n   * @example\n   * const EC = protractor.ExpectedConditions;\n   * // Waits for the URL to be 'foo'.\n   * await browser.wait(EC.urlIs('foo'), 5000);\n   *\n   * @alias ExpectedConditions.urlIs\n   * @param {!string} url The expected URL, which must be an exact match.\n   *\n   * @returns {!function} An expected condition that returns a promise\n   *     representing whether the url equals the string.\n   */\n  urlIs(url: string): (() => Promise<boolean>) {\n    return async(): Promise<boolean> => {\n      const actualUrl = await this.browser.driver.getCurrentUrl();\n      return actualUrl === url;\n    };\n  }\n\n  /**\n   * An expectation for checking that an element is present on the DOM\n   * of a page. This does not necessarily mean that the element is visible.\n   * This is the opposite of 'stalenessOf'.\n   *\n   * @example\n   * const EC = protractor.ExpectedConditions;\n   * // Waits for the element with id 'abc' to be present on the dom.\n   * await browser.wait(EC.presenceOf($('#abc')), 5000);\n   *\n   * @alias ExpectedConditions.presenceOf\n   * @param {!ElementFinder} elementFinder The element to check\n   *\n   * @returns {!function} An expected condition that returns a promise\n   *     representing whether the element is present.\n   */\n  presenceOf(elementFinder: ElementFinder): (() => Promise<boolean>) {\n    return elementFinder.isPresent.bind(elementFinder);\n  }\n\n  /**\n   * An expectation for checking that an element is not attached to the DOM\n   * of a page. This is the opposite of 'presenceOf'.\n   *\n   * @example\n   * const EC = protractor.ExpectedConditions;\n   * // Waits for the element with id 'abc' to be no longer present on the dom.\n   * await browser.wait(EC.stalenessOf($('#abc')), 5000);\n   *\n   * @alias ExpectedConditions.stalenessOf\n   * @param {!ElementFinder} elementFinder The element to check\n   *\n   * @returns {!function} An expected condition that returns a promise\n   *     representing whether the element is stale.\n   */\n  stalenessOf(elementFinder: ElementFinder): (() => Promise<boolean>) {\n    return this.not(this.presenceOf(elementFinder));\n  }\n\n  /**\n   * An expectation for checking that an element is present on the DOM of a\n   * page and visible. Visibility means that the element is not only displayed\n   * but also has a height and width that is greater than 0. This is the\n   * opposite\n   * of 'invisibilityOf'.\n   *\n   * @example\n   * const EC = protractor.ExpectedConditions;\n   * // Waits for the element with id 'abc' to be visible on the dom.\n   * await browser.wait(EC.visibilityOf($('#abc')), 5000);\n   *\n   * @alias ExpectedConditions.visibilityOf\n   * @param {!ElementFinder} elementFinder The element to check\n   *\n   * @returns {!function} An expected condition that returns a promise\n   *     representing whether the element is visible.\n   */\n  visibilityOf(elementFinder: ElementFinder): (() => Promise<boolean>) {\n    return this.and(this.presenceOf(elementFinder), () => {\n      return elementFinder.isDisplayed().then(passBoolean, falseIfMissing);\n    });\n  }\n\n  /**\n   * An expectation for checking that an element is either invisible or not\n   * present on the DOM. This is the opposite of 'visibilityOf'.\n   *\n   * @example\n   * const EC = protractor.ExpectedConditions;\n   * // Waits for the element with id 'abc' to be no longer visible on the dom.\n   * await browser.wait(EC.invisibilityOf($('#abc')), 5000);\n   *\n   * @alias ExpectedConditions.invisibilityOf\n   * @param {!ElementFinder} elementFinder The element to check\n   *\n   * @returns {!function} An expected condition that returns a promise\n   *     representing whether the element is invisible.\n   */\n  invisibilityOf(elementFinder: ElementFinder): (() => Promise<boolean>) {\n    return this.not(this.visibilityOf(elementFinder));\n  }\n\n  /**\n   * An expectation for checking the selection is selected.\n   *\n   * @example\n   * const EC = protractor.ExpectedConditions;\n   * // Waits for the element with id 'myCheckbox' to be selected.\n   * await browser.wait(EC.elementToBeSelected($('#myCheckbox')), 5000);\n   *\n   * @alias ExpectedConditions.elementToBeSelected\n   * @param {!ElementFinder} elementFinder The element to check\n   *\n   * @returns {!function} An expected condition that returns a promise\n   *     representing whether the element is selected.\n   */\n  elementToBeSelected(elementFinder: ElementFinder): (() => Promise<boolean>) {\n    return this.and(this.presenceOf(elementFinder), () => {\n      return elementFinder.isSelected().then(passBoolean, falseIfMissing);\n    });\n  }\n}\n"
  },
  {
    "path": "lib/frameworks/README.md",
    "content": "Framework Adapters for Protractor\n=================================\n\nProtractor can work with any test framework that is adapted here.\n\nEach file details the adapter for one test framework. Each file must export a\n`run` function with the interface:\n\n```ts\n/**\n * @param {Runner} runner The Protractor runner instance.\n * @param {Array.<string>} specs A list of absolute filenames.\n * @return {Promise.<Object>} Promise resolved with the test results.  See\n *   \"Requirements\" section for details.\n */\nexport let run: (runner: Protractor.Runner, specs: string[]) => Promise<Object>\n```\n\nRequirements\n------------\n\n- `runner.emit` must be called with `testPass` and `testFail` messages. These\n  messages must be passed a `testInfo` object with the following structure:\n\n  ```ts\n  testInfo: {\n    category: string,\n    name: string\n  }\n  ```\n\n  The `category` property could be the name of the `describe` block in\n  jasmine/mocha, the `Feature` in cucumber, or the class name in something like\n  jUnit.\n  The `name` property could be the name of an `it` block in jasmine/mocha, the\n  `Scenario` in cucumber, or the method name in something like jUnit.\n\n- `runner.runTestPreparer` must be called after the framework has been\n  initialized but before any spec files are run.  This function returns a\n  promise which should be waited on before executing tests. The framework should\n  also pass an array of extra command line flags it accepts, if any.\n\n- `runner.getConfig().onComplete` must be called when tests are finished.\n  It might return a promise, in which case `exports.run`'s promise should not\n  resolve until after `onComplete`'s promise resolves.\n\n- The returned promise must be resolved when tests are finished and it should\n  return a results object. This object must have a `failedCount` property and\n  optionally a `specResults` object of the following structure:\n\n  ```ts\n  specResults: [{\n    description: string,\n    assertions: [{\n      passed: boolean,\n      errorMsg: string,\n      stackTrace: string\n    }],\n    duration: integer\n  }]\n  ```\n\n### Future requirements\n\nIn Protractor 6.0, the following additional requirement will be added:\n\n- `runner.afterEach` will have to be called after each test finishes.  It will\n  return a promise, which should be waited for before moving onto the next test.\n\nIf you want your framework to be backwards-compatible, you can simply write:\n\n```ts\nif (runner.afterEach) {\n  // Add afterEach caller\n}\n```\n\nFailing to call `runner.afterEach` will cause features like\n`restartBrowserBetweenTests` to fail.  Protractor may also log a warning to the\nconsole. \n\nCustom Frameworks\n-----------------\n\nIf you have created/adapted a custom framework and want it added to\nProtractor core please send a PR so it can evaluated for addition as an\nofficial supported framework. In the meantime you can instruct Protractor\nto use your own framework via the config file:\n\n```ts\nexport let config: Protractor.Config = {\n  // set to \"custom\" instead of jasmine/mocha\n  framework: 'custom',\n  // path relative to the current config file\n  frameworkPath: './frameworks/my_custom_jasmine.js',\n};\n```\n\nMore on this at the [configuration](../config.ts).\n\n**Disclaimer**: current framework interface can change without a major version bump.\n"
  },
  {
    "path": "lib/frameworks/__protractor_internal_afterEach_setup_spec.js",
    "content": "// This is spec file is automatically added by protractor to implement our\n// `afterEach` functionality for jasmine and mocha.\n\nvar hooks = require('./setupAfterEach').hooks;\n\nafterEach(function() {\n  if (hooks.afterEach) { \n    return hooks.afterEach();\n  }\n});\n"
  },
  {
    "path": "lib/frameworks/debugprint.ts",
    "content": "import * as util from 'util';\nimport {Logger} from '../logger';\nimport {Runner} from '../runner';\nimport {RunResults} from '../taskRunner';\n\nconst logger = new Logger('debugger');\n\n/**\n * A debug framework which does not actually run any tests, just spits\n * out the list that would be run.\n *\n * @param {Runner} runner The current Protractor Runner.\n * @param {Array} specs Array of Directory Path Strings.\n * @return {Promise} Promise resolved with the test results\n */\nexport const run = (runner: Runner, specs: Array<string>): Promise<RunResults> => {\n  return new Promise(resolve => {\n    logger.info(`Resolved spec files: ${util.inspect(specs)}`);\n    resolve({failedCount: 0});\n  });\n};\n"
  },
  {
    "path": "lib/frameworks/jasmine.js",
    "content": "let RunnerReporter = function(emitter) {\n  this.emitter = emitter;\n  this.testResult = [],\n  this.failedCount = 0;\n};\n\nRunnerReporter.prototype.jasmineStarted = function() {\n  // Need to initiate startTime here, in case reportSpecStarting is not\n  // called (e.g. when fit is used)\n  this.startTime = new Date();\n};\n\nRunnerReporter.prototype.specStarted = function() {\n  this.startTime = new Date();\n};\n\nRunnerReporter.prototype.specDone = function(result) {\n  const specInfo = {\n    name: result.description,\n    category: result.fullName.slice(0, -result.description.length).trim()\n  };\n  if (result.status == 'passed') {\n    this.emitter.emit('testPass', specInfo);\n  } else if (result.status == 'failed') {\n    this.emitter.emit('testFail', specInfo);\n    this.failedCount++;\n  }\n\n  const entry = {\n    description: result.fullName,\n    assertions: [],\n    duration: new Date().getTime() - this.startTime.getTime()\n  };\n\n  if (result.failedExpectations.length === 0) {\n    entry.assertions.push({\n      passed: true\n    });\n  }\n\n  result.failedExpectations.forEach(item => {\n    entry.assertions.push({\n      passed: item.passed,\n      errorMsg: item.passed ? undefined : item.message,\n      stackTrace: item.passed ? undefined : item.stack\n    });\n  });\n  this.testResult.push(entry);\n};\n\n/**\n * Execute the Runner's test cases through Jasmine.\n *\n * @param {Runner} runner The current Protractor Runner.\n * @param {Array} specs Array of Directory Path Strings.\n * @return {Promise} Promise resolved with the test results\n */\nexports.run = async function(runner, specs) {\n  const JasmineRunner = require('jasmine');\n  const jrunner = new JasmineRunner();\n\n  const jasmineNodeOpts = runner.getConfig().jasmineNodeOpts;\n\n  // On timeout, the flow should be reset. This will prevent webdriver tasks\n  // from overflowing into the next test and causing it to fail or timeout\n  // as well. This is done in the reporter instead of an afterEach block\n  // to ensure that it runs after any afterEach() blocks with webdriver tasks\n  // get to complete first.\n  const reporter = new RunnerReporter(runner);\n  jasmine.getEnv().addReporter(reporter);\n\n  // Jasmine 3 allows for tests to be in random order by default. This does not\n  // work well with e2e tests where the browser state is determined by the\n  // order of the tests. Setting to false will prevent random execution.\n  // See https://jasmine.github.io/api/3.3/Env.html\n  jasmine.getEnv().randomizeTests(false);\n\n  // Add hooks for afterEach\n  require('./setupAfterEach').setup(runner, specs);\n\n  // Filter specs to run based on jasmineNodeOpts.grep and jasmineNodeOpts.invert.\n  jasmine.getEnv().specFilter = function(spec) {\n    var grepMatch = !jasmineNodeOpts ||\n        !jasmineNodeOpts.grep ||\n        spec.getFullName().match(new RegExp(jasmineNodeOpts.grep)) != null;\n    var invertGrep = !!(jasmineNodeOpts && jasmineNodeOpts.invertGrep);\n    if (grepMatch == invertGrep) {\n      spec.disable();\n    }\n    return true;\n  };\n\n  // Run specs in semi-random order\n  if (jasmineNodeOpts.random) {\n    jasmine.getEnv().randomizeTests(true);\n\n    // Sets the randomization seed if randomization is turned on\n    if (jasmineNodeOpts.seed) {\n      jasmine.getEnv().seed(jasmineNodeOpts.seed);\n    }\n  }\n\n  await runner.runTestPreparer();\n  return new Promise((resolve, reject) => {\n    if (jasmineNodeOpts && jasmineNodeOpts.defaultTimeoutInterval) {\n      jasmine.DEFAULT_TIMEOUT_INTERVAL = jasmineNodeOpts.defaultTimeoutInterval;\n    }\n\n    const originalOnComplete = runner.getConfig().onComplete;\n\n    jrunner.onComplete(async(passed) => {\n      try {\n        if (originalOnComplete) {\n          await originalOnComplete(passed);\n        }\n        resolve({\n          failedCount: reporter.failedCount,\n          specResults: reporter.testResult\n        });\n      } catch (err) {\n        reject(err);\n      }\n    });\n\n    jrunner.configureDefaultReporter(jasmineNodeOpts);\n    jrunner.projectBaseDir = '';\n    jrunner.specDir = '';\n    jrunner.addSpecFiles(specs);\n    jrunner.execute();\n  });\n};\n"
  },
  {
    "path": "lib/frameworks/mocha.js",
    "content": "/**\n * Execute the Runner's test cases through Mocha.\n *\n * @param {Runner} runner The current Protractor Runner.\n * @param {Array} specs Array of Directory Path Strings.\n * @return {q.Promise} Promise resolved with the test results\n */\nexports.run = (runner, specs) => {\n  const Mocha = require('mocha');\n  const mocha = new Mocha(runner.getConfig().mochaOpts);\n\n  // Add hooks for afterEach\n  require('./setupAfterEach').setup(runner, specs);\n\n  return new Promise(async (resolve, reject) => {\n    mocha.loadFiles();\n\n    try {\n      await runner.runTestPreparer();\n      specs.forEach((file) => {\n        mocha.addFile(file);\n      });\n      let testResult = [];\n\n      const mochaRunner = mocha.run(async (failures) => {\n        try {\n          if (runner.getConfig().onComplete) {\n            await runner.getConfig().onComplete();\n          }\n          resolve({\n            failedCount: failures,\n            specResults: testResult\n          });\n        } catch (err) {\n          reject(err);\n        }\n      });\n\n      mochaRunner.on('pass', (test) => {\n        const testInfo = {\n          name: test.title,\n          category: test.fullTitle().slice(0, -test.title.length).trim()\n        };\n        runner.emit('testPass', testInfo);\n        testResult.push({\n          description: test.title,\n          assertions: [{\n            passed: true\n          }],\n          duration: test.duration\n        });\n      });\n\n      mochaRunner.on('fail', (test) => {\n        const testInfo = {\n          name: test.title,\n          category: test.fullTitle().slice(0, -test.title.length).trim()\n        };\n        runner.emit('testFail', testInfo);\n        testResult.push({\n          description: test.title,\n          assertions: [{\n            passed: false,\n            errorMsg: test.err.message,\n            stackTrace: test.err.stack\n          }],\n          duration: test.duration\n        });\n      });\n    } catch (err) {\n      reject(err);\n    }\n  });  \n};\n"
  },
  {
    "path": "lib/frameworks/setupAfterEach.js",
    "content": "/**\n * Setup afterEach hook for jasmine/mocha tests.\n *\n * One of the main purposes of this file is to give `__protractor_internal_afterEach_setup_spec.js`\n * a place to look up `runner.afterEach` at runtime without using globals.\n * This file needs to be separate from `__protractor_internal_afterEach_setup_spec.js` so that that\n * file is not prematurely executed.\n */\n\nvar path = require('path');\n\n// Queried by `protractor_internal_afterEach_setup_spec.js` for the `afterEach` hook\nvar hooks = {\n  afterEach: null\n};\n\nexports.hooks = hooks;\n\n/**\n * Setup `runner.afterEach` to be called after every spec. \n *\n * @param {Runner} runner The current Protractor Runner.\n * @param {Array} specs Array of Directory Path Strings.  Must be a reference to the same array\n *   instance used by the framework\n */\nexports.setup = function(runner, specs) {\n  hooks.afterEach = runner.afterEach.bind(runner);\n  specs.push(path.resolve(__dirname, '__protractor_internal_afterEach_setup_spec.js'));\n};\n"
  },
  {
    "path": "lib/index.ts",
    "content": "import {ElementHelper, ProtractorBrowser} from './browser';\nimport {ElementArrayFinder, ElementFinder} from './element';\nimport {ProtractorExpectedConditions} from './expectedConditions';\nimport {ProtractorBy} from './locators';\nimport {Ptor} from './ptor';\n\n// Re-export selenium-webdriver types from typings directory.\nexport {Actions, Browser, Builder, Button, Capabilities, Capability, error, EventEmitter, FileDetector, Key, logging, promise, Session, until, WebDriver, WebElement, WebElementPromise} from 'selenium-webdriver';\n// Re-export public types.\nexport {ElementHelper, ProtractorBrowser} from './browser';\nexport {Config} from './config';\nexport {ElementArrayFinder, ElementFinder} from './element';\nexport {ProtractorExpectedConditions} from './expectedConditions';\nexport {Locator, ProtractorBy} from './locators';\nexport {PluginConfig, ProtractorPlugin} from './plugins';\nexport {Ptor} from './ptor';\nexport {Runner} from './runner';\n\nexport const utils = {\n  firefox: require('selenium-webdriver/firefox'),\n  http: require('selenium-webdriver/http'),\n  remote: require('selenium-webdriver/remote')\n};\n\nexport {Command, Name as CommandName} from 'selenium-webdriver/lib/command';\n\n// Export API instances based on the global Protractor object.\n// We base this on NodeJS `global` because we do not want to mask\n// with a different instance of Protractor if the module is\n// installed both globally and locally.\n\n// Because these properties are set dynamically by the runner in setupGlobals_, they are not\n// guaranteed to be created at import time. Also, the browser object can change if browser.reset()\n// is called. Thus, we export these as properties so they will be resolved dynamically.\nexport declare let protractor: Ptor;\nObject.defineProperty(exports, 'protractor', {get: () => (global as any)['protractor']});\n\nfunction registerGlobal(name: string) {\n  Object.defineProperty(\n      exports, name, {get: () => exports.protractor ? exports.protractor[name] : undefined});\n}\n\nexport declare let browser: ProtractorBrowser;\nexport declare let $: (search: string) => ElementFinder;\nexport declare let $$: (search: string) => ElementArrayFinder;\nexport declare let element: ElementHelper;\nexport declare let By: ProtractorBy;\nexport declare let by: ProtractorBy;\nexport declare let ExpectedConditions: ProtractorExpectedConditions;\n\nregisterGlobal('browser');\nregisterGlobal('$');\nregisterGlobal('$$');\nregisterGlobal('element');\nregisterGlobal('By');\nregisterGlobal('by');\nregisterGlobal('ExpectedConditions');\n"
  },
  {
    "path": "lib/launcher.ts",
    "content": "/**\n * The launcher is responsible for parsing the capabilities from the\n * input configuration and launching test runners.\n */\nimport * as fs from 'fs';\n\nimport {Config} from './config';\nimport {ConfigParser} from './configParser';\nimport {ConfigError, ErrorHandler, ProtractorError} from './exitCodes';\nimport {Logger} from './logger';\nimport {TaskRunner} from './taskRunner';\nimport {TaskScheduler} from './taskScheduler';\nimport {runFilenameOrFn_} from './util';\n\n\nlet logger = new Logger('launcher');\nlet RUNNERS_FAILED_EXIT_CODE = 100;\n\n/**\n * Keeps track of a list of task results. Provides method to add a new\n * result, aggregate the results into a summary, count failures,\n * and save results into a JSON file.\n */\nclass TaskResults {\n  // TODO: set a type for result\n  results_: any[] = [];\n\n  add(result: any): void {\n    this.results_.push(result);\n  }\n\n  totalSpecFailures(): number {\n    return this.results_.reduce((specFailures, result) => {\n      return specFailures + result.failedCount;\n    }, 0);\n  }\n\n  totalProcessFailures(): number {\n    return this.results_.reduce((processFailures, result) => {\n      return !result.failedCount && result.exitCode !== 0 ? processFailures + 1 : processFailures;\n    }, 0);\n  }\n\n  saveResults(filepath: string): void {\n    let jsonOutput = this.results_.reduce((jsonOutput, result) => {\n      return jsonOutput.concat(result.specResults);\n    }, []);\n\n    let json = JSON.stringify(jsonOutput, null, '  ');\n    fs.writeFileSync(filepath, json);\n  }\n\n  reportSummary(): void {\n    let specFailures = this.totalSpecFailures();\n    let processFailures = this.totalProcessFailures();\n    this.results_.forEach((result: any) => {\n      let capabilities = result.capabilities;\n      let shortName = (capabilities.logName) ?\n          capabilities.logName :\n          (capabilities.browserName) ? capabilities.browserName : '';\n      shortName += (capabilities.version) ? capabilities.version : '';\n      shortName += (capabilities.logName && capabilities.count < 2) ? '' : ' #' + result.taskId;\n      if (result.failedCount) {\n        logger.info(shortName + ' failed ' + result.failedCount + ' test(s)');\n      } else if (result.exitCode !== 0) {\n        logger.info(shortName + ' failed with exit code: ' + result.exitCode);\n      } else {\n        logger.info(shortName + ' passed');\n      }\n    });\n\n    if (specFailures && processFailures) {\n      logger.info(\n          'overall: ' + specFailures + ' failed spec(s) and ' + processFailures +\n          ' process(es) failed to complete');\n    } else if (specFailures) {\n      logger.info('overall: ' + specFailures + ' failed spec(s)');\n    } else if (processFailures) {\n      logger.info('overall: ' + processFailures + ' process(es) failed to complete');\n    }\n  }\n}\n\nlet taskResults_ = new TaskResults();\n\n/**\n * Initialize and run the tests.\n * Exits with 1 on test failure, and RUNNERS_FAILED_EXIT_CODE on unexpected\n * failures.\n *\n * @param {string=} configFile\n * @param {Object=} additionalConfig\n */\nlet initFn = async function(configFile: string, additionalConfig: Config) {\n  let configParser = new ConfigParser();\n  if (configFile) {\n    configParser.addFileConfig(configFile);\n  }\n  if (additionalConfig) {\n    configParser.addConfig(additionalConfig);\n  }\n  let config = configParser.getConfig();\n  Logger.set(config);\n  logger.debug('Running with --troubleshoot');\n  logger.debug('Protractor version: ' + require('../package.json').version);\n  logger.debug('Your base url for tests is ' + config.baseUrl);\n\n  // Run beforeLaunch\n  await runFilenameOrFn_(config.configDir, config.beforeLaunch);\n  // 1) If getMultiCapabilities is set, resolve that as\n  // `multiCapabilities`.\n  if (config.getMultiCapabilities && typeof config.getMultiCapabilities === 'function') {\n    if (config.multiCapabilities.length || config.capabilities) {\n      logger.warn(\n          'getMultiCapabilities() will override both capabilities ' +\n          'and multiCapabilities');\n    }\n    // If getMultiCapabilities is defined and a function, use this.\n    const waitMultiConfig = await config.getMultiCapabilities();\n    config.multiCapabilities = waitMultiConfig;\n    config.capabilities = null;\n  }\n\n  // 2) Set `multicapabilities` using `capabilities`,\n  // `multicapabilities`, or default\n  if (config.capabilities) {\n    if (config.multiCapabilities.length) {\n      logger.warn(\n          'You have specified both capabilities and ' +\n          'multiCapabilities. This will result in capabilities being ' +\n          'ignored');\n    } else {\n      // Use capabilities if multiCapabilities is empty.\n      config.multiCapabilities = [config.capabilities];\n    }\n  } else if (!config.multiCapabilities.length) {\n    // Default to chrome if no capabilities given\n    config.multiCapabilities = [{browserName: 'chrome'}];\n  }\n\n  // 3) If we're in `elementExplorer` mode, throw an error and exit.\n  if (config.elementExplorer || config.framework === 'explorer') {\n    const err = new Error(\n        'Deprecated: Element explorer depends on the ' +\n        'WebDriver control flow, and thus is no longer supported.');\n    logger.error(err);\n    process.exit(1);\n  }\n\n  // 4) Run tests.\n  let scheduler = new TaskScheduler(config);\n\n  process.on('uncaughtException', (exc: (Error|string)) => {\n    let e = (exc instanceof Error) ? exc : new Error(exc);\n    if (config.ignoreUncaughtExceptions) {\n      // This can be a sign of a bug in the test framework, that it may\n      // not be handling WebDriver errors properly. However, we don't\n      // want these errors to prevent running the tests.\n      logger.warn('Ignoring uncaught error ' + exc);\n      return;\n    }\n    logger.error(e.message);\n    logger.error(e.stack);\n    if (e instanceof ProtractorError) {\n      let protractorError = e as ProtractorError;\n      process.exit(protractorError.code);\n    } else {\n      process.exit(1);\n    }\n  });\n\n  process.on('unhandledRejection', (reason: Error | any, p: Promise<any>) => {\n    if (reason.stack.match('angular testability are undefined') ||\n        reason.stack.match('angular is not defined')) {\n      logger.warn(\n          'Unhandled promise rejection error: This is usually occurs ' +\n          'when a browser.get call is made and a previous async call was ' +\n          'not awaited');\n    }\n    logger.warn(p);\n  });\n\n  process.on('exit', (code: number) => {\n    if (code) {\n      logger.error('Process exited with error code ' + code);\n    } else if (scheduler.numTasksOutstanding() > 0) {\n      logger.error(\n          'BUG: launcher exited with ' + scheduler.numTasksOutstanding() + ' tasks remaining');\n      process.exit(RUNNERS_FAILED_EXIT_CODE);\n    }\n  });\n\n  // Run afterlaunch and exit\n  const cleanUpAndExit = async (exitCode: number) => {\n    try {\n      const returned = await runFilenameOrFn_(config.configDir, config.afterLaunch, [exitCode]);\n      if (typeof returned === 'number') {\n        process.exit(returned);\n      } else {\n        process.exit(exitCode);\n      }\n    } catch (err) {\n      logger.error('Error:', err);\n      process.exit(1);\n    }\n  };\n\n  const totalTasks = scheduler.numTasksOutstanding();\n  let forkProcess = false;\n  if (totalTasks > 1) {  // Start new processes only if there are >1 tasks.\n    forkProcess = true;\n    if (config.debug) {\n      throw new ConfigError(\n          logger, 'Cannot run in debug mode with multiCapabilities, count > 1, or sharding');\n    }\n  }\n\n  const createNextTaskRunner = async () => {\n    return new Promise(async (resolve) => {\n      const task = scheduler.nextTask();\n      if (task) {\n        const taskRunner = new TaskRunner(configFile, additionalConfig, task, forkProcess);\n        try {\n          const result = await taskRunner.run();\n          if (result.exitCode && !result.failedCount) {\n            logger.error('Runner process exited unexpectedly with error code: ' + result.exitCode);\n          }\n          taskResults_.add(result);\n          task.done();\n          await createNextTaskRunner();\n          // If all tasks are finished\n          if (scheduler.numTasksOutstanding() === 0) {\n            resolve();\n          }\n          logger.info(scheduler.countActiveTasks() + ' instance(s) of WebDriver still running');\n        } catch (err) {\n          const errorCode = ErrorHandler.parseError(err);\n          logger.error('Error:', (err as any).stack || err.message || err);\n          await cleanUpAndExit(errorCode ? errorCode : RUNNERS_FAILED_EXIT_CODE);\n        }\n      } else {\n        resolve();\n      }\n    });\n  };\n\n  const maxConcurrentTasks = scheduler.maxConcurrentTasks();\n  for (let i = 0; i < maxConcurrentTasks; ++i) {\n    await createNextTaskRunner();\n  }\n  logger.info('Running ' + scheduler.countActiveTasks() + ' instances of WebDriver');\n\n  // By now all runners have completed.\n  // Save results if desired\n  if (config.resultJsonOutputFile) {\n    taskResults_.saveResults(config.resultJsonOutputFile);\n  }\n\n  taskResults_.reportSummary();\n  let exitCode = 0;\n  if (taskResults_.totalProcessFailures() > 0) {\n    exitCode = RUNNERS_FAILED_EXIT_CODE;\n  } else if (taskResults_.totalSpecFailures() > 0) {\n    exitCode = 1;\n  }\n  await cleanUpAndExit(exitCode);\n  // Start `const maxConcurrentTasks` workers for handling tasks in\n  // the beginning. As a worker finishes a task, it will pick up the next\n  // task from the scheduler's queue until all tasks are gone.\n\n};\n\nexport let init = initFn;\n"
  },
  {
    "path": "lib/locators.ts",
    "content": "import {By, ByHash, WebDriver, WebElement} from 'selenium-webdriver';\n\nlet clientSideScripts = require('./clientsidescripts');\n\n// Explicitly define webdriver.By.\n// We do this because we want to inherit the static methods of webdriver.By, as opposed to\n// inheriting from the webdriver.By class itself, which is actually analogous to ProtractorLocator.\nexport class WebdriverBy {\n  className: (className: string) => By = By.className;\n  css: (css: string) => By = By.css;\n  id: (id: string) => By = By.id;\n  linkText: (linkText: string) => By = By.linkText;\n  js: (js: string|Function, ...var_args: any[]) => (webdriver: WebDriver) => Promise<any> = By.js;\n  name: (name: string) => By = By.name;\n  partialLinkText: (partialText: string) => By = By.partialLinkText;\n  tagName: (tagName: string) => By = By.tagName;\n  xpath: (xpath: string) => By = By.xpath;\n}\nexport type WebDriverLocator = By | ByHash | Function;\n\n// Protractor locator strategy\nexport interface ProtractorLocator {\n  findElementsOverride:\n      (driver: WebDriver, using: WebElement, rootSelector: string) => Promise<WebElement[]>;\n  row?: (index: number) => Locator;\n  column?: (index: string) => Locator;\n  toString?: () => string;\n}\nexport type Locator = ProtractorLocator | WebDriverLocator;\n\nexport function isProtractorLocator(x: Locator): x is ProtractorLocator {\n  return x && (typeof(x as any).findElementsOverride === 'function');\n}\n\n/**\n * The Protractor Locators. These provide ways of finding elements in\n * Angular applications by binding, model, etc.\n *\n * @alias by\n * @extends {webdriver.By}\n */\nexport class ProtractorBy extends WebdriverBy {\n  [key: string]: any;\n\n  /**\n   * Add a locator to this instance of ProtractorBy. This locator can then be\n   * used with element(by.locatorName(args)).\n   *\n   * @view\n   * <button ng-click=\"doAddition()\">Go!</button>\n   *\n   * @example\n   * // Add the custom locator.\n   * by.addLocator('buttonTextSimple',\n   *     function(buttonText, opt_parentElement, opt_rootSelector) {\n   *   // This function will be serialized as a string and will execute in the\n   *   // browser. The first argument is the text for the button. The second\n   *   // argument is the parent element, if any.\n   *   var using = opt_parentElement || document,\n   *       buttons = using.querySelectorAll('button');\n   *\n   *   // Return an array of buttons with the text.\n   *   return Array.prototype.filter.call(buttons, function(button) {\n   *     return button.textContent === buttonText;\n   *   });\n   * });\n   *\n   * // Use the custom locator.\n   * await element(by.buttonTextSimple('Go!')).click();\n   *\n   * @alias by.addLocator(locatorName, functionOrScript)\n   * @param {string} name The name of the new locator.\n   * @param {Function|string} script A script to be run in the context of\n   *     the browser. This script will be passed an array of arguments\n   *     that contains any args passed into the locator followed by the\n   *     element scoping the search and the css selector for the root angular\n   *     element. It should return an array of elements.\n   */\n  addLocator(name: string, script: Function|string) {\n    this[name] = (...args: any[]): ProtractorLocator => {\n      const locatorArguments = args;\n      return {\n        findElementsOverride: async(driver: WebDriver, using: WebElement, rootSelector: string):\n            Promise<WebElement[]> => {\n              let findElementArguments: any[] = [script];\n              for (let i = 0; i < locatorArguments.length; i++) {\n                findElementArguments.push(locatorArguments[i]);\n              }\n              findElementArguments.push(using);\n              findElementArguments.push(rootSelector);\n\n              return await driver.findElements(By.js.apply(By, findElementArguments));\n            },\n        toString: (): string => {\n          return 'by.' + name + '(\"' + Array.prototype.join.call(locatorArguments, '\", \"') + '\")';\n        }\n      };\n    };\n  }\n\n  /**\n   * Find an element by text binding. Does a partial match, so any elements\n   * bound to variables containing the input string will be returned.\n   *\n   * Note: For AngularJS version 1.2, the interpolation brackets, (usually\n   * {{}}), are optionally allowed in the binding description string. For\n   * Angular version 1.3+, they are not allowed, and no elements will be found\n   * if they are used.\n   *\n   * @view\n   * <span>{{person.name}}</span>\n   * <span ng-bind=\"person.email\"></span>\n   *\n   * @example\n   * var span1 = element(by.binding('person.name'));\n   * expect(await span1.getText()).toBe('Foo');\n   *\n   * var span2 = element(by.binding('person.email'));\n   * expect(await span2.getText()).toBe('foo@bar.com');\n   *\n   * // You can also use a substring for a partial match\n   * var span1alt = element(by.binding('name'));\n   * expect(await span1alt.getText()).toBe('Foo');\n   *\n   * // This works for sites using Angular 1.2 but NOT 1.3\n   * var deprecatedSyntax = element(by.binding('{{person.name}}'));\n   *\n   * @param {string} bindingDescriptor\n   * @returns {ProtractorLocator} location strategy\n   */\n  binding(bindingDescriptor: string): ProtractorLocator {\n    return {\n      findElementsOverride: async(driver: WebDriver, using: WebElement, rootSelector: string):\n          Promise<WebElement[]> => {\n            return await driver.findElements(By.js(\n                clientSideScripts.findBindings, bindingDescriptor, false, using, rootSelector));\n          },\n      toString: (): string => {\n        return 'by.binding(\"' + bindingDescriptor + '\")';\n      }\n    };\n  }\n\n  /**\n   * Find an element by exact binding.\n   *\n   * @view\n   * <span>{{ person.name }}</span>\n   * <span ng-bind=\"person-email\"></span>\n   * <span>{{person_phone|uppercase}}</span>\n   *\n   * @example\n   * expect(await element(by.exactBinding('person.name')).isPresent()).toBe(true);\n   * expect(await element(by.exactBinding('person-email')).isPresent()).toBe(true);\n   * expect(await element(by.exactBinding('person')).isPresent()).toBe(false);\n   * expect(await element(by.exactBinding('person_phone')).isPresent()).toBe(true);\n   * expect(await element(by.exactBinding('person_phone|uppercase')).isPresent()).toBe(true);\n   * expect(await element(by.exactBinding('phone')).isPresent()).toBe(false);\n   *\n   * @param {string} bindingDescriptor\n   * @returns {ProtractorLocator} location strategy\n   */\n  exactBinding(bindingDescriptor: string): ProtractorLocator {\n    return {\n      findElementsOverride: async(driver: WebDriver, using: WebElement, rootSelector: string):\n          Promise<WebElement[]> => {\n            return await driver.findElements(By.js(\n                clientSideScripts.findBindings, bindingDescriptor, true, using, rootSelector));\n          },\n      toString: (): string => {\n        return 'by.exactBinding(\"' + bindingDescriptor + '\")';\n      }\n    };\n  }\n\n  /**\n   * Find an element by ng-model expression.\n   *\n   * @alias by.model(modelName)\n   * @view\n   * <input type=\"text\" ng-model=\"person.name\">\n   *\n   * @example\n   * var input = element(by.model('person.name'));\n   * await input.sendKeys('123');\n   * expect(await input.getAttribute('value')).toBe('Foo123');\n   *\n   * @param {string} model ng-model expression.\n   * @returns {ProtractorLocator} location strategy\n   */\n  model(model: string): ProtractorLocator {\n    return {\n      findElementsOverride: async(driver: WebDriver, using: WebElement, rootSelector: string):\n          Promise<WebElement[]> => {\n            return await driver.findElements(\n                By.js(clientSideScripts.findByModel, model, using, rootSelector));\n          },\n      toString: (): string => {\n        return 'by.model(\"' + model + '\")';\n      }\n    };\n  }\n\n  /**\n   * Find a button by text.\n   *\n   * @view\n   * <button>Save</button>\n   *\n   * @example\n   * element(by.buttonText('Save'));\n   *\n   * @param {string} searchText\n   * @returns {ProtractorLocator} location strategy\n   */\n  buttonText(searchText: string): ProtractorLocator {\n    return {\n      findElementsOverride: async(driver: WebDriver, using: WebElement, rootSelector: string):\n          Promise<WebElement[]> => {\n            return driver.findElements(\n                By.js(clientSideScripts.findByButtonText, searchText, using, rootSelector));\n          },\n      toString: (): string => {\n        return 'by.buttonText(\"' + searchText + '\")';\n      }\n    };\n  }\n\n  /**\n   * Find a button by partial text.\n   *\n   * @view\n   * <button>Save my file</button>\n   *\n   * @example\n   * element(by.partialButtonText('Save'));\n   *\n   * @param {string} searchText\n   * @returns {ProtractorLocator} location strategy\n   */\n  partialButtonText(searchText: string): ProtractorLocator {\n    return {\n      findElementsOverride: async(driver: WebDriver, using: WebElement, rootSelector: string):\n          Promise<WebElement[]> => {\n            return driver.findElements(\n                By.js(clientSideScripts.findByPartialButtonText, searchText, using, rootSelector));\n          },\n      toString: (): string => {\n        return 'by.partialButtonText(\"' + searchText + '\")';\n      }\n    };\n  }\n\n  // Generate either by.repeater or by.exactRepeater\n  private byRepeaterInner(exact: boolean, repeatDescriptor: string): ProtractorLocator {\n    let name = 'by.' + (exact ? 'exactR' : 'r') + 'epeater';\n    return {\n      findElementsOverride: async(\n          driver: WebDriver, using: WebElement, rootSelector: string): Promise<WebElement[]> => {\n        return driver.findElements(By.js(\n            clientSideScripts.findAllRepeaterRows, repeatDescriptor, exact, using, rootSelector));\n      },\n      toString: (): string => {\n        return name + '(\"' + repeatDescriptor + '\")';\n      },\n      row: (index: number): ProtractorLocator => {\n        return {\n          findElementsOverride: async(driver: WebDriver, using: WebElement, rootSelector: string):\n              Promise<WebElement[]> => {\n                return await driver.findElements(By.js(\n                    clientSideScripts.findRepeaterRows, repeatDescriptor, exact, index, using,\n                    rootSelector));\n              },\n          toString: (): string => {\n            return name + '(' + repeatDescriptor + '\").row(\"' + index + '\")\"';\n          },\n          column: (binding: string): ProtractorLocator => {\n            return {\n              findElementsOverride:\n                  async(driver: WebDriver, using: WebElement, rootSelector: string):\n                      Promise<WebElement[]> => {\n                        return driver.findElements(By.js(\n                            clientSideScripts.findRepeaterElement, repeatDescriptor, exact, index,\n                            binding, using, rootSelector));\n                      },\n              toString: (): string => {\n                return name + '(\"' + repeatDescriptor + '\").row(\"' + index + '\").column(\"' +\n                    binding + '\")';\n              }\n            };\n          }\n        };\n      },\n      column: (binding: string): ProtractorLocator => {\n        return {\n          findElementsOverride: async(driver: WebDriver, using: WebElement, rootSelector: string):\n              Promise<WebElement[]> => {\n                return driver.findElements(By.js(\n                    clientSideScripts.findRepeaterColumn, repeatDescriptor, exact, binding, using,\n                    rootSelector));\n              },\n          toString: (): string => {\n            return name + '(\"' + repeatDescriptor + '\").column(\"' + binding + '\")';\n          },\n          row: (index: number): ProtractorLocator => {\n            return {\n              findElementsOverride:\n                  async(driver: WebDriver, using: WebElement, rootSelector: string):\n                      Promise<WebElement[]> => {\n                        return driver.findElements(By.js(\n                            clientSideScripts.findRepeaterElement, repeatDescriptor, exact, index,\n                            binding, using, rootSelector));\n                      },\n              toString: (): string => {\n                return name + '(\"' + repeatDescriptor + '\").column(\"' + binding + '\").row(\"' +\n                    index + '\")';\n              }\n            };\n          }\n        };\n      }\n    };\n  }\n\n  /**\n   * Find elements inside an ng-repeat.\n   *\n   * @view\n   * <div ng-repeat=\"cat in pets\">\n   *   <span>{{cat.name}}</span>\n   *   <span>{{cat.age}}</span>\n   * </div>\n   *\n   * <div class=\"book-img\" ng-repeat-start=\"book in library\">\n   *   <span>{{$index}}</span>\n   * </div>\n   * <div class=\"book-info\" ng-repeat-end>\n   *   <h4>{{book.name}}</h4>\n   *   <p>{{book.blurb}}</p>\n   * </div>\n   *\n   * @example\n   * // Returns the DIV for the second cat.\n   * let secondCat = element(by.repeater('cat in pets').row(1));\n   *\n   * // Returns the SPAN for the first cat's name.\n   * let firstCatName = element(by.repeater('cat in pets').\n   *     row(0).column('cat.name'));\n   *\n   * // Returns a promise that resolves to an array of WebElements from a column\n   * let ages = element.all(by.repeater('cat in pets').column('cat.age'));\n   *\n   * // Returns a promise that resolves to an array of WebElements containing\n   * // all top level elements repeated by the repeater. For 2 pets rows\n   * // resolves to an array of 2 elements.\n   * let rows = element.all(by.repeater('cat in pets'));\n   *\n   * // Returns a promise that resolves to an array of WebElements containing\n   * // all the elements with a binding to the book's name.\n   * let divs = element.all(by.repeater('book in library').column('book.name'));\n   *\n   * // Returns a promise that resolves to an array of WebElements containing\n   * // the DIVs for the second book.\n   * let bookInfo = element.all(by.repeater('book in library').row(1));\n   *\n   * // Returns the H4 for the first book's name.\n   * let firstBookName = element(by.repeater('book in library').\n   *     row(0).column('book.name'));\n   *\n   * // Returns a promise that resolves to an array of WebElements containing\n   * // all top level elements repeated by the repeater. For 2 books divs\n   * // resolves to an array of 4 elements.\n   * let divs = element.all(by.repeater('book in library'));\n   *\n   * @param {string} repeatDescriptor\n   * @returns {ProtractorLocator} location strategy\n   */\n  repeater(repeatDescriptor: string): ProtractorLocator {\n    return this.byRepeaterInner(false, repeatDescriptor);\n  }\n\n  /**\n   * Find an element by exact repeater.\n   *\n   * @view\n   * <li ng-repeat=\"person in peopleWithRedHair\"></li>\n   * <li ng-repeat=\"car in cars | orderBy:year\"></li>\n   *\n   * @example\n   * expect(await element(by.exactRepeater('person in peopleWithRedHair')).isPresent()).toBe(true);\n   * expect(await element(by.exactRepeater('person in people')).isPresent()).toBe(false);\n   * expect(await element(by.exactRepeater('car in cars')).isPresent()).toBe(true);\n   *\n   * @param {string} repeatDescriptor\n   * @returns {ProtractorLocator} location strategy\n   */\n  exactRepeater(repeatDescriptor: string): ProtractorLocator {\n    return this.byRepeaterInner(true, repeatDescriptor);\n  }\n\n  /**\n   * Find elements by CSS which contain a certain string.\n   *\n   * @view\n   * <ul>\n   *   <li class=\"pet\">Dog</li>\n   *   <li class=\"pet\">Cat</li>\n   * </ul>\n   *\n   * @example\n   * // Returns the li for the dog, but not cat.\n   * var dog = element(by.cssContainingText('.pet', 'Dog'));\n   *\n   * @param {string} cssSelector css selector\n   * @param {string|RegExp} searchText text search\n   * @returns {ProtractorLocator} location strategy\n   */\n  cssContainingText(cssSelector: string, searchText: string|RegExp): ProtractorLocator {\n    searchText = (searchText instanceof RegExp) ? '__REGEXP__' + searchText.toString() : searchText;\n    return {\n      findElementsOverride: async(driver: WebDriver, using: WebElement, rootSelector: string):\n          Promise<WebElement[]> => {\n            return await driver.findElements(By.js(\n                clientSideScripts.findByCssContainingText, cssSelector, searchText, using,\n                rootSelector));\n          },\n      toString: (): string => {\n        return 'by.cssContainingText(\"' + cssSelector + '\", \"' + searchText + '\")';\n      }\n    };\n  }\n\n  /**\n   * Find an element by ng-options expression.\n   *\n   * @alias by.options(optionsDescriptor)\n   * @view\n   * <select ng-model=\"color\" ng-options=\"c for c in colors\">\n   *   <option value=\"0\" selected=\"selected\">red</option>\n   *   <option value=\"1\">green</option>\n   * </select>\n   *\n   * @example\n   * var allOptions = element.all(by.options('c for c in colors'));\n   * expect(await allOptions.count()).toEqual(2);\n   * var firstOption = allOptions.first();\n   * expect(await firstOption.getText()).toEqual('red');\n   *\n   * @param {string} optionsDescriptor ng-options expression.\n   * @returns {ProtractorLocator} location strategy\n   */\n  options(optionsDescriptor: string): ProtractorLocator {\n    return {\n      findElementsOverride: async(driver: WebDriver, using: WebElement, rootSelector: string):\n          Promise<WebElement[]> => {\n            return await driver.findElements(\n                By.js(clientSideScripts.findByOptions, optionsDescriptor, using, rootSelector));\n          },\n      toString: (): string => {\n        return 'by.option(\"' + optionsDescriptor + '\")';\n      }\n    };\n  }\n\n  /**\n   * Find an element by css selector within the Shadow DOM.\n   *\n   * @alias by.deepCss(selector)\n   * @view\n   * <div>\n   *   <span id=\"outerspan\">\n   *   <\"shadow tree\">\n   *     <span id=\"span1\"></span>\n   *     <\"shadow tree\">\n   *       <span id=\"span2\"></span>\n   *     </>\n   *   </>\n   * </div>\n   * @example\n   * var spans = element.all(by.deepCss('span'));\n   * expect(await spans.count()).toEqual(3);\n   *\n   * @param {string} selector a css selector within the Shadow DOM.\n   * @returns {Locator} location strategy\n   */\n  deepCss(selector: string): Locator {\n    // TODO(julie): syntax will change from /deep/ to >>> at some point.\n    // When that is supported, switch it here.\n    return By.css('* /deep/ ' + selector);\n  }\n}\n"
  },
  {
    "path": "lib/logger.ts",
    "content": "import * as fs from 'fs';\nimport * as path from 'path';\nimport {Config} from './config';\n\n// Will use chalk if chalk is available to add color to console logging\nlet chalk: any;\nlet printRed: Function;\nlet printYellow: Function;\nlet printGray: Function;\n\ntry {\n  chalk = require('chalk');\n  printRed = chalk.red;\n  printYellow = chalk.yellow;\n  printGray = chalk.gray;\n} catch (e) {\n  printRed = printYellow = printGray = (msg: any) => {\n    return msg;\n  };\n}\n\nexport enum LogLevel {\n  ERROR,\n  WARN,\n  INFO,\n  DEBUG\n}\n\nexport enum WriteTo {\n  CONSOLE,\n  FILE,\n  BOTH,\n  NONE\n}\n\nlet logFile = 'protractor.log';  // the default log file name\n\n/**\n * Logger class adds timestamp output, log levels, and identifiers to help\n * when debugging. Also could write to console, file, both, or none.\n */\nexport class Logger {\n  static logLevel: LogLevel = LogLevel.INFO;\n  static showTimestamp: boolean = true;\n  static showId: boolean = true;\n  static writeTo: WriteTo = WriteTo.CONSOLE;\n  static fd: any;\n  static firstWrite: boolean = false;\n\n  /**\n   * Set up the logging configuration from the protractor configuration file.\n   * @param config The protractor configuration\n   */\n  static set(config: Config): void {\n    if (config.troubleshoot) {\n      Logger.logLevel = LogLevel.DEBUG;\n    } else if (config.logLevel) {\n      Logger.logLevel = LogLevel[config.logLevel];\n    }\n  }\n\n  /**\n   * Set up the write location. If writing to a file, get the file descriptor.\n   * @param writeTo The enum for where to write the logs.\n   * @param opt_logFile An optional parameter to override the log file location.\n   */\n  static setWrite(writeTo: WriteTo, opt_logFile?: string): void {\n    if (opt_logFile) {\n      logFile = opt_logFile;\n    }\n    Logger.writeTo = writeTo;\n    if (Logger.writeTo == WriteTo.FILE || Logger.writeTo == WriteTo.BOTH) {\n      Logger.fd = fs.openSync(path.resolve(logFile), 'a');\n      Logger.firstWrite = false;\n    }\n  }\n\n  /**\n   * Creates a logger instance with an ID for the logger.\n   * @constructor\n   */\n  constructor(private id: string) {}\n\n  /**\n   * Log INFO\n   * @param ...msgs multiple arguments to be logged.\n   */\n  info(...msgs: any[]): void {\n    this.log_(LogLevel.INFO, msgs);\n  }\n\n  /**\n   * Log DEBUG\n   * @param ...msgs multiple arguments to be logged.\n   */\n  debug(...msgs: any[]): void {\n    this.log_(LogLevel.DEBUG, msgs);\n  }\n\n  /**\n   * Log WARN\n   * @param ...msgs multiple arguments to be logged.\n   */\n  warn(...msgs: any[]): void {\n    this.log_(LogLevel.WARN, msgs);\n  }\n\n  /**\n   * Log ERROR\n   * @param ...msgs multiple arguments to be logged.\n   */\n  error(...msgs: any[]): void {\n    this.log_(LogLevel.ERROR, msgs);\n  }\n\n  /**\n   * For the log level set, check to see if the messages should be logged.\n   * @param logLevel The log level of the message.\n   * @param msgs The messages to be logged\n   */\n  log_(logLevel: LogLevel, msgs: any[]): void {\n    switch (Logger.logLevel) {\n      case LogLevel.ERROR:\n        if (logLevel <= LogLevel.ERROR) {\n          this.print_(logLevel, msgs);\n        }\n        break;\n      case LogLevel.WARN:\n        if (logLevel <= LogLevel.WARN) {\n          this.print_(logLevel, msgs);\n        }\n        break;\n      case LogLevel.INFO:\n        if (logLevel <= LogLevel.INFO) {\n          this.print_(logLevel, msgs);\n        }\n        break;\n      case LogLevel.DEBUG:\n        if (logLevel <= LogLevel.DEBUG) {\n          this.print_(logLevel, msgs);\n        }\n        break;\n      default:\n        throw new Error('Invalid log level');\n    }\n  }\n\n  /**\n   * Format with timestamp, log level, identifier, and message and log to\n   * specified medium (console, file, both, none).\n   * @param logLevel The log level of the message.\n   * @param msgs The messages to be logged.\n   */\n  print_(logLevel: LogLevel, msgs: any[]): void {\n    let consoleLog: string = '';\n    let fileLog: string = '';\n\n    if (Logger.showTimestamp) {\n      consoleLog += Logger.timestamp_(WriteTo.CONSOLE);\n      fileLog += Logger.timestamp_(WriteTo.FILE);\n    }\n    consoleLog += Logger.level_(logLevel, this.id, WriteTo.CONSOLE);\n    fileLog += Logger.level_(logLevel, this.id, WriteTo.FILE);\n    if (Logger.showId) {\n      consoleLog += Logger.id_(logLevel, this.id, WriteTo.CONSOLE);\n      fileLog += Logger.id_(logLevel, this.id, WriteTo.FILE);\n    }\n    consoleLog += ' -';\n    fileLog += ' - ';\n\n    switch (Logger.writeTo) {\n      case WriteTo.CONSOLE:\n        msgs.unshift(consoleLog);\n        console.log.apply(console, msgs);\n        break;\n      case WriteTo.FILE:\n        // for the first line written to the file, add a space\n        if (!Logger.firstWrite) {\n          fs.writeSync(Logger.fd, '\\n');\n          Logger.firstWrite = true;\n        }\n        fileLog += ' ' + Logger.msgToFile_(msgs);\n        fs.writeSync(Logger.fd, fileLog + '\\n');\n        break;\n      case WriteTo.BOTH:\n        // for the first line written to the file, add a space\n        if (!Logger.firstWrite) {\n          fs.writeSync(Logger.fd, '\\n');\n          Logger.firstWrite = true;\n        }\n        fileLog += ' ' + Logger.msgToFile_(msgs);\n        fs.writeSync(Logger.fd, fileLog + '\\n');\n        msgs.unshift(consoleLog);\n        console.log.apply(console, msgs);\n        break;\n      case WriteTo.NONE:\n        break;\n    }\n  }\n\n  /**\n   * Get a timestamp formatted with [hh:mm:ss]\n   * @param writeTo The enum for where to write the logs.\n   * @return The string of the formatted timestamp\n   */\n  static timestamp_(writeTo: WriteTo): string {\n    let d = new Date();\n    let ts = '[';\n    let hours = d.getHours() < 10 ? '0' + d.getHours() : d.getHours();\n    let minutes = d.getMinutes() < 10 ? '0' + d.getMinutes() : d.getMinutes();\n    let seconds = d.getSeconds() < 10 ? '0' + d.getSeconds() : d.getSeconds();\n    if (writeTo == WriteTo.CONSOLE) {\n      ts += printGray(hours + ':' + minutes + ':' + seconds) + ']';\n    } else {\n      ts += hours + ':' + minutes + ':' + seconds + ']';\n    }\n    ts += ' ';\n    return ts;\n  }\n\n  /**\n   * Get the identifier of the logger as '/<id>'\n   * @param logLevel The log level of the message.\n   * @param writeTo The enum for where to write the logs.\n   * @return The string of the formatted id\n   */\n  static id_(logLevel: LogLevel, id: string, writeTo: WriteTo): string {\n    if (writeTo === WriteTo.FILE) {\n      return '/' + id;\n    } else if (logLevel === LogLevel.ERROR) {\n      return printRed('/' + id);\n    } else if (logLevel === LogLevel.WARN) {\n      return printYellow('/' + id);\n    } else {\n      return '/' + id;\n    }\n  }\n\n  /**\n   * Get the log level formatted with the first letter. For info, it is I.\n   * @param logLevel The log level of the message.\n   * @param writeTo The enum for where to write the logs.\n   * @return The string of the formatted log level\n   */\n  static level_(logLevel: LogLevel, id: string, writeTo: WriteTo): string {\n    let level = LogLevel[logLevel].toString();\n    if (writeTo === WriteTo.FILE) {\n      return level[0];\n    } else if (logLevel === LogLevel.ERROR) {\n      return printRed(level[0]);\n    } else if (logLevel === LogLevel.WARN) {\n      return printYellow(level[0]);\n    } else {\n      return level[0];\n    }\n  }\n\n  /**\n   * Convert the list of messages to a single string message.\n   * @param msgs The list of messages.\n   * @return The string of the formatted messages\n   */\n  static msgToFile_(msgs: any[]): string {\n    let log = '';\n    for (let pos = 0; pos < msgs.length; pos++) {\n      let msg = msgs[pos];\n      let ret: any;\n      if (typeof msg === 'object') {\n        ret = JSON.stringify(msg);\n      } else {\n        ret = msg;\n      }\n      if (pos !== msgs.length - 1) {\n        ret += ' ';\n      }\n      log += ret;\n    }\n    return log;\n  }\n}\n"
  },
  {
    "path": "lib/plugins.ts",
    "content": "import {ProtractorBrowser} from './browser';\nimport {Config} from './config';\nimport {ConfigParser} from './configParser';\nimport {Logger} from './logger';\n\nlet logger = new Logger('plugins');\n\nexport interface PluginConfig {\n  path?: string;\n  package?: string;\n  inline?: ProtractorPlugin;\n  name?: string;\n  [key: string]: any;\n}\n\nexport interface ProtractorPlugin {\n  /**\n   * Sets up plugins before tests are run. This is called after the WebDriver\n   * session has been started, but before the test framework has been set up.\n   *\n   * @this {Object} bound to module.exports.\n   *\n   * @throws {*} If this function throws an error, a failed assertion is added to\n   *     the test results.\n   *\n   * @return {Promise=} Can return a promise, in which case protractor will wait\n   *     for the promise to resolve before continuing.  If the promise is\n   *     rejected, a failed assertion is added to the test results.\n   */\n  setup?(): void|Promise<void>;\n\n  /**\n   * This is called before the test have been run but after the test framework has\n   * been set up.  Analogous to a config file's `onPrepare`.\n   *\n   * Very similar to using `setup`, but allows you to access framework-specific\n   * variables/functions (e.g. `jasmine.getEnv().addReporter()`).\n   *\n   * @this {Object} bound to module.exports.\n   *\n   * @throws {*} If this function throws an error, a failed assertion is added to\n   *     the test results.\n   *\n   * @return {Promise=} Can return a promise, in which case protractor will wait\n   *     for the promise to resolve before continuing.  If the promise is\n   *     rejected, a failed assertion is added to the test results.\n   */\n  onPrepare?(): void|Promise<void>;\n\n  /**\n   * This is called after the tests have been run, but before the WebDriver\n   * session has been terminated.\n   *\n   * @this {Object} bound to module.exports.\n   *\n   * @throws {*} If this function throws an error, a failed assertion is added to\n   *     the test results.\n   *\n   * @return {Promise=} Can return a promise, in which case protractor will wait\n   *     for the promise to resolve before continuing.  If the promise is\n   *     rejected, a failed assertion is added to the test results.\n   */\n  teardown?(): void|Promise<void>;\n\n  /**\n   * Called after the test results have been finalized and any jobs have been\n   * updated (if applicable).\n   *\n   * @this {Object} bound to module.exports.\n   *\n   * @throws {*} If this function throws an error, it is outputted to the console.\n   *     It is too late to add a failed assertion to the test results.\n   *\n   * @return {Promise=} Can return a promise, in which case protractor will wait\n   *     for the promise to resolve before continuing.  If the promise is\n   *     rejected, an error is logged to the console.\n   */\n  postResults?(): void|Promise<void>;\n\n  /**\n   * Called after each test block (in Jasmine, this means an `it` block)\n   * completes.\n   *\n   * @param {boolean} passed True if the test passed.\n   * @param {Object} testInfo information about the test which just ran.\n   *\n   * @this {Object} bound to module.exports.\n   *\n   * @throws {*} If this function throws an error, a failed assertion is added to\n   *     the test results.\n   *\n   * @return {Promise=} Can return a promise, in which case protractor will wait\n   *     for the promise to resolve before outputting test results.  Protractor\n   *     will *not* wait before executing the next test; however, if the promise\n   *     is rejected, a failed assertion is added to the test results.\n   */\n  postTest?(passed: boolean, testInfo: any): void|Promise<void>;\n\n  /**\n   * This is called inside browser.get() directly after the page loads, and before\n   * angular bootstraps.\n   *\n   * @param {ProtractorBrowser} browser The browser instance which is loading a page.\n   *\n   * @this {Object} bound to module.exports.\n   *\n   * @throws {*} If this function throws an error, a failed assertion is added to\n   *     the test results.\n   *\n   * @return {Promise=} Can return a promise, in which case\n   *     protractor will wait for the promise to resolve before continuing.  If\n   *     the promise is rejected, a failed assertion is added to the test results.\n   */\n  onPageLoad?(browser: ProtractorBrowser): void|Promise<void>;\n\n  /**\n   * This is called inside browser.get() directly after angular is done\n   * bootstrapping/synchronizing.  If `await browser.waitForAngularEnabled()`\n   * is `false`, this will not be called.\n   *\n   * @param {ProtractorBrowser} browser The browser instance which is loading a page.\n   *\n   * @this {Object} bound to module.exports.\n   *\n   * @throws {*} If this function throws an error, a failed assertion is added to\n   *     the test results.\n   *\n   * @return {Promise=} Can return a promise, in which case\n   *     protractor will wait for the promise to resolve before continuing.  If\n   *     the promise is rejected, a failed assertion is added to the test results.\n   */\n  onPageStable?(browser: ProtractorBrowser): void|Promise<void>;\n\n  /**\n   * Between every webdriver action, Protractor calls browser.waitForAngular() to\n   * make sure that Angular has no outstanding $http or $timeout calls.\n   * You can use waitForPromise() to have Protractor additionally wait for your\n   * custom promise to be resolved inside of browser.waitForAngular().\n   *\n   * @param {ProtractorBrowser} browser The browser instance which needs invoked `waitForAngular`.\n   *\n   * @this {Object} bound to module.exports.\n   *\n   * @throws {*} If this function throws an error, a failed assertion is added to\n   *     the test results.\n   *\n   * @return {Promise=} Can return a promise, in which case\n   *     protractor will wait for the promise to resolve before continuing. If the\n   *     promise is rejected, a failed assertion is added to the test results, and\n   *     protractor will continue onto the next command. If nothing is returned or\n   *     something other than a promise is returned, protractor will continue\n   *     onto the next command.\n   */\n  waitForPromise?(browser: ProtractorBrowser): Promise<void>;\n\n  /**\n   * Between every webdriver action, Protractor calls browser.waitForAngular() to\n   * make sure that Angular has no outstanding $http or $timeout calls.\n   * You can use waitForCondition() to have Protractor additionally wait for your\n   * custom condition to be truthy.  If specified, this function will be called\n   * repeatedly until truthy.\n   *\n   * @param {ProtractorBrowser} browser The browser instance which needs invoked `waitForAngular`.\n   *\n   * @this {Object} bound to module.exports.\n   *\n   * @throws {*} If this function throws an error, a failed assertion is added to\n   *     the test results.\n   *\n   * @return {Promise<boolean>|boolean} If truthy, Protractor\n   *     will continue onto the next command. If falsy, webdriver will\n   *     continuously re-run this function until it is truthy.  If a rejected promise\n   *     is returned, a failed assertion is added to the test results, and Protractor\n   *     will continue onto the next command.\n   */\n  waitForCondition?(browser: ProtractorBrowser): Promise<boolean>|boolean;\n\n  /**\n   * Used to turn off default checks for angular stability\n   *\n   * Normally Protractor waits for all $timeout and $http calls to be processed\n   * before executing the next command.  This can be disabled using\n   * browser.ignoreSynchronization, but that will also disable any\n   * <Plugin>.waitForPromise or <Plugin>.waitForCondition checks.  If you want\n   * to disable synchronization with angular, but leave intact any custom plugin\n   * synchronization, this is the option for you.\n   *\n   * This is used by plugin authors who want to replace Protractor's\n   * synchronization code with their own.\n   *\n   * @type {boolean}\n   */\n  skipAngularStability?: boolean;\n\n  /**\n   * The name of the plugin.  Used when reporting results.\n   *\n   * If you do not specify this property, it will be filled in with something\n   * reasonable (e.g. the plugin's path) by Protractor at runtime.\n   *\n   * @type {string}\n   */\n  name?: string;\n\n  /**\n   * The plugin's configuration object.\n   *\n   * Note: this property is added by Protractor at runtime.  Any pre-existing\n   * value will be overwritten.\n   *\n   * Note: that this is not the entire Protractor config object, just the entry\n   * in the `plugins` array for this plugin.\n   *\n   * @type {Object}\n   */\n  config?: PluginConfig;\n\n  /**\n   * Adds a failed assertion to the test's results.\n   *\n   * Note: this property is added by Protractor at runtime.  Any pre-existing\n   * value will be overwritten.\n   *\n   * @param {string} message The error message for the failed assertion\n   * @param {specName: string, stackTrace: string} options Some optional extra\n   *     information about the assertion:\n   *       - specName The name of the spec which this assertion belongs to.\n   *            Defaults to `PLUGIN_NAME + ' Plugin Tests'`.\n   *       - stackTrace The stack trace for the failure.  Defaults to undefined.\n   *     Defaults to `{}`.\n   *\n   * @throws {Error} Throws an error if called after results have been reported\n   */\n  addFailure?(message?: string, info?: {specName?: string, stackTrace?: string}): void;\n\n  /**\n   * Adds a passed assertion to the test's results.\n   *\n   * Note: this property is added by Protractor at runtime.  Any pre-existing\n   * value will be overwritten.\n   *\n   * @param {specName: string} options Extra information about the assertion:\n   *       - specName The name of the spec which this assertion belongs to.\n   *            Defaults to `PLUGIN_NAME + ' Plugin Tests'`.\n   *     Defaults to `{}`.\n   *\n   * @throws {Error} Throws an error if called after results have been reported\n   */\n  addSuccess?(info?: {specName?: string}): void;\n\n  /**\n   * Warns the user that something is problematic.\n   *\n   * Note: this property is added by Protractor at runtime.  Any pre-existing\n   * value will be overwritten.\n   *\n   * @param {string} message The message to warn the user about\n   * @param {specName: string} options Extra information about the assertion:\n   *       - specName The name of the spec which this assertion belongs to.\n   *            Defaults to `PLUGIN_NAME + ' Plugin Tests'`.\n   *     Defaults to `{}`.\n   */\n  addWarning?(message?: string, info?: {specName?: string}): void;\n}\n\n/**\n * The plugin API for Protractor.  Note that this API is unstable. See\n * plugins/README.md for more information.\n *\n * @constructor\n * @param {Object} config parsed from the config file\n */\nexport class Plugins {\n  pluginObjs: ProtractorPlugin[];\n  assertions: {[key: string]: AssertionResult[]};\n  resultsReported: boolean;\n\n  constructor(config: Config) {\n    this.pluginObjs = [];\n    this.assertions = {};\n    this.resultsReported = false;\n    if (config.plugins) {\n      config.plugins.forEach((pluginConf, i) => {\n        let path: string;\n        if (pluginConf.path) {\n          path = ConfigParser.resolveFilePatterns(pluginConf.path, true, config.configDir)[0];\n          if (!path) {\n            throw new Error('Invalid path to plugin: ' + pluginConf.path);\n          }\n        } else {\n          path = pluginConf.package;\n        }\n\n        let pluginObj: ProtractorPlugin;\n        if (path) {\n          pluginObj = require(path) as ProtractorPlugin;\n        } else if (pluginConf.inline) {\n          pluginObj = pluginConf.inline;\n        } else {\n          throw new Error(\n              'Plugin configuration did not contain a valid path or ' +\n              'inline definition.');\n        }\n\n        this.annotatePluginObj(pluginObj, pluginConf, i);\n\n        logger.debug('Plugin \"' + pluginObj.name + '\" loaded.');\n        this.pluginObjs.push(pluginObj);\n      });\n    }\n  }\n\n  /**\n   * Adds properties to a plugin's object\n   *\n   * @see docs/plugins.md#provided-properties-and-functions\n   */\n  private annotatePluginObj(obj: ProtractorPlugin, conf: PluginConfig, i: number): void {\n    let addAssertion =\n        (info: {specName?: string, stackTrace?: string}, passed: boolean, message?: string) => {\n          if (this.resultsReported) {\n            throw new Error(\n                'Cannot add new tests results, since they were already ' +\n                'reported.');\n          }\n          info = info || {};\n          const specName = info.specName || (obj.name + ' Plugin Tests');\n          const assertion: AssertionResult = {passed: passed};\n          if (!passed) {\n            assertion.errorMsg = message;\n            if (info.stackTrace) {\n              assertion.stackTrace = info.stackTrace;\n            }\n          }\n          this.assertions[specName] = this.assertions[specName] || [];\n          this.assertions[specName].push(assertion);\n        };\n\n    obj.name = obj.name || conf.name || conf.path || conf.package || ('Plugin #' + i);\n    obj.config = conf;\n    obj.addFailure = (message?, info?) => {\n      addAssertion(info, false, message);\n    };\n    obj.addSuccess = (options?) => {\n      addAssertion(options, true);\n    };\n    obj.addWarning = (message?, options?) => {\n      options = options || {};\n      logger.warn(\n          'Warning ' +\n          (options.specName ? 'in ' + options.specName : 'from \"' + obj.name + '\" plugin') + ': ' +\n          message);\n    };\n  }\n\n  private printPluginResults(specResults: SpecResult[]) {\n    const green = '\\x1b[32m';\n    const red = '\\x1b[31m';\n    const normalColor = '\\x1b[39m';\n\n    const printResult = (message: string, pass: boolean) => {\n      logger.info(pass ? green : red, '\\t', pass ? 'Pass: ' : 'Fail: ', message, normalColor);\n    };\n\n    for (const specResult of specResults) {\n      const passed = specResult.assertions.map(x => x.passed).reduce((x, y) => (x && y), true);\n      printResult(specResult.description, passed);\n      if (!passed) {\n        for (const assertion of specResult.assertions) {\n          if (!assertion.passed) {\n            logger.error('\\t\\t' + assertion.errorMsg);\n            if (assertion.stackTrace) {\n              logger.error('\\t\\t' + assertion.stackTrace.replace(/\\n/g, '\\n\\t\\t'));\n            }\n          }\n        }\n      }\n    }\n  }\n\n  /**\n   * Gets the tests results generated by any plugins\n   *\n   * @see lib/frameworks/README.md#requirements for a complete description of what\n   *     the results object must look like\n   *\n   * @return {Object} The results object\n   */\n  getResults() {\n    const results = {failedCount: 0, specResults: [] as SpecResult[]};\n    for (const specName in this.assertions) {\n      results.specResults.push({description: specName, assertions: this.assertions[specName]});\n      results.failedCount +=\n          this.assertions[specName].filter(assertion => !assertion.passed).length;\n    }\n    this.printPluginResults(results.specResults);\n    this.resultsReported = true;\n    return results;\n  }\n\n  /**\n   * Returns true if any loaded plugin has skipAngularStability enabled.\n   *\n   * @return {boolean}\n   */\n  skipAngularStability() {\n    const result = this.pluginObjs.some(pluginObj => pluginObj.skipAngularStability);\n    return result;\n  }\n\n  /**\n   * @see docs/plugins.md#writing-plugins for information on these functions\n   */\n  setup = this.pluginFunFactory('setup');\n  onPrepare = this.pluginFunFactory('onPrepare');\n  teardown = this.pluginFunFactory('teardown');\n  postResults = this.pluginFunFactory('postResults');\n  postTest = this.pluginFunFactory('postTest');\n  onPageLoad = this.pluginFunFactory('onPageLoad');\n  onPageStable = this.pluginFunFactory('onPageStable');\n  waitForPromise = this.pluginFunFactory('waitForPromise');\n  waitForCondition = this.pluginFunFactory('waitForCondition', true);\n\n  /**\n   * Calls a function from a plugin safely.  If the plugin's function throws an\n   * exception or returns a rejected promise, that failure will be logged as a\n   * failed test result instead of crashing protractor.  If the tests results have\n   * already been reported, the failure will be logged to the console.\n   *\n   * @param {Object} pluginObj The plugin object containing the function to be run\n   * @param {string} funName The name of the function we want to run\n   * @param {*[]} args The arguments we want to invoke the function with\n   * @param {boolean} resultsReported If the results have already been reported\n   * @param {*} failReturnVal The value to return if the function fails\n   *\n   * @return {Promise} A promise which resolves to the\n   *     function's return value\n   */\n  private safeCallPluginFun(\n      pluginObj: ProtractorPlugin, funName: string, args: any[], failReturnVal: any): Promise<any> {\n    const resolver = async (done: (result: any) => void) => {\n      const logError = (e: any) => {\n        if (this.resultsReported) {\n          this.printPluginResults([{\n            description: pluginObj.name + ' Runtime',\n            assertions: [{\n              passed: false,\n              errorMsg: 'Failure during ' + funName + ': ' + (e.message || e),\n              stackTrace: e.stack\n            }]\n          }]);\n        } else {\n          pluginObj.addFailure(\n              'Failure during ' + funName + ': ' + e.message || e, {stackTrace: e.stack});\n        }\n        done(failReturnVal);\n      };\n      try {\n        const result = await(pluginObj as any)[funName].apply(pluginObj, args);\n        done(result);\n      } catch (e) {\n        logError(e);\n      }\n    };\n    return new Promise(resolver);\n  }\n\n  /**\n   * Generates the handler for a plugin function (e.g. the setup() function)\n   *\n   * @param {string} funName The name of the function to make a handler for\n   * @param {boolean=} failReturnVal The value that the function should return if the plugin crashes\n   *\n   * @return The handler\n   */\n  private pluginFunFactory(funName: string, failReturnVal?: boolean):\n      (...args: any[]) => Promise<any[]>;\n  private pluginFunFactory(funName: string, failReturnVal?: boolean):\n      (...args: any[]) => Promise<any[]>;\n  private pluginFunFactory(funName: string, failReturnVal?: boolean) {\n    return (...args: any[]) => {\n      const promises =\n          this.pluginObjs.filter(pluginObj => typeof(pluginObj as any)[funName] === 'function')\n              .map(pluginObj => this.safeCallPluginFun(pluginObj, funName, args, failReturnVal));\n      return Promise.all(promises);\n    };\n  }\n}\n\nexport interface SpecResult {\n  description: string;\n  assertions: AssertionResult[];\n}\n\nexport interface AssertionResult {\n  passed: boolean;\n  errorMsg?: string;\n  stackTrace?: string;\n}\n"
  },
  {
    "path": "lib/ptor.ts",
    "content": "import * as webdriver from 'selenium-webdriver';\nimport * as chrome from 'selenium-webdriver/chrome';\nimport * as firefox from 'selenium-webdriver/firefox';\nimport * as http from 'selenium-webdriver/http';\nimport * as command from 'selenium-webdriver/lib/command';\nimport * as remote from 'selenium-webdriver/remote';\n\nimport {ElementHelper, ProtractorBrowser} from './browser';\nimport {ElementArrayFinder, ElementFinder} from './element';\nimport {ProtractorExpectedConditions} from './expectedConditions';\nimport {ProtractorBy} from './locators';\n\nexport class Ptor {\n  // Variables tied to the global namespace.\n  browser: ProtractorBrowser;\n  $ = function(search: string): ElementFinder {\n    return null;\n  };\n  $$ = function(search: string): ElementArrayFinder {\n    return null;\n  };\n  element: ElementHelper;\n  By: ProtractorBy;\n  by: ProtractorBy;\n  wrapDriver:\n      (webdriver: webdriver.WebDriver, baseUrl?: string, rootElement?: string,\n       untrackOutstandingTimeouts?: boolean) => ProtractorBrowser;\n  ExpectedConditions: ProtractorExpectedConditions;\n\n  // Export protractor classes.\n  ProtractorBrowser = require('./browser').ProtractorBrowser;\n  ElementFinder = require('./element').ElementFinder;\n  ElementArrayFinder = require('./element').ElementArrayFinder;\n  ProtractorBy = require('./locators').ProtractorBy;\n  ProtractorExpectedConditions = require('./expectedConditions').ProtractorExpectedConditions;\n\n  // Export selenium webdriver.\n  Actions = webdriver.Actions;\n  Browser = webdriver.Browser;\n  Builder = webdriver.Builder;\n  Button = webdriver.Button;\n  Capabilities = webdriver.Capabilities;\n  Capability = webdriver.Capability;\n  EventEmitter = webdriver.EventEmitter;\n  FileDetector = webdriver.FileDetector;\n  Key = webdriver.Key;\n  Session = webdriver.Session;\n  WebDriver = webdriver.WebDriver;\n  WebElement = webdriver.WebElement;\n  WebElementPromise = webdriver.WebElementPromise;\n  error = webdriver.error;\n  logging = webdriver.logging;\n  promise = webdriver.promise;\n  until = webdriver.until;\n  Command = command.Command;\n  CommandName = command.Name;\n  utils = {firefox: firefox, http: http, remote: remote, chrome: chrome};\n}\n\nexport let protractor = new Ptor();\n"
  },
  {
    "path": "lib/runner.ts",
    "content": "import {EventEmitter} from 'events';\nimport * as util from 'util';\n\nimport {ProtractorBrowser} from './browser';\nimport {Config} from './config';\nimport {buildDriverProvider, DriverProvider} from './driverProviders';\nimport {Logger} from './logger';\nimport {Plugins} from './plugins';\nimport {protractor} from './ptor';\nimport {joinTestLogs, runFilenameOrFn_} from './util';\n\ndeclare let global: any;\ndeclare let process: any;\n\nlet logger = new Logger('runner');\n/*\n * Runner is responsible for starting the execution of a test run and triggering\n * setup, teardown, managing config, etc through its various dependencies.\n *\n * The Protractor Runner is a node EventEmitter with the following events:\n * - testPass\n * - testFail\n * - testsDone\n *\n * @param {Object} config\n * @constructor\n */\nexport class Runner extends EventEmitter {\n  config_: Config;\n  preparer_: any;\n  driverprovider_: DriverProvider;\n  o: any;\n  plugins_: Plugins;\n  restartPromise: Promise<any>;\n  frameworkUsesAfterEach: boolean;\n  ready_?: Promise<void>;\n\n  constructor(config: Config) {\n    super();\n    this.config_ = config;\n    if (config.v8Debug) {\n      // Call this private function instead of sending SIGUSR1 because Windows.\n      process['_debugProcess'](process.pid);\n    }\n\n    if (config.nodeDebug) {\n      process['_debugProcess'](process.pid);\n      const nodedebug = require('child_process').fork('debug', ['localhost:5858']);\n      process.on('exit', () => {\n        nodedebug.kill('SIGTERM');\n      });\n      nodedebug.on('exit', () => {\n        process.exit(1);\n      });\n    }\n\n    if (config.capabilities && config.capabilities.seleniumAddress) {\n      config.seleniumAddress = config.capabilities.seleniumAddress;\n    }\n    this.loadDriverProvider_(config);\n    this.setTestPreparer(config.onPrepare);\n  }\n\n  /**\n   * Registrar for testPreparers - executed right before tests run.\n   * @public\n   * @param {string/Fn} filenameOrFn\n   */\n  setTestPreparer(filenameOrFn: string|Function): void {\n    this.preparer_ = filenameOrFn;\n  }\n\n  /**\n   * Executor of testPreparer\n   * @public\n   * @param {string[]=} An optional list of command line arguments the framework will accept.\n   * @return {Promise} A promise that will resolve when the test preparers\n   *     are finished.\n   */\n  runTestPreparer(extraFlags?: string[]): Promise<any> {\n    let unknownFlags = this.config_.unknownFlags_ || [];\n    if (extraFlags) {\n      unknownFlags = unknownFlags.filter((f) => extraFlags.indexOf(f) === -1);\n    }\n    if (unknownFlags.length > 0 && !this.config_.disableChecks) {\n      // TODO: Make this throw a ConfigError in Protractor 6.\n      logger.warn(\n          'Ignoring unknown extra flags: ' + unknownFlags.join(', ') + '. This will be' +\n          ' an error in future versions, please use --disableChecks flag to disable the ' +\n          ' Protractor CLI flag checks. ');\n    }\n    return this.plugins_.onPrepare().then(() => {\n      return runFilenameOrFn_(this.config_.configDir, this.preparer_);\n    });\n  }\n\n  /**\n   * Called after each test finishes.\n   *\n   * Responsible for `restartBrowserBetweenTests`\n   *\n   * @public\n   * @return {Promise} A promise that will resolve when the work here is done\n   */\n  afterEach(): Promise<void> {\n    let ret: Promise<void>;\n    this.frameworkUsesAfterEach = true;\n    if (this.config_.restartBrowserBetweenTests) {\n      this.restartPromise = this.restartPromise || Promise.resolve(protractor.browser.restart());\n      ret = this.restartPromise;\n      this.restartPromise = undefined;\n    }\n    return ret || Promise.resolve();\n  }\n\n  /**\n   * Grab driver provider based on type\n   * @private\n   *\n   * Priority\n   * 1) if directConnect is true, use that\n   * 2) if seleniumAddress is given, use that\n   * 3) if a Sauce Labs account is given, use that\n   * 4) if a seleniumServerJar is specified, use that\n   * 5) try to find the seleniumServerJar in protractor/selenium\n   */\n  loadDriverProvider_(config: Config) {\n    this.config_ = config;\n    this.driverprovider_ = buildDriverProvider(this.config_);\n  }\n\n  /**\n   * Responsible for cleaning up test run and exiting the process.\n   * @private\n   * @param {int} Standard unix exit code\n   */\n  async exit_(exitCode: number): Promise<number> {\n    const returned =\n        await runFilenameOrFn_(this.config_.configDir, this.config_.onCleanUp, [exitCode]);\n    if (typeof returned === 'number') {\n      return returned;\n    } else {\n      return exitCode;\n    }\n  }\n\n  /**\n   * Getter for the Runner config object\n   * @public\n   * @return {Object} config\n   */\n  getConfig(): Config {\n    return this.config_;\n  }\n\n  /**\n   * Sets up convenience globals for test specs\n   * @private\n   */\n  setupGlobals_(browser_: ProtractorBrowser) {\n    // Keep $, $$, element, and by/By under the global protractor namespace\n    protractor.browser = browser_;\n    protractor.$ = browser_.$;\n    protractor.$$ = browser_.$$;\n    protractor.element = browser_.element;\n    protractor.by = protractor.By = ProtractorBrowser.By;\n    protractor.ExpectedConditions = browser_.ExpectedConditions;\n\n    if (!this.config_.noGlobals) {\n      // Export protractor to the global namespace to be used in tests.\n      global.browser = browser_;\n      global.$ = browser_.$;\n      global.$$ = browser_.$$;\n      global.element = browser_.element;\n      global.by = global.By = protractor.By;\n      global.ExpectedConditions = protractor.ExpectedConditions;\n    }\n\n    global.protractor = protractor;\n\n    if (!this.config_.skipSourceMapSupport) {\n      // Enable sourcemap support for stack traces.\n      require('source-map-support').install();\n    }\n    // Required by dart2js machinery.\n    // https://code.google.com/p/dart/source/browse/branches/bleeding_edge/dart/sdk/lib/js/dart2js/js_dart2js.dart?spec=svn32943&r=32943#487\n    global.DartObject = function(o: any) {\n      this.o = o;\n    };\n  }\n\n  /**\n   * Create a new driver from a driverProvider. Then set up a\n   * new protractor instance using this driver.\n   * This is used to set up the initial protractor instances and any\n   * future ones.\n   *\n   * @param {Plugin} plugins The plugin functions\n   * @param {ProtractorBrowser=} parentBrowser The browser which spawned this one\n   *\n   * @return {Protractor} a protractor instance.\n   * @public\n   */\n  async createBrowser(plugins: any, parentBrowser?: ProtractorBrowser): Promise<any> {\n    let config = this.config_;\n    let driver = await this.driverprovider_.getNewDriver();\n\n    let blockingProxyUrl: string;\n    if (config.useBlockingProxy) {\n      blockingProxyUrl = this.driverprovider_.getBPUrl();\n    }\n\n    let initProperties = {\n      baseUrl: config.baseUrl,\n      rootElement: config.rootElement as string | Promise<string>,\n      untrackOutstandingTimeouts: config.untrackOutstandingTimeouts,\n      params: config.params,\n      getPageTimeout: config.getPageTimeout,\n      allScriptsTimeout: config.allScriptsTimeout,\n      debuggerServerPort: config.debuggerServerPort,\n      ng12Hybrid: config.ng12Hybrid,\n      waitForAngularEnabled: true as boolean | Promise<boolean>\n    };\n\n    if (parentBrowser) {\n      initProperties.baseUrl = parentBrowser.baseUrl;\n      initProperties.rootElement = parentBrowser.angularAppRoot();\n      initProperties.untrackOutstandingTimeouts = !parentBrowser.trackOutstandingTimeouts_;\n      initProperties.params = parentBrowser.params;\n      initProperties.getPageTimeout = parentBrowser.getPageTimeout;\n      initProperties.allScriptsTimeout = parentBrowser.allScriptsTimeout;\n      initProperties.debuggerServerPort = parentBrowser.debuggerServerPort;\n      initProperties.ng12Hybrid = parentBrowser.ng12Hybrid;\n      initProperties.waitForAngularEnabled = parentBrowser.waitForAngularEnabled();\n    }\n\n    let browser_ = new ProtractorBrowser(\n        driver, initProperties.baseUrl, initProperties.rootElement,\n        initProperties.untrackOutstandingTimeouts, blockingProxyUrl);\n\n    browser_.params = initProperties.params;\n    browser_.plugins_ = plugins || new Plugins({});\n    if (initProperties.getPageTimeout) {\n      browser_.getPageTimeout = initProperties.getPageTimeout;\n    }\n    if (initProperties.allScriptsTimeout) {\n      browser_.allScriptsTimeout = initProperties.allScriptsTimeout;\n    }\n    if (initProperties.debuggerServerPort) {\n      browser_.debuggerServerPort = initProperties.debuggerServerPort;\n    }\n    if (initProperties.ng12Hybrid) {\n      browser_.ng12Hybrid = initProperties.ng12Hybrid;\n    }\n\n    await browser_.waitForAngularEnabled(initProperties.waitForAngularEnabled);\n    // TODO(selenium4): Options does not have a setScriptTimeout method.\n    await(driver.manage() as any).setTimeouts({script: initProperties.allScriptsTimeout || 0});\n\n\n    browser_.getProcessedConfig = () => {\n      return Promise.resolve(config);\n    };\n\n    browser_.forkNewDriverInstance = async(\n        useSameUrl: boolean, copyMockModules: boolean, copyConfigUpdates = true): Promise<any> => {\n      let newBrowser = await this.createBrowser(plugins);\n      if (copyMockModules) {\n        newBrowser.mockModules_ = browser_.mockModules_;\n      }\n      if (useSameUrl) {\n        const currentUrl = await browser_.driver.getCurrentUrl();\n        await newBrowser.get(currentUrl);\n      }\n      return newBrowser;\n    };\n\n    let replaceBrowser = async () => {\n      let newBrowser = await browser_.forkNewDriverInstance(false, true);\n      if (browser_ === protractor.browser) {\n        this.setupGlobals_(newBrowser);\n      }\n      return newBrowser;\n    };\n\n    browser_.restart = async () => {\n      // Note: because tests are not paused at this point, any async\n      // calls here are not guaranteed to complete before the tests resume.\n      const restartedBrowser = await replaceBrowser();\n      await this.driverprovider_.quitDriver(browser_.driver);\n      return restartedBrowser;\n    };\n\n    return browser_;\n  }\n\n  /**\n   * Final cleanup on exiting the runner.\n   *\n   * @return {Promise} A promise which resolves on finish.\n   * @private\n   */\n  shutdown_(): Promise<void> {\n    return DriverProvider.quitDrivers(\n        this.driverprovider_, this.driverprovider_.getExistingDrivers());\n  }\n\n  /**\n   * The primary workhorse interface. Kicks off the test running process.\n   *\n   * @return {Promise} A promise which resolves to the exit code of the tests.\n   * @public\n   */\n  async run(): Promise<number> {\n    let testPassed: boolean;\n    let plugins = this.plugins_ = new Plugins(this.config_);\n    let pluginPostTestPromises: any;\n    let browser_: any;\n    let results: any;\n\n    if (this.config_.framework !== 'explorer' && !this.config_.specs.length) {\n      throw new Error('Spec patterns did not match any files.');\n    }\n\n    if (this.config_.webDriverLogDir || this.config_.highlightDelay) {\n      this.config_.useBlockingProxy = true;\n    }\n\n    // 1) Setup environment\n    // noinspection JSValidateTypes\n    await this.driverprovider_.setupEnv();\n\n    // 2) Create a browser and setup globals\n    browser_ = await this.createBrowser(plugins);\n    this.setupGlobals_(browser_);\n    try {\n      const session = await browser_.getSession();\n      logger.debug(\n          'WebDriver session successfully started with capabilities ' +\n          util.inspect(session.getCapabilities()));\n    } catch (err) {\n      logger.error('Unable to start a WebDriver session.');\n      throw err;\n    }\n\n    // 3) Setup plugins\n    await plugins.setup();\n\n    // 4) Execute test cases\n    // Do the framework setup here so that jasmine and mocha globals are\n    // available to the onPrepare function.\n    let frameworkPath = '';\n    if (this.config_.framework === 'jasmine' || this.config_.framework === 'jasmine2') {\n      frameworkPath = './frameworks/jasmine.js';\n    } else if (this.config_.framework === 'mocha') {\n      frameworkPath = './frameworks/mocha.js';\n    } else if (this.config_.framework === 'debugprint') {\n      // Private framework. Do not use.\n      frameworkPath = './frameworks/debugprint.js';\n    } else if (this.config_.framework === 'explorer') {\n      // Private framework. Do not use.\n      frameworkPath = './frameworks/explorer.js';\n    } else if (this.config_.framework === 'custom') {\n      if (!this.config_.frameworkPath) {\n        throw new Error(\n            'When config.framework is custom, ' +\n            'config.frameworkPath is required.');\n      }\n      frameworkPath = this.config_.frameworkPath;\n    } else {\n      throw new Error(\n          'config.framework (' + this.config_.framework + ') is not a valid framework.');\n    }\n\n    if (this.config_.restartBrowserBetweenTests) {\n      // TODO(sjelin): replace with warnings once `afterEach` support is required\n      let restartDriver = async () => {\n        if (!this.frameworkUsesAfterEach) {\n          this.restartPromise = await browser_.restart();\n        }\n      };\n      this.on('testPass', restartDriver);\n      this.on('testFail', restartDriver);\n    }\n\n    // We need to save these promises to make sure they're run, but we\n    // don't\n    // want to delay starting the next test (because we can't, it's just\n    // an event emitter).\n    pluginPostTestPromises = [];\n\n    this.on('testPass', (testInfo: any) => {\n      pluginPostTestPromises.push(plugins.postTest(true, testInfo));\n    });\n    this.on('testFail', (testInfo: any) => {\n      pluginPostTestPromises.push(plugins.postTest(false, testInfo));\n    });\n    logger.debug('Running with spec files ' + this.config_.specs);\n    let testResults = await require(frameworkPath).run(this, this.config_.specs);\n\n    // 5) Wait for postTest plugins to finish\n    results = testResults;\n    await Promise.all(pluginPostTestPromises);\n\n    // 6) Teardown plugins\n    await plugins.teardown();\n\n    // 7) Teardown\n    results = joinTestLogs(results, plugins.getResults());\n    this.emit('testsDone', results);\n    testPassed = results.failedCount === 0;\n    if (this.driverprovider_.updateJob) {\n      await this.driverprovider_.updateJob({'passed': testPassed});\n    }\n    await this.driverprovider_.teardownEnv();\n\n    // 8) Let plugins do final cleanup\n    await plugins.postResults();\n\n    // 9) Exit process\n    const exitCode = testPassed ? 0 : 1;\n\n    await this.shutdown_();\n\n    return this.exit_(exitCode);\n  }\n}\n"
  },
  {
    "path": "lib/runnerCli.ts",
    "content": "/**\n * This serves as the main function for starting a test run that has been\n * requested by the launcher.\n */\n\nimport {ConfigParser} from './configParser';\nimport {Logger} from './logger';\nimport {Runner} from './runner';\n\nlet logger = new Logger('runnerCli');\n\nprocess.on('message', (m: any) => {\n  if (m.command === 'run') {\n    if (!m.capabilities) {\n      throw new Error('Run message missing capabilities');\n    }\n    // Merge in config file options.\n    let configParser = new ConfigParser();\n    if (m.configFile) {\n      configParser.addFileConfig(m.configFile);\n    }\n    if (m.additionalConfig) {\n      configParser.addConfig(m.additionalConfig);\n    }\n    let config = configParser.getConfig();\n    Logger.set(config);\n\n    // Grab capabilities to run from launcher.\n    config.capabilities = m.capabilities;\n\n    // Get specs to be executed by this runner\n    config.specs = m.specs;\n\n    // Launch test run.\n    let runner = new Runner(config);\n\n    // Pipe events back to the launcher.\n    runner.on('testPass', () => {\n      process.send({event: 'testPass'});\n    });\n    runner.on('testFail', () => {\n      process.send({event: 'testFail'});\n    });\n    runner.on('testsDone', (results: any) => {\n      process.send({event: 'testsDone', results: results});\n    });\n\n    runner.run()\n        .then((exitCode: number) => {\n          process.exit(exitCode);\n        })\n        .catch((err: Error) => {\n          logger.info(err.message);\n          process.exit(1);\n        });\n  } else {\n    throw new Error('command ' + m.command + ' is invalid');\n  }\n});\n"
  },
  {
    "path": "lib/selenium-webdriver/locators.js",
    "content": "// Used to provide better protractor documentation for webdriver. These files\n// are not used to provide code for protractor and are only used for the website.\n\n/**\n * @fileoverview Factory methods for the supported locator strategies.\n */\n\ngoog.provide('webdriver');\n\n/**\n * A collection of factory functions for creating {@link webdriver.Locator}\n * instances.\n */\nwebdriver.By = {};\n\n/**\n * Locates elements that have a specific class name. The returned locator\n * is equivalent to searching for elements with the CSS selector \".clazz\".\n *\n * @view\n * <ul class=\"pet\">\n *   <li class=\"dog\">Dog</li>\n *   <li class=\"cat\">Cat</li>\n * </ul>\n *\n * @example\n * // Returns the web element for dog\n * var dog = element(by.className('dog'));\n * expect(dog.getText()).toBe('Dog');\n *\n * @param {string} className The class name to search for.\n * @returns {!webdriver.Locator} The new locator.\n * @see http://www.w3.org/TR/2011/WD-html5-20110525/elements.html#classes\n * @see http://www.w3.org/TR/CSS2/selector.html#class-html\n */\nwebdriver.By.className = webdriver.Locator.factory_('class name');\n\n\n/**\n * Locates elements using a CSS selector. For browsers that do not support\n * CSS selectors, WebDriver implementations may return an\n * {@linkplain bot.Error.State.INVALID_SELECTOR invalid selector} error. An\n * implementation may, however, emulate the CSS selector API.\n *\n * @view\n * <ul class=\"pet\">\n *   <li class=\"dog\">Dog</li>\n *   <li class=\"cat\">Cat</li>\n * </ul>\n *\n * @example\n * // Returns the web element for cat\n * var cat = element(by.css('.pet .cat'));\n * expect(cat.getText()).toBe('Cat');\n *\n * @param {string} selector The CSS selector to use.\n * @returns {!webdriver.Locator} The new locator.\n * @see http://www.w3.org/TR/CSS2/selector.html\n */\nwebdriver.By.css = webdriver.Locator.factory_('css selector');\n\n\n/**\n * Locates an element by its ID.\n *\n * @view\n * <ul id=\"pet_id\">\n *   <li id=\"dog_id\">Dog</li>\n *   <li id=\"cat_id\">Cat</li>\n * </ul>\n *\n * @example\n * // Returns the web element for dog\n * var dog = element(by.id('dog_id'));\n * expect(dog.getText()).toBe('Dog');\n *\n * @param {string} id The ID to search for.\n * @returns {!webdriver.Locator} The new locator.\n */\nwebdriver.By.id = webdriver.Locator.factory_('id');\n\n\n/**\n * Locates link elements whose {@linkplain webdriver.WebElement#getText visible\n * text} matches the given string.\n *\n * @view\n * <a href=\"http://www.google.com\">Google</a>\n *\n * @example\n * expect(element(by.linkText('Google')).getTagName()).toBe('a');\n *\n * @param {string} text The link text to search for.\n * @returns {!webdriver.Locator} The new locator.\n */\nwebdriver.By.linkText = webdriver.Locator.factory_('link text');\n\n\n/**\n * Locates an elements by evaluating a JavaScript expression, which may\n * be either a function or a string. Like\n * {@link webdriver.WebDriver.executeScript}, the expression is evaluated\n * in the context of the page and cannot access variables from\n * the test file.\n *\n * The result of this expression must be an element or list of elements.\n *\n * @alias by.js(expression)\n * @view\n * <span class=\"small\">One</span>\n * <span class=\"medium\">Two</span>\n * <span class=\"large\">Three</span>\n *\n * @example\n * var wideElement = element(by.js(function() {\n *   var spans = document.querySelectorAll('span');\n *   for (var i = 0; i < spans.length; ++i) {\n *     if (spans[i].offsetWidth > 100) {\n *      return spans[i];\n *     }\n *   }\n * }));\n * expect(wideElement.getText()).toEqual('Three');\n *\n * @param {!(string|Function)} script The script to execute.\n * @param {...*} var_args The arguments to pass to the script.\n * @returns {!webdriver.Locator}\n */\nwebdriver.By.js = function(script, var_args) {};\n\n\n/**\n * Locates elements whose {@code name} attribute has the given value.\n *\n * @view\n * <ul>\n *   <li name=\"dog_name\">Dog</li>\n *   <li name=\"cat_name\">Cat</li>\n * </ul>\n *\n * @example\n * // Returns the web element for dog\n * var dog = element(by.name('dog_name'));\n * expect(dog.getText()).toBe('Dog');\n *\n * @param {string} name The name attribute to search for.\n * @returns {!webdriver.Locator} The new locator.\n */\nwebdriver.By.name = webdriver.Locator.factory_('name');\n\n\n/**\n * Locates link elements whose {@linkplain webdriver.WebElement#getText visible\n * text} contains the given substring.\n *\n * @view\n * <ul>\n *   <li><a href=\"https://en.wikipedia.org/wiki/Doge_(meme)\">Doge meme</a></li>\n *   <li>Cat</li>\n * </ul>\n *\n * @example\n * // Returns the 'a' web element for doge meme and navigate to that link\n * var doge = element(by.partialLinkText('Doge'));\n * doge.click();\n *\n * @param {string} text The substring to check for in a link's visible text.\n * @returns {!webdriver.Locator} The new locator.\n */\nwebdriver.By.partialLinkText = webdriver.Locator.factory_(\n    'partial link text');\n\n\n/**\n * Locates elements with a given tag name. The returned locator is\n * equivalent to using the\n * [getElementsByTagName](https://developer.mozilla.org/en-US/docs/Web/API/Element.getElementsByTagName)\n * DOM function.\n *\n * @view\n * <a href=\"http://www.google.com\">Google</a>\n *\n * @example\n * expect(element(by.tagName('a')).getText()).toBe('Google');\n *\n * @param {string} text The substring to check for in a link's visible text.\n * @returns {!webdriver.Locator} The new locator.\n * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html\n */\nwebdriver.By.tagName = webdriver.Locator.factory_('tag name');\n\n\n/**\n * Locates elements matching a XPath selector. Care should be taken when\n * using an XPath selector with a {@link webdriver.WebElement} as WebDriver\n * will respect the context in the specified in the selector. For example,\n * given the selector {@code \"//div\"}, WebDriver will search from the\n * document root regardless of whether the locator was used with a\n * WebElement.\n *\n * @view\n * <ul>\n *   <li><a href=\"https://en.wikipedia.org/wiki/Doge_(meme)\">Doge meme</a></li>\n *   <li>Cat</li>\n * </ul>\n *\n * @example\n * // Returns the 'a' element for doge meme\n * var li = element(by.xpath('//ul/li/a'));\n * expect(li.getText()).toBe('Doge meme');\n *\n * @param {string} xpath The XPath selector to use.\n * @returns {!webdriver.Locator} The new locator.\n * @see http://www.w3.org/TR/xpath/\n */\nwebdriver.By.xpath = webdriver.Locator.factory_('xpath');\n"
  },
  {
    "path": "lib/selenium-webdriver/webdriver.js",
    "content": "// Used to provide better protractor documentation for webdriver. These files\n// are not used to provide code for protractor and are only used for the website.\n\n/**\n * @fileoverview The heart of the WebDriver JavaScript API.\n */\n\ngoog.provide('webdriver');\n\n/**\n * Class for defining sequences of complex user interactions.\n * @external webdriver.ActionSequence\n * @see http://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/actions_exports_ActionSequence.html\n */\nwebdriver.ActionSequence = function() {};\n\n/**\n * Class for defining sequences of user touch interactions.\n * @external webdriver.TouchSequence\n * @see http://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_TouchSequence.html\n */\nwebdriver.TouchSequence = function() {};\n\n// //////////////////////////////////////////////////////////////////////////////\n// //\n// //  webdriver.WebDriver\n// //\n// /////////////////////////////////////////////////////////////////////////////\n/**\n * Protractor's `browser` object is a wrapper for `selenium-webdriver` WebDriver.\n * It inherits call of WebDriver's methods, but only the methods most useful to\n * Protractor users are documented here.\n *\n * A full list of all functions available on WebDriver can be found\n * in the selenium-webdriver\n * <a href=\"http://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_WebDriver.html\">documentation</a>\n * @constructor\n */\nwebdriver.WebDriver = function() {};\n\n/**\n * Creates a sequence of user actions using this driver. The sequence will not be\n * scheduled for execution until {@link webdriver.ActionSequence#perform} is\n * called.\n *\n * See the selenium webdriver docs <a href=\"http://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/actions_exports_ActionSequence.html\">\n * for more details on action sequences</a>.\n *\n * Mouse actions do not work on Chrome with the HTML5 Drag and Drop API due to a known <a href=\"https://bugs.chromium.org/p/chromedriver/issues/detail?id=841\">\n *   Chromedriver issue</a>\n *\n * @example\n * // Dragging one element to another.\n * browser.actions().\n *     mouseDown(element1).\n *     mouseMove(element2).\n *     mouseUp().\n *     perform();\n *\n * // You can also use the `dragAndDrop` convenience action.\n * browser.actions().\n *     dragAndDrop(element1, element2).\n *     perform();\n *\n * // Instead of specifying an element as the target, you can specify an offset\n * // in pixels. This example double-clicks slightly to the right of an element.\n * browser.actions().\n *     mouseMove(element).\n *     mouseMove({x: 50, y: 0}).\n *     doubleClick().\n *     perform();\n *\n * @returns {!webdriver.ActionSequence} A new action sequence for this instance.\n */\nwebdriver.WebDriver.prototype.actions = function() {};\n\n/**\n * Creates a new touch sequence using this driver. The sequence will not be\n * scheduled for execution until {@link actions.TouchSequence#perform} is\n * called.\n *\n * See the selenium webdriver docs <a href=\"http://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_TouchSequence.html\">\n * for more details on touch sequences</a>.\n *\n * @example\n * browser.touchActions().\n *     tap(element1).\n *     doubleTap(element2).\n *     perform();\n *\n * @return {!webdriver.TouchSequence} A new touch sequence for this instance.\n */\nwebdriver.WebDriver.prototype.touchActions = function() {};\n\n/**\n * Schedules a command to execute JavaScript in the context of the currently\n * selected frame or window. The script fragment will be executed as the body\n * of an anonymous function. If the script is provided as a function object,\n * that function will be converted to a string for injection into the target\n * window.\n *\n * Any arguments provided in addition to the script will be included as script\n * arguments and may be referenced using the {@code arguments} object.\n * Arguments may be a boolean, number, string, or {@linkplain WebElement}.\n * Arrays and objects may also be used as script arguments as long as each item\n * adheres to the types previously mentioned.\n *\n * The script may refer to any variables accessible from the current window.\n * Furthermore, the script will execute in the window's context, thus\n * {@code document} may be used to refer to the current document. Any local\n * variables will not be available once the script has finished executing,\n * though global variables will persist.\n *\n * If the script has a return value (i.e. if the script contains a return\n * statement), then the following steps will be taken for resolving this\n * functions return value:\n *\n * - For a HTML element, the value will resolve to a {@linkplain WebElement}\n * - Null and undefined return values will resolve to null</li>\n * - Booleans, numbers, and strings will resolve as is</li>\n * - Functions will resolve to their string representation</li>\n * - For arrays and objects, each member item will be converted according to\n *     the rules above\n *\n * @example\n * var el = element(by.module('header'));\n * var tag = browser.executeScript('return arguments[0].tagName', el);\n * expect(tag).toEqual('h1');\n *\n * @param {!(string|Function)} script The script to execute.\n * @param {...*} var_args The arguments to pass to the script.\n * @return {!promise.Promise<T>} A promise that will resolve to the\n *    scripts return value.\n * @template T\n */\nwebdriver.WebDriver.prototype.executeScript = function(script, var_args) {};\n\n/**\n * Schedules a command to execute asynchronous JavaScript in the context of the\n * currently selected frame or window. The script fragment will be executed as\n * the body of an anonymous function. If the script is provided as a function\n * object, that function will be converted to a string for injection into the\n * target window.\n *\n * Any arguments provided in addition to the script will be included as script\n * arguments and may be referenced using the {@code arguments} object.\n * Arguments may be a boolean, number, string, or {@code WebElement}.\n * Arrays and objects may also be used as script arguments as long as each item\n * adheres to the types previously mentioned.\n *\n * Unlike executing synchronous JavaScript with {@link #executeScript},\n * scripts executed with this function must explicitly signal they are finished\n * by invoking the provided callback. This callback will always be injected\n * into the executed function as the last argument, and thus may be referenced\n * with {@code arguments[arguments.length - 1]}. The following steps will be\n * taken for resolving this functions return value against the first argument\n * to the script's callback function:\n *\n * - For a HTML element, the value will resolve to a\n *     {@link WebElement}\n * - Null and undefined return values will resolve to null\n * - Booleans, numbers, and strings will resolve as is\n * - Functions will resolve to their string representation\n * - For arrays and objects, each member item will be converted according to\n *     the rules above\n *\n * @example\n * // Example 1\n * // Performing a sleep that is synchronized with the currently selected window\n * var start = new Date().getTime();\n * browser.executeAsyncScript(\n *     'window.setTimeout(arguments[arguments.length - 1], 500);').\n *     then(function() {\n *       console.log(\n *           'Elapsed time: ' + (new Date().getTime() - start) + ' ms');\n *     });\n *\n * // Example 2\n * // Synchronizing a test with an AJAX application:\n * var button = element(by.id('compose-button'));\n * button.click();\n * browser.executeAsyncScript(\n *     'var callback = arguments[arguments.length - 1];' +\n *     'mailClient.getComposeWindowWidget().onload(callback);');\n * browser.switchTo().frame('composeWidget');\n * element(by.id('to')).sendKeys('dog@example.com');\n *\n * // Example 3\n * // Injecting a XMLHttpRequest and waiting for the result.  In this example,\n * // the inject script is specified with a function literal. When using this\n * // format, the function is converted to a string for injection, so it should\n * // not reference any symbols not defined in the scope of the page under test.\n * browser.executeAsyncScript(function() {\n *   var callback = arguments[arguments.length - 1];\n *   var xhr = new XMLHttpRequest();\n *   xhr.open(\"GET\", \"/resource/data.json\", true);\n *   xhr.onreadystatechange = function() {\n *     if (xhr.readyState == 4) {\n *       callback(xhr.responseText);\n *     }\n *   };\n *   xhr.send('');\n * }).then(function(str) {\n *   console.log(JSON.parse(str)['food']);\n * });\n *\n * @param {!(string|Function)} script The script to execute.\n * @param {...*} var_args The arguments to pass to the script.\n * @return {!promise.Promise<T>} A promise that will resolve to the\n *    scripts return value.\n * @template T\n */\nwebdriver.WebDriver.prototype.executeAsyncScript = (script, var_args) => {};\n\n/**\n * Schedules a command to execute a custom function within the context of\n * webdriver's control flow.\n *\n * Most webdriver actions are asynchronous, but the control flow makes sure that\n * commands are executed in the order they were received.  By running your\n * function in the control flow, you can ensure that it is executed before/after\n * other webdriver actions.  Additionally, Protractor will wait until the\n * control flow is empty before deeming a test finished.\n *\n * @example\n * var logText = function(el) {\n *   return el.getText().then((text) => {\n *     console.log(text);\n *   });\n * };\n * var counter = element(by.id('counter'));\n * var button = element(by.id('button'));\n * // Use `browser.call()` to make sure `logText` is run before and after\n * // `button.click()`\n * browser.call(logText, counter);\n * button.click();\n * browser.call(logText, counter);\n *\n * @param {function(...): (T|promise.Promise<T>)} fn The function to\n *     execute.\n * @param {Object=} opt_scope The object in whose scope to execute the function\n *     (i.e. the `this` object for the function).\n * @param {...*} var_args Any arguments to pass to the function.  If any of the\n *     arguments are promised, webdriver will wait for these promised to resolve\n *     and pass the resulting value onto the function.\n * @return {!promise.Promise<T>} A promise that will be resolved\n *     with the function's result.\n * @template T\n */\nwebdriver.WebDriver.prototype.call = function(fn, opt_scope, var_args) {};\n\n/**\n * Schedules a command to wait for a condition to hold or {@link\n * webdriver.promise.Promise promise} to be resolved.\n *\n * This function blocks WebDriver's control flow, not the javascript runtime.\n * It will only delay future webdriver commands from being executed (e.g. it\n * will cause Protractor to wait before sending future commands to the selenium\n * server), and only when the webdriver control flow is enabled.\n *\n * This function returnes a promise, which can be used if you need to block\n * javascript execution and not just the control flow.\n *\n * See also {@link ExpectedConditions}\n *\n * *Example:* Suppose you have a function, `startTestServer`, that returns a\n * promise for when a server is ready for requests. You can block a `WebDriver`\n * client on this promise with:\n *\n * @example\n * var started = startTestServer();\n * browser.wait(started, 5 * 1000, 'Server should start within 5 seconds');\n * browser.get(getServerUrl());\n *\n * @param {!(webdriver.promise.Promise<T>|\n *           webdriver.until.Condition<T>|\n *           function(!webdriver.WebDriver): T)} condition The condition to\n *     wait on, defined as a promise, condition object, or  a function to\n *     evaluate as a condition.\n * @param {number=} opt_timeout How long to wait for the condition to be true. Will default 30 seconds, or to the jasmineNodeOpts.defaultTimeoutInterval in your protractor.conf.js file.\n * @param {string=} opt_message An optional message to use if the wait times\n *     out.\n * @returns {!webdriver.promise.Promise<T>} A promise that will be fulfilled\n *     with the first truthy value returned by the condition function, or\n *     rejected if the condition times out.\n */\nwebdriver.WebDriver.prototype.wait = function() {};\n\n/**\n * Schedules a command to make the driver sleep for the given amount of time.\n * @param {number} ms The amount of time, in milliseconds, to sleep.\n * @returns {!webdriver.promise.Promise.<void>} A promise that will be resolved\n *     when the sleep has finished.\n */\nwebdriver.WebDriver.prototype.sleep = function() {};\n\n/**\n * Schedules a command to retrieve the current page's source. The page source\n * returned is a representation of the underlying DOM: do not expect it to be\n * formatted or escaped in the same way as the response sent from the web\n * server.\n * @return {!promise.Promise<string>} A promise that will be\n *     resolved with the current page source.\n */\nwebdriver.WebDriver.prototype.getPageSource = function() {};\n\n/**\n * Schedules a command to close the current window.\n * @return {!promise.Promise<void>} A promise that will be resolved\n *     when this command has completed.\n */\nwebdriver.WebDriver.prototype.close = function() {};\n\n/**\n * Schedules a command to retrieve the URL of the current page.\n * @returns {!webdriver.promise.Promise.<string>} A promise that will be\n *     resolved with the current URL.\n */\nwebdriver.WebDriver.prototype.getCurrentUrl = function() {};\n\n/**\n * Schedules a command to retrieve the current page's title.\n * @returns {!webdriver.promise.Promise.<string>} A promise that will be\n *     resolved with the current page's title.\n */\nwebdriver.WebDriver.prototype.getTitle = function() {};\n\n/**\n * Schedule a command to take a screenshot. The driver makes a best effort to\n * return a screenshot of the following, in order of preference:\n * <ol>\n *   <li>Entire page\n *   <li>Current window\n *   <li>Visible portion of the current frame\n *   <li>The screenshot of the entire display containing the browser\n * </ol>\n *\n * @returns {!webdriver.promise.Promise.<string>} A promise that will be\n *     resolved to the screenshot as a base-64 encoded PNG.\n */\nwebdriver.WebDriver.prototype.takeScreenshot = function() {};\n\n/**\n * Used to switch WebDriver's focus to a frame or window (e.g. an alert, an\n * iframe, another window).\n *\n * See [WebDriver's TargetLocator Docs](http://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_TargetLocator.html)\n * for more information.\n *\n * @example\n * browser.switchTo().frame(element(by.tagName('iframe')).getWebElement());\n *\n * @return {!TargetLocator} The target locator interface for this\n *     instance.\n */\nwebdriver.WebDriver.prototype.switchTo = function() {}\n\n// /////////////////////////////////////////////////////////////////////////////\n// //\n// //  webdriver.WebElement\n// //\n// /////////////////////////////////////////////////////////////////////////////\n//\n//\n//\n/**\n * Protractor's ElementFinders are wrappers for selenium-webdriver WebElement.\n * A full list of all functions available on WebElement can be found\n * in the selenium-webdriver\n * <a href=\"http://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_WebElement.html\">documentation</a>.\n *\n * @param {!webdriver.WebDriver} driver The webdriver driver or the parent WebDriver instance for this\n *     element.\n * @param {!(webdriver.promise.Promise.<webdriver.WebElement.Id>|\n *           webdriver.WebElement.Id)} id The server-assigned opaque ID for the\n *     underlying DOM element.\n * @constructor\n */\nwebdriver.WebElement = function(driver, id) {};\n\n/**\n * Gets the parent web element of this web element.\n *\n * @view\n * <ul class=\"pet\">\n *  <li class=\"dog\">Dog</li>\n *  <li class=\"cat\">Cat</li>\n * </ul>\n *\n * @example\n * // Using getDriver to find the parent web element to find the cat li\n * var liDog = element(by.css('.dog')).getWebElement();\n * var liCat = liDog.getDriver().findElement(by.css('.cat'));\n *\n * @returns {!webdriver.WebDriver} The parent driver for this instance.\n */\nwebdriver.WebElement.prototype.getDriver = function() {};\n\n\n/**\n * Gets the WebDriver ID string representation for this web element.\n *\n * @view\n * <ul class=\"pet\">\n *   <li class=\"dog\">Dog</li>\n *   <li class=\"cat\">Cat</li>\n * </ul>\n *\n * @example\n * // returns the dog web element\n * var dog = element(by.css('.dog')).getWebElement();\n * expect(dog.getId()).not.toBe(undefined);\n *\n * @returns {!webdriver.promise.Promise.<webdriver.WebElement.Id>} A promise\n *     that resolves to this element's JSON representation as defined by the\n *     WebDriver wire protocol.\n * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol\n */\nwebdriver.WebElement.prototype.getId = function() {};\n\n/**\n * Use {@link ElementFinder.prototype.element} instead\n *\n * @see ElementFinder.prototype.element\n *\n * @param {webdriver.Locator} subLocator\n *\n * @returns {!webdriver.WebElement}\n */\nwebdriver.WebElement.prototype.findElement = function(subLocator) {};\n\n\n/**\n * Schedules a command to click on this element.\n *\n * @view\n * <ul>\n *   <li><a href=\"https://en.wikipedia.org/wiki/Doge_(meme)\">Doge meme</a></li>\n *   <li>Cat</li>\n * </ul>\n *\n * @example\n * // Clicks on the web link\n * element(by.partialLinkText('Doge')).click();\n *\n * @returns {!webdriver.promise.Promise.<void>} A promise that will be resolved\n *     when the click command has completed.\n */\nwebdriver.WebElement.prototype.click = function() {};\n\n\n/**\n * Schedules a command to type a sequence on the DOM element represented by this\n * instance.\n *\n * Modifier keys (SHIFT, CONTROL, ALT, META) are stateful; once a modifier is\n * processed in the keysequence, that key state is toggled until one of the\n * following occurs:\n *\n * - The modifier key is encountered again in the sequence. At this point the\n *   state of the key is toggled (along with the appropriate keyup/down events).\n * - The {@link webdriver.Key.NULL} key is encountered in the sequence. When\n *   this key is encountered, all modifier keys current in the down state are\n *   released (with accompanying keyup events). The NULL key can be used to\n *   simulate common keyboard shortcuts:\n *\n *         element.sendKeys(\"text was\",\n *                          protractor.Key.CONTROL, \"a\", protractor.Key.NULL,\n *                          \"now text is\");\n *         // Alternatively:\n *         element.sendKeys(\"text was\",\n *                          protractor.Key.chord(protractor.Key.CONTROL, \"a\"),\n *                          \"now text is\");\n *\n * - The end of the keysequence is encountered. When there are no more keys\n *   to type, all depressed modifier keys are released (with accompanying keyup\n *   events).\n *\n * If this element is a file input ({@code <input type=\"file\">}), the\n * specified key sequence should specify the path to the file to attach to\n * the element. This is analgous to the user clicking \"Browse...\" and entering\n * the path into the file select dialog.\n *\n *     var form = driver.findElement(By.css('form'));\n *     var element = form.findElement(By.css('input[type=file]'));\n *     element.sendKeys('/path/to/file.txt');\n *     form.submit();\n *\n * For uploads to function correctly, the entered path must reference a file\n * on the _browser's_ machine, not the local machine running this script. When\n * running against a remote Selenium server, a {@link webdriver.FileDetector}\n * may be used to transparently copy files to the remote machine before\n * attempting to upload them in the browser.\n *\n * __Note:__ On browsers where native keyboard events are not supported\n * (e.g. Firefox on OS X), key events will be synthesized. Special\n * punctionation keys will be synthesized according to a standard QWERTY en-us\n * keyboard layout.\n *\n * @param {...(string|!webdriver.promise.Promise<string>)} var_args The sequence\n *     of keys to type. All arguments will be joined into a single sequence.\n * @returns {!webdriver.promise.Promise.<void>} A promise that will be resolved\n *     when all keys have been typed.\n */\nwebdriver.WebElement.prototype.sendKeys = function(var_args) {};\n\n\n/**\n * Gets the tag/node name of this element.\n *\n * @view\n * <span>{{person.name}}</span>\n *\n * @example\n * expect(element(by.binding('person.name')).getTagName()).toBe('span');\n *\n * @returns {!webdriver.promise.Promise.<string>} A promise that will be\n *     resolved with the element's tag name.\n */\nwebdriver.WebElement.prototype.getTagName = function() {};\n\n\n/**\n * Gets the computed style of an element. If the element inherits the named\n * style from its parent, the parent will be queried for its value. Where\n * possible, color values will be converted to their hex representation (e.g.\n * #00ff00 instead of rgb(0, 255, 0)).\n *\n * _Warning:_ the value returned will be as the browser interprets it, so\n * it may be tricky to form a proper assertion.\n *\n * @view\n * <span style='color: #000000'>{{person.name}}</span>\n *\n * @example\n * expect(element(by.binding('person.name')).getCssValue('color')).toBe('#000000');\n *\n * @param {string} cssStyleProperty The name of the CSS style property to look\n *     up.\n * @returns {!webdriver.promise.Promise.<string>} A promise that will be\n *     resolved with the requested CSS value.\n */\nwebdriver.WebElement.prototype.getCssValue = function(cssStyleProperty) {};\n\n\n/**\n * Schedules a command to query for the value of the given attribute of the\n * element. Will return the current value, even if it has been modified after\n * the page has been loaded. More exactly, this method will return the value of\n * the given attribute, unless that attribute is not present, in which case the\n * value of the property with the same name is returned. If neither value is\n * set, null is returned (for example, the \"value\" property of a textarea\n * element). The \"style\" attribute is converted as best can be to a\n * text representation with a trailing semi-colon. The following are deemed to\n * be \"boolean\" attributes and will return either \"true\" or null:\n *\n * async, autofocus, autoplay, checked, compact, complete, controls, declare,\n * defaultchecked, defaultselected, defer, disabled, draggable, ended,\n * formnovalidate, hidden, indeterminate, iscontenteditable, ismap, itemscope,\n * loop, multiple, muted, nohref, noresize, noshade, novalidate, nowrap, open,\n * paused, pubdate, readonly, required, reversed, scoped, seamless, seeking,\n * selected, spellcheck, truespeed, willvalidate\n *\n * Finally, the following commonly mis-capitalized attribute/property names\n * are evaluated as expected:\n *\n * - \"class\"\n * - \"readonly\"\n *\n * @view\n * <div id=\"foo\" class=\"bar\"></div>\n *\n * @example\n * var foo = element(by.id('foo'));\n * expect(foo.getAttribute('class')).toEqual('bar');\n *\n * @param {string} attributeName The name of the attribute to query.\n * @returns {!webdriver.promise.Promise.<?string>} A promise that will be\n *     resolved with the attribute's value. The returned value will always be\n *     either a string or null.\n */\nwebdriver.WebElement.prototype.getAttribute = function(attributeName) {};\n\n\n/**\n * Get the visible innerText of this element, including sub-elements, without\n * any leading or trailing whitespace. Visible elements are not hidden by CSS.\n *\n * @view\n * <div id=\"foo\" class=\"bar\">Inner text</div>\n *\n * @example\n * var foo = element(by.id('foo'));\n * expect(foo.getText()).toEqual('Inner text');\n *\n * @returns {!webdriver.promise.Promise.<string>} A promise that will be\n *     resolved with the element's visible text.\n */\nwebdriver.WebElement.prototype.getText = function() {};\n\n\n/**\n * Schedules a command to compute the size of this element's bounding box, in\n * pixels.\n *\n * @view\n * <div id=\"foo\" style=\"width:50px; height: 20px\">\n *   Inner text\n * </div>\n *\n * @example\n * var foo = element(by.id('foo'));\n * expect(foo.getSize()).toEqual(jasmine.objectContaining({\n *  width: 50,\n *  height: 20\n * });\n *\n * @returns {!webdriver.promise.Promise.<{width: number, height: number}>} A\n *     promise that will be resolved with the element's size as a\n *     {@code {width:number, height:number}} object.\n */\nwebdriver.WebElement.prototype.getSize = function() {};\n\n\n/**\n * Schedules a command to compute the location of this element in page space.\n *\n * @view\n * <div id=\"foo\" style=\"position: absolute; top:20px; left: 15px\">\n *   Inner text\n * </div>\n *\n * @example\n * var foo = element(by.id('foo'));\n * expect(foo.getLocation()).toEqual(jasmine.objectContaining({\n *  x: 15,\n *  y: 20\n * });\n *\n * @returns {!webdriver.promise.Promise.<{x: number, y: number}>} A promise that\n *     will be resolved to the element's location as a\n *     {@code {x:number, y:number}} object.\n */\nwebdriver.WebElement.prototype.getLocation = function() {};\n\n\n/**\n * Schedules a command to query whether the DOM element represented by this\n * instance is enabled, as dicted by the {@code disabled} attribute.\n *\n * @view\n * <input id=\"foo\" disabled=true>\n *\n * @example\n * var foo = element(by.id('foo'));\n * expect(foo.isEnabled()).toBe(false);\n *\n * @returns {!webdriver.promise.Promise.<boolean>} A promise that will be\n *     resolved with whether this element is currently enabled.\n */\nwebdriver.WebElement.prototype.isEnabled = function() {};\n\n\n/**\n * Schedules a command to query whether this element is selected.\n *\n * @view\n * <input id=\"foo\" type=\"checkbox\">\n *\n * @example\n * var foo = element(by.id('foo'));\n * expect(foo.isSelected()).toBe(false);\n * foo.click();\n * expect(foo.isSelected()).toBe(true);\n *\n * @returns {!webdriver.promise.Promise.<boolean>} A promise that will be\n *     resolved with whether this element is currently selected.\n */\nwebdriver.WebElement.prototype.isSelected = function() {};\n\n\n/**\n * Schedules a command to submit the form containing this element (or this\n * element if it is a FORM element). This command is a no-op if the element is\n * not contained in a form.\n *\n * @view\n * <form id=\"login\">\n *   <input name=\"user\">\n * </form>\n *\n * @example\n * var login_form = element(by.id('login'));\n * login_form.submit();\n *\n * @returns {!webdriver.promise.Promise.<void>} A promise that will be resolved\n *     when the form has been submitted.\n */\nwebdriver.WebElement.prototype.submit = function() {};\n\n\n/**\n * Schedules a command to clear the {@code value} of this element. This command\n * has no effect if the underlying DOM element is neither a text INPUT element\n * nor a TEXTAREA element.\n *\n * @view\n * <input id=\"foo\" value=\"Default Text\">\n *\n * @example\n * var foo = element(by.id('foo'));\n * expect(foo.getAttribute('value')).toEqual('Default Text');\n * foo.clear();\n * expect(foo.getAttribute('value')).toEqual('');\n *\n * @returns {!webdriver.promise.Promise.<void>} A promise that will be resolved\n *     when the element has been cleared.\n */\nwebdriver.WebElement.prototype.clear = function() {};\n\n\n/**\n * Schedules a command to test whether this element is currently displayed.\n *\n * @view\n * <div id=\"foo\" style=\"visibility:hidden\">\n *\n * @example\n * var foo = element(by.id('foo'));\n * expect(foo.isDisplayed()).toBe(false);\n *\n * @returns {!webdriver.promise.Promise.<boolean>} A promise that will be\n *     resolved with whether this element is currently visible on the page.\n */\nwebdriver.WebElement.prototype.isDisplayed = function() {};\n\n\n/**\n * Take a screenshot of the visible region encompassed by this element's\n * bounding rectangle.\n *\n * @view\n * <div id=\"foo\">Inner Text</div>\n *\n * @example\n * function writeScreenShot(data, filename) {\n *   var stream = fs.createWriteStream(filename);\n *   stream.write(new Buffer(data, 'base64'));\n *   stream.end();\n * }\n * var foo = element(by.id('foo'));\n * foo.takeScreenshot().then((png) => {\n *   writeScreenShot(png, 'foo.png');\n * });\n *\n * Note that this is a new feature in WebDriver and may not be supported by\n * your browser's driver. It isn't yet supported in Chromedriver as of 2.21.\n *\n * @param {boolean=} opt_scroll Optional argument that indicates whether the\n *     element should be scrolled into view before taking a screenshot. Defaults\n *     to false.\n * @returns {!webdriver.promise.Promise.<string>} A promise that will be\n *     resolved to the screenshot as a base-64 encoded PNG.\n */\nwebdriver.WebElement.prototype.takeScreenshot = function(opt_scroll) {};\n"
  },
  {
    "path": "lib/taskLogger.ts",
    "content": "import * as os from 'os';\nimport {Logger} from './logger';\n\nlet logger = new Logger('testLogger');\n\nexport class TaskLogger {\n  private buffer: string = '';\n  private insertTag: boolean = true;\n\n  /**\n   * Log output such that metadata are appended.\n   * Calling log(data) will not flush to console until you call flush()\n   *\n   * @constructor\n   * @param {object} task Task that is being reported.\n   * @param {number} pid PID of process running the task.\n   */\n  constructor(private task: any, private pid: number) {\n    this.logHeader_();\n  }\n\n  /**\n   * Log the header for the current task including information such as\n   * PID, browser name/version, task Id, specs being run.\n   *\n   * @private\n   */\n  private logHeader_(): void {\n    let output = 'PID: ' + this.pid + os.EOL;\n    if (this.task.specs.length === 1) {\n      output += 'Specs: ' + this.task.specs.toString() + os.EOL + os.EOL;\n    }\n    this.log(output);\n  }\n\n  /**\n   * Prints the contents of the buffer without clearing it.\n   */\n  public peek(): void {\n    if (this.buffer) {\n      // Flush buffer if nonempty\n      logger.info(os.EOL + '------------------------------------' + os.EOL);\n      logger.info(this.buffer);\n      logger.info(os.EOL);\n    }\n  }\n\n  /**\n   * Flushes the buffer to stdout.\n   */\n  public flush(): void {\n    if (this.buffer) {\n      this.peek();\n      this.buffer = '';\n    }\n  }\n\n  /**\n   * Log the data in the argument such that metadata are appended.\n   * The data will be saved to a buffer until flush() is called.\n   *\n   * @param {string} data\n   */\n  public log(data: string): void {\n    let tag = '[';\n    let capabilities = this.task.capabilities;\n    tag += (capabilities.logName) ? capabilities.logName :\n                                    (capabilities.browserName) ? capabilities.browserName : '';\n    tag += (capabilities.version) ? (' ' + capabilities.version) : '';\n    tag += (capabilities.platform) ? (' ' + capabilities.platform) : '';\n    tag += (capabilities.logName && capabilities.count < 2) ? '' : ' #' + this.task.taskId;\n    tag += '] ';\n\n    data = data.toString();\n    for (let i = 0; i < data.length; i++) {\n      if (this.insertTag) {\n        this.insertTag = false;\n        // This ensures that the '\\x1B[0m' appears before the tag, so that\n        // data remains correct when color is not processed.\n        // See https://github.com/angular/protractor/pull/1216\n        if (data[i] === '\\x1B' && data.substring(i, i + 4) === '\\x1B[0m') {\n          this.buffer += ('\\x1B[0m' + tag);\n          i += 3;\n          continue;\n        }\n\n        this.buffer += tag;\n      }\n      if (data[i] === '\\n') {\n        this.insertTag = true;\n      }\n      this.buffer += data[i];\n    }\n  }\n}\n"
  },
  {
    "path": "lib/taskRunner.ts",
    "content": "import * as child_process from 'child_process';\nimport {EventEmitter} from 'events';\n\nimport {Config} from './config';\nimport {ConfigParser} from './configParser';\nimport {Runner} from './runner';\nimport {TaskLogger} from './taskLogger';\n\nexport interface RunResults {\n  taskId?: number;\n  specs?: Array<string>;\n  capabilities?: any;\n  failedCount?: number;\n  exitCode?: number;\n  specResults?: Array<any>;\n}\n\n/**\n * A runner for running a specified task (capabilities + specs).\n * The TaskRunner can either run the task from the current process (via\n * './runner.js') or from a new process (via './runnerCli.js').\n *\n * @constructor\n * @param {string} configFile Path of test configuration.\n * @param {object} additionalConfig Additional configuration.\n * @param {object} task Task to run.\n * @param {boolean} runInFork Whether to run test in a forked process.\n * @constructor\n */\nexport class TaskRunner extends EventEmitter {\n  constructor(\n      private configFile: string, private additionalConfig: Config, private task: any,\n      private runInFork: boolean) {\n    super();\n  }\n\n  /**\n   * Sends the run command.\n   * @return {Promise} A promise that will resolve when the task finishes\n   *     running. The promise contains the following parameters representing the\n   *     result of the run:\n   *       taskId, specs, capabilities, failedCount, exitCode, specResults\n   */\n  public async run(): Promise<any> {\n    let runResults: RunResults = {\n      taskId: this.task.taskId,\n      specs: this.task.specs,\n      capabilities: this.task.capabilities,\n      // The following are populated while running the test:\n      failedCount: 0,\n      exitCode: -1,\n      specResults: []\n    };\n\n    let configParser = new ConfigParser();\n    if (this.configFile) {\n      configParser.addFileConfig(this.configFile);\n    }\n    if (this.additionalConfig) {\n      configParser.addConfig(this.additionalConfig);\n    }\n    let config = configParser.getConfig();\n    config.capabilities = this.task.capabilities;\n    config.specs = this.task.specs;\n\n    if (this.runInFork) {\n      return new Promise((resolve, reject) => {\n        let childProcess = child_process.fork(\n            __dirname + '/runnerCli.js', process.argv.slice(2), {cwd: process.cwd(), silent: true});\n        let taskLogger = new TaskLogger(this.task, childProcess.pid);\n\n        // stdout pipe\n        childProcess.stdout.on('data', (data: string) => {\n          taskLogger.log(data);\n        });\n\n        // stderr pipe\n        childProcess.stderr.on('data', (data: string) => {\n          taskLogger.log(data);\n        });\n\n        childProcess\n            .on('message',\n                (m: any) => {\n                  if (config.verboseMultiSessions) {\n                    taskLogger.peek();\n                  }\n                  switch (m.event) {\n                    case 'testPass':\n                      process.stdout.write('.');\n                      break;\n                    case 'testFail':\n                      process.stdout.write('F');\n                      break;\n                    case 'testsDone':\n                      runResults.failedCount = m.results.failedCount;\n                      runResults.specResults = m.results.specResults;\n                      break;\n                  }\n                })\n            .on('error',\n                (err: any) => {\n                  taskLogger.flush();\n                  reject(err);\n                })\n            .on('exit', (code: number) => {\n              taskLogger.flush();\n              runResults.exitCode = code;\n              resolve(runResults);\n            });\n\n        childProcess.send({\n          command: 'run',\n          configFile: this.configFile,\n          additionalConfig: this.additionalConfig,\n          capabilities: this.task.capabilities,\n          specs: this.task.specs\n        });\n      });\n    } else {\n      let runner = new Runner(config);\n\n      runner.on('testsDone', (results: RunResults) => {\n        runResults.failedCount = results.failedCount;\n        runResults.specResults = results.specResults;\n      });\n\n      const exitCode = await runner.run();\n      runResults.exitCode = exitCode;\n      return runResults;\n    }\n  }\n}\n"
  },
  {
    "path": "lib/taskScheduler.ts",
    "content": "import {Config} from './config';\nimport {ConfigParser} from './configParser';\n\nexport interface Task {\n  capabilities: any;\n  specs: Array<string>;\n  taskId: string;\n  done: any;\n}\n\n/**\n * The taskScheduler keeps track of the spec files that needs to run next\n * and which task is running what.\n */\nexport class TaskQueue {\n  numRunningInstances: number = 0;\n  maxInstance: number;\n  specsIndex: number = 0;\n\n  // A queue of specs for a particular capacity\n  constructor(public capabilities: any, public specLists: any) {\n    this.maxInstance = capabilities.maxInstances || 1;\n  }\n}\n\nexport class TaskScheduler {\n  taskQueues: Array<TaskQueue>;\n  rotationIndex: number;\n\n  /**\n   * A scheduler to keep track of specs that need running and their associated\n   * capabilities. It will suggest a task (combination of capabilities and spec)\n   * to run while observing the following config rules:\n   * multiCapabilities, shardTestFiles, and maxInstance.\n   * Precondition: multiCapabilities is a non-empty array\n   * (capabilities and getCapabilities will both be ignored)\n   *\n   * @constructor\n   * @param {Object} config parsed from the config file\n   */\n  constructor(private config: Config) {\n    let excludes = ConfigParser.resolveFilePatterns(config.exclude, true, config.configDir);\n    let allSpecs =\n        ConfigParser.resolveFilePatterns(ConfigParser.getSpecs(config), false, config.configDir)\n            .filter((path: string) => {\n              return excludes.indexOf(path) < 0;\n            });\n\n    let taskQueues: Array<TaskQueue> = [];\n    config.multiCapabilities.forEach((capabilities) => {\n      let capabilitiesSpecs = allSpecs;\n      if (capabilities.specs) {\n        let capabilitiesSpecificSpecs =\n            ConfigParser.resolveFilePatterns(capabilities.specs, false, config.configDir);\n        capabilitiesSpecs = capabilitiesSpecs.concat(capabilitiesSpecificSpecs);\n      }\n\n      if (capabilities.exclude) {\n        let capabilitiesSpecExcludes =\n            ConfigParser.resolveFilePatterns(capabilities.exclude, true, config.configDir);\n        capabilitiesSpecs = capabilitiesSpecs.filter((path) => {\n          return capabilitiesSpecExcludes.indexOf(path) < 0;\n        });\n      }\n\n      let specLists: Array<Array<string>> = [];\n      // If we shard, we return an array of one element arrays, each containing\n      // the spec file. If we don't shard, we return an one element array\n      // containing an array of all the spec files\n      if (capabilities.shardTestFiles) {\n        capabilitiesSpecs.forEach((spec) => {\n          specLists.push([spec]);\n        });\n      } else {\n        specLists.push(capabilitiesSpecs);\n      }\n\n      capabilities.count = capabilities.count || 1;\n\n      for (let i = 0; i < capabilities.count; ++i) {\n        taskQueues.push(new TaskQueue(capabilities, specLists));\n      }\n    });\n    this.taskQueues = taskQueues;\n    this.rotationIndex = 0;  // Helps suggestions to rotate amongst capabilities\n  }\n\n  /**\n   * Get the next task that is allowed to run without going over maxInstance.\n   *\n   * @return {{capabilities: Object, specs: Array.<string>, taskId: string,\n   * done: function()}}\n   */\n  public nextTask(): Task {\n    for (let i = 0; i < this.taskQueues.length; ++i) {\n      let rotatedIndex = ((i + this.rotationIndex) % this.taskQueues.length);\n      let queue = this.taskQueues[rotatedIndex];\n      if (queue.numRunningInstances < queue.maxInstance &&\n          queue.specsIndex < queue.specLists.length) {\n        this.rotationIndex = rotatedIndex + 1;\n        ++queue.numRunningInstances;\n        let taskId = '' + rotatedIndex + 1;\n        if (queue.specLists.length > 1) {\n          taskId += '-' + queue.specsIndex;\n        }\n        let specs = queue.specLists[queue.specsIndex];\n        ++queue.specsIndex;\n\n        return {\n          capabilities: queue.capabilities,\n          specs: specs,\n          taskId: taskId,\n          done: function() {\n            --queue.numRunningInstances;\n          }\n        };\n      }\n    }\n    return null;\n  }\n\n  /**\n   * Get the number of tasks left to run or are currently running.\n   *\n   * @return {number}\n   */\n  public numTasksOutstanding(): number {\n    let count = 0;\n    this.taskQueues.forEach((queue) => {\n      count += queue.numRunningInstances + (queue.specLists.length - queue.specsIndex);\n    });\n    return count;\n  }\n\n  /**\n   * Get maximum number of concurrent tasks required/permitted.\n   *\n   * @return {number}\n   */\n  public maxConcurrentTasks(): number {\n    if (this.config.maxSessions && this.config.maxSessions > 0) {\n      return this.config.maxSessions;\n    } else {\n      let count = 0;\n      this.taskQueues.forEach((queue) => {\n        count += Math.min(queue.maxInstance, queue.specLists.length);\n      });\n      return count;\n    }\n  }\n\n  /**\n   * Returns number of tasks currently running.\n   *\n   * @return {number}\n   */\n  public countActiveTasks() {\n    let count = 0;\n    this.taskQueues.forEach((queue) => {\n      count += queue.numRunningInstances;\n    });\n    return count;\n  }\n}\n"
  },
  {
    "path": "lib/util.ts",
    "content": "import * as path from 'path';\nimport * as webdriver from 'selenium-webdriver';\n\nlet STACK_SUBSTRINGS_TO_FILTER = [\n  'node_modules/jasmine/', 'node_modules/selenium-webdriver', 'at Module.', 'at Object.Module.',\n  'at Function.Module', '(timers.js:', 'protractor/lib/'\n];\n\n\n/**\n * Utility function that filters a stack trace to be more readable. It removes\n * Jasmine test frames and webdriver promise resolution.\n * @param {string} text Original stack trace.\n * @return {string}\n */\nexport function filterStackTrace(text: string): string {\n  if (!text) {\n    return text;\n  }\n  let lines = text.split(/\\n/).filter((line) => {\n    for (let filter of STACK_SUBSTRINGS_TO_FILTER) {\n      if (line.indexOf(filter) !== -1) {\n        return false;\n      }\n    }\n    return true;\n  });\n  return lines.join('\\n');\n}\n\n/**\n * Internal helper for abstraction of polymorphic filenameOrFn properties.\n * @param {object} filenameOrFn The filename or function that we will execute.\n * @param {Array.<object>}} args The args to pass into filenameOrFn.\n * @return {Promise} A promise that will resolve when filenameOrFn completes.\n */\nexport async function runFilenameOrFn_(\n    configDir: string, filenameOrFn: any, args?: any[]): Promise<any> {\n  if (filenameOrFn && !(typeof filenameOrFn === 'string' || typeof filenameOrFn === 'function')) {\n    throw new Error('filenameOrFn must be a string or function');\n  }\n\n  if (typeof filenameOrFn === 'string') {\n    filenameOrFn = require(path.resolve(configDir, filenameOrFn));\n  }\n  if (typeof filenameOrFn === 'function') {\n    let results;\n    try {\n      results = await filenameOrFn.apply(null, args);\n    } catch (err) {\n      if (typeof err === 'string') {\n        err = new Error(err);\n      } else {\n        err = err as Error;\n        if (!err.stack) {\n          err.stack = new Error().stack;\n        }\n      }\n      err.stack = exports.filterStackTrace(err.stack);\n      throw err;\n    }\n    return results;\n  } else {\n    return undefined;\n  }\n}\n\n/**\n * Joins two logs of test results, each following the format of <framework>.run\n * @param {object} log1\n * @param {object} log2\n * @return {object} The joined log\n */\nexport function joinTestLogs(log1: any, log2: any): any {\n  return {\n    failedCount: log1.failedCount + log2.failedCount,\n    specResults: (log1.specResults || []).concat(log2.specResults || [])\n  };\n}\n\n/**\n * Returns false if an error indicates a missing or stale element, re-throws\n * the error otherwise\n *\n * @param {*} The error to check\n * @throws {*} The error it was passed if it doesn't indicate a missing or stale\n *   element\n * @return {boolean} false, if it doesn't re-throw the error\n */\nexport function falseIfMissing(error: any) {\n  if ((error instanceof webdriver.error.NoSuchElementError) ||\n      (error instanceof webdriver.error.StaleElementReferenceError)) {\n    return false;\n  } else {\n    throw error;\n  }\n}\n\n/**\n * Return a boolean given boolean value.\n *\n * @param {boolean} value\n * @returns {boolean} given value\n */\nexport function passBoolean(value: boolean) {\n  return value;\n}"
  },
  {
    "path": "lib/webdriver-js-extender/index.js",
    "content": "// Used to provide better protractor documentation for methods given by\n// `webdriver-js-extender`.\n\n/**\n * @fileoverview Extra methods provided by webdriver-js-extender.\n */\n\ngoog.provide('webdriver_extensions');\n\n// /////////////////////////////////////////////////////////////////////////////\n// //\n// //  webdriver_extensions.ExtendedWebDriver\n// //\n// /////////////////////////////////////////////////////////////////////////////\n/**\n * Protractor's `browser` object is a wrapper for an instance of\n * `ExtendedWebDriver`, provided by `webdriver-js-extender`, which itself is\n * just an instance of `selenium-webdriver`'s WebDriver with some extra methods\n * added in. The `browser` object inherits all of WebDriver's and\n * ExtendedWebDriver's methods, but only the methods most useful to Protractor\n * users are documented here.\n *\n * ***If you are not using an appium server, `browser` may sometimes inherit\n * directly from a normal `WebDriver` instance, and thus not inherit any of\n * the extra methods defined by `webdriver-js-extender`.  Even when `browser`\n * does inherit from `ExtendedWebDriver`, these extra methods will only work if\n * your server implements the Appium API.***\n *\n * More information about `webdriver-js-extender` can be found on the [GitHub\n * repo](https://github.com/angular/webdriver-js-extender).\n * @alias ExtendedWebDriver\n * @constructor\n * @extends {webdriver.WebDriver}\n */\nwebdriver_extensions.ExtendedWebDriver = function() {};\n\n/**\n * Various appium commands, including the commands implemented by `wd`.  The\n * names may be different however, and commands which are implemented already by\n * `selenium-webdriver` are not re-implemented by `webdriver-js-extender`.\n *\n * See the [GitHub repo](https://github.com/angular/webdriver-js-extender) for\n * details. \n *\n * @returns {!webdriver.promise.Promise.<*>}\n */\nwebdriver_extensions.ExtendedWebDriver.prototype.Appium_Commands = function() {};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"protractor\",\n  \"description\": \"Webdriver E2E test wrapper for Angular.\",\n  \"homepage\": \"https://github.com/angular/protractor\",\n  \"keywords\": [\n    \"angular\",\n    \"test\",\n    \"testing\",\n    \"webdriver\",\n    \"webdriverjs\",\n    \"selenium\"\n  ],\n  \"author\": \"Julie Ralph <ju.ralph@gmail.com>\",\n  \"dependencies\": {\n    \"blocking-proxy\": \"^1.0.0\",\n    \"browserstack\": \"^1.5.1\",\n    \"chalk\": \"^1.1.3\",\n    \"glob\": \"^7.0.3\",\n    \"jasmine\": \"^3.3.1\",\n    \"saucelabs\": \"^1.5.0\",\n    \"selenium-webdriver\": \"^4.0.0-alpha.1\",\n    \"source-map-support\": \"~0.4.0\",\n    \"webdriver-manager\": \"13.0.0\",\n    \"yargs\": \"^15.3.1\"\n  },\n  \"devDependencies\": {\n    \"@types/chalk\": \"^0.4.28\",\n    \"@types/glob\": \"^5.0.29\",\n    \"@types/jasmine\": \"^3.3.0\",\n    \"@types/loglevel\": \"^1.5.3\",\n    \"@types/minimatch\": \"^2.0.28\",\n    \"@types/node\": \"^6.0.46\",\n    \"@types/selenium-webdriver\": \"^4.0.0\",\n    \"@types/yargs\": \"^12.0.1\",\n    \"body-parser\": \"^1.18.3\",\n    \"chai\": \"~3.5.0\",\n    \"chai-as-promised\": \"~5.3.0\",\n    \"clang-format\": \"1.0.49\",\n    \"expect.js\": \"~0.3.1\",\n    \"express\": \"^4.16.4\",\n    \"gulp\": \"^4.0.0\",\n    \"gulp-clang-format\": \"1.0.23\",\n    \"gulp-tslint\": \"^8.1.3\",\n    \"lodash\": \"^4.17.11\",\n    \"marked\": \"^0.3.3\",\n    \"mocha\": \"^6.0.2\",\n    \"rimraf\": \"~2.5.3\",\n    \"semver\": \"^5.3.0\",\n    \"tslint\": \"^5.11.0\",\n    \"tslint-eslint-rules\": \"^5.4.0\",\n    \"typescript\": \"^3.2.2\",\n    \"vrsource-tslint-rules\": \"^4.0.1\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/angular/protractor.git\"\n  },\n  \"bin\": {\n    \"protractor\": \"bin/protractor\",\n    \"webdriver-manager\": \"bin/webdriver-manager\"\n  },\n  \"main\": \"built/index.js\",\n  \"typings\": \"built/index.d.ts\",\n  \"scripts\": {\n    \"format\": \"gulp format\",\n    \"install_testapp\": \"cd testapp && npm install\",\n    \"prepublish\": \"gulp prepublish\",\n    \"pretest\": \"gulp pretest\",\n    \"start\": \"cd testapp && npm start\",\n    \"test\": \"node scripts/test.js\",\n    \"tsc\": \"tsc\",\n    \"website\": \"cd website && npm start\",\n    \"compile_to_es5\": \"gulp compile_to_es5\"\n  },\n  \"license\": \"MIT\",\n  \"engines\": {\n    \"node\": \">=8.8.x\"\n  },\n  \"version\": \"6.0.0\",\n  \"publishConfig\":{\n    \"registry\": \"https://wombat-dressing-room.appspot.com\"\n  }\n}\n"
  },
  {
    "path": "release.md",
    "content": "Protractor Release Checklist\n----------------------------\n\nSay the previous release was 0.0.J, the current release is 0.0.K, and the next release will be 0.0.L.\n\n - Check that features and bug fixes are in by looking at the milestone tag for 0.0.K. Create a milestone for 0.0.L, and bump anything that doesn't need to be finished from 0.0.K to 0.0.L.\n\n - Check if there are new versions of [selenium and iedriver](http://selenium-release.storage.googleapis.com/index.html), [chromedriver](http://chromedriver.storage.googleapis.com/index.html), or [latest browsers](https://saucelabs.com/platforms) that the configuration needs to be updated against. We test against the latest two versions of Chrome, Firefox, and IE.\n\n   - The latest selenium version should be used in spec/ciFullConf.js, spec/ciSmokeConf.js, and spec/ciNg2Conf.js.\n   - The versions in config.json/webdriverVersions should be up to date, and you should run `webdriver-manager update` locally.\n   - The latest version of Chrome and Firefox should be used in spec/ciFullConf.js and spec/ciNg2Conf.js. All other browsers we support should be listed in spec/ciSmokeConf.js.\n\n - Make sure [Travis](https://travis-ci.org/angular/protractor/builds) is passing. Note that there is an 'allowed failures' section in Travis - make sure that all failures are known.\n\n - Make sure [CircleCI](https://circleci.com/gh/angular/protractor) is passing (this runs `npm test`)\n\n - Make sure .gitignore and .npmignore are updated with any new files that need to be ignored.\n\n - Make sure that the website and doc generation still work.  Doing so now, before you update the package.json or CHANGELOG.md, will save you a big headache.\n   - Run `./scripts/generate-docs.sh HEAD` to generate the docs against the current commit.\n   - We have to compile down to es5 to get dgeni to work. `generate-docs.sh` can handle some of this but you may have to make minor changes to the codebase/build infrastructure.\n   - Run the unit and e2e tests for the website.\n\n - Update package.json with a version bump. If the changes are only bug fixes, increment the patch (e.g. 0.0.5 -> 0.0.6), otherwise increment the minor version.\n\n - Update CHANGELOG.md.\n   - You can get a list of changes in the correct format by running\n     ```\n     git log 0.0.J..HEAD --format=\"- ([%h](https://github.com/angular/protractor/commit/%H)) %n%w(100,2,2)%B\" > /tmp/changes.txt\n     ```\n\n   - Create a new section in CHANGELOG.md and copy in features (`feat`), big dependency version updates (`deps`), bug fixes (`fix`), and breaking changes. No need to note chores or stylistic changes - the changelog should be primarily useful to someone using Protractor, not developing on it.\n\n   - Breaking changes should be in their own section and include before/after examples of how to fix code that needs to change.\n\n - Make a commit with the API and package.json changes titled chore(release): version bump and changelog for 0.0.K.\n\n - Tag the release with `git tag 0.0.K`\n\n - Push to github\n\n - Push tags to github (`git push <remote> --tags`)\n\n - Verify that the changelog and tags look sane on github\n\n - Login to the wombot NPM proxy\n```sh\nnpm login --registry https://wombat-dressing-room.appspot.com\n ```\n\n - NPM publish\n\n - Update the website. Run `./scripts/generate-docs.sh`, then switch to the `gh-pages` branch, edit the commit message with `git commit --amend`, and push the new website.\n\n - Run e2e test against the published website.\n\n - Let people know\n   - Have @ProtractorTest tweet about it\n\n - Close the 0.0.K milestone\n"
  },
  {
    "path": "scripts/browserstack_local_setup.sh",
    "content": "curl -sO https://www.browserstack.com/browserstack-local/BrowserStackLocal-linux-x64.zip\nunzip BrowserStackLocal-linux-x64.zip -d local\n./local/BrowserStackLocal ` echo $BROWSER_STACK_ACCESS_KEY | rev ` &\n"
  },
  {
    "path": "scripts/dependency_test.json",
    "content": "{\n  \"spec_dir\": \"\",\n  \"spec_files\": [\n    \"spec/dependencyTest/*_spec.js\"\n  ]\n}\n"
  },
  {
    "path": "scripts/driverProviderAttachSession.js",
    "content": "#!/usr/bin/env node\n\n'use strict';\n\nconst http = require('http');\nconst child_process = require('child_process');\n\n// Delete session method to be used at the end of the test as well as\n// when the tests fail.\nconst deleteSession = (sessionId, err) => {\n  return new Promise(resolve => {\n    const deleteOptions = {\n      hostname: 'localhost',\n      port: 4444,\n      path: '/wd/hub/session/' + sessionId,\n      method: 'DELETE'\n    };\n    const req = http.request(deleteOptions, res => {\n      res.on('end', () => {\n        if (err) {\n          throw err;\n        }\n        resolve();\n      });\n    });\n    req.end();\n  });\n};\n\nconst run = async () => {\n  // 1. Create a new selenium session.\n  const sessionId = await new Promise(resolve => {\n    const postData = JSON.stringify(\n      {'desiredCapabilities': {'browserName': 'chrome'}});\n    const createOptions = {\n      hostname: '127.0.0.1',\n      port: 4444,\n      path: '/wd/hub/session',\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/x-www-form-urlencoded',\n        'Content-Length': Buffer.byteLength(postData)\n      }\n    };\n    let body = '';\n    const req = http.request(createOptions, (res) => {\n      res.on('data', (data) => {\n        body = JSON.parse(data.toString());\n      });\n      res.on('end', () => {\n        resolve(body.sessionId);\n      });\n    });\n    req.write(postData);\n    req.end();\n  });\n\n  await new Promise(resolve => {\n    // 2. After getting the session id, verify that the selenium session exists.\n    // If the session exists, run the protractor test.\n    const checkOptions = {\n      hostname: '127.0.0.1',\n      port: 4444,\n      path: '/wd/hub/sessions',\n      method: 'GET'\n    };\n    \n    let values = [];\n    const req = http.request(checkOptions, (res) => {\n      res.on('data', (chunk) => {\n        values = JSON.parse(chunk.toString())['value'];\n      });\n      res.on('end', () => {\n        let found = false;\n        for (let value of values) {\n          if (value['id'] === sessionId) {\n            found = true;\n          }\n        }\n        if (found) {\n          resolve();\n        } else {\n          throw new Error('The selenium session was not created.');\n        }\n      });\n      res.on('error', (err) => {\n        console.log(err);\n        process.exit(1);\n      });\n    });\n    req.end();\n  });\n\n  // 3. Run Protractor and attach to the session.\n  const runProtractor = child_process.spawnSync('node',\n      ['bin/protractor', 'spec/driverProviderAttachSessionConf.js',\n      '--seleniumSessionId=' + sessionId]);\n  console.log(runProtractor.stdout.toString());\n  if (runProtractor.status !== 0) {\n    const e = new Error('Protractor did not run properly.');\n    await deleteSession(sessionId, e);\n    process.exit(1);\n  }\n\n  // 4. After the protractor test completes, check to see that the session still\n  // exists. If we can find the session, delete it.\n  await new Promise(resolve => {\n    const checkOptions = {\n      hostname: '127.0.0.1',\n      port: 4444,\n      path: '/wd/hub/session/' + sessionId,\n      method: 'GET'\n    };\n    const req = http.request(checkOptions, (res) => {\n      let state = '';\n      res.on('data', (chunk) => {    \n        state = JSON.parse(chunk.toString()).state;\n      });\n      res.on('end', () => {\n        if (state === 'success') {\n          resolve();\n        }\n        else {\n          const e = new Error('The selenium session should still exist.');\n          deleteSession(sessionId, e);\n        }\n      });\n      res.on('error', (err) => {\n        console.log(err);\n        process.exit(1);\n      });\n    });\n    req.end();\n  });\n\n  // 5. Delete the selenium session.\n  await deleteSession(sessionId); \n}\n\nrun().then();\n"
  },
  {
    "path": "scripts/errorTest.js",
    "content": "#!/usr/bin/env node\n\n'use strict';\n\nvar spawn = require('child_process').spawnSync;\nvar exitCodes = require('../built/exitCodes');\n\nvar runProtractor, output, messages;\nvar checkLogs = function(output, messages) {\n  for (var pos in messages) {\n    var message = messages[pos];\n    if (output.indexOf(message) === -1) {\n      throw new Error('\\'' + message + '\\'' + ' does not exist in logs: ' + output);\n    }\n  }\n};\n\n/******************************\n *Below are exit failure tests*\n ******************************/\n\n// assert authentication error for sauce labs\nrunProtractor = spawn('node',\n    ['bin/protractor', 'spec/errorTest/sauceLabsAuthentication.js']);\noutput = runProtractor.stdout.toString();\nmessages = ['Sauce Labs Authentication Error.',\n    'Process exited with error code ' + exitCodes.BrowserError.CODE];\ncheckLogs(output, messages);\n\n// assert authentication error for browser stack\nrunProtractor = spawn('node',\n    ['bin/protractor', 'spec/errorTest/browserStackAuthentication.js']);\noutput = runProtractor.stdout.toString();\nmessages = ['Invalid username or password',\n    'Process exited with error code ' + exitCodes.BrowserError.CODE];\ncheckLogs(output, messages);\n\n\n// assert there is no capabilities in the config\nrunProtractor = spawn('node',\n    ['bin/protractor', 'spec/errorTest/debugMultiCapabilities.js']);\noutput = runProtractor.stdout.toString();\nmessages = [\n  'Cannot run in debug mode with multiCapabilities, count > 1, or sharding',\n  'Process exited with error code ' + exitCodes.ConfigError.CODE];\ncheckLogs(output, messages);\n\nrunProtractor = spawn('node',\n    ['bin/protractor', 'spec/errorTest/getMultiCapabilitiesConf.js']);\noutput = runProtractor.stderr.toString();\nmessages = [\n  'Error: get multi capabilities failed'];\ncheckLogs(output, messages);\n"
  },
  {
    "path": "scripts/generate-docs.sh",
    "content": "#!/bin/sh\ncd \"$( dirname \"${BASH_SOURCE[0]}\" )/..\"\n\n# Check number of parameters\nif [ \"$#\" -gt 1 ]; then\n  echo \"Usage: ./scripts/generate-docs.sh [commit_ref]\"\n  exit 1\nfi\n\n# Check that directory is clean\nif [ $(git status --porcelain | wc -l) != \"0\" ]; then\n  echo -e \"\\033[0;31m\" 1>&2 # Red\n  echo \"We cannot push the generated docs unless the working directory is\" 1>&2\n  echo \"clean.  Either commit your changes, stash them, or generate the\" 1>&2\n  echo \"docs manually by running gulp in /website/ and push them to\" 1>&2\n  echo \"gh-pages at a later date.\" 1>&2\n  echo -e \"\\033[0m\" 1>&2 # Normal color\n  exit 1\nfi\n\n# If a commit ref is passed as a command line option, use that.\n# Otherwise, default to the tag corresponding to the version in package.json.\nif [ \"$#\" -eq 0 ]; then\n  VERSION=$(node scripts/get-version.js)\nelse\n  VERSION=$1\nfi\nEXEC_BRANCH=$(git rev-parse --abbrev-ref HEAD)\n\necho \"Switching to ${VERSION}...\"\ngit checkout \"${VERSION}\"\nif [ $? -ne 0 ]; then\n  echo -e \"\\033[0;31m\" 1>&2 # Red\n  if [ \"$#\" -eq 0 ]; then\n    echo \"The package.json file indicates that the current version is\" 1>&2\n    echo \"\\\"${VERSION}\\\", but there is no corresponding git tag.\" 1>&2\n  else\n    echo \"Cannot checkout \\\"${VERSION}\\\".\" 1>&2\n  fi\n  echo -e \"\\033[0m\" 1>&2 # Normal Color\n  git checkout \"${EXEC_BRANCH}\"\n  exit 1\nfi\n\necho \"Removing temp files...\"\ngit clean -fxd\nif [ $? -ne 0 ]; then\n  echo -e \"\\033[0;31m\" 1>&2 # Red\n  echo \"Could not remove untracked/ignored files.\"\n  echo -e \"\\033[0m\" 1>&2 # Normal Color\n  git checkout \"${EXEC_BRANCH}\"\n  exit 1\nfi\n\necho \"Main \\`npm install\\`...\"\nnpm install\nif [ $? -ne 0 ]; then\n  echo -e \"\\033[0;31m\" 1>&2 # Red\n  echo \"\\`npm install\\` failed.\"\n  echo -e \"\\033[0m\" 1>&2 # Normal Color\n  git checkout \"${EXEC_BRANCH}\"\n  exit 1\nfi\n\n# Compile to es5\n./scripts/compile_to_es5.sh\nif [ $? -ne 0 ]; then\n  git checkout \"${EXEC_BRANCH}\"\n  exit 1\nfi\n\necho \"Installing the testapp...\"\nnpm run install_testapp\nif [ $? -ne 0 ]; then\n  echo -e \"\\033[0;31m\" 1>&2 # Red\n  echo \"Couldn't install testapp.\"\n  echo -e \"\\033[0m\" 1>&2 # Normal Color\n  git checkout \"${EXEC_BRANCH}\"\n  exit 1\nfi\n\necho \"Installing the website...\"\ncd website\nnpm install\nif [ $? -ne 0 ]; then\n  echo -e \"\\033[0;31m\" 1>&2 # Red\n  echo \"Failed to install website dependencies.\"\n  echo -e \"\\033[0m\" 1>&2 # Normal Color\n  git checkout \"${EXEC_BRANCH}\"\n  exit 1\nfi\n\n\necho \"Building the website...\"\nnpm run build\nif [ $? -ne 0 ]; then\n  echo -e \"\\033[0;31m\" 1>&2 # Red\n  echo \"Website build failed.\"\n  echo -e \"\\033[0m\" 1>&2 # Normal Color\n  git checkout \"${EXEC_BRANCH}\"\n  exit 1\nfi\n\necho \"Transfering files to gh-pages...\"\ncd \"..\"\ngit branch -D gh-pages\ngit pull -f https://github.com/angular/protractor.git gh-pages:gh-pages\ngit reset --hard\ngit checkout gh-pages\ncp -r website/build/* .\ngit add -A\ngit commit -m \"chore(website): automatic docs update for ${VERSION}\"\necho -e \"\\033[0;32m\" # Green\necho \"Created update commit in gh-pages branch.\"\necho -e \"\\033[0m\" 1>&2 # Normal Color\ngit checkout \"${EXEC_BRANCH}\"\n"
  },
  {
    "path": "scripts/get-version.js",
    "content": "console.log(require('../package.json').version);\n"
  },
  {
    "path": "scripts/print_logs.sh",
    "content": "#!/bin/bash\n\nLOG_FILES=$LOGS_DIR/*\n\nfor FILE in $LOG_FILES; do\n  echo -e \"\\n\\n\\n\"\n  echo \"==================================================================\"\n  echo \" $FILE\"\n  echo \"==================================================================\"\n  cat $FILE\ndone\n"
  },
  {
    "path": "scripts/sauce_connect_setup.sh",
    "content": "#!/bin/bash\n\nset -e\n\n# Setup and start Sauce Connect for your TravisCI build\n# This script requires your .travis.yml to include the following two private env variables:\n# SAUCE_USERNAME\n# SAUCE_ACCESS_KEY\n# Follow the steps at https://saucelabs.com/opensource/travis to set that up.\n#\n# Curl and run this script as part of your .travis.yml before_script section:\n# before_script:\n#   - curl https://gist.github.com/santiycr/5139565/raw/sauce_connect_setup.sh | bash\n\nCONNECT_DOWNLOAD=\"sc-4.5.4-linux.tar.gz\"\nCONNECT_URL=\"https://saucelabs.com/downloads/${CONNECT_DOWNLOAD}\"\nCONNECT_DIR=\"/tmp/sauce-connect-$RANDOM\"\n\n# Log files are not used for now\n# CONNECT_LOG=\"$LOGS_DIR/sauce-connect\"\n# CONNECT_STDOUT=\"$LOGS_DIR/sauce-connect.stdout\"\n# CONNECT_STDERR=\"$LOGS_DIR/sauce-connect.stderr\"\n\n# Get Connect and start it\nmkdir -p $CONNECT_DIR\ncd $CONNECT_DIR\ncurl $CONNECT_URL -o $CONNECT_DOWNLOAD 2> /dev/null 1> /dev/null\nmkdir sauce-connect\ntar --extract --file=$CONNECT_DOWNLOAD --strip-components=1 --directory=sauce-connect > /dev/null\nrm $CONNECT_DOWNLOAD\n\nSAUCE_ACCESS_KEY=`echo $SAUCE_ACCESS_KEY | rev`\n\n\nARGS=\"\"\n\n# Set tunnel-id only on Travis, to make local testing easier.\nif [ ! -z \"$TRAVIS_JOB_NUMBER\" ]; then\n  ARGS=\"$ARGS --tunnel-identifier $TRAVIS_JOB_NUMBER\"\nfi\nif [ ! -z \"$BROWSER_PROVIDER_READY_FILE\" ]; then\n  ARGS=\"$ARGS --readyfile $BROWSER_PROVIDER_READY_FILE\"\nfi\n\n\necho \"Starting Sauce Connect in the background, args:\"\necho \"  $ARGS\"\nsauce-connect/bin/sc -u $SAUCE_USERNAME -k $SAUCE_ACCESS_KEY $ARGS &\n\n# If you need to debug sauce connect, use the --doctor flag.\n# It will print diagnostic messages but will not start a tunnel.\n# See https://wiki.saucelabs.com/display/DOCS/Sauce+Connect+Proxy+Debugging+and+Diagnostics+with+--doctor+flag\n# sauce-connect/bin/sc -u $SAUCE_USERNAME -k $SAUCE_ACCESS_KEY --doctor $ARGS\n"
  },
  {
    "path": "scripts/test/test_util.js",
    "content": "#!/usr/bin/env node\n\nconst child_process = require('child_process');\nconst fs = require('fs');\n\nclass CommandlineTest {\n  constructor(command) {\n    this.command = command;\n    this.expectedExitCode = 0;\n    this.expectedErrors = [];\n    this.isExitCode = false;\n    this.testLogStream = undefined;\n    this.expectedMinTestDuration = undefined;\n    this.expectedMaxTestDuration = undefined;\n  }\n\n\n  // Only assert the exit code and not failures.\n  // This must be true if the command you're running does not support\n  //   the flag '--resultJsonOutputFile'.\n  assertExitCodeOnly() {\n    this.isExitCode = true;\n    return this;\n  }\n\n  setTestLogFile(filename) {\n    this.testLogStream = fs.createWriteStream(filename, {flags: 'a'});\n  }\n\n  // Set the expected exit code for the test command.\n  expectExitCode(exitCode) {\n    this.expectedExitCode = exitCode;\n    return this;\n  }\n\n  // Set the expected total test duration in milliseconds.\n  expectTestDuration(min, max) {\n    this.expectedMinTestDuration = min;\n    this.expectedMaxTestDuration = max;\n    return this;\n  }\n\n  /**\n   * Add expected error(s) for the test command.\n   * Input is an object or list of objects of the following form:\n   * {\n   *   message: string, // optional regex\n   *   stackTrace: string, //optional regex\n   * }\n   */\n  expectErrors(expectedErrors) {\n    if (expectedErrors instanceof Array) {\n      this.expectedErrors = this.expectedErrors.concat(expectedErrors);\n    } else {\n      this.expectedErrors.push(expectedErrors);\n    }\n    return this;\n  }\n\n  async run() {\n    const start = new Date().getTime();\n    const testOutputPath = `test_output_${start}.tmp`;\n    let output = '';\n\n    const flushAndFail = (errorMsg) => {\n      process.stdout.write(output);\n      throw new Error(errorMsg);\n    };\n\n    try {\n\n      let exitCode = await new Promise((resolve, reject) => {\n        if (!this.assertExitCodeOnly) {\n          this.command = `${this.command} --resultJsonOutputFile ${testOutputPath}`;\n        }\n        const args = this.command.split(/\\s/);\n        const test_process = child_process.spawn(args[0], args.slice(1));\n\n        const processData = (data) => {\n          process.stdout.write('.');\n          output += data;\n          if (this.testLogStream) {\n            this.testLogStream.write(data);\n          }\n        };\n\n        test_process.stdout.on('data', processData);\n        test_process.stderr.on('data', processData);\n\n        test_process.on('error', (err) => {\n          reject(err);\n        });\n\n        test_process.on('exit', function(exitCode) {\n          resolve(exitCode);\n        });\n      });\n      \n      if (this.expectedExitCode !== exitCode) {\n        flushAndFail('expecting exit code: ' + this.expectedExitCode +\n              ', actual: ' + exitCode);\n      }\n\n      if (this.testLogStream) {\n        this.testLogStream.end();\n      }\n\n      // Skip the rest if we are only verify exit code.\n      // Note: we're expecting a file populated by '--resultJsonOutputFile' after\n      //   this point.\n      if (this.assertExitCodeOnly) {\n        return;\n      }\n\n      const raw_data = fs.readFileSync(testOutputPath);\n      const testOutput = JSON.parse(raw_data);\n\n      let actualErrors = [];\n      let duration = 0;\n      testOutput.forEach(function(specResult) {\n        duration += specResult.duration;\n        specResult.assertions.forEach(function(assertion) {\n          if (!assertion.passed) {\n            actualErrors.push(assertion);\n          }\n        });\n      });\n\n      this.expectedErrors.forEach((expectedError) => {\n        let found = false;\n        for (let i = 0; i < actualErrors.length; ++i) {\n          var actualError = actualErrors[i];\n\n          // if expected message is defined and messages don't match\n          if (expectedError.message) {\n            if (!actualError.errorMsg ||\n                !actualError.errorMsg.match(new RegExp(expectedError.message))) {\n                  continue;\n                }\n          }\n          // if expected stackTrace is defined and stackTraces don't match\n          if (expectedError.stackTrace) {\n            if (!actualError.stackTrace ||\n                !actualError.stackTrace.match(new RegExp(expectedError.stackTrace))) {\n                  continue;\n                }\n          }\n          found = true;\n          break;\n        }\n\n        if (!found) {\n          if (expectedError.message && expectedError.stackTrace) {\n            flushAndFail('did not fail with expected error with message: [' +\n              expectedError.message + '] and stackTrace: [' +\n              expectedError.stackTrace + ']');\n          } else if (expectedError.message) {\n            flushAndFail('did not fail with expected error with message: [' +\n              expectedError.message + ']');\n          } else if (expectedError.stackTrace) {\n            flushAndFail('did not fail with expected error with stackTrace: [' +\n              expectedError.stackTrace + ']');\n          }\n        } else {\n          actualErrors.splice(i, 1);\n        }\n      });\n\n      if (actualErrors.length > 0) {\n        flushAndFail('failed with ' + actualErrors.length + ' unexpected failures');\n      }\n\n      if (this.expectedMinTestDuration\n          && duration < this.expectedMinTestDuration) {\n            flushAndFail('expecting test min duration: ' +\n              this.expectedMinTestDuration + ', actual: ' + duration);\n      }\n      if (this.expectedMaxTestDuration\n          && duration > this.expectedMaxTestDuration) {\n            flushAndFail('expecting test max duration: ' +\n              this.expectedMaxTestDuration + ', actual: ' + duration);\n      }\n    } finally {\n      try {\n        fs.unlinkSync(testOutputPath);\n      } catch (err) {\n        // don't do anything\n      }\n    }\n  }\n}\n\n/**\n * Util for running tests and testing functionalities including:\n *   exitCode, test durations, expected errors, and expected stackTrace\n * Note, this will work with any commandline tests, but only if it supports\n *   the flag '--resultJsonOutputFile', unless only exitCode is being tested.\n *   For now, this means protractor tests (jasmine/mocha).\n */\nexports.Executor = function() {\n  let tests = [];\n  this.addCommandlineTest = (command) => {\n    let test = new CommandlineTest(command);\n    tests.push(test);\n    return test;\n  };\n\n  this.runTests = async function(i, logFile, failed) {\n    if (i < tests.length) {\n      try {\n        console.log('running: ' + tests[i].command);\n        if (logFile) {\n          tests[i].setTestLogFile(logFile);\n        }\n        await tests[i].run();\n        console.log('\\n>>> \\033[1;32mpass\\033[0m');\n      } catch (err) {\n        failed = true;\n        console.log('\\n>>> \\033[1;31mfail: ' + err.toString() + '\\033[0m');\n      } finally {\n        this.runTests(i + 1, logFile, failed);\n      }\n    } else {\n      console.log('Summary: ' + (failed ? 'fail' : 'pass'));\n      process.exit(failed ? 1 : 0);\n    }\n  };\n\n  this.execute = (logFile) => {\n    let failed = false;\n    this.runTests(0, logFile, failed);\n  };\n};\n"
  },
  {
    "path": "scripts/test.js",
    "content": "#!/usr/bin/env node\nconst path = require('path');\n\nconst Executor = require('./test/test_util').Executor;\n\nconst passingTests = [\n  'node built/cli.js spec/basicConf.js',\n  'node built/cli.js spec/basicConf.js --useBlockingProxy',\n  'node built/cli.js spec/multiConf.js',\n  'node built/cli.js spec/altRootConf.js',\n  'node built/cli.js spec/inferRootConf.js',\n  'node built/cli.js spec/onCleanUpAsyncReturnValueConf.js',\n  'node built/cli.js spec/onCleanUpNoReturnValueConf.js',\n  'node built/cli.js spec/onCleanUpSyncReturnValueConf.js',\n  'node built/cli.js spec/onPrepareConf.js',\n  'node built/cli.js spec/onPrepareFileConf.js',\n  'node built/cli.js spec/onPreparePromiseConf.js',\n  'node built/cli.js spec/onPreparePromiseFileConf.js',\n  'node built/cli.js spec/mochaConf.js',\n  'node built/cli.js spec/withLoginConf.js',\n  'node built/cli.js spec/suitesConf.js --suite okmany',\n  'node built/cli.js spec/suitesConf.js --suite okspec',\n  'node built/cli.js spec/suitesConf.js --suite okmany,okspec',\n  'node built/cli.js spec/plugins/smokeConf.js',\n  'node built/cli.js spec/plugins/multiPluginConf.js',\n  'node built/cli.js spec/plugins/jasminePostTestConf.js',\n  'node built/cli.js spec/plugins/mochaPostTestConf.js',\n  'node built/cli.js spec/plugins/browserGetSyncedConf.js',\n  'node built/cli.js spec/plugins/browserGetUnsyncedConf.js',\n  'node built/cli.js spec/plugins/waitForAngularConf.js',\n  'node built/cli.js spec/interactionConf.js',\n  'node built/cli.js spec/directConnectConf.js',\n  'node built/cli.js spec/restartBrowserBetweenTestsConf.js',\n  'node spec/driverProviderTest.js',\n  'node built/cli.js spec/driverProviderLocalConf.js',\n  'node built/cli.js spec/driverProviderLocalConf.js --useBlockingProxy',\n  'node built/cli.js spec/getCapabilitiesConf.js',\n  'node built/cli.js spec/controlLockConf.js',\n  'node built/cli.js spec/customFramework.js',\n  'node built/cli.js spec/noGlobalsConf.js',\n  'node built/cli.js spec/angular2Conf.js',\n  'node built/cli.js spec/hybridConf.js',\n  'node built/cli.js spec/built/noCFBasicConf.js',\n  'node built/cli.js spec/built/noCFBasicConf.js --useBlockingProxy',\n  'node built/cli.js spec/built/noCFPluginConf.js',\n  'node scripts/driverProviderAttachSession.js',\n  'node scripts/errorTest.js',\n  // Unit tests\n  'node node_modules/jasmine/bin/jasmine.js JASMINE_CONFIG_PATH=scripts/unit_test.json',\n  // Dependency tests\n  'node node_modules/jasmine/bin/jasmine.js JASMINE_CONFIG_PATH=scripts/dependency_test.json',\n  // Typings tests\n  // TODO(selenium4): consider rewriting this test.\n  // 'node spec/install/test.js'\n];\n\nconst executor = new Executor();\n\npassingTests.forEach((passing_test) => {\n  executor.addCommandlineTest(passing_test)\n      .assertExitCodeOnly();\n});\n\n/*************************\n *Below are failure tests*\n *************************/\n\n// assert stacktrace shows line of failure\nexecutor.addCommandlineTest('node built/cli.js spec/errorTest/singleFailureConf.js')\n    .expectExitCode(1)\n    .expectErrors({\n      stackTrace: 'single_failure_spec1.js:5:38'\n    });\n\n// assert timeout works\nexecutor.addCommandlineTest('node built/cli.js spec/errorTest/timeoutConf.js')\n    .expectExitCode(1)\n    .expectErrors({\n      message: 'Timeout - Async callback was not invoked within timeout ' +\n          'specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.'\n    })\n    .expectTestDuration(0, 1000);\n\nexecutor.addCommandlineTest('node built/cli.js spec/errorTest/afterLaunchChangesExitCodeConf.js')\n    .expectExitCode(11)\n    .expectErrors({\n      message: 'Expected \\'Hiya\\' to equal \\'INTENTIONALLY INCORRECT\\'.'\n    });\n\nexecutor.addCommandlineTest('node built/cli.js spec/errorTest/multiFailureConf.js')\n    .expectExitCode(1)\n    .expectErrors([{\n      message: 'Expected \\'Hiya\\' to equal \\'INTENTIONALLY INCORRECT\\'.',\n      stacktrace: 'single_failure_spec1.js:5:38'\n    }, {\n      message: 'Expected \\'Hiya\\' to equal \\'INTENTIONALLY INCORRECT\\'.',\n      stacktrace: 'single_failure_spec2.js:5:38'\n    }]);\n\nexecutor.addCommandlineTest('node built/cli.js spec/errorTest/shardedFailureConf.js')\n    .expectExitCode(1)\n    .expectErrors([{\n      message: 'Expected \\'Hiya\\' to equal \\'INTENTIONALLY INCORRECT\\'.',\n      stacktrace: 'single_failure_spec1.js:5:38'\n    }, {\n      message: 'Expected \\'Hiya\\' to equal \\'INTENTIONALLY INCORRECT\\'.',\n      stacktrace: 'single_failure_spec2.js:5:38'\n    }]);\n\nexecutor.addCommandlineTest('node built/cli.js spec/errorTest/mochaFailureConf.js')\n    .expectExitCode(1)\n    .expectErrors([{\n      message: 'expected \\'My AngularJS App\\' to equal \\'INTENTIONALLY INCORRECT\\'',\n      stacktrace: 'mocha_failure_spec.js:11:41'\n    }]);\n\nexecutor.addCommandlineTest('node built/cli.js spec/errorTest/pluginsFailingConf.js')\n    .expectExitCode(1)\n    .expectErrors([\n      {message: 'Expected true to be false'},\n      {message: 'from setup'},\n      {message: 'from postTest passing'},\n      {message: 'from postTest failing'},\n      {message: 'from teardown'}\n    ]);\n\nexecutor.addCommandlineTest('node built/cli.js spec/errorTest/slowHttpAndTimeoutConf.js')\n    .expectExitCode(1)\n    .expectErrors([\n      {message: 'The following tasks were pending[\\\\s\\\\S]*\\\\$http: slowcall'},\n      {message: 'The following tasks were pending:[\\\\s\\\\S]*' +\n                '- \\\\$timeout: function\\\\(\\\\) {[\\\\s\\\\S]*' +\n                  '\\\\$scope\\\\.slowAngularTimeoutStatus = \\'done\\';[\\\\s\\\\S]' +\n                '*}'}\n    ]);\n\nexecutor.addCommandlineTest('node built/cli.js spec/errorTest/slowHttpAndTimeoutConf.js ' +\n                            '--untrackOutstandingTimeouts true')\n    .expectExitCode(1)\n    .expectErrors([\n      {message: 'The following tasks were pending[\\\\s\\\\S]*\\\\$http: slowcall'},\n      {message: 'While waiting for element with locator - ' +\n                'Locator: by.binding\\\\(\\\\\"slowAngularTimeoutStatus\\\\\"\\\\)$'}\n    ]);\n\nexecutor.addCommandlineTest('node built/cli.js spec/angular2TimeoutConf.js')\n    .expectExitCode(1)\n    .expectErrors([\n      {message: 'Timed out waiting for asynchronous Angular tasks to finish'},\n    ]);\n\n// If we're running on CircleCI, save stdout and stderr from the test run to a log file.\nif (process.env['CIRCLE_ARTIFACTS']) {\n  executor.execute(path.join(process.env['CIRCLE_ARTIFACTS'], 'test_log.txt'));\n} else {\n  executor.execute();\n}\n"
  },
  {
    "path": "scripts/test_on_travis.sh",
    "content": "SAUCE_ACCESS_KEY=`echo $SAUCE_ACCESS_KEY | rev`\nBROWSER_STACK_ACCESS_KEY=`echo $BROWSER_STACK_ACCESS_KEY | rev`\n\nif [ $JOB = \"smoke\" ]; then\n  node bin/protractor spec/ciSmokeConf.js\nelif [ $JOB = \"full\" ]; then\n  node bin/protractor spec/ciFullConf.js\n  # if [ $? = \"0\" ]; then\n  #   node bin/protractor spec/ciNg2Conf.js\n  # else\n  # \texit 1\n  # fi\nelif [ $JOB = \"bstack\" ]; then\n  node bin/protractor spec/ciBStackConf.js\nelse\n  echo \"Unknown job type. Please set JOB=smoke, JOB=full, or JOB=bstack\"\nfi\n"
  },
  {
    "path": "scripts/testserver.sh",
    "content": "#!/bin/bash\n\n# Start up the server in a way that won't block Travis.\ncd testapp\nnpm run start &\nsleep 1\necho Test application started\n"
  },
  {
    "path": "scripts/travis_setup.sh",
    "content": "if [ $JOB == \"bstack\" ]; then\n  echo \"Setting up Browser Stack\"\n  ./scripts/browserstack_local_setup.sh\nelse\n  echo \"Setting up Sauce Labs\"\n  ./scripts/sauce_connect_setup.sh\n  ./scripts/wait_for_browser_provider.sh\nfi\n"
  },
  {
    "path": "scripts/unit_test.json",
    "content": "{\n  \"spec_dir\": \"\",\n  \"spec_files\": [\n    \"spec/unit/**/*.js\",\n    \"website/docgen/spec/*.js\"\n  ]\n}\n"
  },
  {
    "path": "scripts/wait_for_browser_provider.sh",
    "content": "#!/bin/bash\n\n\n# Wait for Connect to be ready before exiting\nwhile [ ! -f $BROWSER_PROVIDER_READY_FILE ]; do\n  sleep .5\ndone\n"
  },
  {
    "path": "spec/.jshintrc",
    "content": "{\n  \"strict\": false,\n  \"esversion\": 6,\n  \"predef\": [\n    \"protractor\",\n    \"browser\",\n    \"by\",\n    \"element\",\n    \"it\",\n    \"describe\",\n    \"beforeEach\",\n    \"afterEach\"\n  ]\n}\n"
  },
  {
    "path": "spec/altRoot/findelements_spec.js",
    "content": "describe('finding elements when ng-app is nested', () => {\n  beforeEach(async() => {\n    await browser.get('alt_root_index.html#/form');\n  });\n\n  it('should find an element by binding', async() => {\n    const greeting = element(by.binding('{{greeting}}'));\n\n    expect(await greeting.getText()).toEqual('Hiya');\n  });\n\n  it('should find elements outside of angular', async() => {\n    const outside = element(by.id('outside-ng'));\n    const inside = element(by.id('inside-ng'));\n\n    expect(await outside.getText()).toEqual('{{1 + 2}}');\n    expect(await inside.getText()).toEqual('3');\n  });\n});\n"
  },
  {
    "path": "spec/altRootConf.js",
    "content": "var env = require('./environment.js');\n\n// Tests for an Angular app where ng-app is not on the body.\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  // Spec patterns are relative to this config.\n  specs: [\n    'altRoot/*_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  // Selector for the element housing the angular app.\n  rootElement: 'div#nested-ng-app'\n};\n"
  },
  {
    "path": "spec/angular2Conf.js",
    "content": "var env = require('./environment');\n\n// This is the configuration for a smoke test for an Angular TypeScript application.\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  specs: [\n    'ng2/async_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl,\n  allScriptsTimeout: 120000,\n  getPageTimeout: 120000,\n  jasmineNodeOpts: {\n    defaultTimeoutInterval: 120000\n  }\n};\n"
  },
  {
    "path": "spec/angular2TimeoutConf.js",
    "content": "var env = require('./environment');\n\n// This is the configuration for a smoke test for an Angular2 application.\n//\n// *** NOTE ***\n// As Angular2 is in rapid development, the test application that ships with\n// the Protractor repository does not yet contain an Angular2 section. This\n// configuration assumes that you are serving the examples from the\n// angular/angular repository at localhost:8000.\n// See https://github.com/angular/angular/blob/master/DEVELOPER.md for\n// setup instructions.\n//\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n\n  framework: 'jasmine',\n\n  specs: [\n    'ng2/timeout_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl\n};\n"
  },
  {
    "path": "spec/basic/actions_spec.js",
    "content": "describe('using an ActionSequence', function() {\n  beforeEach(function() {\n    browser.get('index.html#/form');\n  });\n\n  // TODO(cnishina): update when mouseMoveTo works in the next release of selenium-webdriver.\n  // Refer to selenium-webdriver issue 3693. https://github.com/SeleniumHQ/selenium/issues/3693\n  xit('should drag and drop', function() {\n    var sliderBar = element(by.name('points'));\n\n    expect(sliderBar.getAttribute('value')).toEqual('1');\n\n    browser.actions().\n        dragAndDrop(sliderBar, {x: 400, y: 20}).\n        perform();\n\n    expect(sliderBar.getAttribute('value')).toEqual('10');\n  });\n});\n"
  },
  {
    "path": "spec/basic/elements_spec.js",
    "content": "const {WebElement} = require('selenium-webdriver');\n\ndescribe('ElementFinder', () => {\n  beforeEach(async() => {\n    // Clear everything between each test.\n    await browser.driver.get('about:blank');\n  });\n\n  it('should return the same result as browser.findElement', async() => {\n    await browser.get('index.html#/form');\n    const nameByElement = element(by.binding('username'));\n\n    expect(await nameByElement.getText()).toEqual(\n        await browser.findElement(by.binding('username')).getText());\n  });\n\n  it('should wait to grab the WebElement until a method is called', async() => {\n    // These should throw no error before a page is loaded.\n    const usernameInput = element(by.model('username'));\n    const name = element(by.binding('username'));\n\n    await browser.get('index.html#/form');\n\n    expect(await name.getText()).toEqual('Anon');\n\n    await usernameInput.clear();\n    await usernameInput.sendKeys('Jane');\n    expect(await name.getText()).toEqual('Jane');\n  });\n\n  it('should chain element actions', async() => {\n    await browser.get('index.html#/form');\n\n    const usernameInput = element(by.model('username'));\n    const name = element(by.binding('username'));\n\n    expect(await name.getText()).toEqual('Anon');\n\n    await usernameInput.clear().sendKeys('Jane');\n    expect(await name.getText()).toEqual('Jane');\n  });\n\n  it('chained call should wait to grab the WebElement until a method is called',\n      async() => {\n    // These should throw no error before a page is loaded.\n    const reused = element(by.id('baz'))\n        .element(by.binding('item.reusedBinding'));\n\n    await browser.get('index.html#/conflict');\n\n    expect(await reused.getText()).toEqual('Inner: inner');\n    expect(await reused.isPresent()).toBe(true);\n  });\n\n  it('should differentiate elements with the same binding by chaining',\n      async() => {\n    await browser.get('index.html#/conflict');\n\n    const outerReused = element(by.binding('item.reusedBinding'));\n    const innerReused = element(by.id('baz'))\n        .element(by.binding('item.reusedBinding'));\n\n    expect(await outerReused.getText()).toEqual('Outer: outer');\n    expect(await innerReused.getText()).toEqual('Inner: inner');\n  });\n\n  it('should chain deeper than 2', async() => {\n    // These should throw no error before a page is loaded.\n    const reused = element(by.css('body')).element(by.id('baz'))\n        .element(by.binding('item.reusedBinding'));\n\n    await browser.get('index.html#/conflict');\n\n    expect(await reused.getText()).toEqual('Inner: inner');\n  });\n\n  it('should determine element presence properly with chaining', async() => {\n    await browser.get('index.html#/conflict');\n    expect(await element(by.id('baz'))\n        .isElementPresent(by.binding('item.reusedBinding')))\n        .toBe(true);\n\n    expect(await element(by.id('baz'))\n        .isElementPresent(by.binding('nopenopenope')))\n        .toBe(false);\n  });\n\n  it('should export an isPresent helper', async() => {\n    await browser.get('index.html#/form');\n\n    expect(await element(by.binding('greet')).isPresent()).toBe(true);\n    expect(await element(by.binding('nopenopenope')).isPresent()).toBe(false);\n  });\n\n  it('should allow handling errors', async() => {\n    await browser.get('index.html#/form');\n    try {\n      await $('.nopenopenope').getText();\n      expect(true).toEqual(false);\n    } catch (err) {\n      expect(true).toEqual(true);\n    }\n  });\n\n  it('should allow handling chained errors', async() => {\n    await browser.get('index.html#/form');\n    try {\n      await await $('.nopenopenope').$('furthernope').getText();\n      expect(true).toEqual(false);\n    } catch (err) {\n      expect(true).toEqual(true);\n    }\n  });\n\n  it('isPresent() should be friendly with out of bounds error', async() => {\n    await browser.get('index.html#/form');\n    const elementsNotPresent = element.all(by.id('notPresentElementID'));\n    expect(await elementsNotPresent.first().isPresent()).toBe(false);\n    expect(await elementsNotPresent.last().isPresent()).toBe(false);\n  });\n\n  it('isPresent() should not raise error on chained finders', async() => {\n    await browser.get('index.html#/form');\n    const elmFinder = $('.nopenopenope').element(by.binding('greet'));\n\n    expect(await elmFinder.isPresent()).toBe(false);\n  });\n\n  it('should export an allowAnimations helper', async() => {\n    await browser.get('index.html#/animation');\n    const animationTop = element(by.id('animationTop'));\n    const toggledNode = element(by.id('toggledNode'));\n\n    expect(await animationTop.allowAnimations()).toBe(true);\n    await animationTop.allowAnimations(false);\n    expect(await animationTop.allowAnimations()).toBe(false);\n\n    expect(await toggledNode.isPresent()).toBe(true);\n    await element(by.id('checkbox')).click();\n    expect(await toggledNode.isPresent()).toBe(false);\n  });\n\n  it('should keep a reference to the original locator', async() => {\n    await browser.get('index.html#/form');\n\n    const byCss = by.css('body');\n    const byBinding = by.binding('greet');\n\n    expect(await element(byCss).locator()).toEqual(byCss);\n    expect(await element(byBinding).locator()).toEqual(byBinding);\n  });\n\n  it('should propagate exceptions', async() => {\n    await browser.get('index.html#/form');\n\n    const invalidElement = element(by.binding('INVALID'));\n    const successful = await invalidElement.getText().then(() => {\n      return true;\n    }, function() {\n      return false;\n    });\n    expect(successful).toEqual(false);\n  });\n\n  it('should be returned from a helper without infinite loops', async() => {\n    await browser.get('index.html#/form');\n    const helperPromise = Promise.resolve(true).then(() => {\n      return element(by.binding('greeting'));\n    });\n\n    await helperPromise.then(async(finalResult) => {\n      expect(await finalResult.getText()).toEqual('Hiya');\n    });\n  });\n\n  it('should be usable in WebDriver functions', async() => {\n    await browser.get('index.html#/form');\n    const greeting = element(by.binding('greeting'));\n    await browser.executeScript('arguments[0].scrollIntoView', greeting);\n  });\n\n  it('should allow null as success handler', async() => {\n    await browser.get('index.html#/form');\n\n    const name = element(by.binding('username'));\n\n    expect(await name.getText()).toEqual('Anon');\n    expect(\n      await name.getText().then(null, function() {})\n    ).toEqual('Anon');\n\n  });\n\n  it('should check equality correctly', async() => {\n    await browser.get('index.html#/form');\n\n    const usernameInput = element(by.model('username'));\n    const name = element(by.binding('username'));\n\n    expect(await usernameInput.equals(usernameInput)).toEqual(true);\n    expect(await usernameInput.equals(name)).toEqual(false);\n  });\n});\n\ndescribe('ElementArrayFinder', () => {\n\n  it('action should act on all elements', async() => {\n    await browser.get('index.html#/conflict');\n\n    const multiElement = element.all(by.binding('item.reusedBinding'));\n    expect(await multiElement.getText())\n        .toEqual(['Outer: outer', 'Inner: inner']);\n  });\n\n  it('click action should act on all elements', async() => {\n    const checkboxesElms = $$('#checkboxes input');\n    await browser.get('index.html');\n\n    expect(await checkboxesElms.isSelected())\n        .toEqual([true, false, false, false]);\n    await checkboxesElms.click();\n    expect(await checkboxesElms.isSelected())\n        .toEqual([false, true, true, true]);\n  });\n\n  it('action should act on all elements selected by filter', async() => {\n    await browser.get('index.html');\n\n    const multiElement = $$('#checkboxes input').filter((_, index) => {\n      return index == 2 || index == 3;\n    });\n    await multiElement.click();\n    expect(await $('#letterlist').getText()).toEqual('wx');\n  });\n\n  it('filter should chain with index correctly', async() => {\n    await browser.get('index.html');\n\n    const elem = $$('#checkboxes input').filter((_, index) => {\n      return index == 2 || index == 3;\n    }).last();\n    await elem.click();\n    expect(await $('#letterlist').getText()).toEqual('x');\n  });\n\n  it('filter should work in page object', async() => {\n    const elements = element.all(by.css('#animals ul li'))\n        .filter(async(elem) => {\n      let text = await elem.getText();\n      return text === 'big dog';\n    });\n\n    await browser.get('index.html#/form');\n    expect(await elements.count()).toEqual(1);\n  });\n\n  it('should be able to get ElementFinder from filtered ElementArrayFinder',\n      async() => {\n    const isDog = async(elem) => {\n      const text = await elem.getText();\n      return text.indexOf('dog') > -1;\n    };\n    const elements = element.all(by.css('#animals ul li')).filter(isDog);\n\n    await browser.get('index.html#/form');\n    expect(await elements.count()).toEqual(3);\n    expect(await elements.get(2).getText()).toEqual('other dog');\n  });\n\n  it('filter should be compoundable', async() => {\n    const isDog = async(elem) => {\n      const text = await elem.getText();\n      return text.indexOf('dog') > -1;\n    };\n    const isBig = async(elem) => {\n      const text = await elem.getText();\n      return text.indexOf('big') > -1;\n    };\n    const elements = element.all(by.css('#animals ul li'))\n        .filter(isDog).filter(isBig);\n\n    await browser.get('index.html#/form');\n    expect(await elements.count()).toEqual(1);\n    const arr = await elements;\n    expect(await arr[0].getText()).toEqual('big dog');\n  });\n\n  it('filter should work with reduce', async() => {\n    const isDog = async(elem) => {\n      const text = await elem.getText();\n      return text.indexOf('dog') > -1;\n    };\n    await browser.get('index.html#/form');\n    const value = element.all(by.css('#animals ul li')).filter(await isDog).\n        reduce(async(currentValue, elem, index, elemArr) => {\n          const text = await elem.getText();\n          return currentValue + index + '/' + elemArr.length + ': ' +\n              text + '\\n';\n        }, '');\n\n    expect(await value).toEqual(\n        '0/3: big dog\\n' +\n        '1/3: small dog\\n' +\n        '2/3: other dog\\n');\n  });\n\n  it('should find multiple elements scoped properly with chaining', async() => {\n    await browser.get('index.html#/conflict');\n\n    let elems = await element.all(by.binding('item'));\n    expect(elems.length).toEqual(4);\n\n    elems = await element(by.id('baz')).all(by.binding('item'));\n    expect(elems.length).toEqual(2);\n  });\n\n  it('should wait to grab multiple chained elements', async() => {\n    // These should throw no error before a page is loaded.\n    const reused = element(by.id('baz')).all(by.binding('item'));\n\n    await browser.get('index.html#/conflict');\n\n    expect(await reused.count()).toEqual(2);\n    expect(await reused.get(0).getText()).toEqual('Inner: inner');\n    expect(await reused.last().getText()).toEqual('Inner other: innerbarbaz');\n  });\n\n  it('should wait to grab elements chained by index', async() => {\n    // These should throw no error before a page is loaded.\n    const reused = element(by.id('baz')).all(by.binding('item'));\n    const first = reused.first();\n    const second = reused.get(1);\n    const last = reused.last();\n\n    await browser.get('index.html#/conflict');\n\n    expect(await reused.count()).toEqual(2);\n    expect(await first.getText()).toEqual('Inner: inner');\n    expect(await second.getText()).toEqual('Inner other: innerbarbaz');\n    expect(await last.getText()).toEqual('Inner other: innerbarbaz');\n  });\n\n  it('should count all elements', async() => {\n    await browser.get('index.html#/form');\n\n    await element.all(by.model('color')).count().then((num) => {\n      expect(num).toEqual(3);\n    });\n\n    // Should also work with promise expect unwrapping\n    expect(await element.all(by.model('color')).count()).toEqual(3);\n  });\n\n  it('should return 0 when counting no elements', async() => {\n    await browser.get('index.html#/form');\n\n    expect(await element.all(by.binding('doesnotexist')).count()).toEqual(0);\n  });\n\n  it('supports isPresent()', async() => {\n    await browser.get('index.html#/form');\n\n    expect(await element.all(by.model('color')).isPresent()).toBeTruthy();\n    expect(await element.all(by.binding('doesnotexist')).isPresent())\n        .toBeFalsy();\n  });\n\n  it('should return not present when an element disappears within an array',\n      async() => {\n    await browser.get('index.html#/form');\n    const elements = await element.all(by.model('color'))\n    const disappearingElem = elements[0];\n    expect(await disappearingElem.isPresent()).toBeTruthy();\n    await browser.get('index.html#/bindings');\n    expect(await disappearingElem.isPresent()).toBeFalsy();\n  });\n\n  it('should get an element from an array', async () => {\n    const colorList = element.all(by.model('color'));\n\n    await browser.get('index.html#/form');\n\n    expect(await colorList.get(0).getAttribute('value')).toEqual('blue');\n    expect(await colorList.get(1).getAttribute('value')).toEqual('green');\n    expect(await colorList.get(2).getAttribute('value')).toEqual('red');\n  });\n\n  it('should get an element from an array by promise index', async() => {\n    const colorList = element.all(by.model('color'));\n    const index = Promise.resolve(1);\n\n    await browser.get('index.html#/form');\n\n    expect(await colorList.get(await index).getAttribute('value')).toEqual('green');\n  });\n\n  it('should get an element from an array using negative indices', async() => {\n    const colorList = element.all(by.model('color'));\n\n    await browser.get('index.html#/form');\n\n    expect(await colorList.get(-3).getAttribute('value')).toEqual('blue');\n    expect(await colorList.get(-2).getAttribute('value')).toEqual('green');\n    expect(await colorList.get(-1).getAttribute('value')).toEqual('red');\n  });\n\n  it('should get the first element from an array', async() => {\n    const colorList = element.all(by.model('color'));\n    await browser.get('index.html#/form');\n\n    expect(await colorList.first().getAttribute('value')).toEqual('blue');\n  });\n\n  it('should get the last element from an array', async() => {\n    const colorList = element.all(by.model('color'));\n    await browser.get('index.html#/form');\n\n    expect(await colorList.last().getAttribute('value')).toEqual('red');\n  });\n\n  it('should perform an action on each element in an array', async() => {\n    const colorList = element.all(by.model('color'));\n    await browser.get('index.html#/form');\n\n    await colorList.each(async(colorElement) => {\n      expect(await colorElement.getText()).not.toEqual('purple');\n    });\n  });\n\n  it('should allow accessing subelements from within each', async() => {\n    await browser.get('index.html#/form');\n    const rows = element.all(by.css('.rowlike'));\n\n    await rows.each(async(row) => {\n      const input = row.element(by.css('.input'));\n      expect(await input.getAttribute('value')).toEqual('10');\n    });\n\n    await rows.each(async(row) => {\n      const input = row.element(by.css('input'));\n      expect(await input.getAttribute('value')).toEqual('10');\n    });\n  });\n\n  it('should keep a reference to the array original locator', async() => {\n    const byCss = by.css('#animals ul li');\n    const byModel = by.model('color');\n    await browser.get('index.html#/form');\n\n    expect(await element.all(byCss).locator()).toEqual(byCss);\n    expect(await element.all(byModel).locator()).toEqual(byModel);\n  });\n\n  it('should map each element on array and with promises', async() => {\n    await browser.get('index.html#/form');\n    const labels = element.all(by.css('#animals ul li'))\n        .map(async(elm, index) => {\n      return {\n        index: index,\n        text: await elm.getText()\n      };\n    });\n\n    expect(await labels).toEqual([\n      {index: 0, text: 'big dog'},\n      {index: 1, text: 'small dog'},\n      {index: 2, text: 'other dog'},\n      {index: 3, text: 'big cat'},\n      {index: 4, text: 'small cat'}\n    ]);\n  });\n\n  it('should map and resolve multiple promises', async() => {\n    await browser.get('index.html#/form');\n    const labels = element.all(by.css('#animals ul li'))\n        .map(async (elm) => {\n      return {\n        text: await elm.getText(),\n        tagName: await elm.getTagName()\n      };\n    });\n\n    const newExpected = (expectedLabel) => {\n      return {\n        text: expectedLabel,\n        tagName: 'li'\n      };\n    };\n\n    expect(await labels).toEqual([\n      newExpected('big dog'),\n      newExpected('small dog'),\n      newExpected('other dog'),\n      newExpected('big cat'),\n      newExpected('small cat')\n    ]);\n  });\n\n  it('should map each element from a literal and promise array', async() => {\n    await browser.get('index.html#/form');\n    let i = 1;\n    const labels = await element.all(by.css('#animals ul li'))\n        .map(() => {\n      return i++;\n    });\n    expect(labels).toEqual([1, 2, 3, 4, 5]);\n  });\n\n  it('should filter elements', async() => {\n    await browser.get('index.html#/form');\n    \n    const filteredElements = await element.all(by.css('#animals ul li'))\n        .filter(async(elem) => {\n      const text = await elem.getText();\n      return text === 'big dog';\n    });\n    const count = filteredElements.length;\n    expect(await count).toEqual(1);\n  });\n\n  it('should reduce elements', async () => {\n    await browser.get('index.html#/form');\n    const value = element.all(by.css('#animals ul li')).\n        reduce(async(currentValue, elem, index, elemArr) => {\n          const text = await elem.getText();\n          return currentValue + index + '/' + elemArr.length + ': ' +\n              text + '\\n';\n        }, '');\n\n    expect(await value).toEqual(\n        '0/5: big dog\\n' +\n        '1/5: small dog\\n' +\n        '2/5: other dog\\n' +\n        '3/5: big cat\\n' +\n        '4/5: small cat\\n');\n  });\n\n  it('should allow using protractor locator within map', async() => {\n    await browser.get('index.html#/repeater');\n\n    const expected = [\n        { first: 'M', second: 'Monday' },\n        { first: 'T', second: 'Tuesday' },\n        { first: 'W', second: 'Wednesday' },\n        { first: 'Th', second: 'Thursday' },\n        { first: 'F', second: 'Friday' }];\n\n    const result = element.all(by.repeater('allinfo in days'))\n        .map(async(el) => {\n      return {\n        first: await el.element(by.binding('allinfo.initial')).getText(),\n        second: await el.element(by.binding('allinfo.name')).getText()\n      };\n    });\n\n    expect(await result).toEqual(expected);\n  });\n});\n\ndescribe('evaluating statements', () => {\n  beforeEach(async() => {\n    await browser.get('index.html#/form');\n  });\n\n  it('should evaluate statements in the context of an element', async() => {\n    const checkboxElem = element(by.id('checkboxes'));\n\n    const output = await checkboxElem.evaluate('show');\n    expect(output).toBe(true);\n\n    // Make sure it works with a promise expectation.\n    expect(await checkboxElem.evaluate('show')).toBe(true);\n  });\n});\n\ndescribe('shortcut css notation', () => {\n  beforeEach(async() => {\n    await browser.get('index.html#/bindings');\n  });\n\n  it('should grab by css', async() => {\n    expect(await $('.planet-info').getText())\n        .toEqual(await element(by.css('.planet-info')).getText());\n    expect(await $$('option').count())\n        .toEqual(await element.all(by.css('option')).count());\n  });\n\n  it('should chain $$ with $', async() => {\n    const options = await element(by.css('select')).all(by.css('option'));\n    const withoutShortcutCount = options.length;\n    const withShortcutCount = await $('select').$$('option').count();\n\n    expect(withoutShortcutCount).toEqual(withShortcutCount);\n  });\n});\n"
  },
  {
    "path": "spec/basic/excludeme_spec.js",
    "content": "describe('should be excluded', function() {\n  it('should fail if included', function() {\n    expect(true).toBe(false);\n  });\n});\n"
  },
  {
    "path": "spec/basic/expected_conditions_spec.js",
    "content": "describe('expected conditions', () => {\n  let EC = null;\n\n  beforeEach(async () => {\n    await browser.get('index.html#/form');\n    EC = protractor.ExpectedConditions;\n  });\n\n  it('should have alertIsPresent', async () => {\n    const alertIsPresent = EC.alertIsPresent();\n    expect(await alertIsPresent.call()).toBe(false);\n\n    const alertButton = $('#alertbutton');\n    await alertButton.click();\n    await browser.wait(protractor.ExpectedConditions.alertIsPresent(), 5000);\n    await browser.switchTo().alert().accept();\n  });\n\n  it('should have presenceOf', async () => {\n    const presenceOfInvalid = EC.presenceOf($('#INVALID'));\n    const presenceOfHideable = EC.presenceOf($('#shower'));\n\n    expect(await presenceOfInvalid.call()).toBe(false);\n    expect(await presenceOfHideable.call()).toBe(true);\n    await element(by.model('show')).click();\n    expect(await presenceOfHideable.call()).toBe(true); // Should be able to reuse.\n  });\n\n  it('should have stalenessOf', async () => {\n    const stalenessOfInvalid = EC.stalenessOf($('#INVALID'));\n    const stalenessOfHideable = EC.stalenessOf($('#shower'));\n\n    expect(await stalenessOfInvalid.call()).toBe(true);\n    expect(await stalenessOfHideable.call()).toBe(false);\n    await element(by.model('show')).click();\n    expect(await stalenessOfHideable.call()).toBe(false);\n  });\n\n  it('should have visibilityOf', async () => {\n    const visibilityOfInvalid = EC.visibilityOf($('#INVALID'));\n    const visibilityOfHideable = EC.visibilityOf($('#shower'));\n\n    expect(await visibilityOfInvalid.call()).toBe(false);\n    expect(await visibilityOfHideable.call()).toBe(true);\n    await element(by.model('show')).click();\n    expect(await visibilityOfHideable.call()).toBe(false);\n  });\n\n  it('should have invisibilityOf', async () => {\n    const invisibilityOfInvalid = EC.invisibilityOf($('#INVALID'));\n    const invisibilityOfHideable = EC.invisibilityOf($('#shower'));\n\n    expect(await invisibilityOfInvalid.call()).toBe(true);\n    expect(await invisibilityOfHideable.call()).toBe(false);\n    await element(by.model('show')).click();\n    expect(await invisibilityOfHideable.call()).toBe(true);\n  });\n\n  it('should have titleContains', async () => {\n    expect(await EC.titleContains('My Angular').call()).toBe(true);\n    expect(await EC.titleContains('My AngularJS App').call()).toBe(true);\n  });\n\n  it('should have titleIs', async () => {\n    expect(await EC.titleIs('My Angular').call()).toBe(false);\n    expect(await EC.titleIs('My AngularJS App').call()).toBe(true);\n  });\n\n  it('should have urlContains', async () => {\n    const baseUrlFromSpec = browser.baseUrl;\n    expect(await EC.urlContains('/form').call()).toBe(true);\n    expect(await EC.urlContains(baseUrlFromSpec+ 'index.html#/form').call()).toBe(true);\n  });\n\n  it('should have urlIs', async () => {\n    const baseUrlFromSpec = browser.baseUrl;\n    expect(await EC.urlIs(browser.baseUrl).call()).toBe(false);\n    expect(await EC.urlIs(baseUrlFromSpec+'index.html#/form').call()).toBe(true);\n  });\n\n  it('should have elementToBeClickable', async () => {\n    const invalidIsClickable = EC.elementToBeClickable($('#INVALID'));\n    const buttonIsClickable = EC.elementToBeClickable($('#disabledButton'));\n\n    expect(await invalidIsClickable.call()).toBe(false);\n    expect(await buttonIsClickable.call()).toBe(true);\n    await element(by.model('disabled')).click();\n    expect(await buttonIsClickable.call()).toBe(false);\n  });\n\n  it('should have textToBePresentInElement', async () => {\n    const invalidHasText = EC.textToBePresentInElement($('#INVALID'), 'shouldnt throw');\n    const hideableHasText = EC.textToBePresentInElement($('#shower'), 'Shown');\n\n    expect(await invalidHasText.call()).toBe(false);\n    expect(await hideableHasText.call()).toBe(true);\n    await element(by.model('show')).click();\n    expect(await hideableHasText.call()).toBe(false);\n  });\n\n  it('should have textToBePresentInElementValue', async () => {\n    const invalid = $('#INVALID');\n    const about = element(by.model('aboutbox'));\n\n    expect(await EC.textToBePresentInElementValue(invalid, 'shouldnt throw').call()).toBe(false);\n    expect(await EC.textToBePresentInElementValue(about, 'text box').call()).toBe(true);\n  });\n\n  it('should have elementToBeSelected', async () => {\n    const checkbox = element(by.model('show'));\n\n    expect(await EC.elementToBeSelected(checkbox).call()).toBe(true);\n    await checkbox.click();\n    expect(await EC.elementToBeSelected(checkbox).call()).toBe(false);\n  });\n\n  it('should have not', async () => {\n    const presenceOfValidElement = EC.presenceOf($('#shower'));\n    expect(await presenceOfValidElement.call()).toBe(true);\n    expect(await EC.not(presenceOfValidElement).call()).toBe(false);\n  });\n\n  it('should have and', async () => {\n    const presenceOfValidElement = EC.presenceOf($('#shower'));\n    const presenceOfInvalidElement = EC.presenceOf($('#INVALID'));\n    const validityOfTitle = EC.titleIs('My AngularJS App');\n\n    expect(await EC.and(presenceOfValidElement, validityOfTitle).call()).toBe(true);\n    // support multiple conditions\n    expect(await EC.and(presenceOfValidElement, validityOfTitle, presenceOfInvalidElement).call()).toBe(false);\n  });\n\n  it('and should shortcircuit', async () => {\n    const invalidElem = $('#INVALID');\n\n    const presenceOfInvalidElement = EC.presenceOf(invalidElem);\n    const isDisplayed = invalidElem.isDisplayed.bind(invalidElem);\n\n    // check isDisplayed on invalid element\n    const condition = EC.and(presenceOfInvalidElement, isDisplayed);\n\n    // Should short circuit after the first condition is false, and not throw error\n    expect(await condition.call()).toBe(false);\n  });\n\n  it('should have or', async () => {\n    const presenceOfValidElement = EC.presenceOf($('#shower'));\n    const presenceOfInvalidElement = EC.presenceOf($('#INVALID'));\n    const presenceOfInvalidElement2 = EC.presenceOf($('#INVALID2'));\n\n    expect(await EC.or(presenceOfInvalidElement, presenceOfInvalidElement2).call()).toBe(false);\n    // support multiple conditions\n    expect(await EC.or(presenceOfInvalidElement, presenceOfInvalidElement2, presenceOfValidElement).call()).toBe(true);\n  });\n\n  it('or should shortcircuit', async () => {\n    const validElem = $('#shower');\n    const invalidElem = $('#INVALID');\n\n    const presenceOfValidElement = EC.presenceOf(validElem);\n    const isDisplayed = invalidElem.isDisplayed.bind(invalidElem);\n\n    // check isDisplayed on invalid element\n    const condition = EC.or(presenceOfValidElement, isDisplayed);\n\n    // Should short circuit after the first condition is true, and not throw error\n    expect(await condition.call()).toBe(true);\n  });\n\n  it('should be able to mix conditions', async () => {\n    const valid = EC.presenceOf($('#shower'));\n    const invalid = EC.presenceOf($('#INVALID'));\n\n    expect(await EC.or(valid, await EC.and(valid, invalid)).call()).toBe(true);\n    expect(await EC.or(EC.not(valid), EC.and(valid, invalid)).call()).toBe(false);\n  });\n\n  describe('for forked browsers', () => {\n    // ensure that we can run EC on forked browser instances\n    it('should have alertIsPresent', async () => {\n      const browser2 = await browser.forkNewDriverInstance();\n      await browser2.get('index.html#/form');\n      const EC2 = browser2.ExpectedConditions;\n      const alertIsPresent = EC2.alertIsPresent();\n      expect(await alertIsPresent.call()).toBe(false);\n\n      const alertButton = browser2.$('#alertbutton');\n      await alertButton.click();\n      await browser2.wait(EC2.alertIsPresent(), 1000);\n\n      await browser2.switchTo().alert().accept();\n    });\n  });\n\n  describe('race condition handling', () => {\n\n    let disabledButton;\n\n    beforeEach(() => {\n      disabledButton = $('#disabledButton[disabled=\"disabled\"]');\n    });\n\n    const enableButtonBeforeCallToUnmatchSelector = async (testElement, fnName) => {\n      const originalFn = testElement[fnName];\n\n      testElement[fnName] = async () => {\n        await element(by.model('disabled')).click();\n        return originalFn.apply(this, arguments);\n      };\n\n      // save original fn with _ prefix\n      testElement[`_${fnName}`] = originalFn;\n    };\n\n    it('can deal with missing elements in visibilityOf', async () => {\n      await enableButtonBeforeCallToUnmatchSelector(disabledButton, 'isDisplayed');\n\n      await element(by.model('disabled')).click();\n\n      expect(await disabledButton._isDisplayed()).toBe(true);\n      expect(await EC.visibilityOf(disabledButton).call()).toBe(false);\n    });\n\n    it('can deal with missing elements in textToBePresentInElement', async () => {\n      await enableButtonBeforeCallToUnmatchSelector(disabledButton, 'getText');\n\n      await element(by.model('disabled')).click();\n\n      expect(await disabledButton._getText()).toBe('Dummy');\n      expect(await EC.textToBePresentInElement(disabledButton, 'Dummy').call()).toBe(false);\n    });\n\n    it('can deal with missing elements in textToBePresentInValue', async () => {\n      await enableButtonBeforeCallToUnmatchSelector(disabledButton, 'getAttribute');\n\n      await element(by.model('disabled')).click();\n\n      expect(await disabledButton._getAttribute('value')).toBe('');\n      expect(await EC.textToBePresentInElementValue(disabledButton, '').call()).toBe(false);\n    });\n\n    it('can deal with missing elements in elementToBeClickable', async () => {\n      await enableButtonBeforeCallToUnmatchSelector(disabledButton, 'isEnabled');\n\n      await element(by.model('disabled')).click();\n\n      expect(await disabledButton._isEnabled()).toBe(false);\n      expect(await EC.elementToBeClickable(disabledButton).call()).toBe(false);\n    });\n  });\n});\n"
  },
  {
    "path": "spec/basic/handling_spec.js",
    "content": "describe('handling timeout errors', () => {\n  it('should call error handler on a timeout', async () => {\n    try {\n      await browser.get('http://dummyUrl', 1);\n      throw 'did not handle error';\n    } catch (err) {\n      expect(err instanceof Error).toBeTruthy();\n    }\n  });\n});\n"
  },
  {
    "path": "spec/basic/lib_spec.js",
    "content": "describe('no protractor at all', () => {\n  it('should still do normal tests', () => {\n    expect(true).toBe(true);\n  });\n});\n\ndescribe('protractor library', () => {\n  it('should expose the correct global variables', () => {\n    expect(protractor).toBeDefined();\n    expect(browser).toBeDefined();\n    expect(by).toBeDefined();\n    expect(By).toBeDefined();\n    expect(element).toBeDefined();\n    expect($).toBeDefined();\n    expect(DartObject).toBeDefined();\n    const obj = {};\n    const dartProxy = new DartObject(obj);\n    expect(dartProxy.o === obj).toBe(true);\n  });\n\n  it('should export other webdriver classes onto the global protractor',\n      () => {\n        // TODO(selenium4): Actions API missing from typings.\n        // expect(protractor.Actions).toBeDefined();\n        expect(protractor.Key.RETURN).toEqual('\\uE006');\n      });\n\n  it('should export custom parameters to the protractor instance', () => {\n    expect(browser.params.login).toBeDefined();\n    expect(browser.params.login.user).toEqual('Jane');\n    expect(browser.params.login.password).toEqual('1234');\n  });\n\n  it('should allow a mix of using protractor and using the driver directly',\n      async() => {\n    await browser.get('index.html');\n    expect(await browser.getCurrentUrl()).toMatch('#/form');\n\n    await browser.driver.findElement(protractor.By.linkText('repeater')).click();\n    expect(await browser.driver.getCurrentUrl()).toMatch('#/repeater');\n\n    await browser.navigate().back();\n    expect(await browser.driver.getCurrentUrl()).toMatch('#/form');\n  });\n\n  it('should have access to the processed config block', async() => {\n    let containsMatching = (arr, string) => {\n      let contains = false;\n      for (let i = 0; i < arr.length; ++i) {\n        if (arr[i].indexOf(string) !== -1) {\n          contains = true;\n        }\n      }\n      return contains;\n    }\n\n    const config = await browser.getProcessedConfig();\n    expect(config.params.login).toBeDefined();\n    expect(config.params.login.user).toEqual('Jane');\n    expect(config.params.login.password).toEqual('1234');\n    expect(containsMatching(config.specs, 'lib_spec.js')).toBe(true);\n    expect(config.capabilities).toBeDefined();\n  });\n\n  it('should allow adding custom locators', async() => {\n    let findMenuItem = () => {\n      const itemName = arguments[0];\n      const menu = document.querySelectorAll('.menu li');\n      for (const i = 0; i < menu.length; ++i) {\n        if (menu[i].textContent == itemName) {\n          return [menu[i]];\n        }\n      }\n    };\n\n    by.addLocator('menuItem', findMenuItem);\n\n    expect(by.menuItem).toBeDefined();\n\n    await browser.get('index.html');\n    expect(await element(by.menuItem('repeater')).isPresent());\n    expect(await element(by.menuItem('repeater')).getText()).toEqual('repeater');\n  });\n\n  it('should allow adding custom varargs locators', async() => {\n    let findMenuItemWithName = function() {\n      const css = arguments[0];\n      const itemName = arguments[1];\n      const menu = document.querySelectorAll(css);\n      for (const i = 0; i < menu.length; ++i) {\n        if (menu[i].textContent == itemName) {\n          return [menu[i]];\n        }\n      }\n    };\n\n    by.addLocator('menuItemWithName', findMenuItemWithName);\n\n    expect(by.menuItemWithName).toBeDefined();\n\n    await browser.get('index.html');\n    expect(await element(by.menuItemWithName('.menu li', 'repeater')).isPresent());\n    expect(await element(by.menuItemWithName('.menu li', 'repeater')).getText())\n        .toEqual('repeater');\n  });\n\n  describe('helper functions', () => {\n    it('should get the absolute URL', async() => {\n      await browser.get('index.html');\n      expect(await browser.getLocationAbsUrl()).toMatch('/form');\n\n      await element(by.linkText('repeater')).click();\n      expect(await browser.getLocationAbsUrl()).toMatch('/repeater');\n    });\n\n    it('should navigate to another url with setLocation', async() => {\n      await browser.get('index.html');\n\n      await browser.setLocation('/repeater');\n\n      expect(await browser.getLocationAbsUrl()).toMatch('/repeater');\n    });\n  });\n});\n"
  },
  {
    "path": "spec/basic/locators_spec.js",
    "content": "describe('locators', () => {\n  beforeEach(async() => {\n    await browser.get('index.html#/form');\n  });\n\n  describe('by binding', () => {\n    it('should find an element by binding', async() => {\n      const greeting = element(by.binding('greeting'));\n\n      expect(await greeting.getText()).toEqual('Hiya');\n    });\n\n    it('should find a binding by partial match', async() => {\n      const greeting = element(by.binding('greet'));\n\n      expect(await greeting.getText()).toEqual('Hiya');\n    });\n\n    it('should find exact match by exactBinding', async() => {\n      const greeting = element(by.exactBinding('greeting'));\n\n      expect(await greeting.getText()).toEqual('Hiya');\n    });\n\n    it('should not find partial match by exactBinding', async() => {\n      const greeting = element(by.exactBinding('greet'));\n\n      expect(await greeting.isPresent()).toBe(false);\n    });\n\n    it('should find an element by binding with ng-bind attribute',\n        async() => {\n      const name = element(by.binding('username'));\n\n      expect(await name.getText()).toEqual('Anon');\n    });\n\n    it('should find an element by binding with ng-bind-template attribute',\n        async() => {\n      const name = element(by.binding('nickname|uppercase'));\n\n      expect(await name.getText()).toEqual('(ANNIE)');\n    });\n  });\n\n  describe('by model', () => {\n    it('should find an element by text input model', async() => {\n      const username = element(by.model('username'));\n      const name = element(by.binding('username'));\n\n      await username.clear();\n      expect(await name.getText()).toEqual('');\n\n      await username.sendKeys('Jane Doe');\n      expect(await name.getText()).toEqual('Jane Doe');\n    });\n\n    it('should find an element by checkbox input model', async() => {\n      expect(await element(by.id('shower')).isDisplayed()).toBe(true);\n\n      await element(by.model('show')).click();  // colors\n\n      expect(await element(by.id('shower')).isDisplayed()).toBe(false);\n    });\n\n    it('should find a textarea by model', async() => {\n      const about = element(by.model('aboutbox'));\n      expect(await about.getAttribute('value')).toEqual('This is a text box');\n\n      await about.clear();\n      await about.sendKeys('Something else to write about');\n\n      expect(await about.getAttribute('value'))\n          .toEqual('Something else to write about');\n    });\n\n    it('should find multiple selects by model', async() => {\n      const selects = element.all(by.model('dayColor.color'));\n      expect(await selects.count()).toEqual(3);\n    });\n\n    it('should find the selected option', async() => {\n      const select = element(by.model('fruit'));\n      const selectedOption = select.element(by.css('option:checked'));\n      expect(await selectedOption.getText()).toEqual('apple');\n    });\n\n    it('should find inputs with alternate attribute forms', async() => {\n      const letterList = element(by.id('letterlist'));\n      expect(await letterList.getText()).toBe('');\n\n      await element(by.model('check.w')).click();\n      expect(await letterList.getText()).toBe('w');\n\n      await element(by.model('check.x')).click();\n      expect(await letterList.getText()).toBe('wx');\n    });\n\n    it('should find multiple inputs', async() => {\n      const arr = await element.all(by.model('color'));\n      expect(arr.length).toEqual(3);\n    });\n\n    it('should clear text from an input model', async() => {\n      const username = element(by.model('username'));\n      const name = element(by.binding('username'));\n\n      await username.clear();\n      expect(await name.getText()).toEqual('');\n\n      await username.sendKeys('Jane Doe');\n      expect(await name.getText()).toEqual('Jane Doe');\n\n      await username.clear();\n      expect(await name.getText()).toEqual('');\n    });\n  });\n\n  describe('by partial button text', () => {\n    it('should find multiple buttons containing \"text\"', async() => {\n      const arr = await element.all(by.partialButtonText('text'));\n      expect(arr.length).toEqual(4);\n      expect(await arr[0].getAttribute('id')).toBe('exacttext');\n      expect(await arr[1].getAttribute('id')).toBe('otherbutton');\n      expect(await arr[2].getAttribute('id')).toBe('submitbutton');\n      expect(await arr[3].getAttribute('id')).toBe('inputbutton');\n    });\n  });\n\n  describe('by button text', () => {\n    it('should find two button containing \"Exact text\"', async() => {\n      const arr = await element.all(by.buttonText('Exact text'));\n      expect(arr.length).toEqual(2);\n      expect(await arr[0].getAttribute('id')).toBe('exacttext');\n      expect(await arr[1].getAttribute('id')).toBe('submitbutton');\n    });\n\n    it('should not find any buttons containing \"text\"', async() => {\n      const arr = await element.all(by.buttonText('text'));\n      expect(arr.length).toEqual(0);\n    });\n\n  });\n\n  describe('by repeater', () => {\n    beforeEach(async() => {\n      await browser.get('index.html#/repeater');\n    });\n\n    it('should find by partial match', async() => {\n      const fullMatch = element(by.repeater('baz in days | filter:\\'T\\'')\n          .row(0).column('baz.initial'));\n      expect(await fullMatch.getText()).toEqual('T');\n\n      const partialMatch = element(by.repeater('baz in days')\n          .row(0).column('b'));\n      expect(await partialMatch.getText()).toEqual('T');\n\n      const partialRowMatch = element(by.repeater('baz in days').row(0));\n      expect(await partialRowMatch.getText()).toEqual('T');\n    });\n\n    it('should return all rows when unmodified', async() => {\n      const arr = await element.all(by.repeater('allinfo in days'));\n      expect(arr.length).toEqual(5);\n      expect(await arr[0].getText()).toEqual('M Monday');\n      expect(await arr[1].getText()).toEqual('T Tuesday');\n      expect(await arr[2].getText()).toEqual('W Wednesday');\n    });\n\n    it('should return a single column', async() => {\n      const initial = await element.all(\n          by.repeater('allinfo in days').column('initial'));\n      \n      expect(initial.length).toEqual(5);\n      expect(await initial[0].getText()).toEqual('M');\n      expect(await initial[1].getText()).toEqual('T');\n      expect(await initial[2].getText()).toEqual('W');\n\n      const names = await element.all(\n          by.repeater('allinfo in days').column('name'));\n\n      expect(names.length).toEqual(5);\n      expect(await names[0].getText()).toEqual('Monday');\n      expect(await names[1].getText()).toEqual('Tuesday');\n      expect(await names[2].getText()).toEqual('Wednesday');\n    });\n\n    it('should allow chaining while returning a single column', async() => {\n      const secondName = element(by.css('.allinfo')).element(\n        by.repeater('allinfo in days').column('name').row(2));\n      expect(await secondName.getText()).toEqual('Wednesday');\n    });\n\n    it('should return a single row', async() => {\n      const secondRow = element(by.repeater('allinfo in days').row(1));\n      expect(await secondRow.getText()).toEqual('T Tuesday');\n    });\n\n    it('should return an individual cell', async() => {\n      const secondNameByRowFirst = element(by.repeater('allinfo in days')\n          .row(1).column('name'));\n\n      const secondNameByColumnFirst = element(by.repeater('allinfo in days')\n          .column('name').row(1));\n\n      expect(await secondNameByRowFirst.getText()).toEqual('Tuesday');\n      expect(await secondNameByColumnFirst.getText()).toEqual('Tuesday');\n    });\n\n    it('should find a using data-ng-repeat', async() => {\n      const byRow = element(by.repeater('day in days').row(2));\n      expect(await byRow.getText()).toEqual('W');\n\n      const byCol = element(by.repeater('day in days').row(2).column('day'));\n      expect(await byCol.getText()).toEqual('W');\n    });\n\n    it('should find using ng:repeat', async() => {\n      const byRow = element(by.repeater('bar in days').row(2));\n      expect(await byRow.getText()).toEqual('W');\n\n      const byCol = element(by.repeater('bar in days').row(2).column('bar'));\n      expect(await byCol.getText()).toEqual('W');\n    });\n\n    it('should determine if repeater elements are present', async() => {\n      expect(await element(by.repeater('allinfo in days').row(3)).isPresent())\n          .toBe(true);\n      // There are only 5 rows, so the 6th row is not present.\n      expect(await element(by.repeater('allinfo in days').row(5)).isPresent())\n          .toBe(false);\n    });\n\n    it('should have by.exactRepeater working', async() => {\n      expect(await element(by.exactRepeater('day in d')).isPresent())\n          .toBe(false);\n      expect(await element(by.exactRepeater('day in days')).isPresent())\n          .toBe(true);\n\n      // Full ng-repeat: baz in tDays = (days | filter:'T')\n      const repeaterWithEqualSign = element(by.exactRepeater('baz in tDays')\n          .row(0));\n      expect(await repeaterWithEqualSign.getText()).toEqual('T');\n\n      // Full ng-repeat: baz in days | filter:'T'\n      const repeaterWithPipe = element(by.exactRepeater('baz in days').row(0));\n      expect(await repeaterWithPipe.getText()).toEqual('T');\n    });\n\n    describe('repeaters using ng-repeat-start and ng-repeat-end', () => {\n      it('should return all elements when unmodified', async() => {\n        const all = await element.all(by.repeater('bloop in days'));\n\n        expect(all.length).toEqual(3 * 5);\n        expect(await all[0].getText()).toEqual('M');\n        expect(await all[1].getText()).toEqual('-');\n        expect(await all[2].getText()).toEqual('Monday');\n        expect(await all[3].getText()).toEqual('T');\n        expect(await all[4].getText()).toEqual('-');\n        expect(await all[5].getText()).toEqual('Tuesday');\n      });\n\n      it('should return a group of elements for a row', async() => {\n        const firstRow = await element.all(by.repeater('bloop in days').row(0));\n\n        expect(firstRow.length).toEqual(3);\n        expect(await firstRow[0].getText()).toEqual('M');\n        expect(await firstRow[1].getText()).toEqual('-');\n        expect(await firstRow[2].getText()).toEqual('Monday');\n      });\n\n      it('should return a group of elements for a column', async() => {\n        const nameColumn = await element.all(\n          by.repeater('bloop in days').column('name'));\n\n        expect(nameColumn.length).toEqual(5);\n        expect(await nameColumn[0].getText()).toEqual('Monday');\n        expect(await nameColumn[1].getText()).toEqual('Tuesday');\n      });\n\n      it('should find an individual element', async() => {\n        const firstInitial = element(\n          by.repeater('bloop in days').row(0).column('bloop.initial'));\n\n        expect(await firstInitial.getText()).toEqual('M');\n      });\n    });\n  });\n\n  describe('by css containing text', () => {\n    it('should find elements by css and partial text', async() => {\n      const arr = await element.all(\n          by.cssContainingText('#animals ul .pet', 'dog'))\n      expect(arr.length).toEqual(2);\n      expect(await arr[0].getAttribute('id')).toBe('bigdog');\n      expect(await arr[1].getAttribute('id')).toBe('smalldog');\n    });\n\n    it('should find elements with text-transform style', async() => {\n      expect(await element(by.cssContainingText('#transformedtext div',\n          'Uppercase')).getAttribute('id')).toBe('textuppercase');\n      expect(await element(by.cssContainingText('#transformedtext div',\n          'Lowercase')).getAttribute('id')).toBe('textlowercase');\n      expect(await element(by.cssContainingText('#transformedtext div',\n          'capitalize')).getAttribute('id')).toBe('textcapitalize');\n    });\n\n    it('should find elements with a regex', async() => {\n      const found = await element.all(by.cssContainingText(\n          '#transformedtext div', /(upper|lower)case/i));\n        \n      expect(found.length).toEqual(2);\n      expect(await found[0].getText()).toBe('UPPERCASE');\n      expect(await found[1].getText()).toBe('lowercase');\n    });\n\n    it('should find elements with a regex with no flags', async() => {\n      // this test matches the non-transformed text.\n      // the text is actually transformed with css,\n      // so you can't match the Node innerText or textContent.\n      const found = await element.all(by.cssContainingText(\n          '#transformedtext div', /Uppercase/));\n\n      expect(found.length).toEqual(1);\n      expect(await found[0].getText()).toBe('UPPERCASE');\n    });\n  });\n\n  describe('by options', () => {\n    it('should find elements by options', async() => {\n      const allOptions = element.all(by.options('fruit for fruit in fruits'));\n      expect(await allOptions.count()).toEqual(4);\n\n      const firstOption = allOptions.first();\n      expect(await firstOption.getText()).toEqual('apple');\n    });\n  });\n\n  describe('by deep css', () => {\n    beforeEach(async() => {\n      await browser.get('index.html#/shadow');\n    });\n\n    // Shadow DOM is not currently supported outside of Chrome.\n    browser.getCapabilities().then((capabilities) => {\n      if (capabilities.get('browserName') == 'chrome') {\n\n        it('should find items inside the shadow DOM', async() => {\n          const parentHeading = element(by.deepCss('.parentshadowheading'));\n          const olderChildHeading = element(by.deepCss('.oldershadowheading'));\n          const youngerChildHeading = element(\n              by.deepCss('.youngershadowheading'));\n\n          expect(await parentHeading.isPresent()).toBe(true);\n          expect(await olderChildHeading.isPresent()).toBe(true);\n          expect(await youngerChildHeading.isPresent()).toBe(true);\n\n          expect(await parentHeading.getText()).toEqual('Parent');\n          expect(await olderChildHeading.getText()).toEqual('Older Child');\n          expect(await youngerChildHeading.getText()).toEqual('Younger Child');\n\n          expect(await element(by.deepCss('.originalcontent')).getText())\n              .toEqual('original content');\n        });\n      }\n    });\n  });\n\n  it('should determine if an element is present', async() => {\n    expect(await browser.isElementPresent(by.binding('greet'))).toBe(true);\n    expect(await browser.isElementPresent(by.binding('nopenopenope')))\n        .toBe(false);\n  });\n\n  it('should determine if an ElementFinder is present', async() => {\n    expect(await browser.isElementPresent(element(by.binding('greet'))))\n        .toBe(true);\n    expect(await browser.isElementPresent(element(by.binding('nopenopenope'))))\n        .toBe(false);\n  });\n});\n"
  },
  {
    "path": "spec/basic/mockmodule_spec.js",
    "content": "describe('mock modules', () => {\n  // A module to override the 'version' service. This function will be\n  // executed in the context of the application under test, so it may\n  // not refer to any local variables.\n  const mockModuleA = () => {\n    let newModule = angular.module('moduleA', []);\n    newModule.value('version', '2');\n  };\n\n  // A second module overriding the 'version' service.\n  // This module shows the use of a string for the load\n  // function.\n  const mockModuleB = `angular.module('moduleB', []).value('version', '3');`;\n\n  // A third module overriding the 'version' service. This function\n  // references the additional arguments provided through addMockModule().\n  const mockModuleC = () => {\n    var newModule = angular.module('moduleC', []);\n    newModule.value('version', arguments[0] + arguments[1]);\n  };\n\n  afterEach(() => {\n    browser.clearMockModules();\n  });\n\n  it('should override services via mock modules', async() => {\n    browser.addMockModule('moduleA', mockModuleA);\n\n    await browser.get('index.html');\n\n    expect(await element(by.css('[app-version]')).getText()).toEqual('2');\n  });\n\n  it('should have the version of the last loaded module', async() => {\n    browser.addMockModule('moduleA', mockModuleA);\n    browser.addMockModule('moduleB', mockModuleB);\n\n    await browser.get('index.html');\n\n    expect(await element(by.css('[app-version]')).getText()).toEqual('3');\n  });\n\n  it('should use the latest module if two are added with the same name',\n      async() => {\n    browser.addMockModule('moduleA', mockModuleA);\n\n    let mockModuleA2 = () => {\n      let newModule = angular.module('moduleA', []);\n      newModule.value('version', '3');\n    };\n\n    browser.addMockModule('moduleA', mockModuleA2);\n\n    await browser.get('index.html');\n\n    expect(await element(by.css('[app-version]')).getText()).toEqual('3');\n  });\n\n  it('should have the version of the module A after deleting module B',\n      async() => {\n    browser.addMockModule('moduleA', mockModuleA);\n    browser.addMockModule('moduleB', mockModuleB);\n\n    browser.removeMockModule('moduleB');\n\n    await browser.get('index.html');\n\n    expect(await element(by.css('[app-version]')).getText()).toEqual('2');\n  });\n\n  it('should remove duplicate mock modules', async() => {\n    browser.addMockModule('moduleA', mockModuleA);\n    browser.addMockModule('moduleA', mockModuleA);\n    browser.removeMockModule('moduleA');\n\n    await browser.get('index.html');\n\n    expect(await element(by.css('[app-version]')).getText()).toEqual('0.1');\n  });\n\n  it('should be a noop to remove a module which does not exist', async() => {\n    browser.addMockModule('moduleA', mockModuleA);\n    browser.removeMockModule('moduleB');\n\n    await browser.get('index.html');\n\n    expect(await element(by.css('[app-version]')).getText()).toEqual('2');\n  });\n\n  it('should have the version provided from parameters through Module C',\n      async() => {\n    browser.addMockModule('moduleC', mockModuleC, '42', 'beta');\n\n    await browser.get('index.html');\n\n    expect(await element(by.css('[app-version]')).getText()).toEqual('42beta');\n  });\n\n  it('should retrieve a list of current mock modules', () => {\n    browser.addMockModule('moduleA', mockModuleA);\n    browser.addMockModule('moduleC', mockModuleC, '2', 'B');\n\n    // Should have 3 mock modules, A, C, and the base.\n    expect(browser.getRegisteredMockModules().length).toBe(3);\n    expect(browser.getRegisteredMockModules()[1]).toEqual(mockModuleA);\n    expect(browser.getRegisteredMockModules()[2]).toEqual(mockModuleC);\n  });\n\n  it('should load mock modules after refresh', async() => {\n    browser.addMockModule('moduleA', mockModuleA);\n\n    await browser.get('index.html');\n    expect(await element(by.css('[app-version]')).getText()).toEqual('2');\n\n    await browser.navigate().refresh();\n    expect(await element(by.css('[app-version]')).getText()).toEqual('2');\n  });\n\n  // Back and forward do NOT work at the moment because of an issue\n  // bootstrapping with Angular\n  /*\n  it('should load mock modules after navigating back and forward', () => {\n    browser.addMockModule('moduleA', mockModuleA);\n\n    browser.get('index.html');\n    expect(element(by.css('[app-version]')).getText()).toEqual('2');\n\n    browser.get('index.html#/repeater');\n    expect(element(by.css('[app-version]')).getText()).toEqual('2');\n\n    browser.navigate().back();\n    expect(element(by.css('[app-version]')).getText()).toEqual('2');\n\n    browser.navigate().forward();\n    expect(element(by.css('[app-version]')).getText()).toEqual('2');\n  });\n  */\n\n  it('should load mock modules after navigating back and forward from link', \n      async() => {\n    const caps = await browser.getCapabilities();\n    if (caps.get('browserName') === 'safari') {\n      // Safari can't handle navigation. Ignore this test.\n      return;\n    } else {\n      browser.addMockModule('moduleA', mockModuleA);\n\n      await browser.get('index.html');\n      expect(await element(by.css('[app-version]')).getText()).toEqual('2');\n\n      await element(by.linkText('repeater')).click();\n      expect(await element(by.css('[app-version]')).getText()).toEqual('2');\n\n      await browser.navigate().back();\n      expect(await element(by.css('[app-version]')).getText()).toEqual('2');\n\n      await browser.navigate().forward();\n      expect(await element(by.css('[app-version]')).getText()).toEqual('2');\n    }\n  });\n});\n"
  },
  {
    "path": "spec/basic/navigation_spec.js",
    "content": "const env = require('./../environment.js');\n\ndescribe('navigation', () => {\n  beforeEach(async () => {\n    await browser.get('index.html#/form');\n  });\n\n  it('should deal with alerts', async () => {\n    const alertButton = $('#alertbutton');\n    await alertButton.click();\n    const alertDialog = await browser.switchTo().alert();\n\n    expect(await alertDialog.getText()).toEqual('Hello');\n\n    await alertDialog.accept();\n  });\n\n  it('should refresh properly', async () => {\n    const username = element(by.model('username'));\n    const name = element(by.binding('username'));\n    await username.clear();\n    expect(await name.getText()).toEqual('');\n\n    await browser.navigate().refresh();\n\n    expect(await name.getText()).toEqual('Anon');\n  });\n  \n  it('should navigate back and forward properly', async () => {\n    await browser.get('index.html#/repeater');\n    expect(await browser.getCurrentUrl()).toEqual(`${env.baseUrl}/ng1/index.html#/repeater`);\n\n    await browser.navigate().back();\n    expect(await browser.getCurrentUrl()).toEqual(`${env.baseUrl}/ng1/index.html#/form`);\n\n    await browser.navigate().forward();\n    expect(await browser.getCurrentUrl()).toEqual(`${env.baseUrl}/ng1/index.html#/repeater`);\n  });\n\n  it('should navigate back and forward properly from link', async () => {\n    await element(by.linkText('repeater')).click();\n    expect(await browser.getCurrentUrl()).toEqual(`${browser.baseUrl}index.html#/repeater`);\n  \n    await browser.navigate().back();\n    expect(await browser.getCurrentUrl()).toEqual(`${browser.baseUrl}index.html#/form`);\n  \n    await browser.navigate().forward();\n    expect(await browser.getCurrentUrl()).toEqual(`${browser.baseUrl}index.html#/repeater`);\n  });\n});\n"
  },
  {
    "path": "spec/basic/polling_spec.js",
    "content": "/**\n * These tests show how to turn off Protractor's synchronization\n * when using applications which poll with $http or $timeout.\n * A better solution is to switch to the angular $interval service if possible.\n */\ndescribe('synchronizing with pages that poll', () => {\n  beforeEach(async () => {\n    await browser.get('index.html#/polling');\n  });\n\n  it('avoids timeouts using waitForAngularEnabled set to false', async () => {\n    const startButton = element(by.id('pollstarter'));\n    \n    const count = element(by.binding('count'));\n    expect(await count.getText()).toEqual('0');\n\n    await startButton.click();\n\n    // Turn this on to see timeouts.\n    await browser.waitForAngularEnabled(false);\n\n    const textBefore = await count.getText();\n    expect(textBefore).toBeGreaterThan(-1);\n\n    await browser.sleep(2000);\n\n    const textAfter = await count.getText();\n    expect(textAfter).toBeGreaterThan(1);\n  });\n\n  it('avoids timeouts using waitForAngularEnabled', async () => {\n    const startButton = element(by.id('pollstarter'));\n\n    const count = element(by.binding('count'));\n    expect(await count.getText()).toEqual('0');\n\n    await startButton.click();\n\n    // Turn this off to see timeouts.\n    await browser.waitForAngularEnabled(false);\n\n    expect(await browser.waitForAngularEnabled()).toBeFalsy();\n\n    const textBefore = await count.getText();\n    expect(textBefore).toBeGreaterThan(-1);\n\n    await browser.sleep(2000);\n\n    const textAfter = await count.getText();\n    expect(textAfter).toBeGreaterThan(1);\n  });\n\n  afterEach(async () => {\n    // Remember to turn it back on when you're done!\n    await browser.waitForAngularEnabled(true);\n  });\n});\n"
  },
  {
    "path": "spec/basic/restart_spec.js",
    "content": "describe('browser.restart', () => {\n  it(`doesn't break waitForAngularEnabled set to false`, async () => {\n    await browser.get('index.html#/polling');\n    await browser.restart();\n\n    await browser.waitForAngularEnabled(false);\n    // Get a non-angular page. It shouldn't fail if waitForAngularEnabled\n    // is turned off.\n    await browser.get('https://google.com/');\n  });\n\n  afterAll(async () => {\n    await browser.waitForAngularEnabled(true);\n  });\n});\n"
  },
  {
    "path": "spec/basic/synchronize_spec.js",
    "content": "describe('synchronizing with slow pages', () => {\n  beforeEach(async () => {\n    await browser.get('index.html#/async');\n  });\n\n  it('waits for http calls', async () => {\n    const status = element(by.binding('slowHttpStatus'));\n    const button = element(by.css('[ng-click=\"slowHttp()\"]'));\n    expect(await status.getText()).toEqual('not started');\n\n    await button.click();\n    expect(await status.getText()).toEqual('done');\n  });\n\n  it('waits for long javascript execution', async () => {\n    const status = element(by.binding('slowFunctionStatus'));\n    const button = element(by.css('[ng-click=\"slowFunction()\"]'));\n    expect(await status.getText()).toEqual('not started');\n\n    await button.click();\n    expect(await status.getText()).toEqual('done');\n  });\n\n  it('DOES NOT wait for timeout', async () => {\n    const status = element(by.binding('slowTimeoutStatus'));\n    const button = element(by.css('[ng-click=\"slowTimeout()\"]'));\n    expect(await status.getText()).toEqual('not started');\n\n    await button.click();\n    expect(await status.getText()).toEqual('pending...');\n  });\n\n  it('waits for $timeout', async () => {\n    const status = element(by.binding('slowAngularTimeoutStatus'));\n    const button = element(by.css('[ng-click=\"slowAngularTimeout()\"]'));\n    expect(await status.getText()).toEqual('not started');\n\n    await button.click();\n    expect(await status.getText()).toEqual('done');\n  });\n\n  it('waits for $timeout then a promise', async () => {\n    const status = element(by.binding('slowAngularTimeoutPromiseStatus'));\n    const button = element(by.css('[ng-click=\"slowAngularTimeoutPromise()\"]'));\n    expect(await status.getText()).toEqual('not started');\n\n    await button.click();\n    expect(await status.getText()).toEqual('done');\n  });\n\n  it('waits for long http call then a promise', async () => {\n    const status = element(by.binding('slowHttpPromiseStatus'));\n    const button = element(by.css('[ng-click=\"slowHttpPromise()\"]'));\n    expect(await status.getText()).toEqual('not started');\n\n    await button.click();\n    expect(await status.getText()).toEqual('done');\n  });\n\n  it('waits for slow routing changes', async () => {\n    const status = element(by.binding('routingChangeStatus'));\n    const button = element(by.css('[ng-click=\"routingChange()\"]'));\n    expect(await status.getText()).toEqual('not started');\n\n    await button.click();\n    expect(await browser.getPageSource()).toMatch('polling mechanism');\n  });\n\n  it('waits for slow ng-include templates to load', async () => {\n    const status = element(by.css('.included'));\n    const button = element(by.css('[ng-click=\"changeTemplateUrl()\"]'));\n    expect(await status.getText()).toEqual('fast template contents');\n\n    await button.click();\n    expect(await status.getText()).toEqual('slow template contents');\n  });\n});\n"
  },
  {
    "path": "spec/basicConf.js",
    "content": "var env = require('./environment.js');\n\n// The main suite of Protractor tests.\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  // Spec patterns are relative to this directory.\n  specs: [\n    'basic/*_spec.js'\n  ],\n\n  // Exclude patterns are relative to this directory.\n  exclude: [\n    'basic/exclude*.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  params: {\n    login: {\n      user: 'Jane',\n      password: '1234'\n    }\n  }\n};\n"
  },
  {
    "path": "spec/ciBStackConf.js",
    "content": "var env = require('./environment.js');\n\n// The main suite of Protractor tests.\nexports.config = {\n  browserstackUser: process.env.BROWSER_STACK_USERNAME,\n  browserstackKey: process.env.BROWSER_STACK_ACCESS_KEY,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  // Spec patterns are relative to this directory.\n  specs: [\n    // TODO(selenium4): revert back to 'basic/*_spec.js', for now just use lib_spec\n    // 'basic/*_spec.js'\n    'basic/lib_spec.js'\n  ],\n\n  // Exclude patterns are relative to this directory.\n  exclude: [\n    'basic/exclude*.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  jasmineNodeOpts: {\n    isVerbose: true,\n    realtimeFailure: true\n  },\n\n  params: {\n    login: {\n      user: 'Jane',\n      password: '1234'\n    }\n  }\n};\nexports.config.capabilities['browserstack.local'] = true;\n"
  },
  {
    "path": "spec/ciFullConf.js",
    "content": "var env = require('./environment.js');\n\n// The main suite of Protractor tests to be run on CI servers.\nexports.config = {\n  sauceUser: process.env.SAUCE_USERNAME,\n  sauceKey: process.env.SAUCE_ACCESS_KEY,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  // Spec patterns are relative to this directory.\n  specs: [\n    'basic/*_spec.js',\n  ],\n\n  // Exclude patterns are relative to this directory.\n  exclude: [\n    'basic/exclude*.js'\n  ],\n\n  multiCapabilities: [{\n    'browserName': 'chrome',\n    'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,\n    'build': process.env.TRAVIS_BUILD_NUMBER,\n    'name': 'Protractor suite tests',\n    'version': '70'\n  }, {\n    'browserName': 'firefox',\n    'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,\n    'build': process.env.TRAVIS_BUILD_NUMBER,\n    'name': 'Protractor suite tests',\n    'version': '60',\n  }],\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  allScriptsTimeout: 120000,\n  getPageTimeout: 120000,\n\n  jasmineNodeOpts: {\n    showTiming: true,\n    defaultTimeoutInterval: 120000\n  },\n\n  params: {\n    login: {\n      user: 'Jane',\n      password: '1234'\n    }\n  }\n};\n"
  },
  {
    "path": "spec/ciNg2Conf.js",
    "content": "exports.config = require('./ciFullConf.js').config;\nexports.config.specs = require('./angular2Conf.js').config.specs;\nexports.config.exclude = undefined;"
  },
  {
    "path": "spec/ciSmokeConf.js",
    "content": "var env = require('./environment.js');\n\n// Smoke tests to be run on CI servers - covers more browsers than\n// ciConf.js, but does not run all tests.\nexports.config = {\n  sauceUser: process.env.SAUCE_USERNAME,\n  sauceKey: process.env.SAUCE_ACCESS_KEY,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  specs: [\n    // TODO(selenium4): revert back. For now just put on lib_spec.js\n    // 'basic/locators_spec.js',\n    // 'basic/mockmodule_spec.js',\n    // 'basic/synchronize_spec.js'\n    'basic/lib_spec.js'\n  ],\n\n  // Two latest versions of IE, and Safari.\n  // The second latest version of Chrome and Firefox (latest versions are\n  // tested against the full suite in ciFullConf)\n  // TODO - add mobile.\n  multiCapabilities: [{\n    'browserName': 'chrome',\n    'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,\n    'build': process.env.TRAVIS_BUILD_NUMBER,\n    'name': 'Protractor smoke tests',\n    'version': '55',\n    'chromedriver-version': '2.27',\n    'platform': 'OS X 10.11'\n  }, {\n    'browserName': 'firefox',\n    'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,\n    'build': process.env.TRAVIS_BUILD_NUMBER,\n    'name': 'Protractor smoke tests',\n    'version': 'beta'\n  }, {\n    // TODO: Add Safari 10 once Saucelabs gets Selenium 3\n    'browserName': 'safari',\n    'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,\n    'build': process.env.TRAVIS_BUILD_NUMBER,\n    'name': 'Protractor smoke tests',\n    'version': '9',\n  }, {\n    'browserName': 'MicrosoftEdge',\n    'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,\n    'build': process.env.TRAVIS_BUILD_NUMBER,\n    'name': 'Protractor smoke tests',\n    'version': '14.14393',\n    'platform': 'Windows 10'\n  }, {\n    'browserName': 'Internet Explorer',\n    'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,\n    'build': process.env.TRAVIS_BUILD_NUMBER,\n    'name': 'Protractor smoke tests',\n    'version': '11',\n    'platform': 'Windows 8.1'\n  }],\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  // Up the timeouts for the slower browsers (IE, Safari).\n  allScriptsTimeout: 120000,\n  getPageTimeout: 120000,\n\n  jasmineNodeOpts: {\n    showTiming: true,\n    defaultTimeoutInterval: 120000\n  },\n\n  params: {\n    login: {\n      user: 'Jane',\n      password: '1234'\n    }\n  }\n};\n"
  },
  {
    "path": "spec/control/spec.js",
    "content": "describe('protractor control flow', () => {\n  it('should not deadlock', async() => {\n    await browser.driver.wait(() => {\n      return true;\n    }, 1000);\n    expect(true).toEqual(true);\n  });\n});\n"
  },
  {
    "path": "spec/controlLockConf.js",
    "content": "const env = require('./environment.js');\nconst webdriver = require('selenium-webdriver');\n\n// Tests for cases that have caused WebDriver promise locks in\n// the past.\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  specs: [\n    'control/spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  onPrepare: async function() {\n    // This is a reasonable use case - do some promise that takes some time,\n    // and then do a wait until something is set up correctly.\n    await new Promise(resolve => {\n      setTimeout(resolve, 100);\n    });\n    // This could also be replaced by an 'execute' to see the same behavior.\n    return await browser.driver.wait(function() {\n      return true;\n    }, 10000, 'onPrepare wait');\n  }\n};\n"
  },
  {
    "path": "spec/custom/framework.js",
    "content": "/**\n * Jasmine framework dummy alias to prove Protractor supports\n * external custom frameworks.\n *\n * @param {Runner} runner The current Protractor Runner.\n * @param {Array} specs Array of Directory Path Strings.\n * @return {q.Promise} Promise resolved with the test results\n */\nexports.run = require('../../lib/frameworks/jasmine.js').run;\n"
  },
  {
    "path": "spec/custom/smoke_spec.js",
    "content": "describe('smoke jasmine tests', () => {\n  it('should do some dummy test', () => {\n    expect(1).toBe(1);\n  });\n});\n"
  },
  {
    "path": "spec/customFramework.js",
    "content": "const env = require('./environment.js');\n\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'custom',\n  frameworkPath: './custom/framework.js',\n\n  specs: [\n    'custom/smoke_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/'\n};\n"
  },
  {
    "path": "spec/dependencyTest/protractor_spec.js",
    "content": "var protractor = require('../../built/index');\n\ndescribe('require(\\'protractor\\')', () => {\n\n  describe('exported protractor classes', () => {\n    it('should be defined', () => {\n      var protractorClasses = ['ProtractorBrowser', 'ElementFinder', 'ElementArrayFinder',\n        'ProtractorBy', 'ProtractorExpectedConditions'];\n      for (var pos in protractorClasses) {\n        var property = protractorClasses[pos];\n        expect(typeof protractor[property]).toEqual('function');\n      }\n    });\n\n    it('should have selenium-webdriver functions defined', () => {\n      // TODO(selenium4): Update Actions when it is in typings. ActionSequence and EventEmitter is deprecated.\n      var seleniumFunctions = [/*'Actions', */'Builder',\n        'Capabilities', 'Command', 'FileDetector', 'Session', 'WebDriver',\n        'WebElement', 'WebElementPromise'];\n      for (var pos in seleniumFunctions) {\n        var propertyObj = seleniumFunctions[pos];\n        expect(typeof protractor[propertyObj]).toEqual('function');\n      }\n    });\n\n    it('should have selenium-webdriver objects defined', () => {\n      var seleniumObjects = ['Browser', 'Button', 'Capability', 'CommandName', 'Key'];\n      for (var pos in seleniumObjects) {\n        var propertyObj = seleniumObjects[pos];\n        expect(typeof protractor[propertyObj]).toEqual('object');\n      }\n    });\n\n\n    describe('browser class', () => {\n      it('should have static variables defined', () => {\n        var staticVariables = ['By'];\n        for (var pos in staticVariables) {\n          var property = staticVariables[pos];\n          expect(typeof protractor.ProtractorBrowser[property]).toEqual('object');\n        }\n      });\n    });\n  });\n\n  describe('promise namespace', () => {\n    it('should have functions defined (spot check)', () => {\n      var promiseFunctions = ['delayed', 'filter'];\n      for (var pos in promiseFunctions) {\n        var property = promiseFunctions[pos];\n        expect(typeof protractor.promise[property]).toEqual('function');\n      }\n    });\n  });\n});\n"
  },
  {
    "path": "spec/dependencyTest/seleniumWebdriver_spec.js",
    "content": "var WebDriver = require('selenium-webdriver').WebDriver;\nvar By = require('selenium-webdriver').By;\nvar Setup = require('./setup');\nvar Chrome = require('selenium-webdriver/chrome');\nvar Firefox = require('selenium-webdriver/firefox');\nvar SeleniumError = require('selenium-webdriver').error;\nvar Remote = require('selenium-webdriver/remote');\n\nvar WEBDRIVER = {\n  staticFunctions: ['createSession'],\n\n  instanceFunctions: ['actions', 'wait', 'sleep', 'getCurrentUrl', 'getTitle',\n    'takeScreenshot', 'getSession', 'getCapabilities', 'quit', \n    'executeAsyncScript', 'wait', 'getWindowHandle',\n    'getAllWindowHandles', 'getPageSource', 'close', 'get', 'findElement',\n    'findElements', 'manage', 'navigate', 'switchTo']\n};\n\nvar WEBELEMENT = {\n  instanceFunctions: ['getDriver', 'getId', 'findElement', 'click', 'sendKeys', 'getTagName',\n    'getCssValue', 'getAttribute', 'getText', 'getRect', 'isEnabled', 'isSelected',\n    'submit', 'clear', 'isDisplayed', 'takeScreenshot']\n};\nvar BY = {\n  staticFunctions: ['className', 'css', 'id', 'linkText', 'js', 'name',\n      'partialLinkText', 'tagName', 'xpath']\n};\nvar SESSION = {\n  instanceFunctions: ['getId', 'getCapabilities']\n};\nvar CHROME = {\n  staticFunctions: ['Driver', 'ServiceBuilder']\n};\nvar FIREFOX = {\n  staticFunction: 'Driver'\n};\n\ndescribe('selenium-webdriver dependency', function() {\n  describe('require(\"selenium-webdriver\").WebDriver', function() {\n    it('should have static functions', function() {\n      for (var pos in WEBDRIVER.staticFunctions) {\n        var staticFunc = WEBDRIVER.staticFunctions[pos];\n        expect(typeof WebDriver[staticFunc] == 'function').toBe(true);\n      }\n    });\n\n    it('should have instance functions', function() {\n      var webdriver = Setup.getWebDriver();\n      for (var pos in WEBDRIVER.instanceFunctions) {\n        var instanceFunc = WEBDRIVER.instanceFunctions[pos];\n        expect(typeof webdriver[instanceFunc] == 'function').toBe(true);\n      }\n    });\n  });\n\n  describe('require(\"selenium-webdriver\").WebElement', function() {\n    it('should have a instance functions', function() {\n      var webElement = Setup.getWebElement();\n      for (var pos in WEBELEMENT.instanceFunctions) {\n        var func = WEBELEMENT.instanceFunctions[pos];\n        expect(typeof webElement[func] == 'function').toBe(true);\n      }\n    });\n  });\n\n  describe('require(\"selenium-webdriver\").By', function() {\n    it('should have a static functions', function() {\n      for (var pos in BY.staticFunctions) {\n        var func = BY.staticFunctions[pos];\n        expect(typeof By[func] == 'function').toBe(true);\n      }\n    });\n  });\n\n  describe('require(\"selenium-webdriver\").Session', function() {\n    it('should have a instance functions', function() {\n      var session = Setup.getSession();\n      for (var pos in SESSION.instanceFunctions) {\n        var func = SESSION.instanceFunctions[pos];\n        expect(typeof session[func] == 'function').toBe(true);\n      }\n    });\n  });\n\n  describe('require(\"selenium-webdriver/chrome\")', function() {\n    it('should have a static functions', function() {\n      for (var pos in CHROME.staticFunctions) {\n        var func = CHROME.staticFunctions[pos];\n        expect(typeof Chrome[func] == 'function').toBe(true);\n      }\n    });\n  });\n\n  describe('require(\"selenium-webdriver/firefox\")', function() {\n    it('should have a ' + FIREFOX.staticFunction + ' function', function() {\n      expect(typeof Firefox[FIREFOX.staticFunction] == 'function').toBe(true);\n    });\n  });\n  describe('require(\"selenium-webdriver\").error', function() {\n    it('should have a NoSuchElementError function', function() {\n      expect(typeof SeleniumError.NoSuchElementError == 'function').toBe(true);\n    });\n    it('should have error codes', function() {\n      expect(typeof SeleniumError.ErrorCode == 'object').toBe(true);\n    });\n    it('should have an error code of NO_SUCH_ALERT', function() {\n      expect(typeof SeleniumError.ErrorCode.NO_SUCH_ALERT == 'number').toBe(true);\n    });\n  });\n  describe('require(\"selenium-webdriver/remote\")', function() {\n    it('should have a SeleniumServer function', function() {\n      expect(typeof Remote['SeleniumServer'] == 'function').toBe(true);\n    });\n  });\n});\n"
  },
  {
    "path": "spec/dependencyTest/setup.js",
    "content": "// lib/webdriver.js exported via index.js\nvar WebDriver = require('selenium-webdriver').WebDriver;\nvar WebElement = require('selenium-webdriver').WebElement;\n\n// lib/sessions.js\nvar Session = require('selenium-webdriver/lib/session').Session;\n\n// executors.js\nvar Executor = require('selenium-webdriver/lib/command').Executor;\n\nvar session = '1234';\nvar seleniumAddress = 'http://localhost:4444/wd/hub';\nvar capabilities = {\n  browserName: 'chrome'\n};\n\n\nvar getExecutor = function() {\n  return new Executor();\n};\n\nvar getWebDriver = function() {\n  return new WebDriver(session, getExecutor());\n};\n\nvar getSession = function() {\n  return new Session(session, capabilities);\n};\n\nvar getWebElement = function() {\n  return new WebElement(getWebDriver(), session);\n};\n\nmodule.exports = {\n  getExecutor: getExecutor,\n  getWebDriver: getWebDriver,\n  getSession: getSession,\n  getWebElement: getWebElement\n};\n"
  },
  {
    "path": "spec/directConnect/directconnect_spec.js",
    "content": "describe('direct connect', () => {\n  it('should instantiate and run', async() => {\n    const usernameInput = element(by.model('username'));\n    const name = element(by.binding('username'));\n\n    await browser.get('index.html#/form');\n\n    expect(await name.getText()).toEqual('Anon');\n\n    await usernameInput.clear();\n    await usernameInput.sendKeys('Jane');\n    expect(await name.getText()).toEqual('Jane');\n  });\n});\n"
  },
  {
    "path": "spec/directConnectConf.js",
    "content": "var env = require('./environment.js');\n\n// A configuration file running a simple direct connect spec\nexports.config = {\n  directConnect: true,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  // Disabled until https://github.com/angular/protractor/issues/4253 is resolved\n  /*\n  multiCapabilities: [{\n    'browserName': 'chrome'\n  }, {\n    'browserName': 'firefox',\n  }],\n  */\n  capabilities: {\n    browserName: 'chrome',\n  },\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  specs: ['directConnect/*_spec.js'],\n\n  jasmineNodeOpts: {\n    showColors: true,\n    defaultTimeoutInterval: 30000\n  }\n};\n"
  },
  {
    "path": "spec/driverProviderAttachSessionConf.js",
    "content": "var env = require('./environment');\n\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  specs: [\n    'driverProviders/attachSession/*_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl,\n};\n"
  },
  {
    "path": "spec/driverProviderLocalConf.js",
    "content": "var env = require('./environment');\n\nexports.config = {\n\n  framework: 'jasmine',\n  SELENIUM_PROMISE_MANAGER: false,\n\n  specs: [\n    'driverProviders/local/*_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl,\n};\n"
  },
  {
    "path": "spec/driverProviderTest.js",
    "content": "/**\n * Sanity integration tests for Driver Providers.\n *\n * Assumed setup:\n * - selenium server running locally at http://localhost:4444\n * - selenium jar and chromedriver in protractor/selenium, where\n *   webdriver-manager stores them.\n * - if you want to test saucelabs, test with --sauceUser and --sauceKey\n * - if you want to test browserstack driverProvider, test with \n     --browserstackUser and --browserstackKey\n * You should verify that there are no lingering processes when these tests\n * complete.\n */\n\nconst argv = require('yargs').argv;\nconst env = require('./environment');\n\nconst Direct = require('../built/driverProviders/direct').Direct;\nconst Hosted = require('../built/driverProviders/hosted').Hosted;\nconst Local = require('../built/driverProviders/local').Local;\nconst Sauce = require('../built/driverProviders/sauce').Sauce;\nconst BrowserStack = require('../built/driverProviders/browserStack').BrowserStack;\n\nconst testDriverProvider = async (driverProvider) => {\n  await driverProvider.setupEnv();\n  const driver = driverProvider.getNewDriver();\n  await driver.get('about:blank');\n  const url = await driver.getCurrentUrl();\n  if (url != 'about:blank') {\n    throw new Error(`url was not about:blank, instead found ${url}`);\n  }\n  \n  if (driverProvider.updateJob) {\n    await driverProvider.updateJob({'passed': true});\n    await driverProvider.teardownEnv();\n  } else {\n    await driverProvider.teardownEnv();\n  }\n};\n\nconst chromeConfig = {\n  capabilities: {\n    browserName: 'chrome'\n  }\n};\n\ntestDriverProvider(new Direct(chromeConfig)).\n    then(() => {\n      console.log('direct.dp with chrome working!');\n    }, (err) => {\n      console.error('direct.dp with chrome failed with', err);\n      throw err;\n    });\n\nconst firefoxConfig = {\n  capabilities: {\n    browserName: 'firefox'\n  }\n};\ntestDriverProvider(new Direct(firefoxConfig)).\n    then(() => {\n      console.log('direct.dp with firefox working!');\n    }, (err) => {\n      console.error('direct.dp with firefox failed with', err);\n      throw err;\n    });\n\nconst hostedConfig = {\n  seleniumAddress: env.seleniumAddress,\n  capabilities: {\n    browserName: 'firefox'\n  }\n};\ntestDriverProvider(new Hosted(hostedConfig)).\n    then(() => {\n      console.log('hosted.dp working!');\n    }, (err) => {\n      console.error('hosted.dp failed with', err);\n      throw err;\n    });\n\nconst hostedPromisedConfig = {\n  seleniumAddress: Promise.resolve(env.seleniumAddress),\n  capabilities: {\n    browserName: 'firefox'\n  }\n};\ntestDriverProvider(new Hosted(hostedPromisedConfig)).\n    then(() => {\n      console.log('hosted.dp with promises working!');\n    }, (err) => {\n      console.error('hosted.dp with promises failed with', err);\n      throw err;\n    });\n\nconst localConfig = {\n  seleniumArgs: [],\n  capabilities: {\n    browserName: 'chrome'\n  }\n};\ntestDriverProvider(new Local(localConfig)).\n    then(() => {\n      console.log('local.dp working!');\n    }, (err) => {\n      console.error('local.dp failed with', err);\n      throw err;\n    });\n\nif (argv.sauceUser && argv.sauceKey) {\n  const sauceConfig = {\n    sauceUser: argv.sauceUser,\n    sauceKey: argv.sauceKey,\n    sauceBuild: argv.sauceBuild,\n    capabilities: {\n      browserName: 'chrome'\n    }\n  };\n  testDriverProvider(new Sauce(sauceConfig)).\n      then(() => {\n        console.log('sauce.dp working!');\n      }, (err) => {\n        console.error('sauce.dp failed with', err);\n        throw err;\n      });\n}\n\nif (argv.browserstackUser && argv.browserstackKey) {\n  const browserStackConfig = {\n    browserstackUser: argv.browserstackUser,\n    browserstackKey: argv.browserstackKey,\n    capabilities: {\n      'build': 'protractor-browserstack-spec',\n      'name': 'protractor-browserstack-spec',\n      'browserName': 'chrome',\n    }\n  };\n  testDriverProvider(new BrowserStack(browserStackConfig)).\n      then(() => {\n        console.log('browserstack.dp working!');\n      }, (err) => {\n        console.error('browserstack.dp failed with', err);\n        throw err;\n      });\n}\n"
  },
  {
    "path": "spec/driverProviders/attachSession/attachSession_spec.js",
    "content": "describe('selenium session id', () => {\n  const URL = '/ng2/#/async';\n\n  beforeEach(async () => {\n    await browser.get(URL);\n  });\n\n  it('should be able to use an existing session', async () => {\n    const incrementButton = element.all(by.css('button.action')).get(0);\n    const incrementValue = element.all(by.css('span.val')).get(0);\n    await incrementButton.click();\n    expect(await incrementValue.getText()).toBe('1');\n  });\n});\n"
  },
  {
    "path": "spec/driverProviders/local/local_spec.js",
    "content": "describe('local driver provider', () => {\n  const URL = '/ng2/#/async';\n\n  it('should get a page and find an element', async() => {\n    await browser.get(URL);\n    const increment = $('#increment');\n    expect(await increment.isPresent()).toBeDefined();\n  });\n\n  it('should get a forked instance, and find an element', async() => {\n    await browser.get(URL);\n    const browser2 = await browser.forkNewDriverInstance();\n    await browser2.get(URL);\n    const increment = browser2.$('#increment');\n    expect(await increment.isPresent()).toBeDefined();\n  });\n});\n"
  },
  {
    "path": "spec/environment.js",
    "content": " // Common configuration files with defaults plus overrides from environment vars\nvar webServerDefaultPort = 8081;\n\nmodule.exports = {\n  // The address of a running selenium server.\n  seleniumAddress:\n    (process.env.SELENIUM_URL || 'http://localhost:4444/wd/hub'),\n\n  // Capabilities to be passed to the webdriver instance.\n  capabilities: {\n    'browserName':\n        (process.env.TEST_BROWSER_NAME || 'chrome'),\n    'version':\n        (process.env.TEST_BROWSER_VERSION || 'ANY')\n  },\n\n  // Default http port to host the web server\n  webServerDefaultPort: webServerDefaultPort,\n\n  // Protractor interactive tests\n  interactiveTestPort: 6969,\n\n  // A base URL for your application under test.\n  baseUrl:\n    'http://' + (process.env.HTTP_HOST || 'localhost') +\n          ':' + (process.env.HTTP_PORT || webServerDefaultPort)\n\n};"
  },
  {
    "path": "spec/errorTest/afterLaunchChangesExitCodeConf.js",
    "content": "var env = require('../environment.js');\n\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  specs: [\n    'baseCase/single_failure_spec1.js'\n  ],\n\n  multiCapabilities: [{\n    'browserName': 'chrome'\n  }],\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  jasmineNodeOpts: {\n    defaultTimeoutInterval: 90000\n  },\n\n  afterLaunch: function(exitCode) {\n    return exitCode + 10;\n  }\n\n};\n"
  },
  {
    "path": "spec/errorTest/baseCase/error_spec.js",
    "content": "describe('finding an element that does not exist', () => {\n  it('should throw an error', async () => {\n    await browser.get('index.html');\n    element(by.binding('INVALID'));  // greeting\n  });\n});\n"
  },
  {
    "path": "spec/errorTest/baseCase/mocha_failure_spec.js",
    "content": "// Use the external Chai As Promised to deal with resolving promises in\n// expectations.\nconst chai = require('chai');\nconst chaiAsPromised = require('chai-as-promised');\nchai.use(chaiAsPromised);\nconst expect = chai.expect;\n\ndescribe('protractor library', () => {\n  it('should fail', async () => {\n    await browser.get('index.html');\n    expect(await browser.getTitle()).to.equal('INTENTIONALLY INCORRECT');\n  });\n});\n"
  },
  {
    "path": "spec/errorTest/baseCase/single_failure_spec1.js",
    "content": "describe('single failure spec1', () => {\n  it('should fail expectation', async () => {\n    await browser.get('index.html');\n    const greeting = element(by.binding('greeting'));\n    expect(await greeting.getText()).toEqual('INTENTIONALLY INCORRECT');\n  });\n});\n"
  },
  {
    "path": "spec/errorTest/baseCase/single_failure_spec2.js",
    "content": "describe('single failure spec2', () => {\n  it('should fail expectation', async () => {\n    await browser.get('index.html');\n    const greeting = element(by.binding('greeting'));\n    expect(await greeting.getText()).toEqual('INTENTIONALLY INCORRECT');\n  });\n});\n"
  },
  {
    "path": "spec/errorTest/baseCase/slow_http_and_timeout_spec.js",
    "content": "describe('slow asynchronous events', () => {\n  beforeEach(async () => {\n    await browser.get('index.html#/async');\n  });\n\n  it('waits for http calls', async () => {\n    const status = element(by.binding('slowHttpStatus'));\n    const button = element(by.css('[ng-click=\"slowHttp()\"]'));\n\n    expect(await status.getText()).toEqual('not started');\n\n    await button.click();\n\n    expect(await status.getText()).toEqual('done');\n  });\n\n  it('waits for $timeout', async () => {\n    const status = element(by.binding('slowAngularTimeoutStatus'));\n    const button = element(by.css('[ng-click=\"slowAngularTimeout()\"]'));\n\n    expect(await status.getText()).toEqual('not started');\n\n    await button.click();\n\n    expect(await status.getText()).toEqual('done');\n  });\n});\n"
  },
  {
    "path": "spec/errorTest/baseCase/success_spec.js",
    "content": "describe('success spec', () => {\n  it('should pass', async () => {\n    await browser.get('index.html');\n    const greeting = element(by.binding('greeting'));\n    expect(await greeting.getText()).toEqual('Hiya');\n  });\n});\n"
  },
  {
    "path": "spec/errorTest/baseCase/timeout_spec.js",
    "content": "describe('timeout spec', () => {\n  it('should timeout due to jasmine spec limit', async () => {\n    await browser.get('index.html#/form');\n  }, 1);\n});\n"
  },
  {
    "path": "spec/errorTest/browserStackAuthentication.js",
    "content": "const env = require('../environment.js');\n\nexports.config = {\n  browserstackUser: 'foobar',\n  browserstackKey: 'foobar',\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  specs: [\n    '../../example/example_spec.js'\n  ],\n\n  capabilities: {\n    'browserName': 'chrome'\n  },\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  jasmineNodeOpts: {\n    defaultTimeoutInterval: 90000\n  }\n\n};\n"
  },
  {
    "path": "spec/errorTest/debugMultiCapabilities.js",
    "content": "const env = require('../environment.js');\n\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n  framework: 'jasmine',\n  debug: true,\n  specs: [\n    '../../example/example_spec.js'\n  ],\n  multiCapabilities: [{\n    'browserName': 'chrome'\n  },{\n    'browserName': 'firefox'\n  }],\n  baseUrl: env.baseUrl,\n};\n"
  },
  {
    "path": "spec/errorTest/getMultiCapabilitiesConf.js",
    "content": "const env = require('../environment.js');\n\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  // Spec patterns are relative to this directory.\n  specs: [\n    '../../basic/mock*'\n  ],\n\n  getMultiCapabilities: function() {\n    return new Promise(() => {\n      throw new Error('get multi capabilities failed');\n    });\n  },\n\n  baseUrl: env.baseUrl + '/ng1/'\n};\n"
  },
  {
    "path": "spec/errorTest/mochaFailureConf.js",
    "content": "const env = require('../environment.js');\n\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  specs: [\n    'baseCase/mocha_failure_spec.js'\n  ],\n\n  multiCapabilities: [{\n    'browserName': 'chrome'\n  }],\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  mochaOpts: {\n    reporter: 'spec',\n    timeout: 4000\n  },\n\n  framework: 'mocha'\n};\n"
  },
  {
    "path": "spec/errorTest/multiFailureConf.js",
    "content": "const env = require('../environment.js');\n\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  specs: [\n    'baseCase/single_failure_spec1.js',\n    'baseCase/single_failure_spec2.js'\n  ],\n\n  multiCapabilities: [{\n    'browserName': 'chrome'\n  }],\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  jasmineNodeOpts: {\n    defaultTimeoutInterval: 90000\n  }\n\n};\n"
  },
  {
    "path": "spec/errorTest/pluginsFailingConf.js",
    "content": "const env = require('../environment.js');\n\n// A small suite to make sure the full functionality of plugins work\nexports.config = {\n  // seleniumAddress: env.seleniumAddress,\n  mockSelenium: true,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  // Spec patterns are relative to this directory.\n  specs: [\n    '../plugins/specs/fail_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  // Plugin patterns are relative to this directory.\n  plugins: [{\n    path: '../plugins/plugins/basic_plugin.js'\n  }, {\n    path: '../plugins/plugins/failing_plugin.js'\n  }]\n};\n"
  },
  {
    "path": "spec/errorTest/sauceLabsAuthentication.js",
    "content": "const env = require('../environment.js');\n\nexports.config = {\n  sauceUser: 'foobar',\n  sauceKey: 'foobar',\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  specs: [\n    '../../example/example_spec.js'\n  ],\n\n  capabilities: {\n    'browserName': 'chrome'\n  },\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  jasmineNodeOpts: {\n    defaultTimeoutInterval: 90000\n  }\n\n};\n"
  },
  {
    "path": "spec/errorTest/shardedFailureConf.js",
    "content": "const env = require('../environment.js');\n\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  specs: [\n    'baseCase/single_failure_spec1.js',\n    'baseCase/single_failure_spec2.js'\n  ],\n\n  multiCapabilities: [{\n    'browserName': 'chrome',\n    maxInstances: 2,\n    shardTestFiles: true\n  }],\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  jasmineNodeOpts: {\n    defaultTimeoutInterval: 90000\n  }\n\n};\n"
  },
  {
    "path": "spec/errorTest/singleFailureConf.js",
    "content": "const env = require('../environment.js');\n\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  specs: [\n    'baseCase/single_failure_spec1.js'\n  ],\n\n  multiCapabilities: [{\n    'browserName': 'chrome'\n  }],\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  jasmineNodeOpts: {\n    defaultTimeoutInterval: 90000\n  }\n\n};\n"
  },
  {
    "path": "spec/errorTest/slowHttpAndTimeoutConf.js",
    "content": "const env = require('../environment.js');\n\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  specs: [\n    'baseCase/slow_http_and_timeout_spec.js'\n  ],\n\n  multiCapabilities: [{\n    'browserName': 'chrome'\n  }],\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  allScriptsTimeout: 1000 // Each test waits on something that has a 5 second tick.\n};\n"
  },
  {
    "path": "spec/errorTest/timeoutConf.js",
    "content": "const env = require('../environment.js');\n\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  specs: [\n    'baseCase/timeout_spec.js'\n  ],\n\n  multiCapabilities: [{\n    'browserName': 'chrome'\n  }],\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  jasmineNodeOpts: {\n    defaultTimeoutInterval: 90000\n  }\n\n};\n"
  },
  {
    "path": "spec/getCapabilitiesConf.js",
    "content": "const env = require('./environment.js');\n\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  // Spec patterns are relative to this directory.\n  specs: [\n    'basic/mock*'\n  ],\n\n  framework: 'debugprint',\n  getMultiCapabilities: async function() {\n    // Wait for a server to be ready or get capabilities asynchronously.\n    return await new Promise(resolve => {\n      setTimeout(() => {\n        resolve([{\n          'browserName': 'firefox'\n        }]);\n      }, 1000);\n    });\n  },\n\n  baseUrl: env.baseUrl + '/ng1/'\n};\n\n"
  },
  {
    "path": "spec/hybrid/async_spec.js",
    "content": "describe('async angular1/2 hybrid using ngUpgrade application', () => {\n  describe('@angular/upgrade/static', () => {\n    it('should be able to click buttons and wait for $timeout', async () => {\n      await browser.get('/upgrade');\n\n      const rootBtn = $$('my-app button').first();\n      expect(await rootBtn.getText()).toEqual('Click Count: 0');\n      await rootBtn.click();\n      expect(await rootBtn.getText()).toEqual('Click Count: 1');\n\n      const ng2Btn = $$('ng2 button').first();\n      expect(await ng2Btn.getText()).toEqual('Click Count: 0');\n      await ng2Btn.click();\n      expect(await ng2Btn.getText()).toEqual('Click Count: 1');\n\n      const ng1Btn = $('ng1 button');\n      expect(await ng1Btn.getText()).toEqual('Click Count: 0');\n      await ng1Btn.click();\n      expect(await ng1Btn.getText()).toEqual('Click Count: 1');\n    });\n\n    it('should be able to automatically infer ng1/ng2/ngUpgrade', async () => {\n      await browser.get('/upgrade');\n      expect(await $('h1').getText()).toBe('My App');\n      await browser.get('/ng1');\n      expect(await $$('h4').first().getText()).toBe('Bindings');\n      await browser.get('/upgrade');\n      expect(await $('h1').getText()).toBe('My App');\n      await browser.get('/ng2');\n      expect(await $('h1').getText()).toBe('Test App for Angular 2');\n      await browser.get('/upgrade');\n      expect(await $('h1').getText()).toBe('My App');\n    });\n  });\n\n  describe('@angular/upgrade (not static)', () => {\n    it('should be able to click buttons and wait for $timeout', async () => {\n      await browser.get('/upgrade?no_static');\n\n      const rootBtn = $$('my-app button').first();\n      expect(await rootBtn.getText()).toEqual('Click Count: 0');\n      await rootBtn.click();\n      expect(await rootBtn.getText()).toEqual('Click Count: 1');\n\n      const ng2Btn = $$('ng2 button').first();\n      expect(await ng2Btn.getText()).toEqual('Click Count: 0');\n      await ng2Btn.click();\n      expect(await ng2Btn.getText()).toEqual('Click Count: 1');\n\n      const ng1Btn = $('ng1 button');\n      expect(await ng1Btn.getText()).toEqual('Click Count: 0');\n      await ng1Btn.click();\n      expect(await ng1Btn.getText()).toEqual('Click Count: 1');\n    });\n  });\n});\n\ndescribe('async angular1/2 hybrid using downgrade application', () => {\n  it('should be able to click buttons and wait for $timeout', async () => {\n      await browser.get('/upgrade?downgrade');\n\n      const rootBtn = $$('my-app button').first();\n      expect(await rootBtn.getText()).toEqual('Click Count: 0');\n      await rootBtn.click();\n      expect(await rootBtn.getText()).toEqual('Click Count: 1');\n\n      const ng2Btn = $$('ng2 button').first();\n      expect(await ng2Btn.getText()).toEqual('Click Count: 0');\n      await ng2Btn.click();\n      expect(await ng2Btn.getText()).toEqual('Click Count: 1');\n  });\n});\n"
  },
  {
    "path": "spec/hybridConf.js",
    "content": "const env = require('./environment');\n\n// This is the configuration for a smoke test for a hybrid ng1/ng2 application.\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  specs: [\n    'hybrid/async_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl\n};\n"
  },
  {
    "path": "spec/inferRootConf.js",
    "content": "var env = require('./environment.js');\n\n// Tests for an Angular app where ng-app is not on the body.\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  // Spec patterns are relative to this config.\n  specs: [\n    'altRoot/*_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n};\n"
  },
  {
    "path": "spec/install/.gitignore",
    "content": "node_modules\nnpm-debug.log\ntmp/\npackage-lock.json"
  },
  {
    "path": "spec/install/browserjs_spec.js",
    "content": "describe('browser', () => {\n  let session1;\n  let session2;\n\n  afterEach(async () => {\n    await browser.restart();\n  });\n\n  it('should load a browser session', async () => {\n    await browser.get('http://angularjs.org');\n    const session = await browser.getSession();\n    session1 = session.getId();\n    expect(session1).not.toBeUndefined();\n  });\n\n  it('should have a new browser session', async () => {\n    await browser.get('http://angularjs.org');\n    const session = await browser.getSession();\n    session2 = session.getId();\n    expect(session2).not.toBeUndefined();\n    expect(session1).not.toEqual(session2);\n  });\n});\n"
  },
  {
    "path": "spec/install/browserts_spec.ts",
    "content": "import {browser} from 'protractor';\nimport {WebDriver} from 'selenium-webdriver';\n\ndescribe('browser', () => {\n  let session1: string;\n  let session2: string;\n\n  afterEach(async () => {\n    await browser.restart();\n  });\n  \n  it('should load a browser session', async () => {\n    await browser.get('http://angularjs.org');\n    const session = await browser.getSession();\n    session1 = session.getId();\n    expect(session1).not.toBeUndefined(); \n  });\n\n  it('should have a new browser session', async () => {\n    await browser.get('http://angularjs.org');\n    const session = await browser.getSession();\n    session2 = session.getId();\n    expect(session2).not.toBeUndefined();\n    expect(session1).not.toEqual(session2);\n  });\n});\n"
  },
  {
    "path": "spec/install/conf.ts",
    "content": "import {Config} from 'protractor';\n\nvar env = require('../../environment');\n\nexport let config: Config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n  capabilities: env.capabilities,\n  specs: [\n    'browserts_spec.js',\n    'browserjs_spec.js'\n  ],\n  framework: 'jasmine'\n}\n"
  },
  {
    "path": "spec/install/javascript_spec.js",
    "content": "describe('javascript', function () {\n    it('should have global objects that match the protractor namespace', function () {\n        expect(protractor.browser === browser).toBeTruthy();\n        expect(protractor.by === by).toBeTruthy();\n        expect(protractor.By === By).toBeTruthy();\n        expect(protractor.$ === $).toBeTruthy();\n        expect(protractor.$$ === $$).toBeTruthy();\n        expect(protractor.ExpectedConditions === ExpectedConditions).toBeTruthy();\n    });\n    it('should have selenium-webdriver components for the protractor namespace', function () {\n        // expect(typeof protractor.promise.all).toEqual('function');\n        // expect(typeof protractor.promise.defer).toEqual('function');\n        // expect(typeof protractor.promise.Promise).toEqual('function');\n        // expect(typeof protractor.ActionSequence).toEqual('function');\n        expect(typeof protractor.Browser).toEqual('object');\n        expect(typeof protractor.Builder).toEqual('function');\n        expect(typeof protractor.Capabilities).toEqual('function');\n        expect(typeof protractor.Capability).toEqual('object');\n        // expect(typeof protractor.EventEmitter).toEqual('function');\n        expect(typeof protractor.FileDetector).toEqual('function');\n        expect(typeof protractor.Key).toEqual('object');\n        expect(typeof protractor.Session).toEqual('function');\n        expect(typeof protractor.WebDriver).toEqual('function');\n        expect(typeof protractor.WebElement).toEqual('function');\n        expect(typeof protractor.WebElementPromise).toEqual('function');\n        expect(typeof protractor.error).toEqual('object');\n        expect(typeof protractor.logging).toEqual('object');\n        expect(typeof protractor.promise).toEqual('object');\n        expect(typeof protractor.until).toEqual('object');\n        expect(typeof protractor.Command).toEqual('function');\n        expect(typeof protractor.CommandName).toEqual('object');\n        expect(typeof protractor.utils.firefox).toEqual('object');\n        expect(typeof protractor.utils.http).toEqual('object');\n        expect(typeof protractor.utils.remote).toEqual('object');\n    });\n    it('should have protractor class definitions', function () {\n        expect(typeof protractor.ProtractorBrowser).toBe('function');\n        expect(typeof protractor.ElementFinder).toBe('function');\n        expect(typeof protractor.ElementArrayFinder).toBe('function');\n        expect(typeof protractor.ProtractorBy).toBe('function');\n        expect(typeof protractor.ProtractorExpectedConditions).toBe('function');\n    });\n});\n"
  },
  {
    "path": "spec/install/package.json",
    "content": "{\n  \"name\": \"protractor-install\",\n  \"version\": \"1.0.0\",\n  \"description\": \"e2e typescript => javascript => running\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"tsc\": \"tsc\",\n    \"test\": \"protractor tmp/conf.js\"\n  },\n  \"author\": \"\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"rimraf\": \"^2.5.4\"\n  },\n  \"devDependencies\": {\n    \"@types/jasmine\": \"^2.5.47\",\n    \"jasmine\": \"^2.8.0\",\n    \"protractor\": \"file:../../\",\n    \"selenium-webdriver\": \"^4.0.0-alpha.1\",\n    \"typescript\": \"^3.2.2\"\n  }\n}\n"
  },
  {
    "path": "spec/install/test.js",
    "content": "\"use strict\";\nvar path = require('path');\nvar child_process = require('child_process');\nvar rimraf = require('rimraf');\nvar spawnSync = child_process.spawnSync;\n\nclass TestUtils {\n  static runCommand(task, args, options) {\n    var child = spawnSync(task, args, options);\n    return child.output;\n  };\n};\n\nfunction tsc() {\n  rimraf.sync(path.resolve(__dirname, 'tmp'));\n  var options = {cwd: __dirname};\n  var output = TestUtils.runCommand('npm', ['run', 'tsc'], options);\n  if (output && output[1]) {\n    var options = {cwd: path.resolve('.')};\n    console.log(output[2].toString());\n    if (output[1].toString().indexOf('error') >= 0) {\n      throw new Error('tsc failed.');\n    }\n  } else {\n    throw new Error('Something went wrong in function tsc.')\n  }\n}\n\nfunction test(file) {\n  var options = {cwd: __dirname};\n  var output = TestUtils.runCommand('node',\n      ['node_modules/protractor/bin/protractor',file],\n      options);\n  if (output && output[1]) {\n    console.log(output[1].toString());\n    var lines = output[1].toString().split('\\n');\n    for (var pos in lines) {\n      var line = lines[pos];\n      if (line.indexOf(' specs, ') >= 0) {\n        var failures = line.split(' specs, ')[1].charAt(0);\n        if (failures !== '0') {\n          throw new Error('Failed with ' + failures + ' failure(s).');\n        } else {\n          console.log('must have passed?');\n        }\n      }\n    }\n  } else {\n    throw new Error('Something went wrong in function test.')\n  }\n}\n\ntsc();\n// test('tmp/conf.js');\n// test('tmp/typescript_conf.js');\n"
  },
  {
    "path": "spec/install/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es6\",\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\",\n    \"sourceMap\": true,\n    \"declaration\": true,\n    \"removeComments\": false,\n    \"noImplicitAny\": true,\n    \"types\": [\"node\", \"jasmine\"],\n    \"outDir\": \"tmp/\"\n  },\n  \"exclude\": [\n    \"node_modules\"\n  ]\n}\n"
  },
  {
    "path": "spec/install/typescript_conf.ts",
    "content": "import {Config} from 'protractor';\n\nexport let config: Config = {\n  mockSelenium: true,\n  SELENIUM_PROMISE_MANAGER: false,\n  specs: ['typescript_spec.js'],\n  framework: 'jasmine'\n}\n"
  },
  {
    "path": "spec/install/typescript_spec.ts",
    "content": "import {browser, by, By, element, $, $$, ExpectedConditions, protractor} from 'protractor';\n\ndescribe('typescript imports', () => {\n  it('should have global objects that match the protractor namespace', () => {\n    expect(protractor.browser === browser).toBeTruthy();\n    expect(protractor.by === by).toBeTruthy();\n    expect(protractor.By === By).toBeTruthy();\n    expect(protractor.$ === $).toBeTruthy();\n    expect(protractor.$$ === $$).toBeTruthy();\n    expect(protractor.ExpectedConditions === ExpectedConditions).toBeTruthy();\n  });\n  it('should have selenium-webdriver components for the protractor namespace', () => {\n    // expect(typeof protractor.promise.all).toEqual('function');\n    // expect(typeof protractor.promise.defer).toEqual('function');\n    // expect(typeof protractor.promise.Promise).toEqual('function');\n    // expect(typeof protractor.ActionSequence).toEqual('function');\n    expect(typeof protractor.Browser).toEqual('object');\n    expect(typeof protractor.Builder).toEqual('function');\n    expect(typeof protractor.Capabilities).toEqual('function');\n    expect(typeof protractor.Capability).toEqual('object');\n    // expect(typeof protractor.EventEmitter).toEqual('function');\n    expect(typeof protractor.FileDetector).toEqual('function');\n    expect(typeof protractor.Key).toEqual('object');\n    expect(typeof protractor.Session).toEqual('function');\n    expect(typeof protractor.WebDriver).toEqual('function');\n    expect(typeof protractor.WebElement).toEqual('function');\n    expect(typeof protractor.WebElementPromise).toEqual('function');\n    expect(typeof protractor.error).toEqual('object');\n    expect(typeof protractor.logging).toEqual('object');\n    expect(typeof protractor.promise).toEqual('object');\n    expect(typeof protractor.until).toEqual('object');\n    expect(typeof protractor.Command).toEqual('function');\n    expect(typeof protractor.CommandName).toEqual('object');\n    expect(typeof protractor.utils.firefox).toEqual('object');\n    expect(typeof protractor.utils.http).toEqual('object');\n    expect(typeof protractor.utils.remote).toEqual('object');\n  });\n  it('should have protractor class definitions', () => {\n    expect(typeof protractor.ProtractorBrowser).toBe('function');\n    expect(typeof protractor.ElementFinder).toBe('function');\n    expect(typeof protractor.ElementArrayFinder).toBe('function');\n    expect(typeof protractor.ProtractorBy).toBe('function');\n    expect(typeof protractor.ProtractorExpectedConditions).toBe('function');\n  });\n});\n"
  },
  {
    "path": "spec/interaction/interaction_spec.js",
    "content": "class Person {\n\n  constructor(name, browser) {\n    this.name = name;\n    this.browser = browser;\n    this.$ = browser.$;\n    this.element = browser.element;\n  }\n\n  async openApp() {\n    await this.browser.get('index.html#/interaction');\n  };\n\n  async login() {\n    await this.element(by.model('userInput')).sendKeys(this.name);\n    await this.$('#sendUser').click();\n  };\n\n  async clearMessages() {\n    await this.$('#clearMessages').click();\n  };\n\n  async sendMessage(msg) {\n    await this.element(by.model('message')).sendKeys(msg);\n    await this.$('#sendMessage').click();\n  };\n\n  getMessages() {\n    return this.element.all(by.repeater('msg in messages track by $index'));\n  };\n};\n\ndescribe('Browser', () => {\n\n  let newBrowser;\n\n  afterEach(async() => {\n    // Calling quit will remove the browser.\n    // You can choose to not quit the browser, and protractor will quit all of\n    // them for you when it exits (i.e. if you need a static number of browsers\n    // throughout all of your tests). However, I'm forking browsers in my tests\n    // and don't want to pile up my browser count.\n    if (newBrowser) {\n      await newBrowser.quit();\n    }\n  });\n\n  it('should be able to fork', async() => {\n    await browser.get('index.html');\n    newBrowser = await browser.forkNewDriverInstance();\n    expect(newBrowser).not.toEqual(browser);\n    expect(newBrowser.driver).not.toEqual(browser.driver);\n    expect(await newBrowser.driver.getCurrentUrl()).toEqual('data:,');\n  });\n\n  it('should be able to navigate to same url on fork', async() => {\n    await browser.get('index.html');\n    newBrowser = await browser.forkNewDriverInstance(true);\n    expect(await newBrowser.driver.getCurrentUrl()).toMatch('index.html#/form');\n  });\n\n  it('should be able to copy mock modules on fork', async() => {\n    const mockModule = () => {\n      const newModule = angular.module('mockModule', []);\n      newModule.value('version', '2');\n    };\n\n    browser.addMockModule('mockModule', mockModule);\n    await browser.get('index.html');\n\n    newBrowser = await browser.forkNewDriverInstance(true, true);\n    expect(await newBrowser.element(by.css('[app-version]')).getText())\n        .toEqual('2');\n  });\n\n\n  describe('Multiple browsers', () => {\n    let p0, p1;\n\n    beforeEach(async() => {\n      // default browser.\n      p0 = new Person('p0', browser);\n      await p0.openApp();\n      await p0.login();\n      await p0.clearMessages();\n\n      // Any additional browsers can be instantiated via browser.forkNewDriverInstance().\n      newBrowser = await browser.forkNewDriverInstance(true);\n      p1 = new Person('p1', newBrowser);\n      await p1.openApp();\n      await p1.login();\n    });\n\n    it('should be able to interact', async() => {\n      expect(await p0.getMessages().count()).toEqual(0);\n\n      await p0.sendMessage('p0');\n      await browser.sleep(100); // The app polls every 100ms for updates.\n      expect(await p0.getMessages().count()).toEqual(1);\n      expect(await p1.getMessages().count()).toEqual(1);\n\n      await p1.sendMessage('p1');\n      await browser.sleep(100); // The app polls every 100ms for updates.\n      expect(await p0.getMessages().count()).toEqual(2);\n      expect(await p1.getMessages().count()).toEqual(2);\n    });\n\n    it('should perform actions in sync', async() => {\n      const ACTIONS = 10;\n      expect(await p0.getMessages().count()).toEqual(0);\n\n      let expectedMessages = [];\n      let i;\n      for (i = 0; i < ACTIONS; ++i) {\n        await p0.sendMessage(i);\n        expectedMessages.push('p0: ' + i);\n      }\n      for (i = 0; i < ACTIONS; ++i) {\n        await p1.sendMessage(i);\n        expectedMessages.push('p1: ' + i);\n      }\n      for (i = 0; i < ACTIONS; ++i) {\n        await p0.sendMessage(i);\n        await p1.sendMessage(i);\n        expectedMessages.push('p0: ' + i);\n        expectedMessages.push('p1: ' + i);\n      }\n\n      await browser.sleep(100); // The app polls every 100ms for updates.\n      expect(await p0.getMessages().getText()).toEqual(expectedMessages);\n      expect(await p1.getMessages().getText()).toEqual(expectedMessages);\n    });\n  });\n});\n"
  },
  {
    "path": "spec/interactionConf.js",
    "content": "var env = require('./environment.js');\n\n// Test having two browsers interacting with each other.\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  // Spec patterns are relative to this directory.\n  specs: [\n    'interaction/*_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/'\n};\n"
  },
  {
    "path": "spec/login/login_spec.js",
    "content": "const env = require('../environment.js');\n\ndescribe('pages with login', () => {\n  it('should log in with a non-Angular page', async() => {\n    await browser.get(env.baseUrl + '/ng1/index.html');\n\n    const angularElement = element(by.model('username'));\n    expect(await angularElement.getAttribute('value')).toEqual('Anon');\n\n    // Make sure the cookie is still set.\n    const cookie = await browser.manage().getCookie('testcookie');\n    expect(cookie.value).toEqual('Jane-1234');\n  });\n});\n"
  },
  {
    "path": "spec/mocha/lib_spec.js",
    "content": "// Use the external Chai As Promised to deal with resolving promises in\n// expectations.\nconst chai = require('chai');\nconst chaiAsPromised = require('chai-as-promised');\nchai.use(chaiAsPromised);\n\nconst expect = chai.expect;\n\n// Chai's expect().to.exist style makes default jshint unhappy.\n// jshint expr:true\n\ndescribe('no protractor at all', () => {\n  it('should still do normal tests', () => {\n    expect(true).to.equal(true);\n  });\n});\n\ndescribe('protractor library', () => {\n  it.skip('should be able to skip tests', () => {\n    expect(true).to.equal(false);\n  });\n\n  it('should expose the correct global variables', () => {\n    expect(protractor).to.exist;\n    expect(browser).to.exist;\n    expect(by).to.exist;\n    expect(element).to.exist;\n    expect($).to.exist;\n  });\n\n  it('should wrap webdriver', async function() {\n    // Mocha will report the spec as slow if it goes over this time in ms.\n    this.slow(6000);\n    \n    await browser.get('index.html');\n    expect(await browser.getTitle()).to.equal('My AngularJS App');\n  });\n\n  describe('with async tests', () => {\n    let finished = false;\n\n    it('should wait for async operations to finish', async() => {\n      await browser.get('index.html');\n      finished = true;\n    });\n\n    after('verify mocha waited', () => {\n      if(!finished) {\n        throw new Error('Mocha did not wait for async!');\n      }\n    });\n  });\n});\n"
  },
  {
    "path": "spec/mochaConf.js",
    "content": "var env = require('./environment.js');\n\n// A small suite to make sure the mocha framework works.\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'mocha',\n\n  // Spec patterns are relative to this directory.\n  specs: [\n    'mocha/*_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  mochaOpts: {\n    reporter: 'spec',\n    timeout: 4000\n  }\n};\n"
  },
  {
    "path": "spec/multiConf.js",
    "content": "// A suite of tests to run on two browsers at once.\nvar env = require('./environment.js');\n\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n\n  framework: 'jasmine',\n\n  // Spec patterns are relative to this directory.\n  specs: [\n    'basic/lib_spec.js'\n  ],\n\n  multiCapabilities: [{\n    'browserName': 'chrome'\n  }, {\n    'browserName': 'firefox'\n  }],\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  params: {\n    login: {\n      user: 'Jane',\n      password: '1234'\n    }\n  }\n};\n"
  },
  {
    "path": "spec/ng2/async_spec.js",
    "content": "describe('async angular2 application', () => {\n  const URL = '/ng2/#/async';\n\n  beforeEach(async() => {\n    await browser.get(URL);\n  });\n\n  it('should work with synchronous actions', async() => {\n    const increment = $('#increment');\n    await increment.$('.action').click();\n\n    expect(await increment.$('.val').getText()).toEqual('1');\n  });\n\n  it('should wait for asynchronous actions', async() => {\n    const timeout = $('#delayedIncrement');\n\n    // At this point, the async action is still pending, so the count should\n    // still be 0.\n    expect(await timeout.$('.val').getText()).toEqual('0');\n\n    await timeout.$('.action').click();\n\n    expect(await timeout.$('.val').getText()).toEqual('1');\n  });\n\n  it('should turn off when ignoreSynchronization is true', async() => {\n    // const timeout = $('#delayedIncrement');\n\n    // At this point, the async action is still pending, so the count should\n    // still be 0.\n    expect(await $('#delayedIncrement').$('.val').getText()).toEqual('0');\n\n    await browser.waitForAngularEnabled(false);\n\n    await $('#delayedIncrement').$('.action').click();\n    await $('#delayedIncrement').$('.cancel').click();\n\n    await browser.waitForAngularEnabled(true);\n\n    // whenStable should be called since the async action is cancelled. The\n    // count should still be 0;\n    expect(await $('#delayedIncrement').$('.val').getText()).toEqual('0');\n  });\n\n  it('should wait for a series of asynchronous actions', async() => {\n    const timeout = $('#chainedDelayedIncrements');\n\n    // At this point, the async action is still pending, so the count should\n    // still be 0.\n    expect(await timeout.$('.val').getText()).toEqual('0');\n\n    await timeout.$('.action').click();\n\n    expect(await timeout.$('.val').getText()).toEqual('10');\n  });\n\n  describe('long async spec', () => {\n    let originalTimeout;\n    beforeEach(() => {\n      originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;\n      jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000;\n    });\n\n    it('should wait for a series of periodic increments', async() => {\n      const timeout = $('#periodicIncrement_unzoned');\n\n      // Waits for the val to count 2.\n      const EC = protractor.ExpectedConditions;\n      await timeout.$('.action').click();\n      // Increase waiting time from 4s to 7s due to slow connection during SauceLabs tests\n      await browser.wait(EC.textToBePresentInElement(timeout.$('.val'), '1'),\n          7000);\n      await timeout.$('.cancel').click();\n\n      const text = await timeout.$('.val').getText();\n      await browser.driver.sleep(3000);\n      expect(await timeout.$('.val').getText()).toEqual(text);\n    });\n\n    afterEach(() => {\n      jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;\n    });\n  });\n});\n"
  },
  {
    "path": "spec/ng2/timeout_spec.js",
    "content": "describe('async angular2 application timeout', () => {\n  const URL = '/ng2/#/async';\n\n  it('should timeout if intervals are used in the NgZone', async () => {\n    await browser.get(URL);\n    const timeout = $('#periodicIncrement');\n    await timeout.$('.action').click();\n    await timeout.$('.cancel').click();\n  });\n});\n"
  },
  {
    "path": "spec/noGlobals/noGlobals_spec.js",
    "content": "describe('configuration with no globals', () => {\n  const URL = '/ng2/#/async';\n\n  it('should have objects belonging to protractor namespace', () => {\n    expect(typeof protractor).toEqual('object');\n    expect(typeof protractor.browser).toEqual('object');\n    expect(typeof protractor.$).toEqual('function');\n    expect(typeof protractor.$$).toEqual('function');\n    expect(typeof protractor.element).toEqual('function');\n    expect(typeof protractor.by).toEqual('object');\n    expect(typeof protractor.By).toEqual('object');\n  });\n\n  it('should not have other globals', () => {\n    expect(typeof browser).toEqual('undefined');\n    expect(typeof $).toEqual('undefined');\n    expect(typeof $$).toEqual('undefined');\n    expect(typeof element).toEqual('undefined');\n    expect(typeof by).toEqual('undefined');\n    expect(typeof By).toEqual('undefined');\n  });\n\n  it('should be able to use methods under the protractor namespace', async () => {\n    await protractor.browser.get(URL);\n    const increment = protractor.$('#increment');\n    expect(typeof increment).toEqual('object');\n    await increment.$('.action').click();\n    expect(await increment.$('.val').getText()).toEqual('1');\n  });\n});\n"
  },
  {
    "path": "spec/noGlobalsConf.js",
    "content": "const env = require('./environment');\n\n// This is the configuration for a smoke test for an Angular2 application.\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  specs: [\n    'noGlobals/noGlobals_spec.js'\n  ],\n\n  noGlobals: true,\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl,\n};\n"
  },
  {
    "path": "spec/onCleanUp/onCleanUp_spec.js",
    "content": "describe('onCleanUp function in the config', () => {\n  it('should not be affected by tests', () => {\n    expect(true).toBe(true);\n  });\n});\n"
  },
  {
    "path": "spec/onCleanUpAsyncReturnValueConf.js",
    "content": "const env = require('./environment.js');\n\n// Test that onCleanUp actions are performed.\nexports.config = {\n  mockSelenium: true,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  specs: [\n    'onCleanUp/*_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  onCleanUp: async(exitCode) => {\n    return await new Promise(resolve => {\n      setTimeout(resolve(exitCode), 500);\n    });\n  }\n};\n"
  },
  {
    "path": "spec/onCleanUpNoReturnValueConf.js",
    "content": "const env = require('./environment.js');\n\n// Test that onCleanUp actions are performed.\nexports.config = {\n  mockSelenium: true,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  specs: [\n    'onCleanUp/*_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  onCleanUp: (/* exitCode */) => {\n    // no return\n  }\n};\n"
  },
  {
    "path": "spec/onCleanUpSyncReturnValueConf.js",
    "content": "const env = require('./environment.js');\n\n// Test that onCleanUp actions are performed.\nexports.config = {\n  mockSelenium: true,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  specs: [\n    'onCleanUp/*_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  onCleanUp: (exitCode) => {\n    return exitCode;\n  }\n};\n"
  },
  {
    "path": "spec/onPrepare/asyncstartup.js",
    "content": "module.exports = async() => {\n  browser.params.password = '12345';\n  return await new Promise((resolve, _) => {\n    setTimeout(resolve, 1000);\n  });\n}\n"
  },
  {
    "path": "spec/onPrepare/onPrepare_spec.js",
    "content": "describe('onPrepare function in the config', () => {\n  it('should have a special variable set in onPrepare', () => {\n    expect(browser.params.password).toEqual('12345');\n  });\n});\n"
  },
  {
    "path": "spec/onPrepare/startup.js",
    "content": "browser.params.password = '12345';\n"
  },
  {
    "path": "spec/onPrepareConf.js",
    "content": "// Configuration using a function in onPrepare to set a parameter before\n// testing.\nconst env = require('./environment.js');\n\n// The main suite of Protractor tests.\nexports.config = {\n  mockSelenium: true,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  specs: [\n    'onPrepare/*_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  onPrepare: () => {\n    browser.params.password = '12345';\n  }\n};\n"
  },
  {
    "path": "spec/onPrepareFileConf.js",
    "content": "const env = require('./environment.js');\n\n// Configuration using a string in onPrepare to load a file with code to\n// execute once before tests.\nexports.config = {\n  mockSelenium: true,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  // Spec patterns are relative to this directory.\n  specs: [\n    'onPrepare/*_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  onPrepare: 'onPrepare/startup.js'\n};\n"
  },
  {
    "path": "spec/onPreparePromiseConf.js",
    "content": "// Configuration using a function in onPrepare to set a parameter before\n// testing.\nconst env = require('./environment.js');\n\n// The main suite of Protractor tests.\nexports.config = {\n  mockSelenium: true,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  specs: [\n    'onPrepare/*_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  onPrepare: async() => {\n    browser.params.password = '12345';\n    return await new Promise(resolve => {\n      setTimeout(resolve, 1000);\n    });\n  }\n};\n"
  },
  {
    "path": "spec/onPreparePromiseFileConf.js",
    "content": "var env = require('./environment.js');\n\n// Configuration using a string in onPrepare to load a file with code to\n// execute once before tests.\nexports.config = {\n  mockSelenium: true,\n\n  framework: 'jasmine',\n\n  // Spec patterns are relative to this directory.\n  specs: [\n    'onPrepare/*_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  onPrepare: 'onPrepare/asyncstartup.js'\n};\n"
  },
  {
    "path": "spec/plugins/browserGetSyncedConf.js",
    "content": "const env = require('../environment.js');\n\n// Make sure that borwser-related plugin hooks work with browser sync on\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  // Spec patterns are relative to this directory.\n  specs: [\n    'specs/browser_get_wait_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  // Plugin patterns are relative to this directory.\n  plugins: [{\n    inline: {\n      onPageLoad: async function() {\n        return await new Promise(resolve => {\n          setTimeout(() => {\n            protractor.ON_PAGE_LOAD = true;\n            resolve();\n          }, 5000);\n        });\n      },\n      onPageStable: async function() {\n        if (protractor.ON_PAGE_LOAD) {\n          this.addSuccess();\n        } else {\n          this.addFailure(\n              'onPageLoad did not finish before onPageStable began');\n        }\n        return await new Promise(resolve => {\n          setTimeout(() => {\n            protractor.ON_PAGE_SYNC = true;\n            resolve();\n          }, 5000);\n        });\n      },\n      teardown: function() {\n        if (protractor.ON_PAGE_SYNC) {\n          this.addSuccess();\n        } else {\n          this.addFailure('onPageStable did not finish before teardown');\n        }\n      }\n    }\n  }]\n};\n"
  },
  {
    "path": "spec/plugins/browserGetUnsyncedConf.js",
    "content": "const env = require('../environment.js');\n\n// Make sure that borwser-related plugin hooks work with browser sync off\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  // Spec patterns are relative to this directory.\n  specs: [\n    'specs/browser_get_wait_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  // Plugin patterns are relative to this directory.\n  plugins: [{\n    inline: {\n      setup: async function() {\n        await browser.waitForAngularEnabled(false);\n      },\n      onPageLoad: async function() {\n        return await new Promise(resolve => {\n          setTimeout(() => {\n            protractor.ON_PAGE_LOAD = true;\n            resolve();\n          }, 5000);\n        });\n      },\n      onPageStable: function() {\n        this.addFailure('onPageStable should not have ran when ' +\n            'await browser.waitForAngularEnabled() is false.');\n      },\n      teardown: function() {\n        if (protractor.ON_PAGE_LOAD) {\n          this.addSuccess();\n        } else {\n          this.addFailure('onPageLoad did not finish before teardown');\n        }\n      }\n    }\n  }]\n};\n"
  },
  {
    "path": "spec/plugins/jasminePostTestConf.js",
    "content": "exports.config = require('./postTestConfTemplate')('jasmine');\n"
  },
  {
    "path": "spec/plugins/mochaPostTestConf.js",
    "content": "exports.config = require('./postTestConfTemplate')('mocha');\n"
  },
  {
    "path": "spec/plugins/multiPluginConf.js",
    "content": "var env = require('../environment.js');\n\n// A small suite to make sure the full functionality of plugins work\nexports.config = {\n  mockSelenium: true,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  // Spec patterns are relative to this directory.\n  specs: [\n    'specs/bigger_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  // Plugin patterns are relative to this directory.\n  plugins: [{\n    path: 'plugins/basic_plugin.js'\n  }, {\n    path: 'plugins/async_plugin.js'\n  }, {\n    inline: {\n      setup: function() {\n        protractor.__INLINE_PLUGIN_RAN = true;\n      }\n    }\n  }]\n};\n"
  },
  {
    "path": "spec/plugins/plugins/async_plugin.js",
    "content": "module.exports = {\n  setup: async function() {\n    await new Promise(resolve => {\n      setTimeout(resolve, 100);\n    });\n    this.addSuccess();\n  },\n\n  teardown: async function() {\n    await new Promise(resolve => {\n      setTimeout(resolve, 100);\n    });\n    this.addSuccess();\n  },\n\n  postResults: function() {\n    // This function should cause no failures.\n  },\n\n  postTest: async function() {\n    await new Promise(resolve => {\n      setTimeout(resolve, 100);\n    });\n    this.addSuccess();\n  },\n\n  name: 'some plugin name'\n};\n"
  },
  {
    "path": "spec/plugins/plugins/basic_plugin.js",
    "content": "module.exports = {\n  setup: function() {\n    protractor.__BASIC_PLUGIN_RAN_SETUP = true;\n  },\n  onPrepare: function() {\n    protractor.__BASIC_PLUGIN_RAN_ON_PREPARE = true;\n  }\n};\n"
  },
  {
    "path": "spec/plugins/plugins/failing_plugin.js",
    "content": "module.exports = {\n  setup: async function() {\n    await new Promise(resolve => {\n      setTimeout(resolve, 100);\n    });\n    self.addFailure('from setup');\n  },\n\n  teardown: async function() {\n    await new Promise(resolve => {\n      setTimeout(resolve, 100);\n    });\n    self.addFailure('from teardown');\n  },\n\n  postResults: function() {\n    // This function should cause no failures.\n  },\n\n  postTest: async function(passed) {\n    await new Promise(resolve => {\n      setTimeout(resolve, 100);\n    });\n    self.addFailure('from postTest ' + (passed ? 'passing' : 'failing'));\n  }\n};\n"
  },
  {
    "path": "spec/plugins/plugins/post_test_plugin.js",
    "content": "exports.postTest = function(passed, testInfo) {\n  if (passed !== true) {\n    this.addFailure('`passed` should have been `true`, but got `' +\n        JSON.stringify(passed) + '`');\n  } else {\n    this.addSuccess();\n  }\n  if (testInfo.name !== 'name') {\n    this.addFailure('`testInfo.name` should have been `\"name\"`, but got `' +\n        JSON.stringify(testInfo.name) + '`');\n  } else {\n    this.addSuccess();\n  }\n  if (testInfo.category !== 'category') {\n    this.addFailure('`testInfo.category` should have been `\"category\"`, but ' +\n        'got `' + JSON.stringify(testInfo.category) + '`');\n  } else {\n    this.addSuccess();\n  }\n};\n"
  },
  {
    "path": "spec/plugins/postTestConfTemplate.js",
    "content": "var env = require('../environment.js');\n\nmodule.exports = (framework) => {\n  return {\n    mockSelenium: true,\n    SELENIUM_PROMISE_MANAGER: false,\n\n    framework: framework,\n\n    specs: [\n      'specs/simple_spec.js'\n    ],\n\n    capabilities: env.capabilities,\n\n    baseUrl: env.baseUrl + '/ng1/',\n\n    plugins: [{\n      path: 'plugins/post_test_plugin.js'\n    }]\n  };\n};\n"
  },
  {
    "path": "spec/plugins/skipStabilityConf.js",
    "content": "var env = require('../environment.js');\n\n// Verifies that plugins can change skipAngularStability on the fly.\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  // Spec patterns are relative to this directory.\n  specs: [\n    'specs/skip_stability_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  // Define a plugin that allows skipAngularStability to be changed.\n  plugins: [{\n    inline: {\n      setup: function() {\n        this.skipAngularStability = false;\n        var plugin = this;\n\n        protractor._PluginSetSkipStability = function(newValue) {\n          plugin.skipAngularStability = newValue;\n        };\n      }\n    }\n  }]\n};\n"
  },
  {
    "path": "spec/plugins/smokeConf.js",
    "content": "var env = require('../environment.js');\n\n// A small suite to make sure the basic functionality of plugins work\n// Tests the (potential) edge case of exactly one plugin being used\nexports.config = {\n  mockSelenium: true,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  // Spec patterns are relative to this directory.\n  specs: [\n    'specs/smoke_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  // Plugin patterns are relative to this directory.\n  plugins: [{\n    path: 'plugins/basic_plugin.js'\n  }]\n};\n"
  },
  {
    "path": "spec/plugins/specs/bigger_spec.js",
    "content": "describe('check if plugin setup ran', () => {\n  it('should have set protractor.__BASIC_PLUGIN_RAN', () => {\n    expect(protractor.__BASIC_PLUGIN_RAN_SETUP).toBe(true);\n  });\n\n  it('should have set protractor.__INLINE_PLUGIN_RAN', () => {\n    expect(protractor.__INLINE_PLUGIN_RAN).toBe(true);\n  });\n});\n"
  },
  {
    "path": "spec/plugins/specs/browser_get_wait_spec.js",
    "content": "describe('category', () => {\n  it('name', async() => {\n    await browser.get('index.html');\n    await browser.waitForAngular();\n  });\n});\n"
  },
  {
    "path": "spec/plugins/specs/fail_spec.js",
    "content": "describe('check if plugin setup ran', () => {\n  it('should have set protractor.__BASIC_PLUGIN_RAN', () =>  {\n    expect(protractor.__BASIC_PLUGIN_RAN_SETUP).toBe(true);\n  });\n\n  it('should run multiple tests which fail', () =>  {\n    expect(true).toBe(false);\n  });\n});\n"
  },
  {
    "path": "spec/plugins/specs/simple_spec.js",
    "content": "describe('category', () => {\n  it('name', () => {\n  });\n});\n"
  },
  {
    "path": "spec/plugins/specs/skip_stability_spec.js",
    "content": "describe('plugins that can disable synchronization', function() {\n  beforeEach(function() {\n    browser.get('index.html#/async');\n  });\n\n  it('DOES NOT wait for $timeout with synchronization disabled', function() {\n    protractor._PluginSetSkipStability(true);\n    var status = element(by.binding('slowAngularTimeoutStatus'));\n    var button = element(by.css('[ng-click=\"slowAngularTimeout()\"]'));\n\n    expect(status.getText()).toEqual('not started');\n\n    button.click();\n\n    expect(status.getText()).toEqual('pending...');\n  });\n\n  it('waits for $timeout with synchronization enabled', function() {\n    protractor._PluginSetSkipStability(false);\n    var status = element(by.binding('slowAngularTimeoutStatus'));\n    var button = element(by.css('[ng-click=\"slowAngularTimeout()\"]'));\n\n    expect(status.getText()).toEqual('not started');\n\n    button.click();\n\n    expect(status.getText()).toEqual('done');\n  });\n});\n"
  },
  {
    "path": "spec/plugins/specs/smoke_spec.js",
    "content": "describe('check if plugin setup ran', () => {\n  it('should have set protractor.__BASIC_PLUGIN_RAN_*', () => {\n    expect(protractor.__BASIC_PLUGIN_RAN_SETUP).toBe(true);\n    expect(protractor.__BASIC_PLUGIN_RAN_ON_PREPARE).toBe(true);\n  });\n});\n"
  },
  {
    "path": "spec/plugins/waitForAngularConf.js",
    "content": "var env = require('../environment.js');\n\n// A small suite to make sure that the plugin hooks for waitForAngular work\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  // Spec patterns are relative to this directory.\n  specs: [\n    'specs/browser_get_wait_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  // Plugin patterns are relative to this directory.\n  plugins: [{\n    inline: {\n      waitForPromise: async function() {\n        return await new Promise(resolve => {\n          setTimeout(() => {\n            protractor.WAIT_FOR_PROMISE = true;\n            resolve();\n          }, 5000);\n        });\n      },\n      waitForCondition: function() {\n        protractor.WAIT_FOR_CONDITION_COUNT =\n            (protractor.WAIT_FOR_CONDITION_COUNT || 0) + 1;\n        return protractor.WAIT_FOR_CONDITION_COUNT > 5;\n      },\n      teardown: function() {\n        if (protractor.WAIT_FOR_PROMISE) {\n          this.addSuccess();\n        } else {\n          this.addFailure('waitForPromise did not finish before teardown');\n        }\n        if (protractor.WAIT_FOR_CONDITION_COUNT > 5) {\n          this.addSuccess();\n        } else {\n          this.addFailure('waitForCondition did not get called enough');\n        }\n      }\n    }\n  }]\n};\n"
  },
  {
    "path": "spec/restartBrowserBetweenTests/setCookies_spec.js",
    "content": "const env = require('../environment.js');\n\ndescribe('pages with login', () => {\n  it('should set a cookie', async() => {\n    await browser.get(env.baseUrl + '/ng1/index.html');\n\n    await browser.manage().addCookie({name:'testcookie', value: 'Jane-1234'});\n\n    // Make sure the cookie is set.\n    const cookie = await browser.manage().getCookie('testcookie');\n    expect(cookie.value).toEqual('Jane-1234');\n  });\n\n  it('should check the cookie is gone', async() => {\n    await browser.get(env.baseUrl + '/ng1/index.html');\n\n    // Make sure the cookie is gone.\n    const cookie = await browser.manage().getCookie('testcookie');\n    expect(cookie).toEqual(null);\n  });\n});\n"
  },
  {
    "path": "spec/restartBrowserBetweenTestsConf.js",
    "content": "const env = require('./environment.js');\n\n// The main suite of Protractor tests.\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  // Spec patterns are relative to this directory.\n  specs: [\n    'restartBrowserBetweenTests/*_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  restartBrowserBetweenTests: true\n};\n"
  },
  {
    "path": "spec/shardingConf.js",
    "content": "// A suite of tests to run on two browsers at once, splitting test files between\n// the two instances of chrome.\nvar env = require('./environment.js');\n\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n\n  // Spec patterns are relative to this directory.\n  specs: [\n    'basic/mock*',\n    'basic/lib_spec.js'\n  ],\n\n  // Exclude patterns are relative to this directory.\n  exclude: [\n    'basic/exclude*.js'\n  ],\n\n  framework: 'debugprint',\n  maxSessions: 3,\n  multiCapabilities: [{\n    'browserName': 'chrome',\n    maxInstances: 2\n  }, {\n    'browserName': 'chrome',\n    shardTestFiles: true,\n    maxInstances: 1,\n    specs: 'basic/polling*' // Capacity specific specs\n  }, {\n    shardTestFiles: true,\n    'browserName': 'firefox',\n    maxInstances: 2,\n    count: 2,\n    specs: 'basic/action*'\n  }],\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  params: {\n    login: {\n      user: 'Jane',\n      password: '1234'\n    }\n  }\n};\n\n"
  },
  {
    "path": "spec/suites/always_fail_spec.js",
    "content": "describe('This test suite', () => {\n  it('should never be ran through the --suite option', () => {\n    expect(true).toBe(false);\n  });\n});\n"
  },
  {
    "path": "spec/suites/ok_2_spec.js",
    "content": "describe('This test suite', () => {\n  it('should be ran through the --suite option', () => {\n    expect(true).toBe(true);\n  });\n});\n"
  },
  {
    "path": "spec/suites/ok_spec.js",
    "content": "describe('This test suite', () => {\n  it('should be ran through the --suite option', () => {\n    expect(true).toBe(true);\n  });\n});\n"
  },
  {
    "path": "spec/suitesConf.js",
    "content": "var env = require('./environment.js');\n\nexports.config = {\n  mockSelenium: true,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  // Spec patterns are relative to this directory.\n  suites: {\n    okspec: 'suites/ok_spec.js',\n    okmany: ['suites/ok_spec.js', 'suites/ok_2_spec.js'],\n    failingtest: 'suites/always_fail_spec.js'\n  },\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/'\n};\n"
  },
  {
    "path": "spec/ts/basic/element_spec.ts",
    "content": "// Based off of spec/basic/elements_spec.js\nimport {$, browser, by, element, ElementArrayFinder, ElementFinder} from '../../..';\n\ndescribe('ElementFinder', () => {\n  it('should return the same result as browser.findElement', async() => {\n    await browser.get('index.html#/form');\n    const nameByElement = element(by.binding('username'));\n\n    expect(await nameByElement.getText())\n        .toEqual(await browser.findElement(by.binding('username')).getText());\n  });\n\n  it('should wait to grab the WebElement until a method is called', async() => {\n    // These should throw no error before a page is loaded.\n    const usernameInput = element(by.model('username'));\n    const name = element(by.binding('username'));\n\n    await browser.get('index.html#/form');\n\n    expect(await name.getText()).toEqual('Anon');\n\n    await usernameInput.clear();\n    await usernameInput.sendKeys('Jane');\n    expect(await name.getText()).toEqual('Jane');\n  });\n\n  it('should chain element actions', async() => {\n    await browser.get('index.html#/form');\n\n    const usernameInput = element(by.model('username'));\n    const name = element(by.binding('username'));\n\n    expect(await name.getText()).toEqual('Anon');\n\n    await((usernameInput.clear() as any) as ElementFinder).sendKeys('Jane');\n    expect(await name.getText()).toEqual('Jane');\n  });\n\n  it('should run chained element actions in sequence', async () => {\n    // Testing private methods is bad :(\n    let els = new ElementArrayFinder(browser, () => {\n      return Promise.resolve([null]);\n    });\n    let applyAction_: (actionFn: (value: any, index: number, array: any) => any) =>\n        ElementArrayFinder = (ElementArrayFinder as any).prototype.applyAction_;\n    let order: string[] = [];\n\n    let deferredA: Promise<void>;\n    els = applyAction_.call(els, () => {\n      deferredA = new Promise<void>(resolve => {\n        order.push('a');\n        resolve();\n      });\n    });\n    let deferredB: Promise<void>;\n    els = applyAction_.call(els, () => {\n      deferredB = new Promise<void>(resolve => {\n        order.push('b');\n        resolve();\n      });\n    });\n\n    await deferredB;\n    setTimeout(async () => {\n      await deferredA;\n      await els;\n      expect(order).toEqual(['a', 'b']);\n    }, 100);\n  });\n\n  it('chained call should wait to grab the WebElement until a method is called',\n      async() => {\n    // These should throw no error before a page is loaded.\n    const reused = element(by.id('baz'))\n        .element(by.binding('item.reusedBinding'));\n\n    await browser.get('index.html#/conflict');\n\n    expect(await reused.getText()).toEqual('Inner: inner');\n    expect(await reused.isPresent()).toBe(true);\n  });\n\n  it('should differentiate elements with the same binding by chaining',\n      async() => {\n    await browser.get('index.html#/conflict');\n\n    const outerReused = element(by.binding('item.reusedBinding'));\n    const innerReused = element(by.id('baz')).element(by.binding('item.reusedBinding'));\n\n    expect(await outerReused.getText()).toEqual('Outer: outer');\n    expect(await innerReused.getText()).toEqual('Inner: inner');\n  });\n\n  it('should chain deeper than 2', async() => {\n    // These should throw no error before a page is loaded.\n    const reused = element(by.css('body')).element(by.id('baz'))\n        .element(by.binding('item.reusedBinding'));\n\n    await browser.get('index.html#/conflict');\n\n    expect(await reused.getText()).toEqual('Inner: inner');\n  });\n\n  it('should allow handling errors', async() => {\n    await browser.get('index.html#/form');\n    try {\n      await $('.nopenopenope').getText();\n\n      // The above line should have throw an error. Fail.\n      expect(true).toEqual(false);\n    } catch (e) {\n      expect(true).toEqual(true);\n    }\n  });\n\n  it('should allow handling chained errors', async() => {\n    await browser.get('index.html#/form');\n    try {\n      await $('.nopenopenope').$('furthernope').getText();\n\n      // The above line should have throw an error. Fail.\n      expect(true).toEqual(false);\n    } catch (e) {\n      expect(true).toEqual(true);\n    }\n  });\n\n  it('should keep a reference to the original locator', async() => {\n    await browser.get('index.html#/form');\n\n    const byCss = by.css('body');\n    const byBinding = by.binding('greet');\n\n    expect(await element(byCss).locator()).toEqual(byCss);\n    expect(await element(byBinding).locator()).toEqual(byBinding);\n  });\n\n  it('should propagate exceptions', async() => {\n    await browser.get('index.html#/form');\n\n    const invalidElement = element(by.binding('INVALID'));\n    const successful = invalidElement.getText().then(\n        function() {\n          return true;\n        } as any as (() => Promise<boolean>),\n        function() {\n          return false;\n        } as any as (() => Promise<boolean>));\n    expect(await successful).toEqual(false);\n  });\n\n  it('should be returned from a helper without infinite loops', async() => {\n    await browser.get('index.html#/form');\n    const helperPromise = Promise.resolve(true).then(() => {\n      return element(by.binding('greeting'));\n    });\n\n    await helperPromise.then(async(finalResult: ElementFinder) => {\n      expect(await finalResult.getText()).toEqual('Hiya');\n    });\n  });\n\n  it('should be usable in WebDriver functions', async() => {\n    await browser.get('index.html#/form');\n    const greeting = element(by.binding('greeting'));\n    await browser.executeScript('arguments[0].scrollIntoView', greeting);\n  });\n\n  it('should allow null as success handler', async() => {\n    await browser.get('index.html#/form');\n\n    const name = element(by.binding('username'));\n\n    expect(await name.getText()).toEqual('Anon');\n    expect(await name.getText().then(null, function() {})).toEqual('Anon');\n\n  });\n\n  it('should check equality correctly', async() => {\n    await browser.get('index.html#/form');\n\n    const usernameInput = element(by.model('username'));\n    const name = element(by.binding('username'));\n\n    expect(await usernameInput.equals(usernameInput)).toEqual(true);\n    expect(await usernameInput.equals(name)).toEqual(false);\n  });\n});\n"
  },
  {
    "path": "spec/ts/noCFBasicConf.ts",
    "content": "import {Config} from '../..';\nconst env = require('../environment.js');\n\nexport let config: Config = {\n  seleniumAddress: env.seleniumAddress,\n\n  framework: 'jasmine',\n\n  specs: [\n    'basic/*_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  SELENIUM_PROMISE_MANAGER: false\n};\n"
  },
  {
    "path": "spec/ts/noCFPluginConf.ts",
    "content": "import {Config, protractor} from '../..';\nconst env = require('../environment.js');\n\nexport let config: Config = {\n  seleniumAddress: env.seleniumAddress,\n\n  framework: 'jasmine',\n\n  specs: [\n    'plugin/plugin_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  SELENIUM_PROMISE_MANAGER: false,\n\n  plugins: [{\n    inline: {\n      onPageLoad: async function() {\n        await new Promise(resolve => {\n          setTimeout(resolve, 100);\n        });\n        (protractor as any).ON_PAGE_LOAD = true;\n      }\n    }\n  }]\n};\n"
  },
  {
    "path": "spec/ts/plugin/plugin_spec.ts",
    "content": "import {browser, protractor} from '../../..';\n\ndescribe('plugins', () => {\n  it('should have run the onPageLoad hook', async() => {\n    await browser.get('index.html');\n    expect((protractor as any).ON_PAGE_LOAD).toBe(true);\n  });\n});\n"
  },
  {
    "path": "spec/unit/configParser_test.js",
    "content": "var ConfigParser = require('../../built/configParser').ConfigParser;\nvar ConfigError = require('../../built/exitCodes').ConfigError;\nvar ProtractorError = require('../../built/exitCodes').ProtractorError;\nvar Logger = require('../../built/logger').Logger;\nvar WriteTo = require('../../built/logger').WriteTo;\nvar path = require('path');\n\ndescribe('the config parser', function() {\n  describe('exceptions', function() {\n\n    beforeAll(function() {\n      ProtractorError.SUPRESS_EXIT_CODE = true;\n      Logger.writeTo = WriteTo.NONE;\n    });\n\n    afterAll(function() {\n      ProtractorError.SUPRESS_EXIT_CODE = false;\n      Logger.writeTo = WriteTo.CONSOLE;\n    });\n\n    it('should throw an error if the file is not found', function() {\n      var config = new ConfigParser();\n      var errorFound = false;\n      try {\n        config.addFileConfig('foobar.js');\n      } catch (err) {\n        errorFound = true;\n        expect(err.code).toEqual(ConfigError.CODE);\n        expect(err.stack).toMatch('Cannot find module');\n      }\n      expect(errorFound).toBe(true);\n    });\n\n    it('should throw an error if the file does not have export config', function() {\n      var config = new ConfigParser();\n      var errorFound = false;\n      try {\n        config.addFileConfig(path.resolve('./spec/environment.js'));\n      } catch (err) {\n        errorFound = true;\n        expect(err.code).toEqual(ConfigError.CODE);\n        expect(err.stack).toMatch('did not export a config object');\n      }\n      expect(errorFound).toBe(true);\n    });\n\n    it('should throw an error when the spec file does not resolve', function() {\n      var errorFound = false;\n      try {\n        var config = {\n          suite:'foo.js,bar.js'\n        };\n        ConfigParser.getSpecs(config);\n      } catch (err) {\n        errorFound = true;\n        expect(err.code).toEqual(ConfigError.CODE);\n        expect(err.stack).toMatch('Unknown test suite: foo.js');\n      }\n      expect(errorFound).toBe(true);\n    });\n  });\n\n  it('should have a default config', function() {\n    var config = new ConfigParser().getConfig();\n    expect(config.specs).toEqual([]);\n    expect(config.rootElement).toEqual('');\n  });\n\n  it('should merge in config from an object', function() {\n    var toAdd = {\n      rootElement: '.mydiv'\n    };\n    var config = new ConfigParser().addConfig(toAdd).getConfig();\n    expect(config.specs).toEqual([]);\n    expect(config.rootElement).toEqual('.mydiv');\n  });\n\n  it('should merge in config from a file', function() {\n    var config = new ConfigParser().\n        addFileConfig(__dirname + '/data/config.js').\n        getConfig();\n\n    expect(config.rootElement).toEqual('.mycontainer');\n    expect(config.onPrepare.indexOf(\n      path.normalize('/spec/unit/data/foo/bar.js'))).not.toEqual(-1);\n    expect(config.specs.length).toEqual(1);\n    expect(config.specs[0]).toEqual('fakespec[AB].js');\n  });\n\n  it('should keep filepaths relative to the cwd when merging', function() {\n    var toAdd = {\n      onPrepare: 'baz/qux.js'\n    };\n\n    var config = new ConfigParser().addConfig(toAdd).getConfig();\n\n    expect(config.onPrepare).toEqual(path.normalize(process.cwd() + '/baz/qux.js'));\n  });\n\n  describe('getSpecs()', function() {\n    it(`should return all the specs from \"config.suites\" if no other sources\n        are provided`, function() {\n      var config = {\n        specs: [],\n        suites: {\n          foo: 'foo.spec.js',\n          bar: 'bar.spec.js'\n        }\n      };\n\n      var specs = ConfigParser.getSpecs(config);\n\n      expect(specs).toEqual(['foo.spec.js', 'bar.spec.js']);\n    });\n  });\n\n  describe('resolving globs', function() {\n    it('should resolve relative to the cwd', function() {\n      spyOn(process, 'cwd').and.returnValue(__dirname + '/');\n      var toAdd = {\n        specs: 'data/*spec[AB].js'\n      };\n      var config = new ConfigParser().addConfig(toAdd).getConfig();\n      var specs = ConfigParser.resolveFilePatterns(config.specs);\n      expect(specs.length).toEqual(2);\n      expect(specs[0].indexOf(path.normalize('unit/data/fakespecA.js'))).not.toEqual(-1);\n      expect(specs[1].indexOf(path.normalize('unit/data/fakespecB.js'))).not.toEqual(-1);\n    });\n\n    it('should resolve relative to the config file dir', function() {\n      var config = new ConfigParser().\n          addFileConfig(__dirname + '/data/config.js').\n          getConfig();\n      var specs = ConfigParser.resolveFilePatterns(\n          config.specs, false, config.configDir);\n      expect(specs.length).toEqual(2);\n      expect(specs[0].indexOf(path.normalize('unit/data/fakespecA.js'))).not.toEqual(-1);\n      expect(specs[1].indexOf(path.normalize('unit/data/fakespecB.js'))).not.toEqual(-1);\n    });\n\n    it('should not try to expand non-glob patterns', function() {\n      var toAdd = {\n        specs: 'data/fakespecA.js:5'\n      };\n      var config = new ConfigParser().addConfig(toAdd).getConfig();\n      var specs = ConfigParser.resolveFilePatterns(config.specs);\n      expect(specs.length).toEqual(1);\n      expect(specs[0].indexOf(path.normalize('data/fakespecA.js:5'))).not.toEqual(-1);\n    });\n  });\n});\n"
  },
  {
    "path": "spec/unit/data/config.js",
    "content": "exports.config = {\n  onPrepare: 'foo/bar.js',\n  specs: ['fakespec[AB].js'],\n  rootElement: '.mycontainer'\n};\n"
  },
  {
    "path": "spec/unit/data/fakespecA.js",
    "content": "// Blank\n"
  },
  {
    "path": "spec/unit/data/fakespecB.js",
    "content": "// Blank\n"
  },
  {
    "path": "spec/unit/data/fakespecC.js",
    "content": "// Blank\n"
  },
  {
    "path": "spec/unit/driverProviders/direct_test.js",
    "content": "const fs = require('fs'),\n    os = require('os'),\n    path = require('path');\nconst BrowserError = require('../../../built/exitCodes').BrowserError;\nconst ProtractorError = require('../../../built/exitCodes').ProtractorError;\nconst Logger = require('../../../built/logger').Logger;\nconst WriteTo = require('../../../built/logger').WriteTo;\nconst Direct = require('../../../built/driverProviders').Direct;\nlet webdriver, file;\n\ndescribe('direct connect', () => {\n  beforeEach(() => {\n    ProtractorError.SUPRESS_EXIT_CODE = true;\n    Logger.setWrite(WriteTo.NONE);\n  });\n\n  afterEach(() => {\n    ProtractorError.SUPRESS_EXIT_CODE = false;\n    Logger.setWrite(WriteTo.CONSOLE);\n  });\n\n  describe('without the chrome driver', () => {\n    it('should throw an error if no drivers are present', async () => {\n      const config = {\n        directConnect: true,\n        capabilities: { browserName: 'chrome' },\n        chromeDriver: '/foo/bar/chromedriver'\n      };\n      let errorFound = false;\n      try {\n        webdriver = new Direct(config);\n        await webdriver.getNewDriver();\n      } catch(e) {\n        errorFound = true;\n        expect(e.code).toBe(BrowserError.CODE);\n      }\n      expect(errorFound).toBe(true);\n    });\n  });\n\n  describe('with chromedriver drivers', () => {\n    let chromedriver = '';\n    beforeEach(() => {\n      // add files to selenium folder\n      file = 'chromedriver';\n      chromedriver = path.resolve(os.tmpdir(), file);\n      fs.openSync(chromedriver, 'w');\n    });\n\n    afterEach(() => {\n      try {\n        fs.unlinkSync(chromedriver);\n      } catch(e) {\n      }\n    });\n\n    it('should throw an error if the driver cannot be used', async () => {\n      const config = {\n        directConnect: true,\n        capabilities: { browserName: 'foobar explorer' },\n        chromeDriver: chromedriver\n      };\n      let errorFound = false;\n      try {\n        webdriver = new Direct(config);\n        await webdriver.getNewDriver();\n      } catch(e) {\n        errorFound = true;\n        expect(e.code).toBe(BrowserError.CODE);\n      }\n      expect(errorFound).toBe(true);\n    });\n  });\n\n  describe('binary does not exist', () => {\n    it('should throw an error if the update-config.json does not exist', async () => {\n      spyOn(fs, 'readFileSync').and.callFake(() => { return null; });\n      const config = {\n        directConnect: true,\n        capabilities: { browserName: 'chrome' },\n      };\n      let errorFound = false;\n      try {\n        webdriver = new Direct(config);\n        await webdriver.getNewDriver();\n      } catch(e) {\n        errorFound = true;\n        expect(e.code).toBe(BrowserError.CODE);\n      }\n      expect(errorFound).toBe(true);\n    });\n  });\n});\n"
  },
  {
    "path": "spec/unit/driverProviders/local_test.js",
    "content": "const fs = require('fs'),\n    os = require('os'),\n    path = require('path');\nconst BrowserError = require('../../../built/exitCodes').BrowserError;\nconst ProtractorError = require('../../../built/exitCodes').ProtractorError;\nconst Logger = require('../../../built/logger').Logger;\nconst WriteTo = require('../../../built/logger').WriteTo;\nconst Local = require('../../../built/driverProviders').Local;\nlet webdriver, file;\n\ndescribe('local connect', () => {\n  beforeEach(() => {\n    ProtractorError.SUPRESS_EXIT_CODE = true;\n    Logger.setWrite(WriteTo.NONE);\n  });\n\n  afterEach(() => {\n    ProtractorError.SUPRESS_EXIT_CODE = false;\n    Logger.setWrite(WriteTo.CONSOLE);\n  });\n\n  describe('without the selenium standalone jar', () => {\n    it('should throw an error jar file is not present', async () => {\n      const config = {\n        capabilities: { browserName: 'chrome' },\n        seleniumServerJar: '/foo/bar/selenium.jar'\n      };\n      let errorFound = false;\n      try {\n        webdriver = new Local(config);\n        await webdriver.setupEnv();\n      } catch(e) {\n        errorFound = true;\n        expect(e.code).toBe(BrowserError.CODE);\n      }\n      expect(errorFound).toBe(true);\n    });\n  });\n\n  describe('with the selenium standalone jar', () => {\n    it('should throw an error if the selenium sever jar cannot be used', async () => {\n      let jarFile = '';\n      const config = {\n        capabilities: { browserName: 'foobar explorer' },\n        seleniumServerJar: jarFile\n      };\n      let errorFound = false;\n      try {\n        webdriver = new Local(config);\n        await webdriver.getNewDriver();\n      } catch(e) {\n        errorFound = true;\n        expect(e.code).toBe(BrowserError.CODE);\n      }\n      expect(errorFound).toBe(true);\n    });\n  });\n\n  describe('binary does not exist', () => {\n    it('should throw an error if the update-config.json does not exist', async () => {\n      spyOn(fs, 'readFileSync').and.callFake(() => { return null; });\n      const config = {\n        capabilities: { browserName: 'chrome' },\n        openSync: fs.openSync\n      };\n      let errorFound = false;\n      try {\n        webdriver = new Local(config);\n        await webdriver.setupDriverEnv();\n      } catch(e) {\n        errorFound = true;\n        expect(e.code).toBe(BrowserError.CODE);\n      }\n      expect(errorFound).toBe(true);\n    });\n  });\n});\n"
  },
  {
    "path": "spec/unit/logger_test.js",
    "content": "var fs = require('fs'),\n    os = require('os'),\n    path = require('path');\nvar LogLevel = require('../../built/logger').LogLevel,\n    Logger = require('../../built/logger').Logger,\n    WriteTo = require('../../built/logger').WriteTo;\n\ndescribe('the logger', function() {\n  var logFile = path.resolve(os.tmpdir(), 'logger_test.log');\n  var logger = new Logger('FooBar');\n\n  beforeEach(function() {\n    try { fs.unlinkSync(path.resolve(logFile)); } catch(e) { }\n  });\n\n  afterEach(function() {\n    try { fs.unlinkSync(path.resolve(logFile)); } catch(e) { }\n  });\n\n  describe('log strings to file', function() {\n    beforeEach(function() {\n      Logger.setWrite(WriteTo.FILE, logFile);\n    });\n\n    var writeString = function() {\n      logger.debug('hello debug');\n      logger.info('hello info');\n      logger.warn('hello warn');\n      logger.error('hello error');\n    };\n\n    afterEach(function() {\n      Logger.setWrite(WriteTo.CONSOLE);\n      Logger.logLevel = LogLevel.DEBUG;\n    });\n\n    it('should write debug, info, warn, and error to file', function() {\n      Logger.logLevel = LogLevel.DEBUG;\n      writeString();\n      var lines = fs.readFileSync(path.resolve(logFile)).toString();\n      var linesSplit = lines.split('\\n');\n      expect(linesSplit.length).toBe(6);\n      expect(linesSplit[1]).toContain('D/FooBar');\n      expect(linesSplit[2]).toContain('I/FooBar');\n      expect(linesSplit[3]).toContain('W/FooBar');\n      expect(linesSplit[4]).toContain('E/FooBar');\n    });\n\n    it('should write info, warn, and error to file', function() {\n      Logger.logLevel = LogLevel.INFO;\n      writeString();\n      var lines = fs.readFileSync(path.resolve(logFile)).toString();\n      var linesSplit = lines.split('\\n');\n      expect(linesSplit.length).toBe(5);\n      expect(linesSplit[1]).toContain('I/FooBar');\n      expect(linesSplit[2]).toContain('W/FooBar');\n      expect(linesSplit[3]).toContain('E/FooBar');\n    });\n\n    it('should write warn and error to file', function() {\n      Logger.logLevel = LogLevel.WARN;\n      writeString();\n      var lines = fs.readFileSync(path.resolve(logFile)).toString();\n      var linesSplit = lines.split('\\n');\n      expect(linesSplit.length).toBe(4);\n      expect(linesSplit[1]).toContain('W/FooBar');\n      expect(linesSplit[2]).toContain('E/FooBar');\n    });\n\n    it('should write error to file', function() {\n      Logger.logLevel = LogLevel.ERROR;\n      writeString();\n      var lines = fs.readFileSync(path.resolve(logFile)).toString();\n      var linesSplit = lines.split('\\n');\n      expect(linesSplit.length).toBe(3);\n      expect(linesSplit[1]).toContain('E/FooBar');\n    });\n  });\n\n  describe('log json objects/array to file', function() {\n    beforeEach(function() {\n      Logger.setWrite(WriteTo.FILE, logFile);\n    });\n\n    afterEach(function() {\n      Logger.setWrite(WriteTo.CONSOLE);\n    });\n\n    it('should write obj to file', function() {\n      var obj = { foo: 'bar' };\n      logger.info(obj);\n      var lines = fs.readFileSync(path.resolve(logFile)).toString();\n      var linesSplit = lines.split('\\n');\n      expect(linesSplit.length).toBe(3);\n      expect(linesSplit[1]).toContain('{\"foo\":\"bar\"}');\n    });\n\n    it('should write an array to file', function() {\n      var arr = [ 'foo', 'bar', 'foobar' ];\n      logger.info(arr);\n      var lines = fs.readFileSync(path.resolve(logFile)).toString();\n      var linesSplit = lines.split('\\n');\n      expect(linesSplit.length).toBe(3);\n      expect(linesSplit[1]).toContain('[\"foo\",\"bar\",\"foobar\"]');\n    });\n  });\n\n  describe('log different types', function() {\n    beforeEach(function() {\n      Logger.setWrite(WriteTo.FILE, logFile);\n    });\n\n    afterEach(function() {\n      Logger.setWrite(WriteTo.CONSOLE);\n    });\n\n    it('should write json objects and strings', function() {\n      var obj = { foo: 'bar' };\n      var arr = [ 'foo', 'bar', 'foobar' ];\n      var msg = 'foobar';\n      logger.info(obj, arr, msg);\n      var lines = fs.readFileSync(path.resolve(logFile)).toString();\n      var linesSplit = lines.split('\\n');\n      expect(linesSplit.length).toBe(3);\n      expect(linesSplit[1]).toContain('{\"foo\":\"bar\"} [\"foo\",\"bar\",\"foobar\"] foobar');\n    });\n  });\n\n  describe('default log level is configurable', function () {\n    beforeEach(function() {\n      Logger.logLevel = LogLevel.ERROR;\n    });\n\n    afterEach(function() {\n      Logger.logLevel = LogLevel.DEBUG;\n    });\n\n    it('should be configurable statically', function () {\n      Logger.logLevel = LogLevel.WARN;\n      expect(Logger.logLevel).toBe(LogLevel.WARN);\n    });\n\n    it('should be configurable with \"troubleshoot\" property', function () {\n      Logger.set({ troubleshoot: true });\n      expect(Logger.logLevel).toBe(LogLevel.DEBUG);\n    });\n\n    it('should be configurable with \"logLevel\" property', function () {\n      Logger.set({ logLevel: 'WARN' });\n      expect(Logger.logLevel).toBe(LogLevel.WARN);\n    });\n  });\n});\n"
  },
  {
    "path": "spec/unit/runner_test.js",
    "content": "var Runner = require('../../built/runner').Runner;\nvar Logger = require('../../built/logger').Logger,\n    WriteTo = require('../../built/logger').WriteTo;\n\ndescribe('the Protractor runner', () => {\n  beforeAll(() => {\n    Logger.writeTo = WriteTo.NONE;\n  });\n\n  afterAll(() => {\n    Logger.writeTo = WriteTo.CONSOLE;\n  });\n  it('should export its config', () => {\n    const config = {\n      foo: 'bar'\n    };\n\n    const runner = new Runner(config);\n\n    expect(runner.getConfig()).toEqual(config);\n  });\n\n  it('should run', async () => {\n    const config = {\n      mockSelenium: true,\n      specs: ['*.js'],\n      framework: 'debugprint'\n    };\n    let exitCode;\n    const runner = new Runner(config);\n    runner.exit_ = function(exit) {\n      exitCode = exit;\n    };\n\n    await runner.run()\n    expect(exitCode).toEqual(0);\n  });\n\n  it('should fail with no specs', async () => {\n    const config = {\n      mockSelenium: true,\n      specs: [],\n      framework: 'debugprint'\n    };\n\n    const runner = new Runner(config);\n    let errMessage = 'No error found';\n    try {\n      await runner.run()\n    } catch (err) {\n      errMessage = err.message;\n    }\n    expect(errMessage).not.toBe('No error found');\n  });\n\n  it('should fail when no custom framework is defined', async () => {\n    const config = {\n      mockSelenium: true,\n      specs: ['*.js'],\n      framework: 'custom'\n    };\n\n    const runner = new Runner(config);\n    let errMessage = 'No error found';\n    try {\n      await runner.run()\n    } catch (err) {\n      errMessage = err.message;\n    }\n    expect(errMessage).not.toBe('No error found');\n  });\n});\n"
  },
  {
    "path": "spec/unit/taskScheduler_test.js",
    "content": "var TaskScheduler = require('../../built/taskScheduler').TaskScheduler;\nvar ConfigParser = require('../../built/configParser').ConfigParser;\n\ndescribe('the task scheduler', function() {\n\n  it('should schedule single capability tests', function() {\n    var toAdd = {\n      specs: [\n        'spec/unit/data/fakespecA.js',\n        'spec/unit/data/fakespecB.js'\n      ],\n      multiCapabilities: [{\n        browserName: 'chrome'\n      }]\n    };\n    var config = new ConfigParser().addConfig(toAdd).getConfig();\n    var scheduler = new TaskScheduler(config);\n\n    var task = scheduler.nextTask();\n    expect(task.capabilities.browserName).toEqual('chrome');\n    expect(task.specs.length).toEqual(2);\n\n    task.done();\n    expect(scheduler.numTasksOutstanding()).toEqual(0);\n  });\n\n  it('should schedule single capability tests with sharding', function() {\n    var toAdd = {\n      specs: [\n        'spec/unit/data/fakespecA.js',\n        'spec/unit/data/fakespecB.js'\n      ],\n      multiCapabilities: [{\n        shardTestFiles: true,\n        maxInstances: 2,\n        browserName: 'chrome'\n      }]\n    };\n    var config = new ConfigParser().addConfig(toAdd).getConfig();\n    var scheduler = new TaskScheduler(config);\n\n    var task1 = scheduler.nextTask();\n    expect(task1.capabilities.browserName).toEqual('chrome');\n    expect(task1.specs.length).toEqual(1);\n\n    var task2 = scheduler.nextTask();\n    expect(task2.capabilities.browserName).toEqual('chrome');\n    expect(task2.specs.length).toEqual(1);\n\n    task1.done();\n    task2.done();\n    expect(scheduler.numTasksOutstanding()).toEqual(0);\n  });\n\n  it('should schedule single capability tests with count', function() {\n    var toAdd = {\n      specs: [\n        'spec/unit/data/fakespecA.js',\n        'spec/unit/data/fakespecB.js'\n      ],\n      multiCapabilities: [{\n        count: 2,\n        browserName: 'chrome'\n      }]\n    };\n    var config = new ConfigParser().addConfig(toAdd).getConfig();\n    var scheduler = new TaskScheduler(config);\n\n    var task1 = scheduler.nextTask();\n    expect(task1.capabilities.browserName).toEqual('chrome');\n    expect(task1.specs.length).toEqual(2);\n\n    var task2 = scheduler.nextTask();\n    expect(task2.capabilities.browserName).toEqual('chrome');\n    expect(task2.specs.length).toEqual(2);\n\n    task1.done();\n    task2.done();\n    expect(scheduler.numTasksOutstanding()).toEqual(0);\n  });\n\n  it('should schedule multiCapabilities tests', function() {\n    var toAdd = {\n      specs: [\n        'spec/unit/data/fakespecA.js',\n        'spec/unit/data/fakespecB.js'\n      ],\n      multiCapabilities: [{\n        'browserName': 'chrome'\n      }, {\n        'browserName': 'firefox'\n      }]\n    };\n    var config = new ConfigParser().addConfig(toAdd).getConfig();\n    var scheduler = new TaskScheduler(config);\n\n    var task1 = scheduler.nextTask();\n    expect(task1.capabilities.browserName).toEqual('chrome');\n    expect(task1.specs.length).toEqual(2);\n\n    var task2 = scheduler.nextTask();\n    expect(task2.capabilities.browserName).toEqual('firefox');\n    expect(task2.specs.length).toEqual(2);\n\n    task1.done();\n    task2.done();\n    expect(scheduler.numTasksOutstanding()).toEqual(0);\n  });\n\n  it('should obey maxInstances', function() {\n    var toAdd = {\n      specs: [\n        'spec/unit/data/fakespecA.js',\n        'spec/unit/data/fakespecB.js'\n      ],\n      multiCapabilities: [{\n        shardTestFiles: true,\n        maxInstances: 1,\n        browserName: 'chrome'\n      }]\n    };\n    var config = new ConfigParser().addConfig(toAdd).getConfig();\n    var scheduler = new TaskScheduler(config);\n\n    var task1 = scheduler.nextTask();\n    expect(task1.capabilities.browserName).toEqual('chrome');\n    expect(task1.specs.length).toEqual(1);\n\n    var task2 = scheduler.nextTask();\n    expect(task2).toBeNull();\n\n    task1.done();\n    expect(scheduler.numTasksOutstanding()).toEqual(1);\n\n    var task3 = scheduler.nextTask();\n    expect(task3.capabilities.browserName).toEqual('chrome');\n    expect(task3.specs.length).toEqual(1);\n\n    task3.done();\n    expect(scheduler.numTasksOutstanding()).toEqual(0);\n  });\n\n  it('should allow capability-specific specs', function() {\n    var toAdd = {\n      specs: [\n        'spec/unit/data/fakespecA.js',\n        'spec/unit/data/fakespecB.js'\n      ],\n      multiCapabilities: [{\n        'browserName': 'chrome',\n        specs: 'spec/unit/data/fakespecC.js'\n      }]\n    };\n    var config = new ConfigParser().addConfig(toAdd).getConfig();\n    var scheduler = new TaskScheduler(config);\n\n    var task = scheduler.nextTask();\n    expect(task.capabilities.browserName).toEqual('chrome');\n    expect(task.specs.length).toEqual(3);\n\n    task.done();\n    expect(scheduler.numTasksOutstanding()).toEqual(0);\n  });\n\n\n  it('should work with only capability-specific specs', function() {\n    var toAdd = {\n      specs: [\n      ],\n      multiCapabilities: [{\n        'browserName': 'chrome',\n        specs: 'spec/unit/data/fakespecC.js'\n      }]\n    };\n    var config = new ConfigParser().addConfig(toAdd).getConfig();\n    var scheduler = new TaskScheduler(config);\n\n    var task = scheduler.nextTask();\n    expect(task.capabilities.browserName).toEqual('chrome');\n    expect(task.specs.length).toEqual(1);\n\n    task.done();\n    expect(scheduler.numTasksOutstanding()).toEqual(0);\n  });\n\n  it('should handle multiCapabilities with mixture of features', function() {\n    var toAdd = {\n      specs: [\n        'spec/unit/data/fakespecA.js',\n        'spec/unit/data/fakespecB.js'\n      ],\n      multiCapabilities: [{\n        'browserName': 'chrome',\n        maxInstances: 2,\n        count: 2\n      }, {\n        'browserName': 'firefox',\n        shardTestFiles: true,\n        maxInstances: 1,\n        count: 2\n      }]\n    };\n    var config = new ConfigParser().addConfig(toAdd).getConfig();\n    var scheduler = new TaskScheduler(config);\n\n    var task1 = scheduler.nextTask();\n    expect(task1.capabilities.browserName).toEqual('chrome');\n    expect(task1.specs.length).toEqual(2);\n    task1.done();\n\n    var task2 = scheduler.nextTask();\n    expect(task2.capabilities.browserName).toEqual('chrome');\n    expect(task2.specs.length).toEqual(2);\n    task2.done();\n\n    var task3 = scheduler.nextTask();\n    expect(task3.capabilities.browserName).toEqual('firefox');\n    expect(task3.specs.length).toEqual(1);\n    task3.done();\n\n    var task4 = scheduler.nextTask();\n    expect(task4.capabilities.browserName).toEqual('firefox');\n    expect(task4.specs.length).toEqual(1);\n    task4.done();\n\n    var task5 = scheduler.nextTask();\n    expect(task5.capabilities.browserName).toEqual('firefox');\n    expect(task5.specs.length).toEqual(1);\n    task5.done();\n\n    var task6 = scheduler.nextTask();\n    expect(task6.capabilities.browserName).toEqual('firefox');\n    expect(task6.specs.length).toEqual(1);\n    task6.done();\n\n    expect(scheduler.numTasksOutstanding()).toEqual(0);\n  });\n\n  it('should exclude capability-specific specs', function() {\n    var toAdd = {\n      specs: [\n        'spec/unit/data/fakespecA.js',\n        'spec/unit/data/fakespecB.js'\n      ],\n      multiCapabilities: [{\n        'browserName': 'chrome',\n        exclude: 'spec/unit/data/fakespecB.js'\n      }]\n    };\n    var config = new ConfigParser().addConfig(toAdd).getConfig();\n    var scheduler = new TaskScheduler(config);\n\n    var task = scheduler.nextTask();\n    expect(task.capabilities.browserName).toEqual('chrome');\n    expect(task.specs.length).toEqual(1);\n\n    task.done();\n    expect(scheduler.numTasksOutstanding()).toEqual(0);\n  });\n\n});\n"
  },
  {
    "path": "spec/withLoginConf.js",
    "content": "var env = require('./environment.js');\n\n// This is the configuration file showing how a suite of tests might\n// handle log-in using the onPrepare field.\nexports.config = {\n  seleniumAddress: env.seleniumAddress,\n  SELENIUM_PROMISE_MANAGER: false,\n\n  framework: 'jasmine',\n\n  specs: [\n    'login/login_spec.js'\n  ],\n\n  capabilities: env.capabilities,\n\n  baseUrl: env.baseUrl + '/ng1/',\n\n  onPrepare: async() => {\n    await browser.driver.get(env.baseUrl + '/ng1/login.html');\n\n    await browser.driver.findElement(by.id('username')).sendKeys('Jane');\n    await browser.driver.findElement(by.id('password')).sendKeys('1234');\n    await browser.driver.findElement(by.id('clickme')).click();\n\n    // Login takes some time, so wait until it's done.\n    // For the test app's login, we know it's done when it redirects to\n    // index.html.\n    return await browser.driver.wait(async() => {\n      const url = await browser.driver.getCurrentUrl();\n      return /index/.test(url);\n    }, 10000);\n  }\n};\n"
  },
  {
    "path": "stress/conf.js",
    "content": "// Configuration for stress testing.\nvar env = require('../spec/environment.js');\n\n// Before running locally, start up sauce connect and the test appliation.\n// Then set the environment variables SAUCE_USERNAME, SAUCE_ACCESS_KEY,\n// TRAVIS_JOB_NUMBER, and TRAVIS_BUILD_NUMBER.\nexports.config = {\n  sauceUser: process.env.SAUCE_USERNAME,\n  sauceKey: process.env.SAUCE_ACCESS_KEY,\n\n  specs: [\n    'spec.js'\n  ],\n\n  // Two latest versions of Chrome, Firefox, IE.\n  multiCapabilities: [{\n    'browserName': 'chrome',\n    'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,\n    'build': process.env.TRAVIS_BUILD_NUMBER,\n    'name': 'Protractor smoke tests',\n    'version': '34',\n    'selenium-version': '2.42.2',\n    'platform': 'OS X 10.9'\n  }, {\n    'browserName': 'chrome',\n    'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,\n    'build': process.env.TRAVIS_BUILD_NUMBER,\n    'name': 'Protractor smoke tests',\n    'version': '35',\n    'selenium-version': '2.42.2',\n    'platform': 'OS X 10.9'\n  }, {\n    'browserName': 'firefox',\n    'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,\n    'build': process.env.TRAVIS_BUILD_NUMBER,\n    'name': 'Protractor smoke tests',\n    'version': '29',\n    'selenium-version': '2.42.2'\n  }, {\n    'browserName': 'firefox',\n    'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,\n    'build': process.env.TRAVIS_BUILD_NUMBER,\n    'name': 'Protractor smoke tests',\n    'version': '30',\n    'selenium-version': '2.42.2'\n  }, {\n    'browserName': 'internet explorer',\n    'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,\n    'build': process.env.TRAVIS_BUILD_NUMBER,\n    'name': 'Protractor smoke tests',\n    'version': '11',\n    'selenium-version': '2.42.2',\n    'platform': 'Windows 7'\n  }, {\n    'browserName': 'internet explorer',\n    'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,\n    'build': process.env.TRAVIS_BUILD_NUMBER,\n    'name': 'Protractor smoke tests',\n    'version': '10',\n    'selenium-version': '2.42.2',\n    'platform': 'Windows 7'\n  }],\n\n  baseUrl: env.baseUrl\n};\n"
  },
  {
    "path": "stress/spec.js",
    "content": "// We just want to do a ton of page navigation for stress tests.\n\nvar ITERS = 20;\n\ndescribe('stress testing', function() {\n  for (var i = 0; i < ITERS; ++i) {\n    it('should run test ' + i, function() {\n      var usernameInput = element(by.model('username'));\n      var name = element(by.binding('username'));\n\n      browser.get('index.html#/form');\n\n      expect(name.getText()).toEqual('Anon');\n\n      usernameInput.clear();\n      usernameInput.sendKeys('B');\n      expect(name.getText()).toEqual('B');\n    });\n  }\n});\n"
  },
  {
    "path": "testapp/.gitignore",
    "content": "*.js.map\n"
  },
  {
    "path": "testapp/app.css",
    "content": "body {\n  font-family: Helvetica, Arial, san-sarif;\n  font-size: 2em;\n}\n\nh1 {\n  text-align: center;\n}\n\nol {\n  position: relative;\n  padding: 0;\n}\n\nli {\n  width: 18%;\n  padding: 3%;\n  display: block;\n  text-align: center;\n  position: absolute;\n  top: 0;\n  margin: auto;\n}\n\nli.left {\n  left: 0;\n}\n\nli.left.mid {\n  left: 20%;\n}\n\nli.mid{\n  left : 40%;\n}\n\nli.right.mid {\n  left: 60%;\n}\n\nli.right {\n  left: 80%;\n}\n\n"
  },
  {
    "path": "testapp/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <title>My AngularJS App</title>\n    <link rel=\"stylesheet\" href=\"app.css\"/>\n  </head>\n  <body>\n    <h1>Choose Version</h1>\n    <ol>\n      <li class=\"left\">\n        <a href=\"ng1\">Angular 1</a>\n      </li>\n      <li class='mid left'>\n        <a href='upgrade?no_static'>Hybrid (JIT)</a>\n      </li>\n      <li class='mid'>\n        <a href='upgrade'>Hybrid (AOT)</a>\n      </li>\n      <li class='mid right'>\n          <a href='upgrade?downgrade'>Hybrid (Downgrade)</a>\n      </li>\n      <li class=\"right\">\n        <a href=\"ng2\">Angular 2</a>\n      </li>\n    </ol>\n  </body>\n</html>\n"
  },
  {
    "path": "testapp/ng1/alt_root_index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>My AngularJS App</title>\n  <link rel=\"stylesheet\" href=\"app.css\"/>\n</head>\n<body>\n\n  <div id=\"outside-ng\">\n    {{1 + 2}}\n  </div>\n\n  <div id=\"nested-ng-app\" ng-app=\"myApp\">\n    <div id=\"inside-ng\">\n      {{1 + 2}}\n    </div>\n\n    <ul class=\"menu\">\n      <li><a href=\"#/repeater\">repeater</a></li>\n      <li><a href=\"#/bindings\">bindings</a></li>\n      <li><a href=\"#/form\">form</a></li>\n      <li><a href=\"#/async\">async</a></li>\n    </ul>\n\n    <div ng-view></div>\n    <div>Angular seed app: v<span app-version></span></div>\n  </div>\n\n  <script src=\"lib/angular_v1.2.9/angular.min.js\"></script>\n  <script src=\"lib/angular_v1.2.9/angular-animate.min.js\"></script>\n  <script src=\"lib/angular_v1.2.9/angular-route.min.js\"></script>\n  <script src=\"components/app-version.js\"></script>\n  <script src=\"async/async.js\"></script>\n  <script src=\"bindings/bindings.js\"></script>\n  <script src=\"conflict/conflict.js\"></script>\n  <script src=\"form/form.js\"></script>\n  <script src=\"polling/polling.js\"></script>\n  <script src=\"repeater/repeater.js\"></script>\n  <script src=\"animation/animation.js\"></script>\n  <script src=\"interaction/interaction.js\"></script>\n  <script src=\"shadow/shadow.js\"></script>\n  <script src=\"app.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "testapp/ng1/animation/animation.css",
    "content": "#animationTop .animate-if {\n  background:white;\n  border:1px solid black;\n  padding:10px;\n}\n\n#animationTop .animate-if.ng-enter,\n#animationTop .animate-if.ng-leave {\n  -webkit-transition:all linear 1s;\n  -moz-transition:all linear 1s;\n  -o-transition:all linear 1s;\n  transition:all linear 1s;\n\n}\n\n#animationTop .animate-if.ng-enter,\n#animationTop .animate-if.ng-leave.ng-leave-active {\n  opacity:0;\n}\n\n#animationTop .animate-if.ng-leave,\n#animationTop .animate-if.ng-enter.ng-enter-active {\n  opacity:1;\n}\n"
  },
  {
    "path": "testapp/ng1/animation/animation.html",
    "content": "<!-- https://docs.angularjs.org/guide/animations -->\n<div id=\"animationTop\">\n  <label>\n    <input id=\"checkbox\" type=\"checkbox\" ng-model=\"checked\" style=\"float:left; margin-right:10px;\">\n    Click Me to toggle with animation\n  </label>\n  <div id=\"toggledNode\" class=\"animate-if\" ng-if=\"checked\" style=\"clear:both;\">\n    I exist!\n  </div>\n</div>\n"
  },
  {
    "path": "testapp/ng1/animation/animation.js",
    "content": "function AnimationCtrl($scope) {\n  $scope.checked = true;\n}\n\nAnimationCtrl.$inject = ['$scope'];\n"
  },
  {
    "path": "testapp/ng1/app.css",
    "content": "/* app css stylesheet */\n\n.menu {\n  list-style: none;\n  border-bottom: 0.1em solid black;\n  margin-bottom: 2em;\n  padding: 0 0 0.5em;\n}\n\n.menu:before {\n  content: \"[\";\n}\n\n.menu:after {\n  content: \"]\";\n}\n\n.menu > li {\n  display: inline;\n}\n\n.menu > li:before {\n  content: \"|\";\n  padding-right: 0.3em;\n}\n\n.menu > li:nth-child(1):before {\n  content: \"\";\n  padding: 0;\n}\n\n.ng-scope {\n  background-color: rgba(0,0,0,.05);\n}\n\n.ng-binding {\n  border: 1px solid rgba(50, 200, 50, .8);\n}\n\n#chat-box {\n  width: 300px;\n  height: 200px;\n  padding: 25px;\n  border: 2px solid;\n  margin: 25px;\n  overflow: scroll;\n}\n"
  },
  {
    "path": "testapp/ng1/app.js",
    "content": "'use strict';\n\n\n// Declare app level module which depends on filters, and services\nangular.module('myApp', ['ngAnimate', 'ngRoute', 'myApp.appVersion']).\n  config(['$routeProvider', function($routeProvider) {\n    $routeProvider.when('/repeater', {templateUrl: 'repeater/repeater.html', controller: RepeaterCtrl});\n    $routeProvider.when('/bindings', {templateUrl: 'bindings/bindings.html', controller: BindingsCtrl});\n    $routeProvider.when('/form', {templateUrl: 'form/form.html', controller: FormCtrl});\n    $routeProvider.when('/async', {templateUrl: 'async/async.html', controller: AsyncCtrl});\n    $routeProvider.when('/conflict', {templateUrl: 'conflict/conflict.html', controller: ConflictCtrl});\n    $routeProvider.when('/polling', {templateUrl: 'polling/polling.html', controller: PollingCtrl});\n    $routeProvider.when('/animation', {templateUrl: 'animation/animation.html', controller: AnimationCtrl});\n    $routeProvider.when('/interaction', {templateUrl: 'interaction/interaction.html', controller: InteractionCtrl});\n    $routeProvider.when('/shadow', {templateUrl: 'shadow/shadow.html', controller: ShadowCtrl});\n    $routeProvider.when('/slowloader', {\n      templateUrl: 'polling/polling.html',\n      controller: PollingCtrl,\n      resolve: {\n        slow: function($timeout) {\n          return $timeout(function() {}, 5000);\n        }\n      }\n    });\n    $routeProvider.otherwise({redirectTo: '/form'});\n  }]);\n"
  },
  {
    "path": "testapp/ng1/async/async.html",
    "content": "Slow things that can happen:\n<ul>\n  <li>\n    <button ng-click=\"slowHttp()\">http</button>\n    <span ng-bind=\"slowHttpStatus\"></span>\n  </li>\n  <li>\n    <button ng-click=\"slowFunction()\">js function</button>\n    <span ng-bind=\"slowFunctionStatus\"></span>\n  </li>\n  <li>\n    <button ng-click=\"slowTimeout()\">timeout</button>\n    <span ng-bind=\"slowTimeoutStatus\"></span>\n  </li>\n  <li>\n    <button ng-click=\"slowAngularTimeout()\">$timeout</button>\n    <span ng-bind=\"slowAngularTimeoutStatus\"></span>\n  </li>\n  <li>\n    <button ng-click=\"slowAngularTimeoutPromise()\">$timeout promise</button>\n    <span ng-bind=\"slowAngularTimeoutPromiseStatus\"></span>\n  </li>\n  <li>\n    <button ng-click=\"slowHttpPromise()\">http promise</button>\n    <span ng-bind=\"slowHttpPromiseStatus\"></span>\n  </li>\n  <li>\n    <button ng-click=\"routingChange()\">routing change</button>\n    <span ng-bind=\"routingChangeStatus\"></span>\n  </li>\n  <li>\n    <button ng-click=\"changeTemplateUrl()\">ng-include URL change</button>\n    <div class=\"included\" ng-include=\"templateUrl\"></div>\n  </li>\n</ul>\n"
  },
  {
    "path": "testapp/ng1/async/async.js",
    "content": "function AsyncCtrl($scope, $http, $timeout, $location) {\n  $scope.slowHttpStatus = 'not started';\n  $scope.slowFunctionStatus = 'not started';\n  $scope.slowTimeoutStatus = 'not started';\n  $scope.slowAngularTimeoutStatus = 'not started';\n  $scope.slowAngularTimeoutPromiseStatus = 'not started';\n  $scope.slowHttpPromiseStatus = 'not started';\n  $scope.routingChangeStatus = 'not started';\n  $scope.templateUrl = 'fastTemplateUrl';\n\n  $scope.slowHttp = function() {\n    $scope.slowHttpStatus = 'pending...';\n    $http({method: 'GET', url: 'slowcall'}).success(function() {\n      $scope.slowHttpStatus = 'done';\n    });\n  };\n\n  $scope.slowFunction = function() {\n    $scope.slowFunctionStatus = 'pending...';\n    for (var i = 0, t = 0; i < 500000000; ++i) {\n      t++;\n    }\n    $scope.slowFunctionStatus = 'done';\n  };\n\n  $scope.slowTimeout = function() {\n    $scope.slowTimeoutStatus = 'pending...';\n    window.setTimeout(function() {\n      $scope.$apply(function() {\n        $scope.slowTimeoutStatus = 'done';\n      });\n    }, 5000);\n  };\n\n  $scope.slowAngularTimeout = function() {\n    $scope.slowAngularTimeoutStatus = 'pending...';\n    $timeout(function() {\n      $scope.slowAngularTimeoutStatus = 'done';\n    }, 5000);\n  };\n\n  $scope.slowAngularTimeoutPromise = function() {\n    $scope.slowAngularTimeoutPromiseStatus = 'pending...';\n    $timeout(function() {\n      // intentionally empty\n    }, 5000).then(function() {\n      $scope.slowAngularTimeoutPromiseStatus = 'done';\n    });\n  };\n\n  $scope.slowHttpPromise = function() {\n    $scope.slowHttpPromiseStatus = 'pending...';\n    $http({method: 'GET', url: 'slowcall'}).success(function() {\n      // intentionally empty\n    }).then(function() {\n      $scope.slowHttpPromiseStatus = 'done';\n    });\n  };\n\n  $scope.routingChange = function() {\n    $scope.routingChangeStatus = 'pending...';\n    $location.url('slowloader');\n  };\n\n  $scope.changeTemplateUrl = function() {\n    $scope.templateUrl = 'slowTemplateUrl';\n  };\n}\n\nAsyncCtrl.$inject = ['$scope', '$http', '$timeout', '$location'];\n"
  },
  {
    "path": "testapp/ng1/bindings/bindings.html",
    "content": "<div>Details for:\n  <select ng-model=\"planet\" ng-options=\"planet.name for planet in planets\">\n</select>\n<div class=\"planet-info\">\n  <div>{{planet.name}}</div>\n  <div>Radius: {{getRadiusKm()}}km</div>\n  <div>Moons: <span ng-repeat=\"moon in planet.moons\">{{moon}}</span></div>\n</div>\n</div>\n"
  },
  {
    "path": "testapp/ng1/bindings/bindings.js",
    "content": "function BindingsCtrl($scope) {\n  $scope.planets = [\n    { name: 'Mercury',\n      radius: 1516\n    },\n    { name: 'Venus',\n      radius: 3760\n    },\n    { name: 'Earth',\n      radius: 3959,\n      moons: ['Luna']\n    },\n    { name: 'Mars',\n      radius: 2106,\n      moons: ['Phobos', 'Deimos']\n    },\n    { name: 'Jupiter',\n      radius: 43441,\n      moons: ['Europa', 'Io', 'Ganymede', 'Castillo']\n    },\n    { name: 'Saturn',\n      radius: 36184,\n      moons: ['Titan', 'Rhea', 'Iapetus', 'Dione']\n    },\n    { name: 'Uranus',\n      radius: 15759,\n      moons: ['Titania', 'Oberon', 'Umbriel', 'Ariel']\n    },\n    { name: 'Neptune',\n      radius: 15299,\n      moons: ['Triton', 'Proteus', 'Nereid', 'Larissa']\n    }\n  ];\n\n  $scope.planet = $scope.planets[0];\n\n  $scope.getRadiusKm = function() {\n    return $scope.planet.radius * 0.6213;\n  };\n}\n\nBindingsCtrl.$inject = ['$scope'];\n"
  },
  {
    "path": "testapp/ng1/components/app-version.js",
    "content": "'use strict';\n\nangular.module('myApp.appVersion', []).\n  value('version', '0.1').\n  directive('appVersion', ['version', function(version) {\n    return function(scope, elm, attrs) {\n      elm.text(version);\n    };\n  }]);\n"
  },
  {
    "path": "testapp/ng1/conflict/conflict.html",
    "content": "<span>Outer: {{item.reusedBinding}}</span>\n<span>Other other: {{item.alsoReused}}</span>\n\n<div id=\"baz\">\n  <div ng-repeat=\"item in wrapper\">\n    <span>Inner: {{item.reusedBinding}}</span>\n    <span>Inner other: {{item.alsoReused}}</span>\n  </div>\n</div>\n"
  },
  {
    "path": "testapp/ng1/conflict/conflict.js",
    "content": "function ConflictCtrl($scope) {\n  $scope.item = {\n    reusedBinding: 'outer',\n    alsoReused: 'outerbarbaz'\n  };\n\n  $scope.wrapper = [{\n    reusedBinding: 'inner',\n    alsoReused: 'innerbarbaz'\n  }];\n}\nConflictCtrl.$inject = ['$scope'];\n"
  },
  {
    "path": "testapp/ng1/form/form.html",
    "content": "<p>Forms using different types of input</p>\n\n<div>\n  <h4>Bindings</h4>\n  <span>{{greeting}}</span>\n  <span data-ng-bind=\"username\"></span>\n  <span data-ng-bind-template=\"({{nickname|uppercase}})\"></span>\n</div>\n\n<div>\n  <h4>Text</h4>\n  <label>Username</label>\n  <input ng-model=\"username\" type=\"text\"/>\n  <label>Nickname</label>\n  <input ng-model=\"nickname\" type=\"text\"/>\n</div>\n\n<div>\n  <h4>Textarea</h4>\n  <textarea ng-model=\"aboutbox\"></textarea>\n</div>\n\n<div>\n  <h4>Multiple input radio</h4>\n  <div ng-style=\"{'color': color}\">Color <br/>\n    <input ng-model=\"color\" value=\"blue\" type=\"radio\"/> blue <br/>\n    <input ng-model=\"color\" value=\"green\" type=\"radio\"/> green <br/>\n    <input ng-model=\"color\" value=\"red\" type=\"radio\"/> red <br/>\n  </div>\n</div>\n\n<div>\n  <h4>Selects</h4>\n  <div ng-repeat=\"dayColor in dayColors\">\n    <select ng-model=\"dayColor.color\" ng-options=\"c for c in colors\"></select>\n  </div>\n\n  <select ng-model=\"fruit\" ng-options=\"fruit for fruit in fruits\">\n    <option value=\"\">{{defaultFruit}}</option>\n  </select>\n  <span>Fruit: {{fruit}}</span>\n</div>\n\n<div id=\"checkboxes\">\n  <h4>Checkboxes</h4>\n  <input ng-model=\"show\" type=\"checkbox\"/> Show?\n  <span id=\"shower\" ng-show=\"show\">Shown!!</span>\n\n  <input ng-model=\"disabled\" type=\"checkbox\"/> Disable?\n  <button id=\"disabledButton\" ng-disabled=\"disabled\">Dummy</button>\n\n  <input ng:model=\"check.w\" ng-true-value=\"'w'\" type=\"checkbox\"/> W\n  <input data-ng-model=\"check.x\" ng-true-value=\"'x'\" type=\"checkbox\"/> X\n\n  <span id=\"letterlist\">{{check.w}}{{check.x}}</span>\n</div>\n\n<div>\n  <h4>Drag and Drop</h4>\n  <input type=\"range\" name=\"points\" min=\"1\" max=\"10\" value=\"1\">\n</div>\n\n<div>\n  <h4>Alert trigger</h4>\n  <button id=\"alertbutton\" ng-click=\"doAlert()\">Open Alert</button>\n</div>\n\n<div>\n  <h4>Buttons</h4>\n  <button id=\"exacttext\">Exact text</button>\n  <button id=\"otherbutton\">Partial button text</button>\n  <button id=\"trapbutton\">No match</button>\n  <button id=\"hiddenbutton\" style=\"display:none\">Can't see me!</button>\n  <input type=\"submit\" value=\"Exact text\" id=\"submitbutton\"/>\n  <input type=\"button\" value=\"Hello text\" id=\"inputbutton\"/>\n</div>\n\n<div id=\"animals\">\n  <h4>Inner text</h4>\n  <ul>\n    <li class=\"pet\" id=\"bigdog\">big dog</li>\n    <li class=\"pet\" id=\"smalldog\">small dog</li>\n    <li class=\"notpet\" id=\"otherdog\">other dog</li>\n    <li class=\"pet\" id=\"bigcat\">big cat</li>\n    <li class=\"pet\" id=\"smallcat\">small cat</li>\n  </ul>\n</div>\n\n<div>\n  <h4>Inputs</h4>\n  <table>\n    <tr class=\"rowlike\">\n      <td><input type=\"text\" class=\"input\" value=\"10\"/></td>\n      <td class=\"notinput\"></td>\n    </tr>\n    <tr class=\"rowlike\">\n      <td><input type=\"text\" class=\"input\" value=\"10\"/></td>\n      <td class=\"notinput\"></td>\n    </tr>\n  </table>\n</div>\n\n<div id=\"transformedtext\">\n  <h4>Transformed text</h4>\n  <div id=\"textuppercase\" style=\"text-transform: uppercase;\">Uppercase</div>\n  <div id=\"textlowercase\" style=\"text-transform: lowercase;\">Lowercase</div>\n  <div id=\"textcapitalize\" style=\"text-transform: capitalize;\">capitalize</div>\n</div>\n"
  },
  {
    "path": "testapp/ng1/form/form.js",
    "content": "function FormCtrl($scope, $window) {\n  $scope.greeting = 'Hiya';\n  $scope.username = 'Anon';\n  $scope.nickname = 'annie';\n  $scope.aboutbox = 'This is a text box';\n  $scope.color = 'blue';\n  $scope.show = true;\n\n  $scope.colors = ['red', 'green', 'blue'];\n  $scope.dayColors = [{day: 'Mon', color: 'red'}, {day: 'Tue', color: 'green'}, {day: 'Wed', color: 'blue'}];\n\n  $scope.fruit = '';\n  $scope.defaultFruit = 'apple';\n  $scope.fruits = ['pear', 'peach', 'banana'];\n\n  $scope.doAlert = function() {\n    $window.alert('Hello');\n  };\n}\nFormCtrl.$inject = ['$scope', '$window'];\n"
  },
  {
    "path": "testapp/ng1/index.html",
    "content": "<!doctype html>\n<html lang=\"en\" ng-app=\"myApp\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>My AngularJS App</title>\n  <link rel=\"stylesheet\" href=\"app.css\"/>\n  <link rel=\"stylesheet\" href=\"animation/animation.css\"/>\n</head>\n<body>\n  <ul class=\"menu\">\n    <li><a href=\"#/repeater\">repeater</a></li>\n    <li><a href=\"#/bindings\">bindings</a></li>\n    <li><a href=\"#/form\">form</a></li>\n    <li><a href=\"#/async\">async</a></li>\n    <li><a href=\"#/conflict\">conflict</a></li>\n    <li><a href=\"#/polling\">polling</a></li>\n    <li><a href=\"#/animation\">animation</a></li>\n    <li><a href=\"#/interaction\">interaction</a></li>\n    <li><a href=\"#/shadow\">shadow dom</a></li>\n  </ul>\n\n  <div ng-view></div>\n\n  <div>Angular seed app: v<span app-version></span></div>\n\n  <script src=\"lib/angular/angular.min.js\"></script>\n  <script src=\"lib/angular/angular-animate.min.js\"></script>\n  <script src=\"lib/angular/angular-route.min.js\"></script>\n\n  <script src=\"components/app-version.js\"></script>\n  <script src=\"async/async.js\"></script>\n  <script src=\"bindings/bindings.js\"></script>\n  <script src=\"conflict/conflict.js\"></script>\n  <script src=\"form/form.js\"></script>\n  <script src=\"polling/polling.js\"></script>\n  <script src=\"repeater/repeater.js\"></script>\n  <script src=\"animation/animation.js\"></script>\n  <script src=\"interaction/interaction.js\"></script>\n  <script src=\"shadow/shadow.js\"></script>\n  <script src=\"app.js\"></script>\n\n</body>\n</html>\n"
  },
  {
    "path": "testapp/ng1/interaction/interaction.html",
    "content": "<div>\n  <h4>A simple chat system</h4>\n\n  <form ng-show=\"!user\">\n    <input ng-model=\"userInput\" placeholder=\"enter a username\" type=\"text\"/>\n    <button id=\"sendUser\" ng-disabled=\"!userInput\" ng-click=\"sendUser()\">Ok</button>\n  </form>\n\n  <div id=\"chat-box\" ng-show=\"user\">\n    <div ng-repeat=\"msg in messages track by $index\">{{msg}}</div>\n  </div>\n  <form ng-show=\"user\">\n    <input ng-model=\"message\" placeholder=\"Enter a message\" type=\"text\"/>\n    <button id=\"sendMessage\" ng-disabled=\"!message\" ng-click=\"sendMessage()\">Send</button>\n    <button id=\"clearMessages\" ng-click=\"clearMessages()\">Clear all messages</button>\n  </form>\n</div>\n"
  },
  {
    "path": "testapp/ng1/interaction/interaction.js",
    "content": "function InteractionCtrl($scope, $interval, $http) {\n\n  $scope.messages = [];\n  $scope.message = '';\n  $scope.user = '';\n  $scope.userInput = '';\n\n  $scope.sendUser = function() {\n    $scope.user = $scope.userInput;\n  };\n\n  var loadMessages = function() {\n    $http.get('chat?q=chatMessages').\n      success(function(data) {\n        $scope.messages = data ? data : [];\n      }).\n      error(function(err) {\n        $scope.messages = ['server request failed with: ' + err];\n      });\n  };\n\n  $scope.sendMessage = function() {\n    var msg = $scope.user + ': ' + $scope.message;\n    $scope.messages.push(msg);\n    $scope.message = '';\n    \n    var data = {\n      key: 'newChatMessage',\n      value: msg\n    };\n    $http.post('chat', data);\n  };\n\n  $scope.clearMessages = function() {\n    $scope.messages = [];\n    \n    var data = {\n      key: 'clearChatMessages'\n    };\n    $http.post('chat', data);\n  };\n\n  $interval(function() {\n    loadMessages();\n  }, 100);\n}\nInteractionCtrl.$inject = ['$scope', '$interval', '$http'];\n"
  },
  {
    "path": "testapp/ng1/lib/angular_version.js",
    "content": "module.exports = '1.5.0';\n"
  },
  {
    "path": "testapp/ng1/login.html",
    "content": "<html>\n  <head>\n    <title>Test Application Login</title>\n    <script>\n      function login() {\n        // Make sure everything works when the login is slow.\n        window.setTimeout(function() {\n          // Set a simple cookie.\n          var username = document.getElementById('username').value;\n          var password = document.getElementById('password').value;\n\n          document.cookie = 'testcookie=' + username + '-' + password;\n\n          window.location.assign('index.html');\n        }, 2000);\n      };\n    </script>\n  </head>\n  <body>\n    <div>Login</div>\n    <div><label>Username</label><input id=\"username\" type=\"text\"/></div>\n    <div><label>Password</label><input id=\"password\" type=\"text\"/></div>\n    <div><button id=\"clickme\" onClick=\"login()\">Go</button></div>\n  </body>\n</html>\n"
  },
  {
    "path": "testapp/ng1/polling/polling.html",
    "content": "<div>This view shows a controller which uses a polling mechanism to\n  contact the server. It is constantly using angular's $timeout.</div>\n<button id=\"pollstarter\" ng-click=\"startPolling()\">Start Polling</button>\n<div>{{count}}</div>\n"
  },
  {
    "path": "testapp/ng1/polling/polling.js",
    "content": "function PollingCtrl($scope, $timeout) {\n  $scope.count = 0;\n\n  $scope.startPolling = function() {\n    function poll() {\n      $timeout(function() {\n        $scope.count++;\n        poll();\n      }, 1000);\n    };\n\n    poll();\n  };\n}\nPollingCtrl.$inject = ['$scope', '$timeout'];\n"
  },
  {
    "path": "testapp/ng1/repeater/repeater.html",
    "content": "<p>A series of repeaters used in tests.</p>\n<ul class=\"allinfo\"><li ng-repeat=\"allinfo in days\">\n  <span>{{allinfo.initial}}</span>\n  <span>{{allinfo.name}}</span>\n</li></ul>\n<ul><li ng-repeat=\"baz in days | filter:'T'\">\n  <span>{{baz.initial}}</span>\n</li></ul>\n<ul><li ng-repeat=\"baz in tDays = (days | filter:'T')\">\n  <span>{{baz.initial}}</span>\n</li></ul>\n<ul><li data-ng-repeat=\"day in days\">\n  <span>{{day.initial}}</span>\n</li></ul>\n<ul><li ng:repeat=\"bar in days\">\n  <span>{{bar.initial}}</span>\n</li></ul>\n<div ng-repeat-start=\"bloop in days\">\n  <span>{{bloop.initial}}</span>\n</div>\n<span>-</span>\n<div ng-repeat-end>\n  <span>{{bloop.name}}</span>\n</div>\n"
  },
  {
    "path": "testapp/ng1/repeater/repeater.js",
    "content": "function RepeaterCtrl($scope) {\n  $scope.days = [\n    {initial: 'M', name: 'Monday'},\n    {initial: 'T', name: 'Tuesday'},\n    {initial: 'W', name: 'Wednesday'},\n    {initial: 'Th', name: 'Thursday'},\n    {initial: 'F', name: 'Friday'}\n  ];\n}\n\nRepeaterCtrl.$inject = ['$scope'];\n"
  },
  {
    "path": "testapp/ng1/shadow/shadow.html",
    "content": "<H1>Elements in shadow DOM</H1>\n<p>Inspired by ChromeDriver's page for shadow dom webdriver tests. The page has a shadow root that in turn contains two shadow roots. So we can check behaviour with both nested roots and younger/older sibling roots.\n<div>\n  <div id=\"innerDiv\" style=\"border-style:solid;border-color:yellow\">\n    <span class='originalcontent'>original content</span>\n  </div>\n</div>\n<template id=\"parentTemplate\">\n  <div id=\"parentDiv\">\n      <div style=\"border-style:solid;border-color:green\">\n      <H3 class=\"shadowheading parentshadowheading\">Parent</H3>\n      <H4>Parent Contents</H4>\n      <content></content>\n      </div>\n  </div>\n</template>\n<template id=\"olderChildTemplate\">\n  <div id=\"olderChildDiv\">\n      <div style=\"border-style:solid;border-color:red\">\n          <H3 class=\"shadowheading oldershadowheading\">Older Child</H3>\n          <H4>Older Child Contents</H4>\n          <content></content>\n      </div>\n  </div>\n</template>\n<template id=\"youngerChildTemplate\">\n  <div id=\"youngerChildDiv\">\n      <div style=\"border-style:solid;border-color:blue\">\n          <H3 class=\"shadowheading youngershadowheading\">Younger Child</H3>\n          <div style=\"border-style:dotted;border-color:blue\">\n              <H4>\n                Younger Child Contents\n              </H4>\n              <content></content>\n          </div>\n          <div style=\"border-style:dashed;border-color:blue\">\n              <H4>Younger Child Shadow</H4>\n              <shadow></shadow>\n          </div>\n      </div>\n  </div>\n</template>\n"
  },
  {
    "path": "testapp/ng1/shadow/shadow.js",
    "content": "function ShadowCtrl($scope) {\n  // This is terrible Angular.js style, do not put DOM manipulation inside\n  // controllers like this.\n  var parentShadowRoot = document.querySelector('#innerDiv')\n      .createShadowRoot();\n  parentShadowRoot.appendChild(document.querySelector('#parentTemplate')\n      .content.cloneNode(true));\n  var olderShadowRoot = parentShadowRoot.querySelector(\"#parentDiv\")\n      .createShadowRoot();\n  olderShadowRoot.appendChild(document.querySelector('#olderChildTemplate')\n      .content.cloneNode(true));\n  var youngerShadowRoot = parentShadowRoot.querySelector(\"#parentDiv\")\n      .createShadowRoot();\n  youngerShadowRoot.appendChild(document.querySelector('#youngerChildTemplate')\n      .content.cloneNode(true));\n}\n\nRepeaterCtrl.$inject = ['$scope'];\n"
  },
  {
    "path": "testapp/ng2/app/app.component.html",
    "content": "<div class=\"container\">\n  <h1 class=\"title\">Test App for Angular 2</h1>\n  <nav class=\"nav\">\n    <a routerLink=\"/\">home</a>\n    <a routerLink=\"/async\">async</a>\n  </nav>\n  <router-outlet></router-outlet>\n</div>\n"
  },
  {
    "path": "testapp/ng2/app/app.component.js",
    "content": "\"use strict\";\nvar __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {\n    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n    if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n    return c > 3 && r && Object.defineProperty(target, key, r), r;\n};\nvar __metadata = (this && this.__metadata) || function (k, v) {\n    if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(k, v);\n};\nvar core_1 = require('@angular/core');\nvar AppComponent = (function () {\n    function AppComponent() {\n    }\n    AppComponent = __decorate([\n        core_1.Component({\n            selector: 'my-app',\n            templateUrl: 'app/app.component.html'\n        }), \n        __metadata('design:paramtypes', [])\n    ], AppComponent);\n    return AppComponent;\n}());\nexports.AppComponent = AppComponent;\n//# sourceMappingURL=app.component.js.map"
  },
  {
    "path": "testapp/ng2/app/app.component.ts",
    "content": "import { Component } from '@angular/core';\n\n@Component({\n  selector: 'my-app',\n  templateUrl: 'app/app.component.html'\n})\nexport class AppComponent {\n\n}\n"
  },
  {
    "path": "testapp/ng2/app/app.module.js",
    "content": "\"use strict\";\nvar __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {\n    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n    if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n    return c > 3 && r && Object.defineProperty(target, key, r), r;\n};\nvar __metadata = (this && this.__metadata) || function (k, v) {\n    if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(k, v);\n};\nvar core_1 = require('@angular/core');\nvar platform_browser_1 = require('@angular/platform-browser');\nvar app_routes_1 = require('./app.routes');\nvar app_component_1 = require('./app.component');\nvar home_component_1 = require('./home/home.component');\nvar async_component_1 = require('./async/async.component');\nvar AppModule = (function () {\n    function AppModule() {\n    }\n    AppModule = __decorate([\n        core_1.NgModule({\n            imports: [\n                platform_browser_1.BrowserModule,\n                app_routes_1.appRouting\n            ],\n            declarations: [\n                app_component_1.AppComponent,\n                home_component_1.HomeComponent,\n                async_component_1.AsyncComponent,\n            ],\n            bootstrap: [app_component_1.AppComponent]\n        }), \n        __metadata('design:paramtypes', [])\n    ], AppModule);\n    return AppModule;\n}());\nexports.AppModule = AppModule;\n//# sourceMappingURL=app.module.js.map"
  },
  {
    "path": "testapp/ng2/app/app.module.ts",
    "content": "import { NgModule } from '@angular/core';\nimport { BrowserModule } from '@angular/platform-browser';\nimport { appRouting } from './app.routes';\nimport { AppComponent } from './app.component';\nimport { HomeComponent } from './home/home.component';\nimport { AsyncComponent } from './async/async.component';\n\n@NgModule({\n  imports: [\n    BrowserModule,\n    appRouting\n  ],\n  declarations: [\n    AppComponent,\n    HomeComponent,\n    AsyncComponent,\n  ],\n  bootstrap: [AppComponent]\n})\nexport class AppModule {}\n"
  },
  {
    "path": "testapp/ng2/app/app.routes.js",
    "content": "\"use strict\";\nvar router_1 = require('@angular/router');\nvar home_component_1 = require('./home/home.component');\nvar async_component_1 = require('./async/async.component');\nexports.routes = [\n    { path: '', component: home_component_1.HomeComponent },\n    { path: 'async', component: async_component_1.AsyncComponent }\n];\nexports.appRouting = router_1.RouterModule.forRoot(exports.routes, { useHash: true });\n//# sourceMappingURL=app.routes.js.map"
  },
  {
    "path": "testapp/ng2/app/app.routes.ts",
    "content": "import { Routes, RouterModule } from '@angular/router';\nimport { HomeComponent } from './home/home.component';\nimport { AsyncComponent } from './async/async.component';\n\nexport const routes: Routes = [\n  { path: '', component: HomeComponent },\n  { path: 'async', component: AsyncComponent }\n];\n\nexport const appRouting = RouterModule.forRoot(routes, { useHash: true });\n"
  },
  {
    "path": "testapp/ng2/app/async/async.component.html",
    "content": "<div id='increment'>\n  <span class='val'>{{val1}}</span>\n  <button class='action' (click)=\"increment()\">Increment</button>\n</div>\n<div id='delayedIncrement'>\n  <span class='val'>{{val2}}</span>\n  <button class='action' (click)=\"delayedIncrement()\">Delayed Increment</button>\n  <button class='cancel' *ngIf=\"timeoutId != null\"\n    (click)=\"cancelDelayedIncrement()\">Cancel</button>\n</div>\n<div id='chainedDelayedIncrements'>\n  <span class='val'>{{val3}}</span>\n  <button class='action'\n    (click)=\"chainedDelayedIncrements(10)\">10 Delayed Increments</button>\n  <button class='cancel' *ngIf=\"chainedTimeoutId != null\"\n    (click)=\"cancelChainedDelayedIncrements()\">Cancel</button>\n</div>\n<div id='periodicIncrement'>\n  <span class='val'>{{val4}}</span>\n  <button class='action'\n    (click)=\"periodicIncrement()\">Periodic Increment</button>\n  <button class='cancel' *ngIf=\"intervalId != null\"\n    (click)=\"cancelPeriodicIncrement()\">Cancel</button>\n</div>\n<div id='periodicIncrement_unzoned'>\n  <span class='val'>{{val5}}</span>\n  <button class='action'\n    (click)=\"periodicIncrement_unzoned()\">Periodic Increment (outside NgZone)</button>\n  <button class='cancel' *ngIf=\"intervalId_unzoned != null\"\n    (click)=\"cancelPeriodicIncrement_unzoned()\">Cancel</button>\n</div>\n"
  },
  {
    "path": "testapp/ng2/app/async/async.component.js",
    "content": "\"use strict\";\nvar __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {\n    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n    if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n    return c > 3 && r && Object.defineProperty(target, key, r), r;\n};\nvar __metadata = (this && this.__metadata) || function (k, v) {\n    if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(k, v);\n};\nvar core_1 = require('@angular/core');\nvar AsyncComponent = (function () {\n    function AsyncComponent(_ngZone) {\n        this._ngZone = _ngZone;\n        this.val1 = 0;\n        this.val2 = 0;\n        this.val3 = 0;\n        this.val4 = 0;\n        this.val5 = 0;\n        this.timeoutId = null;\n        this.chainedTimeoutId = null;\n        this.intervalId = null;\n        this.intervalId_unzoned = null;\n    }\n    ;\n    AsyncComponent.prototype.increment = function () { this.val1++; };\n    ;\n    AsyncComponent.prototype.delayedIncrement = function () {\n        var _this = this;\n        this.cancelDelayedIncrement();\n        this.timeoutId = setTimeout(function () {\n            _this.val2++;\n            _this.timeoutId = null;\n        }, 2000);\n    };\n    ;\n    AsyncComponent.prototype.chainedDelayedIncrements = function (i) {\n        this.cancelChainedDelayedIncrements();\n        var self = this;\n        function helper(_i) {\n            if (_i <= 0) {\n                self.chainedTimeoutId = null;\n                return;\n            }\n            self.chainedTimeoutId = setTimeout(function () {\n                self.val3++;\n                helper(_i - 1);\n            }, 500);\n        }\n        helper(i);\n    };\n    ;\n    AsyncComponent.prototype.periodicIncrement = function () {\n        var _this = this;\n        this.cancelPeriodicIncrement();\n        this.intervalId = setInterval(function () { _this.val4++; }, 2000);\n    };\n    ;\n    AsyncComponent.prototype.periodicIncrement_unzoned = function () {\n        var _this = this;\n        this.cancelPeriodicIncrement_unzoned();\n        this._ngZone.runOutsideAngular(function () {\n            _this.intervalId_unzoned = setInterval(function () {\n                _this._ngZone.run(function () {\n                    _this.val5++;\n                });\n            }, 2000);\n        });\n    };\n    ;\n    AsyncComponent.prototype.cancelDelayedIncrement = function () {\n        if (this.timeoutId != null) {\n            clearTimeout(this.timeoutId);\n            this.timeoutId = null;\n        }\n    };\n    ;\n    AsyncComponent.prototype.cancelChainedDelayedIncrements = function () {\n        if (this.chainedTimeoutId != null) {\n            clearTimeout(this.chainedTimeoutId);\n            this.chainedTimeoutId = null;\n        }\n    };\n    ;\n    AsyncComponent.prototype.cancelPeriodicIncrement = function () {\n        if (this.intervalId != null) {\n            clearInterval(this.intervalId);\n            this.intervalId = null;\n        }\n    };\n    ;\n    AsyncComponent.prototype.cancelPeriodicIncrement_unzoned = function () {\n        if (this.intervalId_unzoned != null) {\n            clearInterval(this.intervalId_unzoned);\n            this.intervalId_unzoned = null;\n        }\n    };\n    ;\n    AsyncComponent = __decorate([\n        core_1.Component({\n            templateUrl: 'app/async/async.component.html',\n        }), \n        __metadata('design:paramtypes', [core_1.NgZone])\n    ], AsyncComponent);\n    return AsyncComponent;\n}());\nexports.AsyncComponent = AsyncComponent;\n//# sourceMappingURL=async.component.js.map"
  },
  {
    "path": "testapp/ng2/app/async/async.component.ts",
    "content": "import { Component, NgZone } from '@angular/core';\n\n@Component({\n  templateUrl: 'app/async/async.component.html',\n})\nexport class AsyncComponent {\n  val1: number = 0;\n  val2: number = 0;\n  val3: number = 0;\n  val4: number = 0;\n  val5: number = 0;\n  timeoutId = null;\n  chainedTimeoutId = null;\n  intervalId = null;\n  intervalId_unzoned = null;\n\n  constructor(private _ngZone: NgZone) {};\n\n  increment(): void { this.val1++; };\n\n  delayedIncrement(): void {\n    this.cancelDelayedIncrement();\n    this.timeoutId = setTimeout(() => {\n      this.val2++;\n      this.timeoutId = null;\n    }, 2000);\n  };\n\n  chainedDelayedIncrements(i: number): void {\n    this.cancelChainedDelayedIncrements();\n\n    var self = this;\n    function helper(_i) {\n      if (_i <= 0) {\n        self.chainedTimeoutId = null;\n        return;\n      }\n\n      self.chainedTimeoutId = setTimeout(() => {\n        self.val3++;\n        helper(_i - 1);\n      }, 500);\n    }\n    helper(i);\n  };\n\n  periodicIncrement(): void {\n    this.cancelPeriodicIncrement();\n    this.intervalId = setInterval(() => { this.val4++; }, 2000)\n  };\n\n  periodicIncrement_unzoned(): void {\n    this.cancelPeriodicIncrement_unzoned();\n    this._ngZone.runOutsideAngular(() => {\n      this.intervalId_unzoned = setInterval(() => {\n        this._ngZone.run(() => {\n          this.val5++;\n        });\n      }, 2000)\n    });\n  };\n\n  cancelDelayedIncrement(): void {\n    if (this.timeoutId != null) {\n      clearTimeout(this.timeoutId);\n      this.timeoutId = null;\n    }\n  };\n\n  cancelChainedDelayedIncrements(): void {\n    if (this.chainedTimeoutId != null) {\n      clearTimeout(this.chainedTimeoutId);\n      this.chainedTimeoutId = null;\n    }\n  };\n\n  cancelPeriodicIncrement(): void {\n    if (this.intervalId != null) {\n      clearInterval(this.intervalId);\n      this.intervalId = null;\n    }\n  };\n\n  cancelPeriodicIncrement_unzoned(): void {\n    if (this.intervalId_unzoned != null) {\n      clearInterval(this.intervalId_unzoned);\n      this.intervalId_unzoned = null;\n    }\n  };\n}\n"
  },
  {
    "path": "testapp/ng2/app/home/home.component.html",
    "content": "this is the home\n"
  },
  {
    "path": "testapp/ng2/app/home/home.component.js",
    "content": "\"use strict\";\nvar __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {\n    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n    if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n    return c > 3 && r && Object.defineProperty(target, key, r), r;\n};\nvar __metadata = (this && this.__metadata) || function (k, v) {\n    if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(k, v);\n};\nvar core_1 = require('@angular/core');\nvar HomeComponent = (function () {\n    function HomeComponent() {\n    }\n    HomeComponent = __decorate([\n        core_1.Component({\n            templateUrl: 'app/home/home.component.html'\n        }), \n        __metadata('design:paramtypes', [])\n    ], HomeComponent);\n    return HomeComponent;\n}());\nexports.HomeComponent = HomeComponent;\n//# sourceMappingURL=home.component.js.map"
  },
  {
    "path": "testapp/ng2/app/home/home.component.ts",
    "content": "import { Component } from '@angular/core';\n\n@Component({\n  templateUrl: 'app/home/home.component.html'\n})\nexport class HomeComponent {\n\n}\n"
  },
  {
    "path": "testapp/ng2/app/main.js",
    "content": "\"use strict\";\nvar platform_browser_dynamic_1 = require('@angular/platform-browser-dynamic');\nvar app_module_1 = require('./app.module');\nplatform_browser_dynamic_1.platformBrowserDynamic().bootstrapModule(app_module_1.AppModule);\n//# sourceMappingURL=main.js.map"
  },
  {
    "path": "testapp/ng2/app/main.ts",
    "content": "import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';\nimport { AppModule } from './app.module';\n\nplatformBrowserDynamic().bootstrapModule(AppModule);\n"
  },
  {
    "path": "testapp/ng2/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <!-- Set the base href -->\n    <base href=\"/ng2/\">\n\n    <title>Test App</title>\n    <link href=\"https://fonts.googleapis.com/css?family=Roboto:400,300,500,400italic,700\" rel=\"stylesheet\" type=\"text/css\">\n    <link rel=\"stylesheet\" href=\"styles.css\">\n    <!-- IE required polyfills, in this exact order -->\n    <script src=\"../node_modules/core-js/client/shim.min.js\"></script>\n    <script src=\"../node_modules/reflect-metadata/Reflect.js\"></script>\n    <script src=\"../node_modules/systemjs/dist/system-polyfills.js\"></script>\n    <script src=\"../node_modules/systemjs/dist/system.src.js\"></script>\n    <script src=\"../node_modules/zone.js/dist/zone.js\"></script>\n\n    <script>\n      System.import('system-config.js').then(function() {\n        System.import('app/main');\n      }).catch(function(err) {\n        console.log('error while importing');\n        console.log(err);\n        console.log(err.stack);\n      });\n    </script>\n  </head>\n\n  <body>\n    <my-app>loading...</my-app>\n  </body>\n\n</html>\n\n\n<!--\nCopyright 2016 Google Inc. All Rights Reserved.\nUse of this source code is governed by an MIT-style license that\ncan be found in the LICENSE file at http://angular.io/license\n-->\n"
  },
  {
    "path": "testapp/ng2/styles.css",
    "content": "body {\n  font-family:\"Roboto\",\"Helvetica Neue Light\",\"Helvetica Neue\",Helvetica,Arial,\"Lucida Grande\",sans-serif;\n  font-size:14px;color:#1a2326\n}\n.container {\n  padding-left: 20px;\n}\n.nav {\n  padding-bottom: 20px;\n}\n"
  },
  {
    "path": "testapp/ng2/system-config.js",
    "content": "/**\n * System configuration for Angular 2 samples\n * Adjust as necessary for your application needs.\n */\n(function (global) {\n  System.config({\n    paths: {\n      // paths serve as alias\n      'npm:': '/node_modules/'\n    },\n    // map tells the System loader where to look for things\n    map: {\n      // our app is within the app folder\n      app: 'app',\n\n      // angular bundles\n      '@angular/core': 'npm:@angular/core/bundles/core.umd.js',\n      '@angular/common': 'npm:@angular/common/bundles/common.umd.js',\n      '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',\n      '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',\n      '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',\n      '@angular/http': 'npm:@angular/http/bundles/http.umd.js',\n      '@angular/router': 'npm:@angular/router/bundles/router.umd.js',\n      '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',\n\n      // other libraries\n      'rxjs':                       'npm:rxjs',\n    },\n    // packages tells the System loader how to load when no filename and/or no extension\n    packages: {\n      app: {\n        main: './main.js',\n        defaultExtension: 'js'\n      },\n      rxjs: {\n        defaultExtension: 'js'\n      },\n      'angular2-in-memory-web-api': {\n        main: './index.js',\n        defaultExtension: 'js'\n      }\n    }\n  });\n})(this);\n"
  },
  {
    "path": "testapp/package.json",
    "content": "{\n  \"name\": \"testapp\",\n  \"version\": \"0.0.1\",\n  \"scripts\": {\n    \"start\": \"npm install && node scripts/web-server.js\",\n    \"tsc\": \"tsc\"\n  },\n  \"dependencies\": {\n    \"@angular/common\": \"5.0.0-beta.7\",\n    \"@angular/compiler\": \"5.0.0-beta.7\",\n    \"@angular/core\": \"5.0.0-beta.7\",\n    \"@angular/http\": \"5.0.0-beta.7\",\n    \"@angular/platform-browser\": \"5.0.0-beta.7\",\n    \"@angular/animations\": \"5.0.0-beta.7\",\n    \"@angular/platform-browser-dynamic\": \"5.0.0-beta.7\",\n    \"@angular/router\": \"5.0.0-beta.7\",\n    \"@angular/upgrade\": \"5.0.0-beta.7\",\n    \"@types/angular\": \"^1.5.20\",\n    \"@types/core-js\": \"^0.9.34\",\n    \"@types/node\": \"^6.0.48\",\n    \"core-js\": \"2.4.1\",\n    \"reflect-metadata\": \"0.1.3\",\n    \"rxjs\": \"5.4.3\",\n    \"systemjs\": \"0.19.27\",\n    \"zone.js\": \"0.8.18\"\n  },\n  \"devDependencies\": {\n    \"concurrently\": \"2.2.0\",\n    \"express\": \"4.13.3\",\n    \"lite-server\": \"2.2.0\",\n    \"typescript\": \"^2.0.10\"\n  }\n}\n"
  },
  {
    "path": "testapp/scripts/web-server.js",
    "content": "#!/usr/bin/env node\n\nvar express = require('express');\nvar bodyParser = require('body-parser')\nvar yargs = require('yargs');\nvar util = require('util');\nvar path = require('path');\nvar env = require('../../spec/environment.js');\n\nvar testApp = express();\nvar DEFAULT_PORT = process.env.HTTP_PORT || env.webServerDefaultPort;\nvar testAppDir = path.resolve(__dirname, '..');\nvar defaultAngular = require(path.resolve(testAppDir, 'ng1/lib/angular_version.js'));\n\nvar argv = yargs.describe('port', 'port').\n    default('port', DEFAULT_PORT).\n    describe('ngversion', 'version of AngularJS to use').\n    default('ngversion', defaultAngular).\n    argv;\n\nvar angularDir = path.resolve(testAppDir, 'ng1/lib/angular_v' + argv.ngversion);\n\nvar main = function() {\n  var port = argv.port;\n  testApp.use('/ng1/lib/angular', express.static(angularDir));\n  testApp.use(express.static(testAppDir));\n  testApp.use(bodyParser.json());\n  testApp.use(testMiddleware);\n  testApp.listen(port);\n  util.puts([\"Starting express web server in\", testAppDir ,\"on port\", port].\n      join(\" \"));\n};\n\nvar storage = {};\nvar testMiddleware = function(req, res, next) {\n  if (/ng[1-2]\\/fastcall/.test(req.path)) {\n    res.status(200).send('done');\n  } else if (/ng[1-2]\\/slowcall/.test(req.path)) {\n    setTimeout(function() {\n      res.status(200).send('finally done');\n    }, 5000);\n  } else if (/ng[1-2]\\/fastTemplateUrl/.test(req.path)) {\n    res.status(200).send('fast template contents');\n  } else if (/ng[1-2]\\/slowTemplateUrl/.test(req.path)) {\n    setTimeout(function() {\n      res.status(200).send('slow template contents');\n    }, 5000);\n  } else if (/ng[1-2]\\/chat/.test(req.path)) {\n    if (req.method === 'GET') {\n      var value;\n      if (req.query.q) {\n        value = storage[req.query.q];\n        res.status(200).send(value);\n      } else {\n        res.status(400).send('must specify query');\n      }\n    } else if (req.method === 'POST') {\n      if (req.body.key == 'newChatMessage') {\n        if (!storage['chatMessages']) {\n          storage['chatMessages'] = [];\n        }\n        storage['chatMessages'].push(req.body.value);\n        res.sendStatus(200);\n      } else if (req.body.key == 'clearChatMessages') {\n        storage['chatMessages'] = [];\n        res.sendStatus(200);\n      } else {\n        res.status(400).send('Unknown command');\n      }\n    } else {\n      res.status(400).send('only accepts GET/POST');\n    }\n  } else {\n    return next();\n  }\n};\n\nmain();\n"
  },
  {
    "path": "testapp/tsconfig-aot.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\",\n    \"sourceMap\": true,\n    \"emitDecoratorMetadata\": true,\n    \"experimentalDecorators\": true,\n    \"lib\": [\"es2015\", \"dom\"],\n    \"noImplicitAny\": true,\n    \"suppressImplicitAnyIndexErrors\": true,\n    \"typeRoots\": [\n      \"./node_modules/@types/\"\n    ]\n  },\n\n  \"files\": [\n    \"upgrade/app/downgrade/main.ts\",\n    \"upgrade/app/downgrade/ng1.ts\",\n    \"upgrade/app/downgrade/ng2.ts\"\n  ]\n\n}\n"
  },
  {
    "path": "testapp/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\",\n    \"sourceMap\": true,\n    \"emitDecoratorMetadata\": true,\n    \"experimentalDecorators\": true,\n    \"removeComments\": false,\n    \"noImplicitAny\": false,\n    \"suppressImplicitAnyIndexErrors\": true,\n    \"typeRoots\": [\n      \"./node_modules/@types/\"\n    ]\n  },\n  \"compileOnSave\": true,\n  \"exclude\": [\n    \"node_modules\",\n    \"**/*-aot.ts\"\n  ]\n}\n"
  },
  {
    "path": "testapp/upgrade/app/downgrade/main.js",
    "content": "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar static_1 = require(\"@angular/upgrade/static\");\nvar ng1_1 = require(\"./ng1\");\nvar ng2_ngfactory_1 = require(\"./ng2.ngfactory\");\n// Bootstrap Ng1 app as usual, but add a downgradedModule for the Angular (2+)\n// part of the application.\nangular.bootstrap(document.body, [ng1_1.ng1module.name, static_1.downgradeModule(ng2_ngfactory_1.AppModuleNgFactory)]);\n//# sourceMappingURL=main.js.map"
  },
  {
    "path": "testapp/upgrade/app/downgrade/main.ts",
    "content": "import {downgradeModule} from '@angular/upgrade/static';\ndeclare var angular: angular.IAngularStatic;\n\nimport {ng1module} from './ng1';\nimport {AppModuleNgFactory} from './ng2.ngfactory';\n\n// Bootstrap Ng1 app as usual, but add a downgradedModule for the Angular (2+)\n// part of the application.\nangular.bootstrap(\n    document.body, [ng1module.name, downgradeModule(AppModuleNgFactory)]);\n"
  },
  {
    "path": "testapp/upgrade/app/downgrade/ng1.js",
    "content": "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nfunction ctrl($scope, $timeout) {\n    $scope.callCount = 0;\n    $scope.clickButton = function () {\n        $timeout(function () {\n            $scope.callCount++;\n        }, 1000);\n    };\n}\nctrl.$inject = ['$scope', '$timeout'];\nexports.ng1module = angular.module('hybrid', []);\nexports.ng1module.component('myApp', {\n    template: \"<h3>ng1</h3><button ng-click=\\\"clickButton()\\\">Click Count: {{callCount}}</button>\\n             <ng2></ng2>\\n            \",\n    controller: ctrl\n});\n//# sourceMappingURL=ng1.js.map"
  },
  {
    "path": "testapp/upgrade/app/downgrade/ng1.metadata.json",
    "content": "[{\"__symbolic\":\"module\",\"version\":3,\"metadata\":{\"ng1module\":{\"__symbolic\":\"error\",\"message\":\"Reference to a local symbol\",\"line\":0,\"character\":12,\"context\":{\"name\":\"angular\"}}}},{\"__symbolic\":\"module\",\"version\":1,\"metadata\":{\"ng1module\":{\"__symbolic\":\"error\",\"message\":\"Reference to a local symbol\",\"line\":0,\"character\":12,\"context\":{\"name\":\"angular\"}}}}]"
  },
  {
    "path": "testapp/upgrade/app/downgrade/ng1.ts",
    "content": "declare var angular: angular.IAngularStatic;\n\nfunction ctrl($scope: any, $timeout: any) {\n  $scope.callCount = 0;\n\n  $scope.clickButton = function() {\n    $timeout(() => {\n      $scope.callCount++;\n    }, 1000);\n  };\n}\nctrl.$inject = ['$scope', '$timeout'];\n\nexport const ng1module = angular.module('hybrid', []);\n\nng1module.component('myApp', {\n  template: `<h3>ng1</h3><button ng-click=\"clickButton()\">Click Count: {{callCount}}</button>\n             <ng2></ng2>\n            `,\n  controller: ctrl\n});\n"
  },
  {
    "path": "testapp/upgrade/app/downgrade/ng2.js",
    "content": "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar core_1 = require(\"@angular/core\");\nvar animations_1 = require(\"@angular/platform-browser/animations\");\nvar static_1 = require(\"@angular/upgrade/static\");\nvar ng1_1 = require(\"./ng1\");\nvar Ng2Component = /** @class */ (function () {\n    function Ng2Component() {\n        var _this = this;\n        this.callCount = 0;\n        this.clickButton = function () {\n            setTimeout(function () {\n                _this.callCount++;\n            }, 1000);\n        };\n    }\n    Ng2Component.decorators = [\n        { type: core_1.Component, args: [{\n                    selector: 'ng2',\n                    template: \"\\n    <h2>ng2</h2>\\n    <button (click)='clickButton()'>Click Count: {{callCount}}</button>\\n  \"\n                },] },\n    ];\n    /** @nocollapse */\n    Ng2Component.ctorParameters = function () { return []; };\n    return Ng2Component;\n}());\nexports.Ng2Component = Ng2Component;\nvar AppModule = /** @class */ (function () {\n    function AppModule() {\n    }\n    AppModule.prototype.ngDoBootstrap = function () { };\n    AppModule.decorators = [\n        { type: core_1.NgModule, args: [{\n                    imports: [\n                        animations_1.BrowserAnimationsModule,\n                    ],\n                    declarations: [\n                        Ng2Component,\n                    ],\n                    entryComponents: [\n                        Ng2Component\n                    ]\n                },] },\n    ];\n    /** @nocollapse */\n    AppModule.ctorParameters = function () { return []; };\n    return AppModule;\n}());\nexports.AppModule = AppModule;\n// Register the Angular 2 component with the Angular 1 module.\nng1_1.ng1module.directive('ng2', // lowerCamel when registering.\n// propagateDigest: false will detach the digest cycle from AngularJS from\n// propagating into the Angular (2+) CD.\nstatic_1.downgradeComponent({ component: Ng2Component, propagateDigest: false }));\n//# sourceMappingURL=ng2.js.map"
  },
  {
    "path": "testapp/upgrade/app/downgrade/ng2.metadata.json",
    "content": "[{\"__symbolic\":\"module\",\"version\":3,\"metadata\":{\"Ng2Component\":{\"__symbolic\":\"class\",\"decorators\":[{\"__symbolic\":\"call\",\"expression\":{\"__symbolic\":\"reference\",\"module\":\"@angular/core\",\"name\":\"Component\"},\"arguments\":[{\"selector\":\"ng2\",\"template\":\"\\n    <h2>ng2</h2>\\n    <button (click)='clickButton()'>Click Count: {{callCount}}</button>\\n  \"}]}]},\"AppModule\":{\"__symbolic\":\"class\",\"decorators\":[{\"__symbolic\":\"call\",\"expression\":{\"__symbolic\":\"reference\",\"module\":\"@angular/core\",\"name\":\"NgModule\"},\"arguments\":[{\"imports\":[{\"__symbolic\":\"reference\",\"module\":\"@angular/platform-browser/animations\",\"name\":\"BrowserAnimationsModule\"}],\"declarations\":[{\"__symbolic\":\"reference\",\"name\":\"Ng2Component\"}],\"entryComponents\":[{\"__symbolic\":\"reference\",\"name\":\"Ng2Component\"}]}]}],\"members\":{\"ngDoBootstrap\":[{\"__symbolic\":\"method\"}]}}}},{\"__symbolic\":\"module\",\"version\":1,\"metadata\":{\"Ng2Component\":{\"__symbolic\":\"class\",\"decorators\":[{\"__symbolic\":\"call\",\"expression\":{\"__symbolic\":\"reference\",\"module\":\"@angular/core\",\"name\":\"Component\"},\"arguments\":[{\"selector\":\"ng2\",\"template\":\"\\n    <h2>ng2</h2>\\n    <button (click)='clickButton()'>Click Count: {{callCount}}</button>\\n  \"}]}]},\"AppModule\":{\"__symbolic\":\"class\",\"decorators\":[{\"__symbolic\":\"call\",\"expression\":{\"__symbolic\":\"reference\",\"module\":\"@angular/core\",\"name\":\"NgModule\"},\"arguments\":[{\"imports\":[{\"__symbolic\":\"reference\",\"module\":\"@angular/platform-browser/animations\",\"name\":\"BrowserAnimationsModule\"}],\"declarations\":[{\"__symbolic\":\"reference\",\"name\":\"Ng2Component\"}],\"entryComponents\":[{\"__symbolic\":\"reference\",\"name\":\"Ng2Component\"}]}]}],\"members\":{\"ngDoBootstrap\":[{\"__symbolic\":\"method\"}]}}}}]"
  },
  {
    "path": "testapp/upgrade/app/downgrade/ng2.ngfactory.js",
    "content": "\"use strict\";\n/**\n * @fileoverview This file is generated by the Angular template compiler.\n * Do not edit.\n * @suppress {suspiciousCode,uselessCode,missingProperties,missingOverride}\n */\n/* tslint:disable */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar i0 = require(\"@angular/core\");\nvar i1 = require(\"./ng2\");\nvar i2 = require(\"@angular/common\");\nvar i3 = require(\"@angular/platform-browser\");\nvar i4 = require(\"@angular/animations/browser\");\nvar i5 = require(\"@angular/platform-browser/animations\");\nvar i6 = require(\"@angular/animations\");\nexports.AppModuleNgFactory = i0.ɵcmf(i1.AppModule, [], function (_l) {\n    return i0.ɵmod([i0.ɵmpd(512, i0.ComponentFactoryResolver, i0.ɵCodegenComponentFactoryResolver, [[8, [exports.Ng2ComponentNgFactory]], [3, i0.ComponentFactoryResolver], i0.NgModuleRef]),\n        i0.ɵmpd(5120, i0.LOCALE_ID, i0.ɵo, [[3, i0.LOCALE_ID]]), i0.ɵmpd(4608, i2.NgLocalization, i2.NgLocaleLocalization, [i0.LOCALE_ID, [2, i2.ɵa]]), i0.ɵmpd(4608, i0.Compiler, i0.Compiler, []), i0.ɵmpd(5120, i0.APP_ID, i0.ɵh, []),\n        i0.ɵmpd(5120, i0.IterableDiffers, i0.ɵm, []), i0.ɵmpd(5120, i0.KeyValueDiffers, i0.ɵn, []), i0.ɵmpd(4608, i3.DomSanitizer, i3.ɵd, [i2.DOCUMENT]),\n        i0.ɵmpd(6144, i0.Sanitizer, null, [i3.DomSanitizer]), i0.ɵmpd(4608, i3.HAMMER_GESTURE_CONFIG, i3.HammerGestureConfig, []), i0.ɵmpd(5120, i3.EVENT_MANAGER_PLUGINS, function (p0_0, p0_1, p1_0, p2_0, p2_1) {\n            return [new i3.ɵDomEventsPlugin(p0_0, p0_1), new i3.ɵKeyEventsPlugin(p1_0),\n                new i3.ɵHammerGesturesPlugin(p2_0, p2_1)];\n        }, [i2.DOCUMENT, i0.NgZone, i2.DOCUMENT, i2.DOCUMENT, i3.HAMMER_GESTURE_CONFIG]),\n        i0.ɵmpd(4608, i3.EventManager, i3.EventManager, [i3.EVENT_MANAGER_PLUGINS, i0.NgZone]),\n        i0.ɵmpd(135680, i3.ɵDomSharedStylesHost, i3.ɵDomSharedStylesHost, [i2.DOCUMENT]),\n        i0.ɵmpd(4608, i3.ɵDomRendererFactory2, i3.ɵDomRendererFactory2, [i3.EventManager,\n            i3.ɵDomSharedStylesHost]), i0.ɵmpd(5120, i4.AnimationDriver, i5.ɵc, []),\n        i0.ɵmpd(5120, i4.ɵAnimationStyleNormalizer, i5.ɵd, []), i0.ɵmpd(4608, i4.ɵAnimationEngine, i5.ɵb, [i4.AnimationDriver, i4.ɵAnimationStyleNormalizer]),\n        i0.ɵmpd(5120, i0.RendererFactory2, i5.ɵe, [i3.ɵDomRendererFactory2, i4.ɵAnimationEngine,\n            i0.NgZone]), i0.ɵmpd(6144, i3.ɵSharedStylesHost, null, [i3.ɵDomSharedStylesHost]),\n        i0.ɵmpd(4608, i0.Testability, i0.Testability, [i0.NgZone]), i0.ɵmpd(4608, i3.Meta, i3.Meta, [i2.DOCUMENT]), i0.ɵmpd(4608, i3.Title, i3.Title, [i2.DOCUMENT]),\n        i0.ɵmpd(4608, i6.AnimationBuilder, i5.ɵBrowserAnimationBuilder, [i0.RendererFactory2,\n            i3.DOCUMENT]), i0.ɵmpd(512, i2.CommonModule, i2.CommonModule, []),\n        i0.ɵmpd(1024, i0.ErrorHandler, i3.ɵa, []), i0.ɵmpd(1024, i0.APP_INITIALIZER, function (p0_0) {\n            return [i3.ɵg(p0_0)];\n        }, [[2, i0.NgProbeToken]]), i0.ɵmpd(512, i0.ApplicationInitStatus, i0.ApplicationInitStatus, [[2, i0.APP_INITIALIZER]]), i0.ɵmpd(131584, i0.ɵg, i0.ɵg, [i0.NgZone, i0.ɵConsole,\n            i0.Injector, i0.ErrorHandler, i0.ComponentFactoryResolver, i0.ApplicationInitStatus]),\n        i0.ɵmpd(2048, i0.ApplicationRef, null, [i0.ɵg]), i0.ɵmpd(512, i0.ApplicationModule, i0.ApplicationModule, [i0.ApplicationRef]), i0.ɵmpd(512, i3.BrowserModule, i3.BrowserModule, [[3, i3.BrowserModule]]), i0.ɵmpd(512, i5.BrowserAnimationsModule, i5.BrowserAnimationsModule, []), i0.ɵmpd(512, i1.AppModule, i1.AppModule, [])]);\n});\nvar styles_Ng2Component = [];\nexports.RenderType_Ng2Component = i0.ɵcrt({ encapsulation: 2, styles: styles_Ng2Component,\n    data: {} });\nfunction View_Ng2Component_0(_l) {\n    return i0.ɵvid(0, [(_l()(), i0.ɵted(null, ['\\n    '])), (_l()(), i0.ɵeld(0, null, null, 1, 'h2', [], null, null, null, null, null)), (_l()(), i0.ɵted(null, ['ng2'])), (_l()(),\n            i0.ɵted(null, ['\\n    '])), (_l()(), i0.ɵeld(0, null, null, 1, 'button', [], null, [[null, 'click']], function (_v, en, $event) {\n            var ad = true;\n            var _co = _v.component;\n            if (('click' === en)) {\n                var pd_0 = (_co.clickButton() !== false);\n                ad = (pd_0 && ad);\n            }\n            return ad;\n        }, null, null)), (_l()(), i0.ɵted(null, ['Click Count: ',\n            ''])), (_l()(), i0.ɵted(null, ['\\n  ']))], null, function (_ck, _v) {\n        var _co = _v.component;\n        var currVal_0 = _co.callCount;\n        _ck(_v, 5, 0, currVal_0);\n    });\n}\nexports.View_Ng2Component_0 = View_Ng2Component_0;\nfunction View_Ng2Component_Host_0(_l) {\n    return i0.ɵvid(0, [(_l()(), i0.ɵeld(0, null, null, 1, 'ng2', [], null, null, null, View_Ng2Component_0, exports.RenderType_Ng2Component)),\n        i0.ɵdid(49152, null, 0, i1.Ng2Component, [], null, null)], null, null);\n}\nexports.View_Ng2Component_Host_0 = View_Ng2Component_Host_0;\nexports.Ng2ComponentNgFactory = i0.ɵccf('ng2', i1.Ng2Component, View_Ng2Component_Host_0, {}, {}, []);\n//# sourceMappingURL=ng2.ngfactory.js.map"
  },
  {
    "path": "testapp/upgrade/app/downgrade/ng2.ngfactory.ts",
    "content": "/**\n * @fileoverview This file is generated by the Angular template compiler.\n * Do not edit.\n * @suppress {suspiciousCode,uselessCode,missingProperties,missingOverride}\n */\n /* tslint:disable */\n\n\nimport * as i0 from '@angular/core';\nimport * as i1 from './ng2';\nimport * as i2 from '@angular/common';\nimport * as i3 from '@angular/platform-browser';\nimport * as i4 from '@angular/animations/browser';\nimport * as i5 from '@angular/platform-browser/animations';\nimport * as i6 from '@angular/animations';\nexport const AppModuleNgFactory:i0.NgModuleFactory<i1.AppModule> = i0.ɵcmf(i1.AppModule,\n    ([] as any[]),(_l:any) => {\n      return i0.ɵmod([i0.ɵmpd(512,i0.ComponentFactoryResolver,i0.ɵCodegenComponentFactoryResolver,\n          [[8,[Ng2ComponentNgFactory]],[3,i0.ComponentFactoryResolver],i0.NgModuleRef]),\n          i0.ɵmpd(5120,i0.LOCALE_ID,i0.ɵo,[[3,i0.LOCALE_ID]]),i0.ɵmpd(4608,i2.NgLocalization,\n              i2.NgLocaleLocalization,[i0.LOCALE_ID,[2,i2.ɵa]]),i0.ɵmpd(4608,i0.Compiler,\n              i0.Compiler,([] as any[])),i0.ɵmpd(5120,i0.APP_ID,i0.ɵh,([] as any[])),\n          i0.ɵmpd(5120,i0.IterableDiffers,i0.ɵm,([] as any[])),i0.ɵmpd(5120,i0.KeyValueDiffers,\n              i0.ɵn,([] as any[])),i0.ɵmpd(4608,i3.DomSanitizer,i3.ɵd,[i2.DOCUMENT]),\n          i0.ɵmpd(6144,i0.Sanitizer,(null as any),[i3.DomSanitizer]),i0.ɵmpd(4608,\n              i3.HAMMER_GESTURE_CONFIG,i3.HammerGestureConfig,([] as any[])),i0.ɵmpd(5120,\n              i3.EVENT_MANAGER_PLUGINS,(p0_0:any,p0_1:any,p1_0:any,p2_0:any,p2_1:any) => {\n                return [new i3.ɵDomEventsPlugin(p0_0,p0_1),new i3.ɵKeyEventsPlugin(p1_0),\n                    new i3.ɵHammerGesturesPlugin(p2_0,p2_1)];\n              },[i2.DOCUMENT,i0.NgZone,i2.DOCUMENT,i2.DOCUMENT,i3.HAMMER_GESTURE_CONFIG]),\n          i0.ɵmpd(4608,i3.EventManager,i3.EventManager,[i3.EVENT_MANAGER_PLUGINS,i0.NgZone]),\n          i0.ɵmpd(135680,i3.ɵDomSharedStylesHost,i3.ɵDomSharedStylesHost,[i2.DOCUMENT]),\n          i0.ɵmpd(4608,i3.ɵDomRendererFactory2,i3.ɵDomRendererFactory2,[i3.EventManager,\n              i3.ɵDomSharedStylesHost]),i0.ɵmpd(5120,i4.AnimationDriver,i5.ɵc,([] as any[])),\n          i0.ɵmpd(5120,i4.ɵAnimationStyleNormalizer,i5.ɵd,([] as any[])),i0.ɵmpd(4608,\n              i4.ɵAnimationEngine,i5.ɵb,[i4.AnimationDriver,i4.ɵAnimationStyleNormalizer]),\n          i0.ɵmpd(5120,i0.RendererFactory2,i5.ɵe,[i3.ɵDomRendererFactory2,i4.ɵAnimationEngine,\n              i0.NgZone]),i0.ɵmpd(6144,i3.ɵSharedStylesHost,(null as any),[i3.ɵDomSharedStylesHost]),\n          i0.ɵmpd(4608,i0.Testability,i0.Testability,[i0.NgZone]),i0.ɵmpd(4608,i3.Meta,\n              i3.Meta,[i2.DOCUMENT]),i0.ɵmpd(4608,i3.Title,i3.Title,[i2.DOCUMENT]),\n          i0.ɵmpd(4608,i6.AnimationBuilder,i5.ɵBrowserAnimationBuilder,[i0.RendererFactory2,\n              i3.DOCUMENT]),i0.ɵmpd(512,i2.CommonModule,i2.CommonModule,([] as any[])),\n          i0.ɵmpd(1024,i0.ErrorHandler,i3.ɵa,([] as any[])),i0.ɵmpd(1024,i0.APP_INITIALIZER,\n              (p0_0:any) => {\n                return [i3.ɵg(p0_0)];\n              },[[2,i0.NgProbeToken]]),i0.ɵmpd(512,i0.ApplicationInitStatus,i0.ApplicationInitStatus,\n              [[2,i0.APP_INITIALIZER]]),i0.ɵmpd(131584,i0.ɵg,i0.ɵg,[i0.NgZone,i0.ɵConsole,\n              i0.Injector,i0.ErrorHandler,i0.ComponentFactoryResolver,i0.ApplicationInitStatus]),\n          i0.ɵmpd(2048,i0.ApplicationRef,(null as any),[i0.ɵg]),i0.ɵmpd(512,i0.ApplicationModule,\n              i0.ApplicationModule,[i0.ApplicationRef]),i0.ɵmpd(512,i3.BrowserModule,\n              i3.BrowserModule,[[3,i3.BrowserModule]]),i0.ɵmpd(512,i5.BrowserAnimationsModule,\n              i5.BrowserAnimationsModule,([] as any[])),i0.ɵmpd(512,i1.AppModule,i1.AppModule,\n              ([] as any[]))]);\n    });\nconst styles_Ng2Component:any[] = ([] as any[]);\nexport const RenderType_Ng2Component:i0.RendererType2 = i0.ɵcrt({encapsulation:2,styles:styles_Ng2Component,\n    data:{}});\nexport function View_Ng2Component_0(_l:any):i0.ɵViewDefinition {\n  return i0.ɵvid(0,[(_l()(),i0.ɵted((null as any),['\\n    '])),(_l()(),i0.ɵeld(0,(null as any),\n      (null as any),1,'h2',([] as any[]),(null as any),(null as any),(null as any),\n      (null as any),(null as any))),(_l()(),i0.ɵted((null as any),['ng2'])),(_l()(),\n      i0.ɵted((null as any),['\\n    '])),(_l()(),i0.ɵeld(0,(null as any),(null as any),\n      1,'button',([] as any[]),(null as any),[[(null as any),'click']],(_v,en,$event) => {\n        var ad:boolean = true;\n        var _co:i1.Ng2Component = _v.component;\n        if (('click' === en)) {\n          const pd_0:any = ((<any>_co.clickButton()) !== false);\n          ad = (pd_0 && ad);\n        }\n        return ad;\n      },(null as any),(null as any))),(_l()(),i0.ɵted((null as any),['Click Count: ',\n      ''])),(_l()(),i0.ɵted((null as any),['\\n  ']))],(null as any),(_ck,_v) => {\n    var _co:i1.Ng2Component = _v.component;\n    const currVal_0:any = _co.callCount;\n    _ck(_v,5,0,currVal_0);\n  });\n}\nexport function View_Ng2Component_Host_0(_l:any):i0.ɵViewDefinition {\n  return i0.ɵvid(0,[(_l()(),i0.ɵeld(0,(null as any),(null as any),1,'ng2',([] as any[]),\n      (null as any),(null as any),(null as any),View_Ng2Component_0,RenderType_Ng2Component)),\n      i0.ɵdid(49152,(null as any),0,i1.Ng2Component,([] as any[]),(null as any),(null as any))],\n      (null as any),(null as any));\n}\nexport const Ng2ComponentNgFactory:i0.ComponentFactory<i1.Ng2Component> = i0.ɵccf('ng2',\n    i1.Ng2Component,View_Ng2Component_Host_0,{},{},([] as any[]));\n//# sourceMappingURL=data:application/json;base64,eyJmaWxlIjoiL3Vzci9sb2NhbC9nb29nbGUvaG9tZS9xaXlpL3Byb3RyYWN0b3IvdGVzdGFwcC91cGdyYWRlL2FwcC9kb3duZ3JhZGUvbmcyLm5nZmFjdG9yeS50cyIsInZlcnNpb24iOjMsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIm5nOi8vL3Vzci9sb2NhbC9nb29nbGUvaG9tZS9xaXlpL3Byb3RyYWN0b3IvdGVzdGFwcC91cGdyYWRlL2FwcC9kb3duZ3JhZGUvbmcyLnRzIiwibmc6Ly8vdXNyL2xvY2FsL2dvb2dsZS9ob21lL3FpeWkvcHJvdHJhY3Rvci90ZXN0YXBwL3VwZ3JhZGUvYXBwL2Rvd25ncmFkZS9uZzIudHMuTmcyQ29tcG9uZW50Lmh0bWwiLCJuZzovLy91c3IvbG9jYWwvZ29vZ2xlL2hvbWUvcWl5aS9wcm90cmFjdG9yL3Rlc3RhcHAvdXBncmFkZS9hcHAvZG93bmdyYWRlL25nMi50cy5OZzJDb21wb25lbnRfSG9zdC5odG1sIl0sInNvdXJjZXNDb250ZW50IjpbIiAiLCJcbiAgICA8aDI+bmcyPC9oMj5cbiAgICA8YnV0dG9uIChjbGljayk9J2NsaWNrQnV0dG9uKCknPkNsaWNrIENvdW50OiB7e2NhbGxDb3VudH19PC9idXR0b24+XG4gICIsIjxuZzI+PC9uZzI+Il0sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztvQkNBQSwyQ0FDSTtNQUFBO01BQUEsOEJBQUksd0NBQVE7YUFBQSw0QkFDWjtNQUFBO1FBQUE7UUFBQTtRQUFRO1VBQUE7VUFBQTtRQUFBO1FBQVI7TUFBQSxnQ0FBZ0M7TUFBQSxNQUFtQzs7SUFBbkM7SUFBQTs7OztvQkNGcEM7TUFBQTthQUFBOzs7OyJ9\n"
  },
  {
    "path": "testapp/upgrade/app/downgrade/ng2.ngsummary.json",
    "content": "{\"summaries\":[{\"symbol\":{\"__symbol\":0,\"members\":[]},\"metadata\":{\"__symbolic\":\"class\"},\"type\":{\"summaryKind\":1,\"type\":{\"reference\":{\"__symbol\":0,\"members\":[]},\"diDeps\":[],\"lifecycleHooks\":[]},\"isComponent\":true,\"selector\":\"ng2\",\"exportAs\":null,\"inputs\":{},\"outputs\":{},\"hostListeners\":{},\"hostProperties\":{},\"hostAttributes\":{},\"providers\":[],\"viewProviders\":[],\"queries\":[],\"viewQueries\":[],\"entryComponents\":[],\"changeDetection\":1,\"template\":{\"animations\":[],\"ngContentSelectors\":[],\"encapsulation\":2},\"componentViewType\":{\"__symbol\":59,\"members\":[]},\"rendererType\":{\"__symbol\":60,\"members\":[]},\"componentFactory\":{\"__symbol\":2,\"members\":[]}}},{\"symbol\":{\"__symbol\":1,\"members\":[]},\"metadata\":{\"__symbolic\":\"class\",\"members\":{\"ngDoBootstrap\":[{\"__symbolic\":\"method\"}]}},\"type\":{\"summaryKind\":2,\"type\":{\"reference\":{\"__symbol\":1,\"members\":[]},\"diDeps\":[],\"lifecycleHooks\":[]},\"entryComponents\":[{\"componentType\":{\"__symbol\":0,\"members\":[]},\"componentFactory\":{\"__symbol\":2,\"members\":[]}}],\"providers\":[{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":3,\"members\":[]}}},\"useClass\":{\"reference\":{\"__symbol\":4,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":5,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":6,\"members\":[]}}}}],\"lifecycleHooks\":[]},\"useFactory\":null,\"deps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":5,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":6,\"members\":[]}}}}],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":7,\"members\":[]},\"diDeps\":[],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":8,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":9,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":10,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":11,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":12,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":13,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":14,\"members\":[]}}}}],\"lifecycleHooks\":[1]}},\"useClass\":{\"reference\":{\"__symbol\":8,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":9,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":10,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":11,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":12,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":13,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":14,\"members\":[]}}}}],\"lifecycleHooks\":[1]},\"useFactory\":null,\"deps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":9,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":10,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":11,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":12,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":13,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":14,\"members\":[]}}}}],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":15,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":16,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":16,\"members\":[]}}},\"useClass\":null,\"useFactory\":null,\"useExisting\":{\"identifier\":{\"reference\":{\"__symbol\":8,\"members\":[]}}},\"multi\":false},\"module\":{\"reference\":{\"__symbol\":15,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":16,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":14,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":17,\"members\":[]}}}}],\"lifecycleHooks\":[]}},\"useClass\":{\"reference\":{\"__symbol\":14,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":17,\"members\":[]}}}}],\"lifecycleHooks\":[]},\"useFactory\":null,\"deps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":17,\"members\":[]}}}}],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":15,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":16,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":18,\"members\":[]},\"diDeps\":[],\"lifecycleHooks\":[]}},\"useClass\":{\"reference\":{\"__symbol\":18,\"members\":[]},\"diDeps\":[],\"lifecycleHooks\":[]},\"useFactory\":null,\"deps\":[],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":15,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":16,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":19,\"members\":[]}}},\"useClass\":null,\"useFactory\":{\"reference\":{\"__symbol\":20,\"members\":[]},\"diDeps\":[]},\"deps\":[],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":15,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":16,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":21,\"members\":[]}}},\"useClass\":null,\"useFactory\":{\"reference\":{\"__symbol\":22,\"members\":[]},\"diDeps\":[]},\"deps\":[],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":15,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":16,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":23,\"members\":[]}}},\"useClass\":null,\"useFactory\":{\"reference\":{\"__symbol\":24,\"members\":[]},\"diDeps\":[]},\"deps\":[],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":15,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":16,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":5,\"members\":[]}}},\"useClass\":null,\"useFactory\":{\"reference\":{\"__symbol\":25,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":true,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":5,\"members\":[]}}}}]},\"deps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":true,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":5,\"members\":[]}}}}],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":15,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":16,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":26,\"members\":[]}}},\"useClass\":null,\"useFactory\":null,\"useExisting\":{\"identifier\":{\"reference\":{\"__symbol\":27,\"members\":[]}}},\"multi\":false},\"module\":{\"reference\":{\"__symbol\":28,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":true,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":28,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":27,\"members\":[]}}},\"useClass\":{\"reference\":{\"__symbol\":29,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":30,\"members\":[]}}}}],\"lifecycleHooks\":[]},\"useFactory\":null,\"deps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":30,\"members\":[]}}}}],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":28,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":true,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":28,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":12,\"members\":[]}}},\"useClass\":null,\"useFactory\":{\"reference\":{\"__symbol\":31,\"members\":[]},\"diDeps\":[]},\"deps\":[],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":28,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":true,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":28,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":32,\"members\":[]}}},\"useClass\":{\"reference\":{\"__symbol\":33,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":30,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":9,\"members\":[]}}}}],\"lifecycleHooks\":[]},\"useFactory\":null,\"deps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":30,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":9,\"members\":[]}}}}],\"multi\":true},\"module\":{\"reference\":{\"__symbol\":28,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":true,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":28,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":32,\"members\":[]}}},\"useClass\":{\"reference\":{\"__symbol\":34,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":30,\"members\":[]}}}}],\"lifecycleHooks\":[]},\"useFactory\":null,\"deps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":30,\"members\":[]}}}}],\"multi\":true},\"module\":{\"reference\":{\"__symbol\":28,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":true,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":28,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":32,\"members\":[]}}},\"useClass\":{\"reference\":{\"__symbol\":35,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":30,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":36,\"members\":[]}}}}],\"lifecycleHooks\":[]},\"useFactory\":null,\"deps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":30,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":36,\"members\":[]}}}}],\"multi\":true},\"module\":{\"reference\":{\"__symbol\":28,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":true,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":28,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":36,\"members\":[]}}},\"useClass\":{\"reference\":{\"__symbol\":37,\"members\":[]},\"diDeps\":[],\"lifecycleHooks\":[]},\"useFactory\":null,\"deps\":[],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":28,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":true,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":28,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":38,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":39,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":40,\"members\":[]}}}}],\"lifecycleHooks\":[]}},\"useClass\":{\"reference\":{\"__symbol\":38,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":39,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":40,\"members\":[]}}}}],\"lifecycleHooks\":[]},\"useFactory\":null,\"deps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":39,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":40,\"members\":[]}}}}],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":28,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":true,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":28,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":41,\"members\":[]}}},\"useClass\":null,\"useFactory\":null,\"useExisting\":{\"identifier\":{\"reference\":{\"__symbol\":38,\"members\":[]}}},\"multi\":false},\"module\":{\"reference\":{\"__symbol\":28,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":true,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":28,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":42,\"members\":[]}}},\"useClass\":null,\"useFactory\":null,\"useExisting\":{\"identifier\":{\"reference\":{\"__symbol\":40,\"members\":[]}}},\"multi\":false},\"module\":{\"reference\":{\"__symbol\":28,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":true,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":28,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":40,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":30,\"members\":[]}}}}],\"lifecycleHooks\":[1]}},\"useClass\":{\"reference\":{\"__symbol\":40,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":30,\"members\":[]}}}}],\"lifecycleHooks\":[1]},\"useFactory\":null,\"deps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":30,\"members\":[]}}}}],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":28,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":true,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":28,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":43,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":9,\"members\":[]}}}}],\"lifecycleHooks\":[]}},\"useClass\":{\"reference\":{\"__symbol\":43,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":9,\"members\":[]}}}}],\"lifecycleHooks\":[]},\"useFactory\":null,\"deps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":9,\"members\":[]}}}}],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":28,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":true,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":28,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":39,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":32,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":9,\"members\":[]}}}}],\"lifecycleHooks\":[]}},\"useClass\":{\"reference\":{\"__symbol\":39,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":32,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":9,\"members\":[]}}}}],\"lifecycleHooks\":[]},\"useFactory\":null,\"deps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":32,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":9,\"members\":[]}}}}],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":28,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":true,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":28,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":17,\"members\":[]}}},\"useClass\":null,\"useFactory\":{\"reference\":{\"__symbol\":44,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":45,\"members\":[]}}}}]},\"deps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":45,\"members\":[]}}}}],\"multi\":true},\"module\":{\"reference\":{\"__symbol\":28,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":true,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":28,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":46,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":30,\"members\":[]}}}}],\"lifecycleHooks\":[]}},\"useClass\":{\"reference\":{\"__symbol\":46,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":30,\"members\":[]}}}}],\"lifecycleHooks\":[]},\"useFactory\":null,\"deps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":30,\"members\":[]}}}}],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":28,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":true,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":28,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":47,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":30,\"members\":[]}}}}],\"lifecycleHooks\":[]}},\"useClass\":{\"reference\":{\"__symbol\":47,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":30,\"members\":[]}}}}],\"lifecycleHooks\":[]},\"useFactory\":null,\"deps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":30,\"members\":[]}}}}],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":28,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":true,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":28,\"members\":[]}}}}],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":48,\"members\":[]}}},\"useClass\":null,\"useFactory\":{\"reference\":{\"__symbol\":49,\"members\":[]},\"diDeps\":[]},\"deps\":[],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":50,\"members\":[]},\"diDeps\":[],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":51,\"members\":[]}}},\"useClass\":{\"reference\":{\"__symbol\":52,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":41,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":53,\"members\":[]}}}}],\"lifecycleHooks\":[]},\"useFactory\":null,\"deps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":41,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":53,\"members\":[]}}}}],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":50,\"members\":[]},\"diDeps\":[],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":54,\"members\":[]}}},\"useClass\":null,\"useFactory\":{\"reference\":{\"__symbol\":55,\"members\":[]},\"diDeps\":[]},\"deps\":[],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":50,\"members\":[]},\"diDeps\":[],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":56,\"members\":[]}}},\"useClass\":{\"reference\":{\"__symbol\":57,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":48,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":54,\"members\":[]}}}}],\"lifecycleHooks\":[]},\"useFactory\":null,\"deps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":48,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":54,\"members\":[]}}}}],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":50,\"members\":[]},\"diDeps\":[],\"lifecycleHooks\":[]}},{\"provider\":{\"token\":{\"identifier\":{\"reference\":{\"__symbol\":41,\"members\":[]}}},\"useClass\":null,\"useFactory\":{\"reference\":{\"__symbol\":58,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":38,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":56,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":9,\"members\":[]}}}}]},\"deps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":38,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":56,\"members\":[]}}}},{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":9,\"members\":[]}}}}],\"multi\":false},\"module\":{\"reference\":{\"__symbol\":50,\"members\":[]},\"diDeps\":[],\"lifecycleHooks\":[]}}],\"modules\":[{\"reference\":{\"__symbol\":7,\"members\":[]},\"diDeps\":[],\"lifecycleHooks\":[]},{\"reference\":{\"__symbol\":15,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":false,\"isOptional\":false,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":16,\"members\":[]}}}}],\"lifecycleHooks\":[]},{\"reference\":{\"__symbol\":28,\"members\":[]},\"diDeps\":[{\"isAttribute\":false,\"isHost\":false,\"isSelf\":false,\"isSkipSelf\":true,\"isOptional\":true,\"token\":{\"identifier\":{\"reference\":{\"__symbol\":28,\"members\":[]}}}}],\"lifecycleHooks\":[]},{\"reference\":{\"__symbol\":50,\"members\":[]},\"diDeps\":[],\"lifecycleHooks\":[]},{\"reference\":{\"__symbol\":1,\"members\":[]},\"diDeps\":[],\"lifecycleHooks\":[]}],\"exportedDirectives\":[],\"exportedPipes\":[]}}],\"symbols\":[{\"__symbol\":0,\"name\":\"Ng2Component\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/upgrade/app/downgrade/ng2.d.ts\"},{\"__symbol\":1,\"name\":\"AppModule\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/upgrade/app/downgrade/ng2.d.ts\"},{\"__symbol\":2,\"name\":\"Ng2ComponentNgFactory\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/upgrade/app/downgrade/ng2.ngfactory.d.ts\"},{\"__symbol\":3,\"name\":\"NgLocalization\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/common/index.d.ts\"},{\"__symbol\":4,\"name\":\"NgLocaleLocalization\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/common/index.d.ts\"},{\"__symbol\":5,\"name\":\"LOCALE_ID\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":6,\"name\":\"ɵa\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/common/index.d.ts\"},{\"__symbol\":7,\"name\":\"CommonModule\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/common/index.d.ts\"},{\"__symbol\":8,\"name\":\"ɵg\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":9,\"name\":\"NgZone\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":10,\"name\":\"ɵConsole\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":11,\"name\":\"Injector\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":12,\"name\":\"ErrorHandler\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":13,\"name\":\"ComponentFactoryResolver\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":14,\"name\":\"ApplicationInitStatus\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":15,\"name\":\"ApplicationModule\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":16,\"name\":\"ApplicationRef\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":17,\"name\":\"APP_INITIALIZER\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":18,\"name\":\"Compiler\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":19,\"name\":\"APP_ID\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":20,\"name\":\"ɵh\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":21,\"name\":\"IterableDiffers\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":22,\"name\":\"ɵm\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":23,\"name\":\"KeyValueDiffers\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":24,\"name\":\"ɵn\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":25,\"name\":\"ɵo\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":26,\"name\":\"Sanitizer\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":27,\"name\":\"DomSanitizer\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/index.d.ts\"},{\"__symbol\":28,\"name\":\"BrowserModule\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/index.d.ts\"},{\"__symbol\":29,\"name\":\"ɵd\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/index.d.ts\"},{\"__symbol\":30,\"name\":\"DOCUMENT\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/common/index.d.ts\"},{\"__symbol\":31,\"name\":\"ɵa\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/index.d.ts\"},{\"__symbol\":32,\"name\":\"EVENT_MANAGER_PLUGINS\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/index.d.ts\"},{\"__symbol\":33,\"name\":\"ɵDomEventsPlugin\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/index.d.ts\"},{\"__symbol\":34,\"name\":\"ɵKeyEventsPlugin\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/index.d.ts\"},{\"__symbol\":35,\"name\":\"ɵHammerGesturesPlugin\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/index.d.ts\"},{\"__symbol\":36,\"name\":\"HAMMER_GESTURE_CONFIG\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/index.d.ts\"},{\"__symbol\":37,\"name\":\"HammerGestureConfig\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/index.d.ts\"},{\"__symbol\":38,\"name\":\"ɵDomRendererFactory2\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/index.d.ts\"},{\"__symbol\":39,\"name\":\"EventManager\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/index.d.ts\"},{\"__symbol\":40,\"name\":\"ɵDomSharedStylesHost\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/index.d.ts\"},{\"__symbol\":41,\"name\":\"RendererFactory2\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":42,\"name\":\"ɵSharedStylesHost\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/index.d.ts\"},{\"__symbol\":43,\"name\":\"Testability\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":44,\"name\":\"ɵg\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/index.d.ts\"},{\"__symbol\":45,\"name\":\"NgProbeToken\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/core/index.d.ts\"},{\"__symbol\":46,\"name\":\"Meta\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/index.d.ts\"},{\"__symbol\":47,\"name\":\"Title\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/index.d.ts\"},{\"__symbol\":48,\"name\":\"AnimationDriver\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/animations/browser/index.d.ts\"},{\"__symbol\":49,\"name\":\"ɵc\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/animations/index.d.ts\"},{\"__symbol\":50,\"name\":\"BrowserAnimationsModule\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/animations/index.d.ts\"},{\"__symbol\":51,\"name\":\"AnimationBuilder\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/animations/index.d.ts\"},{\"__symbol\":52,\"name\":\"ɵBrowserAnimationBuilder\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/animations/index.d.ts\"},{\"__symbol\":53,\"name\":\"DOCUMENT\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/index.d.ts\"},{\"__symbol\":54,\"name\":\"ɵAnimationStyleNormalizer\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/animations/browser/index.d.ts\"},{\"__symbol\":55,\"name\":\"ɵd\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/animations/index.d.ts\"},{\"__symbol\":56,\"name\":\"ɵAnimationEngine\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/animations/browser/index.d.ts\"},{\"__symbol\":57,\"name\":\"ɵb\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/animations/index.d.ts\"},{\"__symbol\":58,\"name\":\"ɵe\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/node_modules/@angular/platform-browser/animations/index.d.ts\"},{\"__symbol\":59,\"name\":\"View_Ng2Component_0\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/upgrade/app/downgrade/ng2.ngfactory.d.ts\"},{\"__symbol\":60,\"name\":\"RenderType_Ng2Component\",\"filePath\":\"/usr/local/google/home/qiyi/protractor/testapp/upgrade/app/downgrade/ng2.ngfactory.d.ts\"}]}"
  },
  {
    "path": "testapp/upgrade/app/downgrade/ng2.ts",
    "content": "import * as angular from 'angular';\nimport { NgModule , Component } from '@angular/core';\nimport { BrowserAnimationsModule } from '@angular/platform-browser/animations';\nimport { downgradeComponent } from '@angular/upgrade/static';\n\nimport { ng1module } from './ng1';\n\n@Component({\n  selector: 'ng2',\n  template: `\n    <h2>ng2</h2>\n    <button (click)='clickButton()'>Click Count: {{callCount}}</button>\n  `\n})\nexport class Ng2Component {\n  callCount: number = 0;\n  clickButton = () => {\n    setTimeout(() => {\n      this.callCount++;\n    }, 1000);\n  };\n}\n\n@NgModule({\n  imports: [\n    BrowserAnimationsModule,\n  ],\n  declarations: [\n    Ng2Component,\n  ],\n  entryComponents: [\n    Ng2Component\n  ]\n})\nexport class AppModule {\n  ngDoBootstrap() {}\n}\n\n\n\n  // Register the Angular 2 component with the Angular 1 module.\nng1module.directive(\n  'ng2',  // lowerCamel when registering.\n  // propagateDigest: false will detach the digest cycle from AngularJS from\n  // propagating into the Angular (2+) CD.\n  downgradeComponent({component: Ng2Component, propagateDigest: false}));\n\n"
  },
  {
    "path": "testapp/upgrade/app/main.js",
    "content": "\"use strict\";\nvar platform_browser_dynamic_1 = require('@angular/platform-browser-dynamic');\nvar static_1 = require('@angular/upgrade/static');\nvar module_1 = require('./module');\nplatform_browser_dynamic_1.platformBrowserDynamic().bootstrapModule(module_1.AppModule).then(function (platformRef) {\n    var upgrade = platformRef.injector.get(static_1.UpgradeModule);\n    upgrade.bootstrap(document.body, ['upgradeApp'], { strictDi: true });\n});\n//# sourceMappingURL=main.js.map"
  },
  {
    "path": "testapp/upgrade/app/main.ts",
    "content": "import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';\nimport { UpgradeModule } from '@angular/upgrade/static';\n\nimport { AppModule } from './module';\n\nplatformBrowserDynamic().bootstrapModule(AppModule).then(platformRef => {\n  const upgrade = platformRef.injector.get(UpgradeModule) as UpgradeModule;\n  upgrade.bootstrap(document.body, ['upgradeApp'], {strictDi: true});\n});\n"
  },
  {
    "path": "testapp/upgrade/app/module.js",
    "content": "\"use strict\";\nvar __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {\n    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n    if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n    return c > 3 && r && Object.defineProperty(target, key, r), r;\n};\nvar __metadata = (this && this.__metadata) || function (k, v) {\n    if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(k, v);\n};\nvar core_1 = require('@angular/core');\nvar platform_browser_1 = require('@angular/platform-browser');\nvar static_1 = require('@angular/upgrade/static');\nvar myApp_1 = require('./myApp');\nvar ng2_1 = require('./ng2');\nvar ng1_1 = require('./ng1');\nvar AppModule = (function () {\n    function AppModule() {\n    }\n    AppModule.prototype.ngDoBootstrap = function () { };\n    AppModule = __decorate([\n        core_1.NgModule({\n            imports: [\n                platform_browser_1.BrowserModule,\n                static_1.UpgradeModule\n            ],\n            declarations: [\n                ng2_1.Ng2Component,\n                ng1_1.Ng1Component,\n            ],\n            entryComponents: [\n                ng2_1.Ng2Component\n            ]\n        }), \n        __metadata('design:paramtypes', [])\n    ], AppModule);\n    return AppModule;\n}());\nexports.AppModule = AppModule;\nangular.module('upgradeApp', [])\n    .directive('ng1', ng1_1.Ng1Directive)\n    .directive('ng2', static_1.downgradeComponent({\n    component: ng2_1.Ng2Component,\n}))\n    .directive('myApp', myApp_1.RootDirective);\n//# sourceMappingURL=module.js.map"
  },
  {
    "path": "testapp/upgrade/app/module.ts",
    "content": "declare var angular: angular.IAngularStatic;\nimport { NgModule } from '@angular/core';\nimport { BrowserModule } from '@angular/platform-browser';\nimport { UpgradeModule, downgradeComponent } from '@angular/upgrade/static';\n\nimport { RootDirective } from './myApp';\nimport { Ng2Component } from './ng2';\nimport { Ng1Directive, Ng1Component } from './ng1';\n\n@NgModule({\n  imports: [\n    BrowserModule,\n    UpgradeModule\n  ],\n  declarations: [\n    Ng2Component,\n    Ng1Component,\n  ],\n  entryComponents: [\n    Ng2Component\n  ]\n})\nexport class AppModule {\n  ngDoBootstrap() {}\n}\n\nangular.module('upgradeApp', [])\n  .directive('ng1', Ng1Directive)\n  .directive('ng2', downgradeComponent({\n    component: Ng2Component,\n  }) as angular.IDirectiveFactory)\n  .directive('myApp', RootDirective);\n"
  },
  {
    "path": "testapp/upgrade/app/myApp.js",
    "content": "\"use strict\";\nfunction ctrl($scope, $timeout) {\n    $scope.callCount = 0;\n    $scope.clickButton = function () {\n        $timeout(function () {\n            $scope.callCount++;\n        }, 1000);\n    };\n}\nctrl.$inject = ['$scope', '$timeout'];\nfunction RootDirective() {\n    return {\n        scope: {},\n        templateUrl: './html/myApp.html',\n        controller: ctrl\n    };\n}\nexports.RootDirective = RootDirective;\n//# sourceMappingURL=myApp.js.map"
  },
  {
    "path": "testapp/upgrade/app/myApp.ts",
    "content": "function ctrl($scope: any, $timeout: any) {\n  $scope.callCount = 0;\n\n  $scope.clickButton = function() {\n    $timeout(() => {\n      $scope.callCount++;\n    }, 1000);\n  };\n}\nctrl.$inject = ['$scope', '$timeout'];\n\nexport function RootDirective() {\n  return {\n    scope: {},\n    templateUrl: './html/myApp.html',\n    controller: ctrl\n  };\n}\n"
  },
  {
    "path": "testapp/upgrade/app/ng1.js",
    "content": "\"use strict\";\nvar __extends = (this && this.__extends) || function (d, b) {\n    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];\n    function __() { this.constructor = d; }\n    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n};\nvar __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {\n    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n    if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n    return c > 3 && r && Object.defineProperty(target, key, r), r;\n};\nvar __metadata = (this && this.__metadata) || function (k, v) {\n    if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(k, v);\n};\nfunction ctrl($scope, $timeout) {\n    $scope.callCount = 0;\n    $scope.clickButton = function () {\n        $timeout(function () {\n            $scope.callCount++;\n        }, 1000);\n    };\n}\nctrl.$inject = ['$scope', '$timeout'];\nfunction Ng1Directive() {\n    return {\n        scope: {},\n        template: '<h3>ng1</h3><button ng-click=\"clickButton()\">Click Count: {{callCount}}</button>',\n        controller: ctrl\n    };\n}\nexports.Ng1Directive = Ng1Directive;\nvar core_1 = require('@angular/core');\nvar static_1 = require('@angular/upgrade/static');\nvar Ng1Component = (function (_super) {\n    __extends(Ng1Component, _super);\n    function Ng1Component(elementRef, injector) {\n        _super.call(this, 'ng1', elementRef, injector);\n    }\n    Ng1Component = __decorate([\n        core_1.Directive({\n            selector: 'ng1'\n        }), \n        __metadata('design:paramtypes', [core_1.ElementRef, core_1.Injector])\n    ], Ng1Component);\n    return Ng1Component;\n}(static_1.UpgradeComponent));\nexports.Ng1Component = Ng1Component;\n//# sourceMappingURL=ng1.js.map"
  },
  {
    "path": "testapp/upgrade/app/ng1.ts",
    "content": "function ctrl($scope: any, $timeout: any) {\n  $scope.callCount = 0;\n\n  $scope.clickButton = function() {\n    $timeout(() => {\n      $scope.callCount++;\n    }, 1000);\n  };\n}\nctrl.$inject = ['$scope', '$timeout'];\n\nexport function Ng1Directive() {\n  return {\n    scope: {},\n    template: '<h3>ng1</h3><button ng-click=\"clickButton()\">Click Count: {{callCount}}</button>',\n    controller: ctrl\n  };\n}\n\nimport { Directive, ElementRef, Injector } from '@angular/core';\nimport { UpgradeComponent } from '@angular/upgrade/static';\n@Directive({\n  selector: 'ng1'\n})\nexport class Ng1Component extends UpgradeComponent {\n  constructor(elementRef: ElementRef, injector: Injector) {\n    super('ng1', elementRef, injector);\n  }\n}\n\n"
  },
  {
    "path": "testapp/upgrade/app/ng2.js",
    "content": "\"use strict\";\nvar __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {\n    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n    if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n    return c > 3 && r && Object.defineProperty(target, key, r), r;\n};\nvar __metadata = (this && this.__metadata) || function (k, v) {\n    if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(k, v);\n};\nvar core_1 = require('@angular/core');\nvar Ng2Component = (function () {\n    function Ng2Component() {\n        var _this = this;\n        this.callCount = 0;\n        this.clickButton = function () {\n            setTimeout(function () {\n                _this.callCount++;\n            }, 1000);\n        };\n    }\n    Ng2Component = __decorate([\n        core_1.Component({\n            selector: 'ng2',\n            templateUrl: './html/ng2.html'\n        }), \n        __metadata('design:paramtypes', [])\n    ], Ng2Component);\n    return Ng2Component;\n}());\nexports.Ng2Component = Ng2Component;\n//# sourceMappingURL=ng2.js.map"
  },
  {
    "path": "testapp/upgrade/app/ng2.ts",
    "content": "import {Component} from '@angular/core';\n\n@Component({\n  selector: 'ng2',\n  templateUrl: './html/ng2.html'\n})\nexport class Ng2Component {\n  callCount: number = 0;\n  clickButton = () => {\n    setTimeout(() => {\n      this.callCount++;\n    }, 1000);\n  };\n}\n"
  },
  {
    "path": "testapp/upgrade/app/no_static/main.js",
    "content": "\"use strict\";\nvar myApp_1 = require('../myApp');\nvar ng2_1 = require('../ng2');\nvar ng1_1 = require('../ng1');\nvar upgrader_1 = require('./upgrader');\nvar ng1module = angular.module('upgradeApp', []);\nng1module.directive('myApp', myApp_1.RootDirective);\nng1module.directive('ng2', upgrader_1.adapter.downgradeNg2Component(ng2_1.Ng2Component));\nng1module.directive('ng1', ng1_1.Ng1Directive);\nupgrader_1.adapter.bootstrap(document.body, ['upgradeApp']);\n//# sourceMappingURL=main.js.map"
  },
  {
    "path": "testapp/upgrade/app/no_static/main.ts",
    "content": "import {RootDirective} from '../myApp'\nimport {Ng2Component} from '../ng2'\nimport {Ng1Directive} from '../ng1'\nimport {adapter} from './upgrader';\n\ndeclare var angular;\n\nvar ng1module = angular.module('upgradeApp', []);\n\nng1module.directive('myApp', RootDirective);\nng1module.directive('ng2', adapter.downgradeNg2Component(Ng2Component));\nng1module.directive('ng1', Ng1Directive);\n\nadapter.bootstrap(document.body, ['upgradeApp']);\n"
  },
  {
    "path": "testapp/upgrade/app/no_static/upgrader.js",
    "content": "\"use strict\";\nvar __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {\n    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n    if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n    return c > 3 && r && Object.defineProperty(target, key, r), r;\n};\nvar __metadata = (this && this.__metadata) || function (k, v) {\n    if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(k, v);\n};\nvar upgrade_1 = require('@angular/upgrade');\nvar platform_browser_1 = require('@angular/platform-browser');\nvar core_1 = require('@angular/core');\nvar ng2_1 = require('../ng2');\nexports.adapter = new upgrade_1.UpgradeAdapter(core_1.forwardRef(function () { return Ng2Module; }));\nvar Ng2Module = (function () {\n    function Ng2Module() {\n    }\n    Ng2Module = __decorate([\n        core_1.NgModule({\n            imports: [\n                platform_browser_1.BrowserModule\n            ],\n            declarations: [\n                ng2_1.Ng2Component, exports.adapter.upgradeNg1Component('ng1')\n            ],\n        }), \n        __metadata('design:paramtypes', [])\n    ], Ng2Module);\n    return Ng2Module;\n}());\nexports.Ng2Module = Ng2Module;\n//# sourceMappingURL=upgrader.js.map"
  },
  {
    "path": "testapp/upgrade/app/no_static/upgrader.ts",
    "content": "import { UpgradeAdapter } from '@angular/upgrade';\nimport { BrowserModule } from '@angular/platform-browser';\nimport { forwardRef, NgModule } from '@angular/core';\nimport { Ng2Component } from '../ng2'\n\n\nexport const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));\n\n@NgModule({\n  imports: [\n    BrowserModule\n  ],\n  declarations: [\n    Ng2Component, adapter.upgradeNg1Component('ng1')\n  ],\n\n})\nexport class Ng2Module {}\n"
  },
  {
    "path": "testapp/upgrade/html/myApp.html",
    "content": "<h1>My App</h1>\n<button ng-click='clickButton()'>Click Count: {{callCount}}</button>\n<ng2></ng2>\n"
  },
  {
    "path": "testapp/upgrade/html/ng1.html",
    "content": "<h3>ng1</h3>\n<button ng-click='clickButton()'>Click Count: {{callCount}}</button>\n"
  },
  {
    "path": "testapp/upgrade/html/ng2.html",
    "content": "<h2>ng2</h2>\n<button (click)='clickButton()'>Click Count: {{callCount}}</button>\n<ng1></ng1>\n"
  },
  {
    "path": "testapp/upgrade/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <!-- Set the base href -->\n    <base href=\"/upgrade/\">\n\n    <title>Test ngUpgrade App</title>\n    <link href=\"https://fonts.googleapis.com/css?family=Roboto:400,300,500,400italic,700\" rel=\"stylesheet\" type=\"text/css\">\n    <link rel=\"stylesheet\" href=\"styles.css\">\n    <script src=\"/ng1/lib/angular/angular.min.js\"></script>\n    <script src=\"/ng1/lib/angular/angular-animate.min.js\"></script>\n    <script src=\"/ng1/lib/angular/angular-route.min.js\"></script>\n\n    <!-- IE required polyfills, in this exact order -->\n    <script src=\"../node_modules/core-js/client/shim.min.js\"></script>\n    <script src=\"../node_modules/reflect-metadata/Reflect.js\"></script>\n    <script src=\"../node_modules/systemjs/dist/system-polyfills.js\"></script>\n    <script src=\"../node_modules/systemjs/dist/system.src.js\"></script>\n    <script src=\"../node_modules/zone.js/dist/zone.js\"></script>\n\n    <script>\n      System.import('systemjs.config.js').then(function () {\n        if (window.location.search.indexOf('no_static') != -1) {\n          System.import('app/no_static/main');\n        }\n        else if (window.location.search.indexOf('downgrade') != -1) {\n          System.import('app/downgrade/main');\n        }\n        else {\n          System.import('app/main');\n        }\n      }).catch(function(err) {\n        console.log('error while importing:');\n        console.log(err);\n        console.log(err.stack);\n      });\n    </script>\n  </head>\n\n  <body>\n    <my-app>Loading...</my-app>\n  </body>\n\n</html>\n\n\n<!--\nCopyright 2016 Google Inc. All Rights Reserved.\nUse of this source code is governed by an MIT-style license that\ncan be found in the LICENSE file at http://angular.io/license\n-->\n"
  },
  {
    "path": "testapp/upgrade/styles.css",
    "content": "body {\n  font-family:\"Roboto\",\"Helvetica Neue Light\",\"Helvetica Neue\",Helvetica,Arial,\"Lucida Grande\",sans-serif;\n  font-size:14px;color:#1a2326\n}\n.container {\n  padding-left: 20px;\n}\n.nav {\n  padding-bottom: 20px;\n}\n"
  },
  {
    "path": "testapp/upgrade/systemjs.config.js",
    "content": "/**\n * System configuration for Angular 2 samples\n * Adjust as necessary for your application needs.\n */\n(function (global) {\n  System.config({\n    paths: {\n      // paths serve as alias\n      'npm:': '/node_modules/'\n    },\n    // map tells the System loader where to look for things\n    map: {\n      // our app is within the app folder\n      app: 'app',\n\n      // angular bundles\n      '@angular/core': 'npm:@angular/core/bundles/core.umd.js',\n      '@angular/common': 'npm:@angular/common/bundles/common.umd.js',\n      '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',\n      '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',\n      '@angular/animations': 'npm:@angular/animations/bundles/animations.umd.js',\n      '@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js',\n      '@angular/platform-browser/animations': 'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js',\n      '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',\n      '@angular/http': 'npm:@angular/http/bundles/http.umd.js',\n      '@angular/router': 'npm:@angular/router/bundles/router.umd.js',\n      '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',\n      '@angular/upgrade': 'npm:@angular/upgrade/bundles/upgrade.umd.js',\n      '@angular/upgrade/static': 'npm:@angular/upgrade/bundles/upgrade-static.umd.js',\n\n      // other libraries\n      'rxjs':                       'npm:rxjs',\n    },\n    // packages tells the System loader how to load when no filename and/or no extension\n    packages: {\n      app: {\n        main: './main.js',\n        defaultExtension: 'js'\n      },\n      rxjs: {\n        defaultExtension: 'js'\n      },\n      'angular2-in-memory-web-api': {\n        main: './index.js',\n        defaultExtension: 'js'\n      }\n    }\n  });\n})(this);\n"
  },
  {
    "path": "ts_spec_config.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es6\",\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\",\n    \"sourceMap\": true,\n    \"declaration\": true,\n    \"removeComments\": false,\n    \"noImplicitAny\": true,\n    \"outDir\": \"spec/built\",\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"selenium-webdriver\": [\"lib/selenium-webdriver\"],\n      \"selenium-webdriver/chrome\": [\"lib/selenium-webdriver/chrome.d.ts\"],\n      \"selenium-webdriver/firefox\": [\"lib/selenium-webdriver/firefox.d.ts\"],\n      \"selenium-webdriver/http\": [\"lib/selenium-webdriver/http.d.ts\"],\n      \"selenium-webdriver/remote\": [\"lib/selenium-webdriver/remote.d.ts\"],\n      \"selenium-webdriver/lib/command\": [\"lib/selenium-webdriver/lib/command.d.ts\"]\n    }\n  },\n  \"include\": [\n    \"spec/ts/**/*\"\n  ]\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es6\",\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\",\n    \"sourceMap\": true,\n    \"declaration\": true,\n    \"removeComments\": false,\n    \"noImplicitAny\": true,\n    \"outDir\": \"built/\",\n    \"baseUrl\": \".\"\n  },\n  \"exclude\": [\n    \"built\",\n    \"node_modules\",\n    \"testapp\",\n    \"website\",\n    \"scripts\",\n    \"exampleTypescript\",\n    \"spec/**/*\",\n    \"lib/selenium-webdriver]\"\n  ]\n}\n"
  },
  {
    "path": "tslint.json",
    "content": "{\n  \"rulesDirectory\": [\n    \"node_modules/vrsource-tslint-rules/rules\",\n    \"node_modules/tslint-eslint-rules/dist/rules\"\n  ],\n  \"rules\": {\n    \"no-duplicate-imports\": true,\n    \"no-duplicate-variable\": true,\n    \"no-jasmine-focus\": true,\n    \"no-var-keyword\": true,\n    \"semicolon\": [true],\n    \"variable-name\": [true, \"ban-keywords\"],\n    \"no-inner-declarations\": [true, \"function\"]\n  }\n}\n"
  },
  {
    "path": "website/.bowerrc",
    "content": "{\n  \"directory\" : \"bower_components\"\n}\n"
  },
  {
    "path": "website/README.md",
    "content": "Protractor website\n==================\n\nGenerates the documentation for the protractor website.\n\n## How to run\n\n```shell\nnpm run compile_to_es5\ncd website/\nnpm install\nnpm run build\n```\n\nThen copy all the files from the `build` directory to the gh-pages branch.\nIf you want to run the website locally then run the following command:\n\n```shell\nnpm start\n```\n\nAnd point your browser to: [localhost:8080](http://localhost:8080/)\n\n## How to run the tests\n\nThe website includes 3 types of tests:\n\n* minijasminenode unit tests for the dgeni modules.\n* karma unit tests for the angular controllers.\n* protractor for e2e testing.\n\n### Start the server first\n\nThe following command will start a server on [localhost:8080](http://localhost:8080/).\nThe server is used to run the protractor tests.\n\n```shell\nnpm start\n```\n\n### Run all the tests\n\n```shell\nnpm test\n```\n\n### Run the dgeni tests\n\n```shell\n./node_modules/.bin/minijasminenode docgen/spec/*\n```\n\n### Run the karma tests\n\n```shell\n./node_modules/karma/bin/karma start\n```\n\n### Run the protractor tests\n\n```shell\n../bin/protractor\n```\n"
  },
  {
    "path": "website/bower.json",
    "content": "{\n  \"name\": \"protractor-site\",\n  \"version\": \"1.0.0\",\n  \"authors\": [\n    \"andres <code@karateca.com>\"\n  ],\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"angular\": \"~1.3.7\",\n    \"angular-mocks\": \"~1.3.7\",\n    \"angular-route\": \"~1.3.7\",\n    \"bootstrap\": \"~3.2.0\",\n    \"lodash\": \"~2.4.1\"\n  }\n}\n"
  },
  {
    "path": "website/css/prettify.css",
    "content": ".pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}"
  },
  {
    "path": "website/css/protractor.less",
    "content": "@import 'prettify.css';\n@import '../bower_components/bootstrap/dist/css/bootstrap.min.css';\n\n@protractor-red: #e23237;\n@protractor-red-dark: #b52e31;\n@padding-top: 50px;\n@api-padding-top: 10px;\n@child-color: #777;\n@mobile-menu-margin: 15px;\n\na code {\n  color: inherit;\n}\n\n.protractor-container {\n  margin-bottom: 50px;\n  padding-top: @padding-top;\n\n  .protractor-header {\n    text-align: center;\n    margin-bottom: 50px;\n    margin-top: 90px - @padding-top;\n  }\n}\n\n.gist {\n  margin: 20px 0;\n}\n\n.github-button {\n  background-color: @protractor-red;\n\n  &:hover {\n    background-color: @protractor-red-dark;\n  }\n\n  img {\n    margin-right: 12px;\n  }\n}\n\n.protractor-logo {\n  display: inline-block;\n  background-size: 100%;\n  @media (max-width: 500px) {\n    background-image: url(../img/protractor-logo-300.png);\n    @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 2dppx) {\n      background-image: url(../img/protractor-logo-600.png);\n    }\n    height: 67px;\n    width: 300px;\n  }\n  @media (min-width: 500px) {\n    background-image: url(../img/protractor-logo-450.png);\n    @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 2dppx) {\n      background-image: url(../img/protractor-logo-900.png);\n    }\n    height: 101px;\n    width: 450px;\n  }\n}\n\n.twitter {\n  margin: 20px 0;\n  text-align: center;\n}\n\n.api-title {\n  margin-top: @api-padding-top;\n}\n\n.api-webdriver {\n  border: 1px solid #F0F0F0;\n  padding: 0.5em;\n  font-style: italic;\n}\n\n.api-left-nav {\n  .title {\n    display: inline-block;\n    font-size: 1.2em;\n    margin: @api-padding-top 0 5px;\n  }\n\n  .child {\n    color: @child-color;\n  }\n\n  .depth-1 {\n    margin-left: 20px;\n  }\n\n  .depth-2 {\n    margin-left: 40px;\n  }\n\n  .depth-3 {\n    margin-left: 60px;\n  }\n}\n\n.api-left-nav a, .mobile-menu a {\n  display: inline-block;\n}\n\n.api-left-nav .extension, .mobile-menu .extension {\n  &:before {\n    content: 'Inherited from ';\n  }\n  font-style: italic;\n}\n\n// The grid is too wide.\n@media (min-width: 1200px) {\n  .container {\n    max-width: 960px;\n  }\n}\n\n.toggle-menu-button-container {\n  padding: 0 @mobile-menu-margin;\n\n  button {\n    margin-top: 10px;\n    width: 100%;\n  }\n}\n\n.mobile-menu {\n  margin: @mobile-menu-margin;\n\n  .title {\n    font-size: 1.5em;\n    margin-top: 10px;\n    text-align: center;\n  }\n\n  .parent {\n    font-size: 1.2em;\n    margin-top: 10px;\n  }\n\n  .child {\n    color: @child-color;\n    font-size: 1.1em;\n    padding-right: 10px;\n  }\n}\n\n//Anchors\n.anchored {\n  div {\n    position: relative;\n    margin-left: -1.5em;\n    padding-left: 1.5em;\n\n    a {\n      position: absolute;\n      top: 0px;\n      bottom: 0px;\n      left: 0em;\n      width: 2em;\n      height: 1em;\n      line-height: 1em;\n      margin: auto;\n      text-align: center;\n      color: inherit;\n      text-decoration: none;\n      font-size: 0.75em;\n      display: none;\n    }\n\n    &:hover a {\n      display: inline-block;\n    }\n  }\n\n  //Offset\n  &:before {\n    content: \" \";\n    display: block;\n    height: 70px;\n    margin-top: -70px;\n    visibility: hidden;\n  }\n}\n"
  },
  {
    "path": "website/docgen/dgeni-config.js",
    "content": "var _ = require('lodash');\nvar path = require('path');\nvar Package = require('dgeni').Package;\n\nvar jsDocProcessor = require('dgeni-packages/jsdoc');\n\n// Configure the tags that will be parsed from the jsDoc.\njsDocProcessor.config(function(parseTagsProcessor) {\n  var tagDefs = parseTagsProcessor.tagDefinitions;\n\n  // Parse the following annotations.\n  tagDefs.push({name: 'alias'});\n  tagDefs.push({name: 'augments'});\n  tagDefs.push({name: 'deprecated'});\n  tagDefs.push({name: 'example'});\n  tagDefs.push({name: 'extends'});\n  tagDefs.push({name: 'external'});\n  tagDefs.push({name: 'private'});\n  tagDefs.push({name: 'type'});\n  tagDefs.push({name: 'view'});\n  tagDefs.push({name: 'template'});\n  tagDefs.push({name: 'fileoverview'});\n  tagDefs.push({name: 'const'});\n  tagDefs.push({name: 'throws'});\n  tagDefs.push({name: 'typedef'});\n  tagDefs.push({name: 'override'});\n  tagDefs.push({name: 'implements'});\n  tagDefs.push({name: 'final'});\n\n  // The name tag should not be required.\n  var nameTag = _.find(tagDefs, {name: 'name'});\n  nameTag.required = false;\n});\n\nvar myPackage = new Package('myPackage', [\n  jsDocProcessor,\n  require('dgeni-packages/nunjucks')\n]);\n\n// Handle Inline Tags\nmyPackage.factory(require('./inline_tags/code'))\n    .config(function(inlineTagProcessor, codeTagDef) {\n      inlineTagProcessor.inlineTagDefinitions.push(codeTagDef);\n    });\n\n/*\n * Add a couple of processors to the pipe to do extra parsing and rendering.\n * Note that the order in which these are included is very important.\n *\n * tag-fixer: Get the name of the function, format the @param and @return\n *     annotations to prepare them for rendering.\n * filter-jsdoc: Filter the functions that will not be part of the output\n *     documentation and generate a unique name for the output partial file.\n * transfer-see: Takes the information in @see tags and appends it to the\n *     description\n * add-links: Add links to the source code for protractor.js, locators.js,\n *     and webdriver.js.\n * add-toc: Generates the table of contents.\n */\nmyPackage.processor(require('./processors/tag-fixer'));\nmyPackage.processor(require('./processors/these-children'));\nmyPackage.processor(require('./processors/filter-jsdoc'));\nmyPackage.processor(require('./processors/set-file-name'));\nmyPackage.processor(require('./processors/transfer-see'));\nmyPackage.processor(require('./processors/add-links'));\nmyPackage.processor(require('./processors/filter-promise'));\nmyPackage.processor(require('./processors/add-toc'));\n\nmyPackage.config(function(readFilesProcessor, templateFinder, writeFilesProcessor) {\n\n  // Go to the protractor project root.\n  readFilesProcessor.basePath = path.resolve(__dirname, '../..');\n\n  readFilesProcessor.sourceFiles = [\n    {include: 'built/browser.js'},\n    {include: 'built/element.js'},\n    {include: 'built/locators.js'},\n    {include: 'built/expectedConditions.js'},\n    {include: 'lib/selenium-webdriver/locators.js'},\n    {include: 'lib/selenium-webdriver/webdriver.js'},\n    {include: 'lib/webdriver-js-extender/index.js'}\n  ];\n\n  // Add a folder to search for our own templates to use when rendering docs\n  templateFinder.templateFolders.unshift(path.resolve('docgen/templates'));\n\n  // Specify how to match docs to templates.\n  // In this case we just use the same static template for all docs\n  templateFinder.templatePatterns.unshift('toc-template.txt');\n\n  // Specify where the writeFilesProcessor will write our generated doc files\n  writeFilesProcessor.outputFolder = 'website/docgen/build';\n});\n\nmodule.exports = myPackage;\n"
  },
  {
    "path": "website/docgen/inline_tags/code.js",
    "content": "var htmlSpecialCharMap = {\n  \"&\": \"&amp;\",\n  \"<\": \"&lt;\",\n  \">\": \"&gt;\",\n  \"/\": '&#x2F;'\n};\n\nmodule.exports = function codeTagDef() {\n  return {\n    name: 'code',\n    handler: function(doc, tag, content) {\n      return '<code ng-non-bindable>' +\n          content.replace(/[&<>\\/]/g, function (s) {\n            return htmlSpecialCharMap[s];\n          }) + '</code>';\n    },\n    description: 'Handle inline code tags',\n    aliases: ['monospace']\n  };\n};\n"
  },
  {
    "path": "website/docgen/processors/add-links.js",
    "content": "var _ = require('lodash');\n\n/**\n * A lookup table with all the types in the parsed files.\n * @type {Object.<string, Array.<Object>>}\n */\nvar typeTable;\n\n/**\n * A lookup table with links to external types.\n * @type {Object.<string, string}\n */\nvar externalTypes = {};\n\n/**\n * The hash used to generate the links to the source code.\n */\nvar linksHash = require('../../../package.json').version;\n\n/**\n * Add a link to the source code.\n * @param {!Object} doc Current document.\n */\nvar addLinkToSourceCode = function(doc) {\n  // Heuristic for the custom docs in the lib/selenium-webdriver/ folder.\n  if (doc.name && doc.name.startsWith('webdriver')) {\n    return;\n  }\n  var template = _.template('https://github.com/angular/protractor/blob/' +\n      '<%= linksHash %>/lib/<%= fileName %>.ts');\n\n  doc.sourceLink = template({\n    linksHash: linksHash,\n    fileName: doc.fileName\n  });\n};\n\n/**\n * Add links to @link annotations. For example: `{@link foo.bar}` will be\n * transformed into `[foo.bar](foo.bar)` and `{@link foo.bar FooBar Link}` will\n * be transfirned into `[FooBar Link](foo.bar)`\n * @param {string} str The string with the annotations.\n * @param {!Object} doc Current document.\n * @return {string} A link in markdown format.\n */\nvar addLinkToLinkAnnotation = function(str, doc) {\n  var oldStr = null;\n  while (str != oldStr) {\n    oldStr = str;\n    var matches = /{\\s*@link[plain]*\\s+([^]+?)\\s*}/.exec(str);\n    if (matches) {\n      var str = str.replace(\n          new RegExp('{\\\\s*@link[plain]*\\\\s+' +\n              matches[1].replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&') + '\\\\s*}'),\n              toMarkdownLinkFormat(matches[1], doc,\n                matches[0].indexOf('linkplain') == -1)\n      );\n    }\n  }\n  return str;\n};\n\n/**\n * Escape the < > | characters.\n * @param {string} str The string to escape.\n */\nvar escape = function(str) {\n  return _.escape(str).replace(/\\|/g, '&#124;').replace(/!\\[/, '&#33;[');\n};\n\n/**\n * Takes a link of the format 'type' or 'type description' and creates one of\n * the format '[description](type)'.\n *\n * Also does some minor reformatting of the type.\n *\n * @param {string} link The raw link.\n * @param {!Object} doc Current document.\n * @return {string} A link for the type.\n */\nvar toMarkdownLinkFormat = function(link, doc, code) {\n  var type, desc;\n\n  // Split type and description\n  var i = link.indexOf(' ');\n  if (i == -1) {\n    desc = type = link;\n  } else {\n    desc = link.substr(i).trim();\n    type = link.substr(0, i).trim();\n  }\n  if (code) {\n    desc = '{@code ' + desc + '}'\n  }\n  desc = desc.replace(new RegExp('\\n', 'g'), ' ');\n\n  if (desc in externalTypes) {\n    type = externalTypes[desc];\n  }\n\n  if (!type.match(/^https?:\\/\\//)) {\n    // Remove extra '()' at the end of types\n    if (type.substr(-2) == '()') {\n      type = type.substr(0, type.length - 2);\n    }\n\n    // Expand '#' at the start of types\n    if (type[0] == '#') {\n      type = doc.name.substr(0, doc.name.lastIndexOf('.') + 1) + type.substr(1);\n    }\n\n    // Replace '#' in the middle of types with '.'\n    type = type.replace(new RegExp('#', 'g'), '.prototype.');\n\n    // Only create a link if it's in the API\n    if (!typeTable[type]) {\n      return desc;\n    }\n  }\n  return '[' + desc + '](' + type + ')';\n};\n\n/**\n * Create the param or return type.\n * @param {!Object} param Parameter.\n * @return {string} Escaped param string with links to the types.\n */\nvar getTypeString = function(param) {\n  var str = param.typeExpression;\n  var type = param.type;\n  if (!type) {\n    return escape(str);\n  }\n\n  var replaceWithLinkIfPresent = function(type) {\n    if (type.name) {\n      str = str.replace(type.name, toMarkdownLinkFormat(type.name));\n    }\n  };\n\n  if (type.type === 'FunctionType') {\n    _.each(type.params, replaceWithLinkIfPresent);\n  } else if (type.type === 'TypeApplication') {\n    // Is this an Array.<type>?\n    var match = str.match(/Array\\.<(.*)>/);\n    if (match) {\n      var typeInsideArray = match[1];\n      str = str.replace(typeInsideArray, toMarkdownLinkFormat(typeInsideArray));\n    }\n  } else if (type.type === 'NameExpression') {\n    replaceWithLinkIfPresent(type);\n  }\n\n  return escape(str);\n};\n\n/**\n * Filters out types with @external annotations and adds their @see link to the\n * externalTypes table\n *\n * @param {Array.<Object>} docs The jsdoc list.\n */\nvar filterExternalDocs = function(docs) {\n  return _.reject(docs, function (doc) {\n    if (!!doc.external) {\n      externalTypes[doc.name] = doc.see[0];\n      return true;\n    }\n    return false;\n  });\n};\n\n/**\n * Add links to the external documents\n */\nmodule.exports = function addLinks() {\n  return {\n    $runAfter: ['extracting-tags'],\n    $runBefore: ['tags-extracted'],\n    $process: function(docs) {\n      typeTable = _.groupBy(docs, 'name');\n\n      docs = filterExternalDocs(docs);\n\n      docs.forEach(function(doc) {\n        addLinkToSourceCode(doc);\n        doc.description = addLinkToLinkAnnotation(doc.description, doc);\n\n        // Add links for the params.\n        _.each(doc.params, function(param) {\n          param.paramString = getTypeString(param);\n          param.description = addLinkToLinkAnnotation(param.description, doc);\n        });\n\n        // Add links for the return types.\n        var returns = doc.returns;\n        if (returns) {\n          doc.returnString = getTypeString(returns);\n          returns.description = addLinkToLinkAnnotation(returns.description);\n        } else {\n          doc.returnString = '';\n        }\n      });\n\n      return docs;\n    }\n  };\n};\n"
  },
  {
    "path": "website/docgen/processors/add-toc.js",
    "content": "var _ = require('lodash');\n\nvar escapeHtml = function(htmlCode) {\n  return htmlCode.\n      replace(/&/g, '&amp;').\n      replace(/\"/g, '&quot;').\n      replace(/</g, '&lt;').\n      replace(/>/g, '&gt;');\n};\n\n/**\n * Add table of contents.\n */\nmodule.exports = function addToc() {\n  return {\n    $runAfter: ['extracting-tags'],\n    $runBefore: ['tags-extracted'],\n    $process: function(docs) {\n      // Get the properties that will be copied to the table of contents.\n      var toc = _.map(docs, function(doc) {\n        if (doc.view) {\n          doc.htmlView = escapeHtml(doc.view);\n        }\n\n        return _.pick(doc,\n            'alias',\n            'description',\n            'example',\n            'extends',\n            'fileName',\n            'htmlView',\n            'name',\n            'params',\n            'returns',\n            'returnString',\n            'sourceLink',\n            'view'\n        );\n      });\n\n      // Replace all the docs with the table of contents.\n      docs.length = 0;\n      docs.push({\n        id: 'x',\n        docType: 'js',\n        outputPath: 'toc.json',\n        template: 'toc',\n        toc: toc,\n        version: require('../../../package.json').version\n      });\n    }\n  };\n};\n"
  },
  {
    "path": "website/docgen/processors/filter-jsdoc.js",
    "content": "var _ = require('lodash');\n\n/**\n * Exclude the following tags.\n * @const {Array.<string>}\n */\nvar excludedTags = ['private', 'type'];\n\n/**\n * Remove docs that should not be in the documentation. Reject type, private,\n * exported functions, and function ending with underscore.\n *\n * @param {Array.<Object>} docs The jsdoc list.\n */\nvar filterDocs = function(docs) {\n  return _.reject(docs, function(doc) {\n    // Skip functions starting with 'exports' and ending with _.\n    if (!doc.name || /^exports/.test(doc.name) || /_\\s*$/.test(doc.name)) {\n      return true;\n    }\n\n    // Exclude docs with tags.\n    if (doc.tags) {\n      var tags = _.pluck(doc.tags.tags, 'tagName');\n      return _.intersection(tags, excludedTags).length;\n    }\n  });\n};\n\n/**\n * Filter functions that will not go into the documentation.\n */\nmodule.exports = function filterJsDocs() {\n  return {\n    $runAfter: ['extracting-tags'],\n    $runBefore: ['tags-extracted'],\n    $process: filterDocs\n  };\n};\n"
  },
  {
    "path": "website/docgen/processors/filter-promise.js",
    "content": "var _ = require('lodash');\n\n/**\n * Remove docs of private variables from selenium's promise.js file.\n *\n * @param {Array.<Object>} docs The jsdoc list.\n */\nvar filter = function(docs) {\n  return _.reject(docs, function(doc) {\n    return doc.fileName == \"promise\" && doc.name &&\n        doc.name.substr(0,7) != \"promise\";\n  });\n};\n\n/**\n * Filter functions that will not go into the documentation.\n */\nmodule.exports = function filterPromise() {\n  return {\n    $runAfter: ['extracting-tags'],\n    $runBefore: ['tags-extracted'],\n    $process: filter\n  };\n};\n"
  },
  {
    "path": "website/docgen/processors/set-file-name.js",
    "content": "/**\n * Set the input file name.\n */\nmodule.exports = function setFileName() {\n  return {\n    $runAfter: ['extracting-tags'],\n    $runBefore: ['tags-extracted'],\n    $process: function(docs) {\n      docs.forEach(function(doc) {\n        doc.fileName = doc.fileInfo.baseName;\n      });\n\n      return docs;\n    }\n  };\n};\n"
  },
  {
    "path": "website/docgen/processors/tag-fixer.js",
    "content": "var _ = require('lodash');\nvar marked = require('marked');\n\n/**\n * Add the description property to the doc. The description is all the text that\n * goes before the first annotation.\n * @param {!Object} doc Current doc.\n */\nvar addDescription = function(doc) {\n  doc.description = marked((doc.description || '').replace(/\\n$/, ''));\n};\n\n/**\n * Find the name of the function.\n */\nvar findName = function(doc) {\n  // Skip if the function has a name.\n  if (doc.name) {\n    return doc.name;\n  }\n\n  try {\n    var node = doc.codeNode;\n\n    // Is this a simple declaration? \"var element = function() {\".\n    if (node.declarations && node.declarations.length) {\n      return node.declarations[0].id.name;\n    }\n\n    // Is this an expression? \"elementFinder.find = function() {\".\n    if (node.expression) {\n      var parts = [];\n\n      /**\n       * Recursively create the function name by examining the object property.\n       * @param obj Parsed object.\n       * @return {string} The name of the function.\n       */\n      function buildName(obj) {\n        if (!obj) {\n          return parts.join('.');\n        }\n\n        if (obj.property && obj.property.name) {\n          parts.unshift(obj.property.name);\n        }\n\n        if (obj.object && obj.object.name) {\n          parts.unshift(obj.object.name);\n        }\n\n        return buildName(obj.object);\n      }\n\n      return buildName(node.expression.left);\n    }\n  } catch (e) {\n    console.log('Could not find document name', doc.file, doc.endingLine);\n  }\n};\n\n/**\n * Replace the new lines in an object property.\n * @param {!Object} obj Object with properties.\n * @param {string} prop Property name.\n */\nvar replaceNewLines = function(obj, prop) {\n  if (obj) {\n    obj[prop] = (obj[prop] || '').replace(/\\n\\s+/g, ' ');\n  }\n};\n\n/**\n * Remove the duplicate param annotations. Go through the params and the return\n * annotations to replace the new lines and escape the types to prepare them\n * for markdown rendering.\n *\n * @param {!Object} doc Document representing a function jsdoc.\n */\nvar fixParamsAndReturns = function(doc) {\n  if (doc.params) {\n    _.each(doc.params, function(param) {\n      replaceNewLines(param, 'description');\n    });\n  }\n\n  // Replace new lines in the return and params descriptions.\n  var returns = doc.returns;\n  if (returns) {\n    replaceNewLines(returns, 'description');\n  }\n};\n\n/**\n * Get the name of the function, format the @param and @return annotations to\n * prepare them for rendering.\n */\nmodule.exports = function tagFixer() {\n  return {\n    $runAfter: ['extracting-tags'],\n    $runBefore: ['tags-extracted'],\n    $process: function(docs) {\n      docs.forEach(function(doc) {\n        addDescription(doc);\n        doc.name = findName(doc);\n        fixParamsAndReturns(doc);\n      });\n\n      return docs;\n    }\n  };\n};\n"
  },
  {
    "path": "website/docgen/processors/these-children.js",
    "content": "/**\n * Set the parents of member functions set in the constructor of an object.\n */\nmodule.exports = function theseChildren() {\n  return {\n    $runAfter: ['extracting-tags'],\n    $runBefore: ['tags-extracted'],\n    $process: function(docs) {\n      var parentDoc = docs[0];\n      for (var i = 0; i < docs.length; i++) {\n        doc = docs[i];\n        if (doc.codeNode && doc.codeNode.expression &&\n            doc.codeNode.expression.left  &&\n            doc.codeNode.expression.left.object  &&\n            doc.codeNode.expression.left.object.type  == 'ThisExpression') {\n          doc.name = parentDoc.name + '.prototype.' + doc.name;\n        } else {\n          parentDoc = doc;\n        }\n      }\n    }\n  };\n};\n"
  },
  {
    "path": "website/docgen/processors/transfer-see.js",
    "content": "/**\n * Transfer contents of @see tags to the description\n */\nmodule.exports = function transferSee() {\n  return {\n    $runAfter: ['extracting-tags'],\n    $runBefore: ['tags-extracted'],\n    $process: function(docs) {\n      docs.forEach(function(doc) {\n        if (doc.see) {\n          doc.see.forEach(function(see) {\n            var breakIndex = see.indexOf('\\n\\n');\n            if (breakIndex == -1) {\n              breakIndex = see.length;\n            }\n            see = 'See {@link ' + see.substr(0, breakIndex) + '}' +\n                see.substr(breakIndex);\n            see = see.replace(/\\n\\n/g, '<br />').replace(/\\n/g, ' ');\n            if (doc.description.length != 0) {\n              doc.description += '<br />';\n            }\n            doc.description += see;\n          });\n        }\n      });\n    }\n  };\n};\n"
  },
  {
    "path": "website/docgen/spec/add-links-spec.js",
    "content": "var linksProcessorFn = require('../processors/add-links');\nvar _ = require('lodash');\n\n\ndescribe('add-links', function() {\n  var linksProcessor;\n\n  beforeEach(function() {\n    linksProcessor = linksProcessorFn();\n  });\n\n  var addLinks = function(docs) {\n    linksProcessor.$process(docs);\n  };\n\n  it('should add protractor link', function() {\n    var doc = {\n      fileName: 'protractor',\n      fileInfo: { filePath: '' },\n      startingLine: 123\n    };\n    addLinks([doc]);\n    expect(doc.sourceLink).toBe('https://github.com/angular/protractor/' +\n        'blob/' + require('../../../package.json').version + '/lib/' +\n        'protractor.ts');\n  });\n\n  it('should add links to types', function() {\n    var docWithFunction = {\n      typeExpression: 'function(webdriver.WebElement, number)',\n      fileName: 'protractor',\n      fileInfo: { filePath: '' },\n      startingLine: 123,\n      returns: {\n        tagDef: {\n          name: 'returns',\n          aliases: ['return'],\n          canHaveType: true\n        },\n        tagName: 'return',\n        description: '',\n        startingLine: 119,\n        typeExpression: 'webdriver.WebElement',\n        type: {\n          type: 'NameExpression',\n          name: 'webdriver.WebElement'\n        },\n        typeList: ['webdriver.WebElement']\n      },\n      params: [\n        {\n          tagDef: {\n            name: 'param',\n            multi: true,\n            docProperty: 'params',\n            canHaveName: true,\n            canHaveType: true\n          },\n          tagName: 'param',\n          description: 'Map function that will be applied to each element.',\n          startingLine: 396,\n          typeExpression: 'function(webdriver.WebElement, number)',\n          type: {\n            type: 'FunctionType',\n            params: [\n              {type: 'NameExpression', name: 'webdriver.WebElement'},\n              {type: 'NameExpression', name: 'number'}\n            ]\n          },\n          typeList: ['function(webdriver.WebElement, number)'],\n          name: 'mapFn'\n        },\n        {\n          tagDef: {\n            name: 'param',\n            multi: true,\n            docProperty: 'params',\n            canHaveName: true,\n            canHaveType: true\n          },\n          tagName: 'param',\n          description: '',\n          startingLine: 171,\n          typeExpression: 'Protractor',\n          type: {\n            type: 'NameExpression',\n            name: 'Protractor'\n          },\n          typeList: ['Protractor'],\n          name: 'ptor'\n        }\n      ]\n    };\n\n    // Given a type and a function.\n    var docs = [\n      {\n        name: 'webdriver.WebElement',\n        fileName: 'webdriver',\n        fileInfo: { filePath: 'selenium-webdriver' },\n        startingLine: 123\n      },\n      docWithFunction,\n      {\n        name: 'Protractor',\n        fileName: 'protractor',\n        fileInfo: { filePath: '' },\n        startingLine: 3\n      }\n    ];\n\n    // When you add links.\n    addLinks(docs);\n\n    // Then ensure the link was added.\n    var getDesc = function(index) {\n      return docs[1].params[index].paramString;\n    };\n    expect(getDesc(0)).toBe(\n        'function([webdriver.WebElement](webdriver.WebElement), number)');\n    expect(getDesc(1)).toBe('[Protractor](Protractor)');\n\n    expect(docs[1].returnString).toBe('[webdriver.WebElement](webdriver.WebElement)');\n  });\n\n  it('should add @link links', function() {\n    // Given a doc with a @link annotation.\n    var docs = [\n      {\n        name: 'webdriver.WebElement',\n        fileName: 'webdriver',\n        fileInfo: { filePath: 'selenium-webdriver' },\n        startingLine: 123\n      },\n      {\n        name: 'element.findElements',\n        description: 'A promise that {@link webdriver.WebElement}s',\n        fileName: 'protractor',\n        fileInfo: { filePath: '' },\n        startingLine: 3,\n        returns: {\n          tagDef: {\n            name: 'returns',\n            aliases: ['return'],\n            canHaveType: true\n          },\n          tagName: 'return',\n          description: 'A promise located {@link webdriver.WebElement}s.',\n          startingLine: 119,\n          typeExpression: 'webdriver.WebElement',\n          type: {\n            type: 'NameExpression',\n            name: 'webdriver.WebElement'\n          },\n          typeList: ['webdriver.WebElement']\n        }\n      }\n    ];\n\n    // When you add links.\n    addLinks(docs);\n\n    // Then ensure a link was added to the type.\n    expect(docs[1].description).\n        toBe('A promise that [{@code webdriver.WebElement}](webdriver.WebElement)s');\n    expect(docs[1].returns.description).\n        toBe('A promise located [{@code webdriver.WebElement}](webdriver.WebElement)s.');\n  });\n\n  it('should add @external links', function() {\n    // Given a doc with a @external annotation.\n    var docs = [\n      {\n        name: 'webdriver.ActionSequence',\n        external: 'webdriver.ActionSequence',\n        see: [ 'http://seleniumhq.github.io/selenium/doc' ],\n      },\n      {\n        name: 'browser.action()',\n        description: 'Creates a new action sequence using the driver.',\n        fileName: 'protractor',\n        fileInfo: {filePath: ''},\n        startingLine: 3,\n        returns: {\n          tagDef: {\n            name: 'returns',\n            aliases: ['return'],\n            canHaveType: true\n          },\n          tagName: 'return',\n          description: 'A new action sequence for this instance.',\n          startingLine: 119,\n          typeExpression: 'webdriver.ActionSequence',\n          type: {\n            type: 'NameExpression',\n            name: 'webdriver.ActionSequence'\n          },\n          typeList: ['webdriver.ActionSequence']\n        }\n      }\n    ];\n\n    // When you add links.\n    addLinks(docs);\n\n    // Then ensure a link was added to the type.\n    expect(docs[1].returnString).toBe('[webdriver.ActionSequence](http://seleniumhq.github.io/selenium/doc)');\n  });\n\n  it('should handle {@link type desc} links', function() {\n    // Given a doc with a @link annotation.\n    var docs = [\n      {\n        name: 'webdriver.WebElement',\n        fileName: 'webdriver',\n        fileInfo: { filePath: 'selenium-webdriver' },\n        startingLine: 123\n      },\n      {\n        name: 'element.findElements',\n        description: 'A promise that {@link webdriver.WebElement Web Elements}',\n        fileName: 'protractor',\n        fileInfo: { filePath: '' },\n        startingLine: 3,\n        returns: {\n          tagDef: {\n            name: 'returns',\n            aliases: ['return'],\n            canHaveType: true\n          },\n          tagName: 'return',\n          description: 'A promise located {@link webdriver.NOT_A_THING Web Elements}.',\n          startingLine: 119,\n          typeExpression: 'webdriver.WebElement',\n          type: {\n            type: 'NameExpression',\n            name: 'webdriver.WebElement'\n          },\n          typeList: ['webdriver.WebElement']\n        }\n      }\n    ];\n\n    // When you add links.\n    addLinks(docs);\n\n    // Then ensure a link was added to the type.\n    expect(docs[1].description).\n        toBe('A promise that [{@code Web Elements}](webdriver.WebElement)');\n    expect(docs[1].returns.description).\n        toBe('A promise located {@code Web Elements}.');\n  });\n\n  it('should handle \"#\" in @link links', function() {\n    // Given a doc with a @link annotation.\n    var docs = [\n      {\n        name: 'webdriver.WebDriver',\n        fileName: 'webdriver',\n        fileInfo: { filePath: 'selenium-webdriver' },\n        startingLine: 123\n      },\n      {\n        name: 'webdriver.WebElement',\n        description: 'A promise that {@link #WebDriver Web Drivers}',\n        fileName: 'webdriver',\n        fileInfo: { filePath: 'selenium-webdriver' },\n        startingLine: 3,\n        returns: {\n          tagDef: {\n            name: 'returns',\n            aliases: ['return'],\n            canHaveType: true\n          },\n          tagName: 'return',\n          description: 'A promise located {@link webdriver#WebElement Web Elements}.',\n          startingLine: 119,\n          typeExpression: 'webdriver.WebElement',\n          type: {\n            type: 'NameExpression',\n            name: 'webdriver.WebElement'\n          },\n          typeList: ['webdriver.WebElement']\n        }\n      }\n    ];\n\n    // When you add links.\n    addLinks(docs);\n\n    // Then ensure a link was added to the type.\n    expect(docs[1].description).\n        toBe('A promise that [{@code Web Drivers}](webdriver.WebDriver)');\n    expect(docs[1].returns.description).\n        toBe('A promise located {@code Web Elements}.');\n  });\n\n  it('should remove extraneous characters from @link links', function() {\n    // Given a doc with a @link annotation.\n    var docs = [\n      {\n        name: 'webdriver.WebElement',\n        fileName: 'webdriver',\n        fileInfo: { filePath: 'selenium-webdriver' },\n        startingLine: 123\n      },\n      {\n        name: 'element.findElements',\n        description: 'A promise that {@link webdriver.WebElement()}',\n        fileName: 'protractor',\n        fileInfo: { filePath: '' },\n        startingLine: 3,\n        returns: {\n          tagDef: {\n            name: 'returns',\n            aliases: ['return'],\n            canHaveType: true\n          },\n          tagName: 'return',\n          description: 'A promise located {@linkplain webdriver.WebElement     Web Elements }.',\n          startingLine: 119,\n          typeExpression: 'webdriver.WebElement',\n          type: {\n            type: 'NameExpression',\n            name: 'webdriver.WebElement'\n          },\n          typeList: ['webdriver.WebElement']\n        }\n      }\n    ];\n\n    // When you add links.\n    addLinks(docs);\n\n    // Then ensure a link was added to the type.\n    expect(docs[1].description).\n        toBe('A promise that [{@code webdriver.WebElement()}](webdriver.WebElement)');\n    expect(docs[1].returns.description).\n        toBe('A promise located [Web Elements](webdriver.WebElement).');\n\n  });\n});\n"
  },
  {
    "path": "website/docgen/spec/element-array-finder-all.json",
    "content": "{\n  \"codeNode\": {\n    \"type\": \"ExpressionStatement\",\n    \"expression\": {\n      \"type\": \"AssignmentExpression\",\n      \"operator\": \"=\",\n      \"left\": {\n        \"type\": \"MemberExpression\",\n        \"object\": {\n          \"type\": \"MemberExpression\",\n          \"object\": {\n            \"type\": \"Identifier\",\n            \"name\": \"ElementArrayFinder\"\n          },\n          \"property\": {\n            \"type\": \"Identifier\",\n            \"name\": \"prototype\"\n          }\n        },\n        \"property\": {\n          \"type\": \"Identifier\",\n          \"name\": \"all\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "website/docgen/spec/element-array-finder.json",
    "content": "{\n  \"codeNode\": {\n    \"type\": \"VariableDeclaration\",\n    \"declarations\": [\n      {\n        \"type\": \"VariableDeclarator\",\n        \"id\": {\n          \"type\": \"Identifier\",\n          \"name\": \"ElementArrayFinder\"\n        }\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "website/docgen/spec/tag-fixer-spec.js",
    "content": "var tagFixerFn = require('../processors/tag-fixer');\nvar elementArrayFinder = require('./element-array-finder.json');\nvar elementAll = require('./element-array-finder-all.json');\nvar _ = require('lodash');\n\ndescribe('tag fixer', function() {\n  var classMethodStatement, constructorStatement, docs, tagFixer;\n\n  beforeEach(function() {\n    tagFixer = tagFixerFn();\n  });\n\n  beforeEach(function() {\n    constructorStatement = _.cloneDeep(elementArrayFinder);\n    classMethodStatement = _.cloneDeep(elementAll);\n\n    docs = [constructorStatement, classMethodStatement];\n  });\n\n  it('should find name for method declaration', function() {\n    // When you process the docs.\n    tagFixer.$process(docs);\n\n    // Then ensure the name was parsed.\n    expect(classMethodStatement.name).toBe('ElementArrayFinder.prototype.all');\n  });\n\n  it('should find name for constructor declaration', function() {\n    // When you process the docs.\n    tagFixer.$process(docs);\n\n    // Then ensure the name was parsed.\n    expect(constructorStatement.name).toBe('ElementArrayFinder');\n  });\n\n  it('should not override name', function() {\n    // Given that the doc has a @name.\n    classMethodStatement.name = 'name1';\n    constructorStatement.name = 'name2';\n\n    // When you process the docs.\n    tagFixer.$process(docs);\n\n    // Then ensure the name was not changed.\n    expect(classMethodStatement.name).toBe('name1');\n    expect(constructorStatement.name).toBe('name2');\n  });\n});\n"
  },
  {
    "path": "website/docgen/spec/transfer-see-spec.js",
    "content": "var seeProcessorFn = require('../processors/transfer-see');\n\n\ndescribe('transfer-see', function() {\n  var seeProcessor;\n\n  beforeEach(function() {\n    seeProcessor = seeProcessorFn();\n  });\n\n  var transferSee = function(docs) {\n    seeProcessor.$process(docs);\n  };\n\n  it('should put @see info in the description', function() {\n    var doc = {\n      description: '',\n      see: ['greetings.HelloWorld']\n    };\n    transferSee([doc]);\n    expect(doc.description).toBe('See {@link greetings.HelloWorld}');\n  });\n\n  it('should append @see info to the description', function() {\n    var doc = {\n      description: 'Hello, World',\n      see: ['greetings.HelloWorld']\n    };\n    transferSee([doc]);\n    expect(doc.description).toBe('Hello, World<br />See {@link greetings.HelloWorld}');\n  });\n\n  it('should handle multiple @see tags', function() {\n    var doc = {\n      description: '',\n      see: ['greetings.HelloWorld', 'greetings.HolaMundo']\n    };\n    transferSee([doc]);\n    expect(doc.description).toBe('See {@link greetings.HelloWorld}<br />' +\n        'See {@link greetings.HolaMundo}');\n  });\n\n  it('should handle information after @see tag', function() {\n    var doc = {\n      description: '',\n      see: ['greetings.HelloWorld\\n\\nHello, World']\n    };\n    transferSee([doc]);\n    expect(doc.description).toBe('See {@link greetings.HelloWorld}<br />Hello, World');\n  });\n});\n"
  },
  {
    "path": "website/docgen/templates/toc-template.txt",
    "content": "{\n  \"version\": \"{{doc.version}}\",\n  \"items\": {{doc.toc|json}}\n}\n"
  },
  {
    "path": "website/gulpfile.js",
    "content": "var path = require('path');\nvar concat = require('gulp-concat');\nvar connect = require('gulp-connect');\nvar del = require('del');\nvar Dgeni = require('dgeni');\nvar gulp = require('gulp');\nvar less = require('gulp-less');\nvar markdown = require('gulp-markdown');\nvar cleanCSS = require('gulp-clean-css');\nvar rename = require('gulp-rename');\nvar replace = require('gulp-replace');\n\nvar paths = {\n  build: ['build', 'docgen/build'],\n  docs: ['../docs/*.md'],\n  dgeniTemplates: ['docgen/templates/*.txt', 'docgen/processors/*.js'],\n  html: ['index.html', 'partials/*.html'],\n  js: [\n    'js/modules.js',\n    'js/**/*.js',\n    'bower_components/bootstrap/dist/js/bootstrap.min.js',\n    'bower_components/lodash/dist/lodash.min.js'\n  ],\n  less: ['css/protractor.less'],\n  outputDir: 'build/'\n};\n\ngulp.task('clean', function(cb) {\n  del(paths.build, cb);\n});\n\n// Generate the table of contents json file using Dgeni. This is output to\n// docgen/build/toc.json\ngulp.task('dgeni', function(done) {\n  var packages = [require('./docgen/dgeni-config')];\n  var dgeni = new Dgeni(packages);\n\n  dgeni.generate().then(function(docs) {\n    console.log(docs.length, 'docs generated');\n  }).then(function() {\n    // Check that docs were generated correctly\n    var toc = require('./docgen/build/toc.json');\n    if (!toc || !Array.isArray(toc.items)) {\n      return Promise.reject('Generated toc.json file is malformatted');\n    }\n    var isBrowser = function(item) {\n      return item.alias == 'browser';\n    };\n    if (!toc.items.some(isBrowser)) {\n      return Promise.reject('Generated toc.json missing docs for Protractor function.');\n    }\n\n    // Copy files over\n    gulp.src(['docgen/build/*.json'])\n        .pipe(gulp.dest(paths.outputDir + '/apiDocs'));\n    done();\n  }).catch(function(error) {\n    done(\n        'Could not generate docs.  ' +\n        'Try running `npm run compile_to_es5` from Protractor\\'s root folder.\\n' +\n        'Origonal Error: ' + error);\n  });\n});\n\ngulp.task('copyFiles', function() {\n  // html.\n  gulp.src('index.html')\n      .pipe(gulp.dest(paths.outputDir));\n  gulp.src('partials/*.html')\n      .pipe(gulp.dest(paths.outputDir + '/partials'));\n\n  // Images.\n  gulp.src('img/**')\n      .pipe(gulp.dest('build/img'));\n});\n\ngulp.task('js', function() {\n  gulp.src(paths.js)\n      .pipe(concat('protractorApp.js'))\n      .pipe(gulp.dest(paths.outputDir));\n});\n\ngulp.task('less', function() {\n  gulp.src(paths.less)\n      .pipe(less())\n      .pipe(cleanCSS())\n      .pipe(gulp.dest(paths.outputDir + '/css'));\n});\n\ngulp.task('connect', function() {\n  connect.server({\n    root: 'build',\n    livereload: true,\n    open: {\n      browser: 'Google Chrome'\n    }\n  });\n});\n\ngulp.task('reloadServer', function() {\n  gulp.src(paths.outputDir)\n      .pipe(connect.reload());\n});\n\ngulp.task('watch', function() {\n  gulp.watch(paths.html, ['copyFiles', 'reloadServer']);\n  gulp.watch(paths.docs, ['markdown', 'reloadServer']);\n  gulp.watch(paths.js, ['js', 'reloadServer']);\n  gulp.watch(paths.less, ['less', 'reloadServer']);\n  gulp.watch(paths.dgeniTemplates, ['dgeni', 'copyFiles', 'reloadServer']);\n});\n\n// Transform md files to html.\ngulp.task('markdown', function() {\n  var version = require('../package.json').version\n  gulp.src(['../docs/*.md', '!../docs/api.md'])\n      // Parse markdown.\n      .pipe(markdown())\n      // Fix urls which reference files only on github.\n      .pipe(replace(\n          /(href|src)=\"(?:([\\-\\.\\w\\/]+)\\/)?(\\w+\\.\\w+(?:#.*)?)?\"/g,\n          function(match, attr, folder, file) {\n            var ext = file ? file.match(/\\w+\\.(\\w+)(?:#.*)?/)[1] : null;\n            // Don't process .md and .png files which are on the website\n            if (((ext == 'md') || (ext == 'png')) && (!folder ||\n                (path.resolve('/docs', folder).split('/')[1] == 'docs'))) {\n              return match;\n            }\n            if (!folder) {\n              folder = 'docs';\n            } else if (folder[0] == '/') {\n              folder = folder.slice(1);\n            }\n            return attr + '=\"https://github.com/angular/protractor/blob/' +\n                version + '/' + folder + '/' + (file || '') + '\"';\n          }\n      ))\n      // Fix in-page hash paths.\n      .pipe(replace(/\"#([^ ]*?)\"/g, '#{{path}}#$1'))\n      // Fix md urls.\n      .pipe(replace(/\"(?:\\/docs\\/)?([\\w\\-]+)\\.md/g, '\"#/$1'))\n      // Fix png urls.\n      .pipe(replace(/\"(?:\\/docs\\/)?([\\w\\-]+\\.png)\"/g, '\"img/$1\"'))\n      // Add anchor links\n      .pipe(replace(/<h2 id=\"([^\"]*)\">(.*?)<\\/h2>/g, '<h2 id=\"$1\" class=\"' +\n          'anchored\"><div><a href=\"#{{path}}#$1\">&#x1f517;</a>$2</div></h2>'))\n      // Decorate tables.\n      .pipe(replace(/<table>/g, '<table class=\"table table-striped\">'))\n      // Fix <code> blocks to not interpolate Angular\n      .pipe(replace(/<code>/g, '<code ng-non-bindable>'))\n      .pipe(rename(function(path) {\n        path.extname = '.html';\n      }))\n      .pipe(gulp.dest('./build/partials'));\n});\n\n// Make version of testapp for github page\ngulp.task('testapp', function() {\n  var stream = gulp.src('../testapp/**/*').\n      pipe(gulp.dest('build/testapp'));\n  gulp.src('testapp/*').\n      pipe(gulp.dest('build/testapp/ng1'));\n  var angular_version = require('../testapp/ng1/lib/angular_version.js');\n  gulp.src('../testapp/ng1/lib/angular_v' + angular_version + '/**/*').\n      pipe(gulp.dest('build/testapp/ng1/lib/angular'));\n  return stream;\n});\n\ngulp.task('cleanup_testapp', ['testapp'], function() {\n  del('build/testapp/ng1/lib/angular_v*');\n});\n\n// Start a server and watch for changes.\ngulp.task('liveReload', [\n  'default',\n  'connect',\n  'watch'\n]);\n\ngulp.task('default', [\n  'testapp',\n  'cleanup_testapp',\n  'dgeni',\n  'less',\n  'markdown',\n  'js',\n  'copyFiles'\n]);\n"
  },
  {
    "path": "website/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head lang=\"en\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <meta charset=\"UTF-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <link rel=\"stylesheet\" href=\"css/protractor.css\"/>\n  <link rel=\"icon\" href=\"img/favicon.ico\" sizes=\"16x16 32x32 48x48 64x64\"\n      type=\"image/x-icon\">\n  <title>Protractor - end-to-end testing for AngularJS</title>\n</head>\n<body ng-app=\"protractorApp\">\n<nav class=\"navbar navbar-default navbar-static navbar-fixed-top\">\n  <div class=\"container-fluid\">\n    <div class=\"navbar-header\">\n      <button class=\"navbar-toggle\" type=\"button\" data-toggle=\"collapse\" data-target=\".bs-example-js-navbar-collapse\">\n        <span class=\"sr-only\">Toggle navigation</span>\n        <span class=\"icon-bar\"></span>\n        <span class=\"icon-bar\"></span>\n        <span class=\"icon-bar\"></span>\n      </button>\n    </div>\n    <div class=\"container\">\n      <div class=\"collapse navbar-collapse bs-example-js-navbar-collapse\">\n        <ul class=\"nav navbar-nav\">\n          <!-- Home -->\n          <li><a href=\"#/\">Home</a></li>\n\n          <!-- Quick start -->\n          <li class=\"dropdown\">\n            <a id=\"drop1\" href=\"javascript:void(0)\" class=\"dropdown-toggle\"\n                data-toggle=\"dropdown\">Quick Start <span class=\"caret\"></span></a>\n            <ul class=\"dropdown-menu\">\n              <li><a tabindex=\"-1\" href=\"#/tutorial\">Tutorial</a></li>\n            </ul>\n          </li>\n\n          <!-- Protractor Setup -->\n          <li class=\"dropdown\">\n            <a id=\"drop2\" href=\"javascript:void(0)\" class=\"dropdown-toggle\"\n                data-toggle=\"dropdown\">Protractor Setup <span class=\"caret\"></span></a>\n            <ul class=\"dropdown-menu\">\n              <li><a tabindex=\"-1\" href=\"#/protractor-setup\">Setting Up Protractor</a></li>\n              <li><a tabindex=\"-1\" href=\"#/server-setup\">Setting Up the Selenium Server</a></li>\n              <li><a tabindex=\"-1\" href=\"#/browser-setup\">Setting Up the Browser</a></li>\n              <li><a tabindex=\"-1\" href=#/frameworks>Choosing a Framework</a></li>\n            </ul>\n          </li>\n\n          <!-- Protractor Tests -->\n          <li class=\"dropdown\">\n            <a id=\"drop3\" href=\"javascript:void(0)\" class=\"dropdown-toggle\"\n                data-toggle=\"dropdown\">Protractor Tests <span class=\"caret\"></span></a>\n            <ul class=\"dropdown-menu\">\n              <li><a tabindex=\"-1\" href=\"#/getting-started\">Getting Started</a></li>\n              <li><a tabindex=\"-1\" href=\"#/tutorial\">Tutorial</a></li>\n              <li><a tabindex=\"-1\" href=\"#/api-overview\">Working with Spec and Config Files</a></li>\n              <li><a tabindex=\"-1\" href=\"#/system-setup\">Setting Up the System Under Test</a></li>\n              <li><a tabindex=\"-1\" href=\"#/locators\">Using Locators</a></li>\n              <li><a tabindex=\"-1\" href=\"#/page-objects\">Using Page Objects to Organize Tests</a></li>\n              <li><a tabindex=\"-1\" href=\"#/debugging\">Debugging Protractor Tests</a></li>\n            </ul>\n          </li>\n\n          <!-- Reference -->\n          <li class=\"dropdown\">\n            <a id=\"drop4\" href=\"javascript:void(0)\" class=\"dropdown-toggle\"\n                data-toggle=\"dropdown\">Reference <span class=\"caret\"></span></a>\n            <ul class=\"dropdown-menu\">\n              <li><a tabindex=\"-1\" href=\"https://github.com/angular/protractor/blob/master/lib/config.ts\">Configuration File</a></li>\n              <li><a tabindex=\"-1\" href=\"#/api\">Protractor API</a></li>\n              <li><a tabindex=\"-1\" href=\"#/style-guide\">Style Guide</a></li>\n              <li><a tabindex=\"-1\" href=\"#/webdriver-vs-protractor\">Protractor Syntax vs WebDriverJS Syntax</a></li>\n              <li><a tabindex=\"-1\" href=\"#/browser-support\">Browser Support</a></li>\n              <li><a tabindex=\"-1\" href=\"#/plugins\">Plugins</a></li>\n              <li><a tabindex=\"-1\" href=\"#/timeouts\">Timeouts</a></li>\n              <li><a tabindex=\"-1\" href=\"#/control-flow\">The WebDriver Control Flow</a></li>\n              <li><a tabindex=\"-1\" href=\"#/typescript\">Using TypeScript</a></li>\n              <li><a tabindex=\"-1\" href=\"#/async-await\">Using async/await</a></li>\n              <li><a tabindex=\"-1\" href=\"#/infrastructure\">How It Works</a></li>\n              <li><a tabindex=\"-1\" href=\"#/jasmine-upgrade\">Upgrading to Jasmine 2.x</a></li>\n              <li><a tabindex=\"-1\" href=\"#/mobile-setup\">Mobile Setup</a></li>\n              <li><a tabindex=\"-1\" href=\"#/faq\">FAQ</a></li>\n            </ul>\n          </li>\n        </ul>\n      </div><!-- /.nav-collapse -->\n    </div>\n  </div><!-- /.container-fluid -->\n</nav>\n<div class=\"container protractor-container\" ng-view autoscroll=\"true\"></div>\n<script src=\"//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js\"></script>\n<script src=\"//ajax.googleapis.com/ajax/libs/angularjs/1.3.7/angular.min.js\"></script>\n<script src=\"//ajax.googleapis.com/ajax/libs/angularjs/1.3.7/angular-route.min.js\"></script>\n<script src=\"protractorApp.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "website/js/api-controller.js",
    "content": "(function() {\n  /**\n   * Controller for the protractor api view.\n   *\n   * @constructor\n   * @ngInject\n   * @param $anchorScroll anchorScroll service.\n   * @param $http HTTP service.\n   * @param $location Location service.\n   * @param $route Route service.\n   * @param $sce Strict Contextual Escaping service.\n   * @param $scope Angular scope.\n   */\n  var ApiCtrl = function($anchorScroll, $http, $location, $route, $sce, $scope) {\n    this.$http = $http;\n    this.$route = $route;\n    this.$scope = $scope;\n\n    this.loadTableOfContents();\n\n    $scope.items = [];\n    $scope.isMenuVisible = false;\n    $scope.currentItem = null;\n\n    // Watch for location changes to show the correct item.\n    $scope.$on('$locationChangeSuccess', function() {\n      // Not going to api? ignore event.\n      if (!$location.url().match(/^\\/api/)) {\n        return;\n      }\n\n      var view = $route.current.params.view,\n          item = _.findWhere($scope.items, {name: view});\n\n      if (view && item) {\n        $scope.showElement(item);\n      } else {\n        // No view? Show default item.\n        $scope.currentItem = defaultItem;\n      }\n    });\n\n    $scope.toggleMenuLabel = function() {\n      return $scope.isMenuVisible ? 'Hide list' : 'Show list';\n    };\n\n    $scope.toggleMenu = function() {\n      $scope.isMenuVisible = !$scope.isMenuVisible;\n    };\n\n    $scope.showElement = function(item) {\n      // Update the query string with the view name.\n      $location.search('view', item.name);\n      $scope.currentItem = item;\n\n      // Scroll to the top.\n      $anchorScroll();\n    };\n\n    $scope.trust = function(html) {\n      return trustHTML($sce, html);\n    };\n  };\n\n  /** Load the json data containing the toc. */\n  ApiCtrl.prototype.loadTableOfContents = function() {\n    var self = this;\n    var $scope = self.$scope;\n\n    this.$http.get('apiDocs/toc.json').success(function(data) {\n      var list = data.items;\n\n      // Remove 'WebdriverBy.prototype' from the list.\n      list = Array.prototype.filter.call(list, function(item) {\n        return item.name !== 'WebdriverBy.prototype';\n      });\n\n      self.setViewProperties(list);\n      self.addExtends(list);\n      var items = self.organizeItems(list);\n\n      $scope.items = items;\n      $scope.version = data.version;\n\n      // Show the view if is defined in the query string.\n      var view = self.$route.current.params.view;\n      if (view) {\n        items.forEach(function(item) {\n          if (view === item.name) {\n            self.$scope.showElement(item);\n          }\n        });\n      }\n\n    });\n  };\n\n  /**\n   * Set the extra properties used by the view to show the docs.\n   * @param list\n   */\n  ApiCtrl.prototype.setViewProperties = function(list) {\n    var itemsByName = this.itemsByName = {};\n\n    var getTitle = function(item) {\n      if (item.alias) {\n        return item.alias;\n      }\n\n      var fnName = item.name;\n      // Is the parent already visited?\n      var parts = fnName.match('(.*)\\\\.prototype\\\\.(.*)');\n      if (parts && itemsByName[parts[1]]) {\n        var parent = itemsByName[parts[1]];\n        return parent.title + '.' + parts[2];\n      }\n\n      return fnName;\n    };\n\n    // Add display name and title.\n    list.forEach(function(item) {\n      item.title = getTitle(item);\n\n      var nameWithoutPrototype = item.name.replace(/\\.prototype/, '');\n\n      itemsByName[item.name] = item;\n      itemsByName[nameWithoutPrototype] = item;\n\n      item.displayName = nameWithoutPrototype;\n\n      // Add short description.\n      if (item.description) {\n        // Find the correct portion of the description\n\n        // The following parsing is OK most of the time\n        var sentenceEnd = item.description.search(/\\.\\s|\\.$/) + 1 || Infinity;\n        var paragraphEnd = item.description.indexOf('</p>') + 4;\n        if (paragraphEnd == 3) {\n          paragraphEnd = Infinity;\n        }\n        var shortDescription = item.description.substring(0, Math.min(\n            item.description.length, sentenceEnd, paragraphEnd)).trim();\n\n        // Remove <p> tags\n        if (shortDescription.substr(0,3) == '<p>') {\n          shortDescription = shortDescription.substr(3);\n          if (shortDescription.substr(-4) == '</p>') {\n            shortDescription = shortDescription.substr(0, shortDescription.length - 4);\n          }\n        }\n        item.shortDescription = shortDescription;\n      }\n    });\n\n    /**\n     * Try to find a parent by matching the longest substring of the display\n     * name.\n     * @param item\n     */\n    var findClosestParent = function(item) {\n      var parts = item.displayName.split('.');\n      for (var i = parts.length - 1; i > 0; i--) {\n        var name = parts.slice(0, i).join('.');\n        if (itemsByName[name]) {\n          return itemsByName[name];\n        }\n      }\n    };\n\n    list.forEach(function(item) {\n      var parent = findClosestParent(item);\n      if (parent) {\n        item.type = 'child';\n        item.displayName =\n            item.displayName.replace(new RegExp('^' + parent.name + '\\\\.'), '');\n\n        if (!parent.children) {\n          parent.children = [];\n        }\n        parent.children.push(item);\n      }\n    });\n  };\n\n  /**\n   * Organize items according to class & inheritance, note every item's depth,\n   * and add file name items to the list.\n   *\n   * @param list The list of items\n   * @return {Array} A modified, reorganized list\n   */\n  ApiCtrl.prototype.organizeItems = function(list) {\n    var newList = [];\n    var self = this;\n\n    var addItemToList = function(item, depth) {\n      if (item.inList) {\n        return;\n      }\n      item.treeClasses = 'depth-' + depth;\n      if (item.extension) {\n        item.treeClasses += ' extension';\n        depth--; // For the children\n      }\n      item.inList = true;\n      newList.push(item);\n      if (item.children) {\n        item.children.forEach(function(child) {\n          addItemToList(child, depth + 1);\n        });\n      }\n      if (item.extends) {\n        var parent = self.itemsByName[item.base.name];\n        if (parent != null) {\n          addItemToList(parent, depth + 1);\n        }\n      }\n    };\n\n    var prevFileName;\n    list.forEach(function(item) {\n      if ((item.type !== 'child') && !item.extension) {\n        if (prevFileName !== item.fileName) {\n          prevFileName = item.fileName;\n          newList.push({\n            displayName: item.fileName,\n            isTitle: true,\n            type: 'title',\n            treeClasses: 'depth-0'\n          });\n        }\n\n        addItemToList(item, 0);\n      }\n    });\n\n    return newList;\n  };\n\n  // TODO: This is a hack for getting the 'Inherited from Webdriver...' stuff.\n  // Instead, move our extra docs to colocate with our fns, remove the selenium-webdriver\n  // folder, and remove this.\n  ApiCtrl.prototype.addExtends = function(list) {\n    var self = this;\n    list.forEach(function(item) {\n      if (!item.extends) {\n        return;\n      }\n      // Remove braces from {type}.\n      var parentName = item.extends.replace(/[{}]/g, '');\n      var nameExpr = new RegExp(parentName + '\\\\.prototype');\n      var parent = self.itemsByName[parentName];\n\n      if (parent) {\n        item.base = parent;\n        parent.extension = true;\n      } else {\n        item.base = {\n          name: parentName,\n          children: _.filter(list, function(item) {\n            return item.name && item.name.match(nameExpr);\n          }),\n        };\n      }\n    });\n  };\n\n  angular.module('protractorApp').controller('ApiCtrl', ApiCtrl);\n})();\n"
  },
  {
    "path": "website/js/directives.js",
    "content": "/**\n * Pretty print directives. They use google-code-prettify.\n * The 'prettyPrintOne' function is defined by Google pretty print at:\n * https://google-code-prettify.googlecode.com/svn/loader/prettify.js\n */\n(function() {\n  var module = angular.module('protractorApp');\n\n  /**\n   * Used to print code bound to the scope.\n   * <pre ptor-code=\"scopeVar\"></pre>\n   */\n  module.directive('ptorCode', function() {\n    return {\n      scope: {\n        code: '=ptorCode'\n      },\n      link: function(scope, element) {\n        scope.$watch('code', function() {\n          element.html(prettyPrintOne(scope.code));\n        });\n      }\n    };\n  });\n\n  /**\n   * Used to pretty print code inside a <code> tag. The tag must have a class\n   * 'lang-js' or 'lang-javascript' in order to be painted.\n   *\n   */\n  module.directive('code', function() {\n    return {\n      restrict: 'E',\n      compile: function(tElement, attrs) {\n        var prettyHtml,\n            shouldPaint = /lang-j.*/.test(attrs.class);\n\n        if (shouldPaint) {\n          prettyHtml = prettyPrintOne(tElement.html());\n        }\n\n        return function(scope, element) {\n          if (shouldPaint) {\n            element.html(prettyHtml);\n          }\n        };\n      }\n    };\n  });\n\n  /**\n   * Show the child functions.\n   */\n  module.directive('ptorFunctionList', function() {\n    return {\n      scope: {\n        list: '=ptorFunctionList'\n      },\n      controller: function($scope, $sce) {\n        $scope.trust = function(html) {\n          return trustHTML($sce, html);\n        };\n      },\n      templateUrl: 'partials/ptor-function-list.html'\n    };\n  });\n\n  /**\n   * Twitter button. Copy pasted from:\n   * https://about.twitter.com/resources/buttons#follow\n   */\n  module.directive('ptorTwitter', function() {\n    var showFollowButton = function() {\n      var tagName = 'script',\n          id = 'twitter-wjs',\n          fjs = document.getElementsByTagName(tagName)[0],\n          protocol = /^http:/.test(document.location) ? 'http' : 'https';\n\n      if (!document.getElementById(id)) {\n        var js = document.createElement(tagName);\n        js.id = id;\n        js.src = protocol + '://platform.twitter.com/widgets.js';\n        fjs.parentNode.insertBefore(js, fjs);\n      } else {\n        // The twitter script was loaded already.\n        window.twttr.widgets.load();\n      }\n    };\n\n    return {\n      link: function() {\n        showFollowButton();\n      },\n      template: '<div class=\"twitter\">' +\n      '<a href=\"https://twitter.com/ProtractorTest\" ' +\n      'class=\"twitter-follow-button\" data-show-count=\"false\" ' +\n      'data-size=\"large\">Follow @ProtractorTest</a></div>'\n    };\n  });\n})();\n"
  },
  {
    "path": "website/js/markdown-controller.js",
    "content": "(function() {\n  /**\n   * Controller for webpages derived from markdown files\n   *\n   * @constructor\n   * @ngInject\n   * @param $location Location service.\n   * @param $scope Angular scope.\n   */\n  var MarkdownCtrl = function($location, $scope) {\n    $scope.path = $location.path();\n  };\n\n  angular.module('protractorApp').controller('MarkdownCtrl', MarkdownCtrl);\n})();\n"
  },
  {
    "path": "website/js/modules.js",
    "content": "'use strict';\n\nangular.module('protractorApp', ['ngRoute']);\n"
  },
  {
    "path": "website/js/prettify.js",
    "content": "!function(){var q=null;window.PR_SHOULD_USE_CONTINUATION=!0;\n(function(){function S(a){function d(e){var b=e.charCodeAt(0);if(b!==92)return b;var a=e.charAt(1);return(b=r[a])?b:\"0\"<=a&&a<=\"7\"?parseInt(e.substring(1),8):a===\"u\"||a===\"x\"?parseInt(e.substring(2),16):e.charCodeAt(1)}function g(e){if(e<32)return(e<16?\"\\\\x0\":\"\\\\x\")+e.toString(16);e=String.fromCharCode(e);return e===\"\\\\\"||e===\"-\"||e===\"]\"||e===\"^\"?\"\\\\\"+e:e}function b(e){var b=e.substring(1,e.length-1).match(/\\\\u[\\dA-Fa-f]{4}|\\\\x[\\dA-Fa-f]{2}|\\\\[0-3][0-7]{0,2}|\\\\[0-7]{1,2}|\\\\[\\S\\s]|[^\\\\]/g),e=[],a=\nb[0]===\"^\",c=[\"[\"];a&&c.push(\"^\");for(var a=a?1:0,f=b.length;a<f;++a){var h=b[a];if(/\\\\[bdsw]/i.test(h))c.push(h);else{var h=d(h),l;a+2<f&&\"-\"===b[a+1]?(l=d(b[a+2]),a+=2):l=h;e.push([h,l]);l<65||h>122||(l<65||h>90||e.push([Math.max(65,h)|32,Math.min(l,90)|32]),l<97||h>122||e.push([Math.max(97,h)&-33,Math.min(l,122)&-33]))}}e.sort(function(e,a){return e[0]-a[0]||a[1]-e[1]});b=[];f=[];for(a=0;a<e.length;++a)h=e[a],h[0]<=f[1]+1?f[1]=Math.max(f[1],h[1]):b.push(f=h);for(a=0;a<b.length;++a)h=b[a],c.push(g(h[0])),\nh[1]>h[0]&&(h[1]+1>h[0]&&c.push(\"-\"),c.push(g(h[1])));c.push(\"]\");return c.join(\"\")}function s(e){for(var a=e.source.match(/\\[(?:[^\\\\\\]]|\\\\[\\S\\s])*]|\\\\u[\\dA-Fa-f]{4}|\\\\x[\\dA-Fa-f]{2}|\\\\\\d+|\\\\[^\\dux]|\\(\\?[!:=]|[()^]|[^()[\\\\^]+/g),c=a.length,d=[],f=0,h=0;f<c;++f){var l=a[f];l===\"(\"?++h:\"\\\\\"===l.charAt(0)&&(l=+l.substring(1))&&(l<=h?d[l]=-1:a[f]=g(l))}for(f=1;f<d.length;++f)-1===d[f]&&(d[f]=++x);for(h=f=0;f<c;++f)l=a[f],l===\"(\"?(++h,d[h]||(a[f]=\"(?:\")):\"\\\\\"===l.charAt(0)&&(l=+l.substring(1))&&l<=h&&\n(a[f]=\"\\\\\"+d[l]);for(f=0;f<c;++f)\"^\"===a[f]&&\"^\"!==a[f+1]&&(a[f]=\"\");if(e.ignoreCase&&m)for(f=0;f<c;++f)l=a[f],e=l.charAt(0),l.length>=2&&e===\"[\"?a[f]=b(l):e!==\"\\\\\"&&(a[f]=l.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return\"[\"+String.fromCharCode(a&-33,a|32)+\"]\"}));return a.join(\"\")}for(var x=0,m=!1,j=!1,k=0,c=a.length;k<c;++k){var i=a[k];if(i.ignoreCase)j=!0;else if(/[a-z]/i.test(i.source.replace(/\\\\u[\\da-f]{4}|\\\\x[\\da-f]{2}|\\\\[^UXux]/gi,\"\"))){m=!0;j=!1;break}}for(var r={b:8,t:9,n:10,v:11,\nf:12,r:13},n=[],k=0,c=a.length;k<c;++k){i=a[k];if(i.global||i.multiline)throw Error(\"\"+i);n.push(\"(?:\"+s(i)+\")\")}return RegExp(n.join(\"|\"),j?\"gi\":\"g\")}function T(a,d){function g(a){var c=a.nodeType;if(c==1){if(!b.test(a.className)){for(c=a.firstChild;c;c=c.nextSibling)g(c);c=a.nodeName.toLowerCase();if(\"br\"===c||\"li\"===c)s[j]=\"\\n\",m[j<<1]=x++,m[j++<<1|1]=a}}else if(c==3||c==4)c=a.nodeValue,c.length&&(c=d?c.replace(/\\r\\n?/g,\"\\n\"):c.replace(/[\\t\\n\\r ]+/g,\" \"),s[j]=c,m[j<<1]=x,x+=c.length,m[j++<<1|1]=\na)}var b=/(?:^|\\s)nocode(?:\\s|$)/,s=[],x=0,m=[],j=0;g(a);return{a:s.join(\"\").replace(/\\n$/,\"\"),d:m}}function H(a,d,g,b){d&&(a={a:d,e:a},g(a),b.push.apply(b,a.g))}function U(a){for(var d=void 0,g=a.firstChild;g;g=g.nextSibling)var b=g.nodeType,d=b===1?d?a:g:b===3?V.test(g.nodeValue)?a:d:d;return d===a?void 0:d}function C(a,d){function g(a){for(var j=a.e,k=[j,\"pln\"],c=0,i=a.a.match(s)||[],r={},n=0,e=i.length;n<e;++n){var z=i[n],w=r[z],t=void 0,f;if(typeof w===\"string\")f=!1;else{var h=b[z.charAt(0)];\nif(h)t=z.match(h[1]),w=h[0];else{for(f=0;f<x;++f)if(h=d[f],t=z.match(h[1])){w=h[0];break}t||(w=\"pln\")}if((f=w.length>=5&&\"lang-\"===w.substring(0,5))&&!(t&&typeof t[1]===\"string\"))f=!1,w=\"src\";f||(r[z]=w)}h=c;c+=z.length;if(f){f=t[1];var l=z.indexOf(f),B=l+f.length;t[2]&&(B=z.length-t[2].length,l=B-f.length);w=w.substring(5);H(j+h,z.substring(0,l),g,k);H(j+h+l,f,I(w,f),k);H(j+h+B,z.substring(B),g,k)}else k.push(j+h,w)}a.g=k}var b={},s;(function(){for(var g=a.concat(d),j=[],k={},c=0,i=g.length;c<i;++c){var r=\ng[c],n=r[3];if(n)for(var e=n.length;--e>=0;)b[n.charAt(e)]=r;r=r[1];n=\"\"+r;k.hasOwnProperty(n)||(j.push(r),k[n]=q)}j.push(/[\\S\\s]/);s=S(j)})();var x=d.length;return g}function v(a){var d=[],g=[];a.tripleQuotedStrings?d.push([\"str\",/^(?:'''(?:[^'\\\\]|\\\\[\\S\\s]|''?(?=[^']))*(?:'''|$)|\"\"\"(?:[^\"\\\\]|\\\\[\\S\\s]|\"\"?(?=[^\"]))*(?:\"\"\"|$)|'(?:[^'\\\\]|\\\\[\\S\\s])*(?:'|$)|\"(?:[^\"\\\\]|\\\\[\\S\\s])*(?:\"|$))/,q,\"'\\\"\"]):a.multiLineStrings?d.push([\"str\",/^(?:'(?:[^'\\\\]|\\\\[\\S\\s])*(?:'|$)|\"(?:[^\"\\\\]|\\\\[\\S\\s])*(?:\"|$)|`(?:[^\\\\`]|\\\\[\\S\\s])*(?:`|$))/,\nq,\"'\\\"`\"]):d.push([\"str\",/^(?:'(?:[^\\n\\r'\\\\]|\\\\.)*(?:'|$)|\"(?:[^\\n\\r\"\\\\]|\\\\.)*(?:\"|$))/,q,\"\\\"'\"]);a.verbatimStrings&&g.push([\"str\",/^@\"(?:[^\"]|\"\")*(?:\"|$)/,q]);var b=a.hashComments;b&&(a.cStyleComments?(b>1?d.push([\"com\",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,\"#\"]):d.push([\"com\",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\\b|[^\\n\\r]*)/,q,\"#\"]),g.push([\"str\",/^<(?:(?:(?:\\.\\.\\/)*|\\/?)(?:[\\w-]+(?:\\/[\\w-]+)+)?[\\w-]+\\.h(?:h|pp|\\+\\+)?|[a-z]\\w*)>/,q])):d.push([\"com\",\n/^#[^\\n\\r]*/,q,\"#\"]));a.cStyleComments&&(g.push([\"com\",/^\\/\\/[^\\n\\r]*/,q]),g.push([\"com\",/^\\/\\*[\\S\\s]*?(?:\\*\\/|$)/,q]));if(b=a.regexLiterals){var s=(b=b>1?\"\":\"\\n\\r\")?\".\":\"[\\\\S\\\\s]\";g.push([\"lang-regex\",RegExp(\"^(?:^^\\\\.?|[+-]|[!=]=?=?|\\\\#|%=?|&&?=?|\\\\(|\\\\*=?|[+\\\\-]=|->|\\\\/=?|::?|<<?=?|>>?>?=?|,|;|\\\\?|@|\\\\[|~|{|\\\\^\\\\^?=?|\\\\|\\\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\\\s*(\"+(\"/(?=[^/*\"+b+\"])(?:[^/\\\\x5B\\\\x5C\"+b+\"]|\\\\x5C\"+s+\"|\\\\x5B(?:[^\\\\x5C\\\\x5D\"+b+\"]|\\\\x5C\"+\ns+\")*(?:\\\\x5D|$))+/\")+\")\")])}(b=a.types)&&g.push([\"typ\",b]);b=(\"\"+a.keywords).replace(/^ | $/g,\"\");b.length&&g.push([\"kwd\",RegExp(\"^(?:\"+b.replace(/[\\s,]+/g,\"|\")+\")\\\\b\"),q]);d.push([\"pln\",/^\\s+/,q,\" \\r\\n\\t\\u00a0\"]);b=\"^.[^\\\\s\\\\w.$@'\\\"`/\\\\\\\\]*\";a.regexLiterals&&(b+=\"(?!s*/)\");g.push([\"lit\",/^@[$_a-z][\\w$@]*/i,q],[\"typ\",/^(?:[@_]?[A-Z]+[a-z][\\w$@]*|\\w+_t\\b)/,q],[\"pln\",/^[$_a-z][\\w$@]*/i,q],[\"lit\",/^(?:0x[\\da-f]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+-]?\\d+)?)[a-z]*/i,q,\"0123456789\"],[\"pln\",/^\\\\[\\S\\s]?/,\nq],[\"pun\",RegExp(b),q]);return C(d,g)}function J(a,d,g){function b(a){var c=a.nodeType;if(c==1&&!x.test(a.className))if(\"br\"===a.nodeName)s(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((c==3||c==4)&&g){var d=a.nodeValue,i=d.match(m);if(i)c=d.substring(0,i.index),a.nodeValue=c,(d=d.substring(i.index+i[0].length))&&a.parentNode.insertBefore(j.createTextNode(d),a.nextSibling),s(a),c||a.parentNode.removeChild(a)}}function s(a){function b(a,c){var d=\nc?a.cloneNode(!1):a,e=a.parentNode;if(e){var e=b(e,1),g=a.nextSibling;e.appendChild(d);for(var i=g;i;i=g)g=i.nextSibling,e.appendChild(i)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),d;(d=a.parentNode)&&d.nodeType===1;)a=d;c.push(a)}for(var x=/(?:^|\\s)nocode(?:\\s|$)/,m=/\\r\\n?|\\n/,j=a.ownerDocument,k=j.createElement(\"li\");a.firstChild;)k.appendChild(a.firstChild);for(var c=[k],i=0;i<c.length;++i)b(c[i]);d===(d|0)&&c[0].setAttribute(\"value\",d);var r=j.createElement(\"ol\");\nr.className=\"linenums\";for(var d=Math.max(0,d-1|0)||0,i=0,n=c.length;i<n;++i)k=c[i],k.className=\"L\"+(i+d)%10,k.firstChild||k.appendChild(j.createTextNode(\"\\u00a0\")),r.appendChild(k);a.appendChild(r)}function p(a,d){for(var g=d.length;--g>=0;){var b=d[g];F.hasOwnProperty(b)?D.console&&console.warn(\"cannot override language handler %s\",b):F[b]=a}}function I(a,d){if(!a||!F.hasOwnProperty(a))a=/^\\s*</.test(d)?\"default-markup\":\"default-code\";return F[a]}function K(a){var d=a.h;try{var g=T(a.c,a.i),b=g.a;\na.a=b;a.d=g.d;a.e=0;I(d,b)(a);var s=/\\bMSIE\\s(\\d+)/.exec(navigator.userAgent),s=s&&+s[1]<=8,d=/\\n/g,x=a.a,m=x.length,g=0,j=a.d,k=j.length,b=0,c=a.g,i=c.length,r=0;c[i]=m;var n,e;for(e=n=0;e<i;)c[e]!==c[e+2]?(c[n++]=c[e++],c[n++]=c[e++]):e+=2;i=n;for(e=n=0;e<i;){for(var p=c[e],w=c[e+1],t=e+2;t+2<=i&&c[t+1]===w;)t+=2;c[n++]=p;c[n++]=w;e=t}c.length=n;var f=a.c,h;if(f)h=f.style.display,f.style.display=\"none\";try{for(;b<k;){var l=j[b+2]||m,B=c[r+2]||m,t=Math.min(l,B),A=j[b+1],G;if(A.nodeType!==1&&(G=x.substring(g,\nt))){s&&(G=G.replace(d,\"\\r\"));A.nodeValue=G;var L=A.ownerDocument,o=L.createElement(\"span\");o.className=c[r+1];var v=A.parentNode;v.replaceChild(o,A);o.appendChild(A);g<l&&(j[b+1]=A=L.createTextNode(x.substring(t,l)),v.insertBefore(A,o.nextSibling))}g=t;g>=l&&(b+=2);g>=B&&(r+=2)}}finally{if(f)f.style.display=h}}catch(u){D.console&&console.log(u&&u.stack||u)}}var D=window,y=[\"break,continue,do,else,for,if,return,while\"],E=[[y,\"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile\"],\n\"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof\"],M=[E,\"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where\"],N=[E,\"abstract,assert,boolean,byte,extends,final,finally,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient\"],\nO=[N,\"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,internal,into,is,let,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where\"],E=[E,\"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN\"],P=[y,\"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None\"],\nQ=[y,\"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END\"],W=[y,\"as,assert,const,copy,drop,enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv,pub,pure,ref,self,static,struct,true,trait,type,unsafe,use\"],y=[y,\"case,done,elif,esac,eval,fi,function,in,local,set,then,until\"],R=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\\d*)\\b/,\nV=/\\S/,X=v({keywords:[M,O,E,\"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END\",P,Q,y],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),F={};p(X,[\"default-code\"]);p(C([],[[\"pln\",/^[^<?]+/],[\"dec\",/^<!\\w[^>]*(?:>|$)/],[\"com\",/^<\\!--[\\S\\s]*?(?:--\\>|$)/],[\"lang-\",/^<\\?([\\S\\s]+?)(?:\\?>|$)/],[\"lang-\",/^<%([\\S\\s]+?)(?:%>|$)/],[\"pun\",/^(?:<[%?]|[%?]>)/],[\"lang-\",\n/^<xmp\\b[^>]*>([\\S\\s]+?)<\\/xmp\\b[^>]*>/i],[\"lang-js\",/^<script\\b[^>]*>([\\S\\s]*?)(<\\/script\\b[^>]*>)/i],[\"lang-css\",/^<style\\b[^>]*>([\\S\\s]*?)(<\\/style\\b[^>]*>)/i],[\"lang-in.tag\",/^(<\\/?[a-z][^<>]*>)/i]]),[\"default-markup\",\"htm\",\"html\",\"mxml\",\"xhtml\",\"xml\",\"xsl\"]);p(C([[\"pln\",/^\\s+/,q,\" \\t\\r\\n\"],[\"atv\",/^(?:\"[^\"]*\"?|'[^']*'?)/,q,\"\\\"'\"]],[[\"tag\",/^^<\\/?[a-z](?:[\\w-.:]*\\w)?|\\/?>$/i],[\"atn\",/^(?!style[\\s=]|on)[a-z](?:[\\w:-]*\\w)?/i],[\"lang-uq.val\",/^=\\s*([^\\s\"'>]*(?:[^\\s\"'/>]|\\/(?=\\s)))/],[\"pun\",/^[/<->]+/],\n[\"lang-js\",/^on\\w+\\s*=\\s*\"([^\"]+)\"/i],[\"lang-js\",/^on\\w+\\s*=\\s*'([^']+)'/i],[\"lang-js\",/^on\\w+\\s*=\\s*([^\\s\"'>]+)/i],[\"lang-css\",/^style\\s*=\\s*\"([^\"]+)\"/i],[\"lang-css\",/^style\\s*=\\s*'([^']+)'/i],[\"lang-css\",/^style\\s*=\\s*([^\\s\"'>]+)/i]]),[\"in.tag\"]);p(C([],[[\"atv\",/^[\\S\\s]+/]]),[\"uq.val\"]);p(v({keywords:M,hashComments:!0,cStyleComments:!0,types:R}),[\"c\",\"cc\",\"cpp\",\"cxx\",\"cyc\",\"m\"]);p(v({keywords:\"null,true,false\"}),[\"json\"]);p(v({keywords:O,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:R}),\n[\"cs\"]);p(v({keywords:N,cStyleComments:!0}),[\"java\"]);p(v({keywords:y,hashComments:!0,multiLineStrings:!0}),[\"bash\",\"bsh\",\"csh\",\"sh\"]);p(v({keywords:P,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),[\"cv\",\"py\",\"python\"]);p(v({keywords:\"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END\",hashComments:!0,multiLineStrings:!0,regexLiterals:2}),[\"perl\",\"pl\",\"pm\"]);p(v({keywords:Q,\nhashComments:!0,multiLineStrings:!0,regexLiterals:!0}),[\"rb\",\"ruby\"]);p(v({keywords:E,cStyleComments:!0,regexLiterals:!0}),[\"javascript\",\"js\"]);p(v({keywords:\"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes\",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),[\"coffee\"]);p(v({keywords:W,cStyleComments:!0,multilineStrings:!0}),[\"rc\",\"rs\",\"rust\"]);\np(C([],[[\"str\",/^[\\S\\s]+/]]),[\"regex\"]);var Y=D.PR={createSimpleLexer:C,registerLangHandler:p,sourceDecorator:v,PR_ATTRIB_NAME:\"atn\",PR_ATTRIB_VALUE:\"atv\",PR_COMMENT:\"com\",PR_DECLARATION:\"dec\",PR_KEYWORD:\"kwd\",PR_LITERAL:\"lit\",PR_NOCODE:\"nocode\",PR_PLAIN:\"pln\",PR_PUNCTUATION:\"pun\",PR_SOURCE:\"src\",PR_STRING:\"str\",PR_TAG:\"tag\",PR_TYPE:\"typ\",prettyPrintOne:D.prettyPrintOne=function(a,d,g){var b=document.createElement(\"div\");b.innerHTML=\"<pre>\"+a+\"</pre>\";b=b.firstChild;g&&J(b,g,!0);K({h:d,j:g,c:b,i:1});\nreturn b.innerHTML},prettyPrint:D.prettyPrint=function(a,d){function g(){for(var b=D.PR_SHOULD_USE_CONTINUATION?c.now()+250:Infinity;i<p.length&&c.now()<b;i++){for(var d=p[i],j=h,k=d;k=k.previousSibling;){var m=k.nodeType,o=(m===7||m===8)&&k.nodeValue;if(o?!/^\\??prettify\\b/.test(o):m!==3||/\\S/.test(k.nodeValue))break;if(o){j={};o.replace(/\\b(\\w+)=([\\w%+\\-.:]+)/g,function(a,b,c){j[b]=c});break}}k=d.className;if((j!==h||e.test(k))&&!v.test(k)){m=!1;for(o=d.parentNode;o;o=o.parentNode)if(f.test(o.tagName)&&\no.className&&e.test(o.className)){m=!0;break}if(!m){d.className+=\" prettyprinted\";m=j.lang;if(!m){var m=k.match(n),y;if(!m&&(y=U(d))&&t.test(y.tagName))m=y.className.match(n);m&&(m=m[1])}if(w.test(d.tagName))o=1;else var o=d.currentStyle,u=s.defaultView,o=(o=o?o.whiteSpace:u&&u.getComputedStyle?u.getComputedStyle(d,q).getPropertyValue(\"white-space\"):0)&&\"pre\"===o.substring(0,3);u=j.linenums;if(!(u=u===\"true\"||+u))u=(u=k.match(/\\blinenums\\b(?::(\\d+))?/))?u[1]&&u[1].length?+u[1]:!0:!1;u&&J(d,u,o);r=\n{h:m,c:d,j:u,i:o};K(r)}}}i<p.length?setTimeout(g,250):\"function\"===typeof a&&a()}for(var b=d||document.body,s=b.ownerDocument||document,b=[b.getElementsByTagName(\"pre\"),b.getElementsByTagName(\"code\"),b.getElementsByTagName(\"xmp\")],p=[],m=0;m<b.length;++m)for(var j=0,k=b[m].length;j<k;++j)p.push(b[m][j]);var b=q,c=Date;c.now||(c={now:function(){return+new Date}});var i=0,r,n=/\\blang(?:uage)?-([\\w.]+)(?!\\S)/,e=/\\bprettyprint\\b/,v=/\\bprettyprinted\\b/,w=/pre|xmp/i,t=/^code$/i,f=/^(?:pre|code|xmp)$/i,\nh={};g()}};typeof define===\"function\"&&define.amd&&define(\"google-code-prettify\",[],function(){return Y})})();}()\n"
  },
  {
    "path": "website/js/routes.js",
    "content": "angular.module('protractorApp').config(function($routeProvider) {\n  $routeProvider.\n      when('/', {\n        templateUrl: 'partials/home.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/api', {\n        templateUrl: 'partials/api.html',\n        controller: 'ApiCtrl',\n        reloadOnSearch: false\n      }).\n      when('/style-guide', {\n        templateUrl: 'partials/style-guide.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/webdriver-vs-protractor', {\n        templateUrl: 'partials/webdriver-vs-protractor.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/api-overview', {\n        templateUrl: 'partials/api-overview.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/browser-setup', {\n        templateUrl: 'partials/browser-setup.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/browser-support', {\n        templateUrl: 'partials/browser-support.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/plugins', {\n        templateUrl: 'partials/plugins.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/control-flow', {\n        templateUrl: 'partials/control-flow.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/debugging', {\n        templateUrl: 'partials/debugging.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/faq', {\n        templateUrl: 'partials/faq.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/frameworks', {\n        templateUrl: 'partials/frameworks.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/getting-started', {\n        templateUrl: 'partials/getting-started.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/infrastructure', {\n        templateUrl: 'partials/infrastructure.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/locators', {\n        templateUrl: 'partials/locators.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/page-objects', {\n        templateUrl: 'partials/page-objects.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/protractor-setup', {\n        templateUrl: 'partials/protractor-setup.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/server-setup', {\n        templateUrl: 'partials/server-setup.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/system-setup', {\n        templateUrl: 'partials/system-setup.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/timeouts', {\n        templateUrl: 'partials/timeouts.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/toc', {\n        templateUrl: 'partials/toc.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/tutorial', {\n        templateUrl: 'partials/tutorial.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/jasmine-upgrade', {\n        templateUrl: 'partials/jasmine-upgrade.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/mobile-setup', {\n        templateUrl: 'partials/mobile-setup.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/typescript', {\n        templateUrl: 'partials/typescript.html',\n        controller: 'MarkdownCtrl'\n      }).\n      when('/async-await', {\n        templateUrl: 'partials/async-await.html',\n        controller: 'MarkdownCtrl'\n      }).\n      otherwise({\n        redirectTo: '/'\n      });\n});\n"
  },
  {
    "path": "website/js/shared.js",
    "content": "/*\n * Use the $sce service to trust the html rendered in the view.\n * Also parse links\n *\n * @param $sce The $sce service from Angular\n * @param {String} html The HTML to trust\n * @return {*} An object that can be passed to $sce.getTrustedHtml(value) to\n *   obtain the original value\n */\nfunction trustHTML($sce, html) {\n  if (!html) {\n    return;\n  }\n\n  // Does it come with a type? Types come escaped as [description](theType).\n  var match;\n  while (match = html.match(/(\\[(.*?)\\]\\((.*?)\\))/)) {\n    var link = '<a href=\"' +\n        (match[3].match(/^https?:\\/\\//) ? '' : '#/api?view=') + match[3] +\n        '\">' + match[2] + '</a>';\n    html = html.replace(match[1], link);\n  }\n\n  return $sce.trustAsHtml(html);\n}\n"
  },
  {
    "path": "website/karma.conf.js",
    "content": "module.exports = function(config) {\n  config.set({\n    basePath: '.',\n    frameworks: ['jasmine'],\n    files: [\n      'bower_components/angular/angular.js',\n      'bower_components/angular-mocks/angular-mocks.js',\n      'bower_components/angular-route/angular-route.js',\n      'bower_components/lodash/dist/lodash.min.js',\n      'js/modules.js',\n      'js/*.js',\n      'test/unit/*.js'\n    ],\n    exclude: [\n      'js/bootstrap.min.js'\n    ],\n    reporters: ['progress'],\n    port: 9876,\n    colors: true,\n    logLevel: config.LOG_INFO,\n    autoWatch: true,\n    browsers: ['Chrome'],\n    singleRun: false\n  });\n};\n"
  },
  {
    "path": "website/package.json",
    "content": "{\n  \"name\": \"website\",\n  \"version\": \"0.0.1\",\n  \"devDependencies\": {\n    \"bower\": \"^1.3.9\",\n    \"del\": \"^0.1.1\",\n    \"dgeni\": \"^0.4.1\",\n    \"dgeni-packages\": \"^0.11.1\",\n    \"esprima\": \"1.1.0\",\n    \"gulp\": \"^3.8.7\",\n    \"gulp-concat\": \"^2.3.4\",\n    \"gulp-connect\": \"^2.0.6\",\n    \"gulp-less\": \"^1.3.3\",\n    \"gulp-markdown\": \"^1.0.0\",\n    \"gulp-clean-css\": \"2.0.6\",\n    \"gulp-rename\": \"^1.2.0\",\n    \"gulp-replace\": \"^0.4.0\",\n    \"jasmine\": \"2.3.2\",\n    \"karma\": \"^0.12.21\",\n    \"karma-chrome-launcher\": \"^0.1.4\",\n    \"karma-jasmine\": \"^0.1.5\",\n    \"lodash\": \"^2.4.1\",\n    \"marked\": \"^0.3.3\"\n  },\n  \"scripts\": {\n    \"build\": \"gulp\",\n    \"clean\": \"gulp clean\",\n    \"prepublish\": \"bower install\",\n    \"prestart\": \"npm install && npm run prepublish\",\n    \"start\": \"gulp liveReload\",\n    \"test\": \"node run-tests.js\"\n  }\n}\n"
  },
  {
    "path": "website/partials/api.html",
    "content": "<div>\n\n  <div class=\"row\">\n    <h3 ng-click=\"currentItem=null\" id=\"title\">Protractor API <small>{{::version}}</small></h3>\n  </div>\n\n  <!-- Search -->\n  <div class=\"row\">\n    <div class=\"form-group\">\n      <label for=\"searchInput\">Search</label>\n      <input id=\"searchInput\"\n          autofocus\n          class=\"form-control\"\n          type=\"search\"\n          placeholder=\"Enter name\"\n          ng-model=\"searchTerm\"/>\n    </div>\n  </div>\n\n  <div class=\"row\">\n    <!-- Small screen -->\n    <div class=\"visible-xs\">\n      <div class=\"toggle-menu-button-container\">\n        <button type=\"button\"\n            id=\"toggle-menu-button\"\n            class=\"btn btn-primary\"\n            ng-click=\"toggleMenu()\"\n            ng-bind=\"toggleMenuLabel()\"></button>\n      </div>\n\n      <div ng-if=\"isMenuVisible\" class=\"mobile-menu\">\n        <span ng-repeat=\"item in items | filter:searchTerm\"\n            ng-switch=\"item.type\">\n            <div ng-switch-when=\"title\"\n                class=\"title\"\n                ng-class=\"item.treeClasses\"\n                ng-bind=\"item.displayName\">\n            </div>\n            <span ng-switch-when=\"child\">\n              <a href=\"#/api?view={{item.name}}\"\n                  class=\"child\"\n                  ng-class=\"item.treeClasses\"\n                  ng-bind=\"item.displayName\"></a>\n            </span>\n            <div ng-switch-default class=\"parent\">\n              <a href=\"#/api?view={{item.name}}\"\n                  ng-class=\"item.treeClasses\"\n                  ng-bind=\"item.title\"></a>\n            </div>\n        </span>\n      </div>\n    </div>\n\n    <!-- Large screen -->\n    <div class=\"hidden-xs\">\n      <div class=\"col-sm-3 api-left-nav\">\n        <ul class=\"list-unstyled\">\n          <li ng-repeat=\"item in items | filter:searchTerm\"\n              title=\"{{item.name}}\"\n              ng-switch=\"item.type\">\n            <span ng-switch-when=\"title\"\n                class=\"title\"\n                ng-class=\"item.treeClasses\"\n                ng-bind=\"item.displayName\">\n            </span>\n            <a ng-switch-when=\"child\"\n                href=\"#/api?view={{item.name}}\"\n                class=\"child\"\n                ng-class=\"item.treeClasses\"\n                ng-bind=\"item.displayName\">\n            </a>\n            <a ng-switch-default\n                href=\"#/api?view={{item.name}}\"\n                ng-class=\"item.treeClasses\"\n                ng-bind=\"item.title\"></a>\n          </li>\n        </ul>\n      </div>\n    </div>\n\n    <div ng-if=\"!currentItem\" class=\"col-sm-9\">\n      <h3 class=\"api-title\">\n        Protractor API Docs\n      </h3>\n      <p>\n        Welcome to the Protractor API documentation. The menu lists all classes and methods\n        exposed by Protractor.\n      </p>\n      <p>\n        Note that Protractor is a wrapper around\n        <a href=\"http://seleniumhq.github.io/selenium/docs/api/javascript/index.html\">selenium-webdriver</a>,\n        the Webdriver bindings for Node.js. See that documentation for advanced usage.\n      </p>\n    </div>\n\n    <div ng-if=\"currentItem\" class=\"col-sm-9\">\n      <h3 class=\"api-title\">\n        {{currentItem.title}}\n        <small ng-if=\"currentItem.sourceLink\">\n          <a href=\"{{currentItem.sourceLink}}\" target=\"_blank\">View code</a>\n        </small>\n        <h4 ng-if=\"currentItem.alias != currentItem.displayName\"\n            class=\"text-muted\">{{currentItem.name}}</h4>\n      </h3>\n      <p ng-bind-html=\"trust(currentItem.description)\"></p>\n\n      <div ng-if=\"currentItem.example\">\n        <h4>Example</h4>\n\n        <!-- View -->\n        <div ng-if=\"currentItem.view\">\n          <h5 class=\"text-muted\">View</h5>\n          <pre ptor-code=\"currentItem.htmlView\"></pre>\n        </div>\n\n        <!-- Code -->\n        <h5 class=\"text-muted\">Code</h5>\n        <pre ptor-code=\"currentItem.example\"></pre>\n      </div>\n\n      <!-- Params -->\n      <div ng-if=\"currentItem.params\">\n        <h4>Params</h4>\n\n        <div class=\"table-responsive\">\n          <table class=\"table\">\n            <thead>\n              <tr>\n                <th>Param</th>\n                <th>Type</th>\n                <th>Description</th>\n              </tr>\n            </thead>\n            <tbody>\n            <tr ng-repeat=\"param in currentItem.params\">\n              <td>{{param.name}}</td>\n              <td ng-bind-html=\"trust(param.paramString)\"></td>\n              <td>{{param.description}}</td>\n            </tr>\n            </tbody>\n          </table>\n        </div>\n      </div>\n\n      <!-- Returns -->\n      <div ng-if=\"currentItem.returns\">\n        <h4>Returns</h4>\n\n        <div class=\"table-responsive\">\n          <table class=\"table\">\n            <thead>\n              <tr>\n                <th>Type</th>\n                <th>Description</th>\n              </tr>\n            </thead>\n            <tbody>\n            <tr>\n              <td ng-bind-html=\"trust(currentItem.returnString)\"></td>\n              <td ng-bind-html=\"trust(currentItem.returns.description)\"></td>\n            </tr>\n            </tbody>\n          </table>\n        </div>\n      </div>\n\n      <!-- Children -->\n      <div ng-if=\"currentItem.children\">\n        <h4>Functions</h4>\n\n        <div ptor-function-list=\"currentItem.children\"></div>\n      </div>\n\n      <!-- Extends -->\n      <div ng-if=\"currentItem.extends\">\n        <h4>Extends {{currentItem.base.title}}</h4>\n\n        <div ptor-function-list=\"currentItem.base.children\"></div>\n\n        <!-- Extension Extends -->\n        <div ng-if=\"currentItem.base.extends\">\n          <h4>Extends {{currentItem.base.base.title}} (via {{currentItem.base.title}})</h4>\n\n          <div ptor-function-list=\"currentItem.base.base.children\"></div>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "website/partials/home.html",
    "content": "<div>\n  <div class=\"protractor-header\">\n    <span class=\"protractor-logo\"></span>\n  </div>\n\n  <div class=\"text-center\">\n    <a class=\"btn btn-large btn-danger github-button\" href=\"https://github.com/angular/protractor\">\n      <img src=\"img/GitHub-Mark-Light-32px.png\" alt=\"github logo\"/>View on GitHub\n    </a>\n  </div>\n\n  <div ptor-twitter></div>\n\n  <p class=\"lead\">\n    Protractor is an end-to-end test framework for Angular and AngularJS applications.\n    Protractor runs tests against your application running in a real browser,\n    interacting with it as a user would.\n  </p>\n\n  <div class=\"row\">\n    <div class=\"col-sm-4\">\n      <h3>Test Like a User</h3>\n      Protractor is built on top of WebDriverJS, which uses native events\n      and browser-specific drivers to interact with your application as a user\n      would.\n    </div>\n    <div class=\"col-sm-4\">\n      <h3>For Angular Apps</h3>\n      Protractor supports Angular-specific locator strategies, which allows you\n      to test Angular-specific elements without any setup effort on your part.\n    </div>\n    <div class=\"col-sm-4\">\n      <h3>Automatic Waiting</h3>\n      You no longer need to add waits and sleeps to your test. Protractor can\n      automatically execute the next step in your test the moment the webpage\n      finishes pending tasks, so you don’t have to worry about waiting for your\n      test and webpage to sync.\n    </div>\n  </div>\n\n  <div class=\"row example\">\n    <div class=\"col-md-12\">\n      <h2>Setup</h2>\n      <p>\n        Use npm to install Protractor globally with:\n      </p>\n      <pre>npm install -g protractor</pre>\n      <p>\n        This will install two command line tools, <code>protractor</code> and\n        <code>webdriver-manager</code>. Try running\n        <code>protractor --version</code> to make sure it's working.\n      </p>\n      <p>\n        The <code>webdriver-manager</code> is a helper tool to easily get an\n        instance of a Selenium Server running. Use it to download the necessary\n        binaries with:\n      </p>\n      <pre>webdriver-manager update</pre>\n      <p>\n        Now start up a server with:\n      </p>\n      <pre>webdriver-manager start</pre>\n      <p>\n        This will start up a Selenium Server and will output a bunch of info\n        logs. Your Protractor test will send requests to this server to control\n        a local browser. You can see information about the status of the server\n        at\n        <a href=\"http://localhost:4444/wd/hub\" target=\"_blank\">http://localhost:4444/wd/hub</a>.\n      </p>\n    </div>\n  </div>\n  <div class=\"row example\">\n    <div class=\"col-md-12\">\n      <h2>Write a test</h2>\n      <p>\n        Open a new command line or terminal window and create a clean folder\n        for testing.\n      </p>\n      <p>\n        Protractor needs two files to run, a spec file and a configuration file.\n      </p>\n      <p>\n        Let's start with a simple test that navigates to the todo list example\n        in the AngularJS website and adds a new todo item to the list.\n      </p>\n      <p>Copy the following into <code>todo-spec.js</code>:</p>\n      <div>\n<pre><code class=\"lang-js\">\ndescribe('angularjs homepage todo list', function() {\n  it('should add a todo', function() {\n    browser.get('https://angularjs.org');\n\n    element(by.model('todoList.todoText')).sendKeys('write first protractor test');\n    element(by.css('[value=\"add\"]')).click();\n\n    var todoList = element.all(by.repeater('todo in todoList.todos'));\n    expect(todoList.count()).toEqual(3);\n    expect(todoList.get(2).getText()).toEqual('write first protractor test');\n\n    // You wrote your first test, cross it off the list\n    todoList.get(2).element(by.css('input')).click();\n    var completedAmount = element.all(by.css('.done-true'));\n    expect(completedAmount.count()).toEqual(2);\n  });\n});\n</code></pre>\n      </div>\n      <p>\n        The <code>describe</code> and <code>it</code> syntax is from the Jasmine\n        framework. <code>browser</code> is a global created by Protractor,\n        which is used for browser-level commands such as navigation with\n        <code>browser.get</code>.\n      </p>\n    </div>\n  </div>\n  <div class=\"row example\">\n    <div class=\"col-md-12\">\n      <h2>Configuration</h2>\n      <p>\n        Now create the configuration file. Copy the following into\n        <code>conf.js</code>:\n      </p>\n<pre><code class=\"lang-js\">\nexports.config = {\n  seleniumAddress: 'http://localhost:4444/wd/hub',\n  specs: ['todo-spec.js']\n};\n</code></pre>\n      <p>\n        This configuration tells Protractor where your test files\n        (<code>specs</code>) are, and where to talk to your Selenium Server\n        (<code>seleniumAddress</code>). It will use the defaults for all other\n        configuration. Chrome is the default browser.\n      </p>\n    </div>\n  </div>\n  <div class=\"row example\">\n    <div class=\"col-md-12\">\n      <h2>Run the test</h2>\n      <p>Now run the test with:</p>\n      <pre>protractor conf.js</pre>\n      <p>\n        You should see a Chrome browser window open up and navigate to the\n        todo list in the AngularJS page, then close itself (this should be very\n        fast!). The test output should be\n        <code>1 test, 3 assertions, 0 failures</code>. Congratulations, you've\n        run your first Protractor test!\n      </p>\n    </div>\n  </div>\n  <div class=\"row\">\n    <div class=\"col-md-12\">\n      <h2>Learn More</h2>\n      <p>Learn more with the\n        <a href=\"#/tutorial\">Tutorial</a>.\n      </p>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "website/partials/ptor-function-list.html",
    "content": "<div class=\"table-responsive\" ng-if=\"list\">\n  <table class=\"table\">\n    <thead>\n      <tr>\n        <th>Function</th>\n        <th>Description</th>\n      </tr>\n    </thead>\n    <tbody>\n    <tr ng-repeat=\"item in list\">\n      <td>\n        <a href=\"#/api?view={{item.name}}\"\n            class=\"child\"\n            ng-bind=\"item.displayName\">\n        </a>\n      </td>\n      <td ng-bind-html=\"trust(item.shortDescription)\"></td>\n    </tr>\n    </tbody>\n  </table>\n</div>\n"
  },
  {
    "path": "website/protractor.conf.js",
    "content": "var shardCount = 2;\n\nexports.config = {\n  directConnect: true,\n\n  // Spec patterns are relative to the location of this config.\n  specs: [\n    'test/e2e/*_spec.js'\n  ],\n\n  maxSessions: shardCount,\n\n  capabilities: {\n    browserName: 'chrome',\n    chromeOptions: {args: ['--disable-extensions']},\n    maxInstances: shardCount,\n    shardTestFiles: true\n  },\n\n  // A base URL for your application under test. Calls to protractor.get()\n  // with relative paths will be prepended with this.\n  baseUrl: 'http://localhost:8080',\n\n  jasmineNodeOpts: {\n    showColors: true,\n    defaultTimeoutInterval: 10000\n  }\n};\n"
  },
  {
    "path": "website/run-tests.js",
    "content": "#!/usr/bin/env node\n\nvar glob = require('glob').sync;\nvar spawn = require('child_process').spawn;\n\nvar scripts = [];\n\n// Dgeni tests.\nscripts.push(\n  'node node_modules/.bin/jasmine JASMINE_CONFIG_PATH=unit_test.json');\n\n// Karma tests.\nscripts.push('node_modules/karma/bin/karma start --singleRun true');\n\n// Protractor.\nscripts.push('../bin/protractor');\n\nvar failed = false;\n\n(function runTests(i) {\n  if (i < scripts.length) {\n    console.log(scripts[i]);\n    var args = scripts[i].split(/\\s/);\n\n    var test = spawn(args[0], args.slice(1), {stdio: 'inherit'});\n    test.on('error', function(err) {\n      throw err;\n    });\n    test.on('exit', function(code) {\n      if (code != 0) {\n        failed = true;\n      }\n      runTests(i + 1);\n    });\n  } else {\n    process.exit(failed ? 1 : 0);\n  }\n}(0));\n"
  },
  {
    "path": "website/test/e2e/api-page.js",
    "content": "/**\n * Api page object.\n * @constructor\n */\nvar ApiPage = function() {\n  this.menu = $('.api-left-nav');\n  this.searchInput = $('#searchInput');\n  this.title = $('.api-title');\n\n  /**\n   * Select an item from the menu.\n   * @param {string} name\n   */\n  this.clickOnMenuItem = function(name) {\n    this.menu.element(by.linkText(name)).click();\n  };\n\n  /**\n   * Get the labels for all the menu items.\n   * @return A promise that resolves to the item labels.\n   */\n  this.getMenuItems = function() {\n    return this.menu.$$('li').map(function(item) {\n      return item.getText();\n    });\n  };\n\n  /**\n   * Click on a param type on the params table.\n   * @param {string} name\n   */\n  this.clickOnParamType = function(name) {\n    $('[ng-if=\"currentItem.params\"]').element(by.linkText(name)).click();\n  };\n\n  /**\n   * Click on a returns type on the returns table.\n   * @param {string} name\n   */\n  this.clickOnReturnsType = function(name) {\n    $('[ng-if=\"currentItem.returns\"]').element(by.linkText(name)).click();\n  };\n\n  /**\n   * Click on a child function of a parent element like element, element.all,\n   * etc.\n   * @param {string} name\n   */\n  this.clickOnChildFunction = function(name) {\n    $('[ng-if=\"currentItem.children\"]').element(by.linkText(name)).click();\n  };\n\n  /**\n   * Gets the function names of a parent item such as element(), element.all(),\n   * browser, and by.\n   * @return {!webdriver.promise.Promise.<string>} A promise that will be\n   *     resolved with the function names.\n   */\n  this.getChildFunctionNames = function() {\n    return $('[ptor-function-list=\"currentItem.children\"]')\n        .$$('a.child')\n        .getText();\n  };\n\n  /**\n   * Click on a type of the extends table.\n   * @param {string} name\n   */\n  this.clickOnExtendsType = function(name) {\n    $('[ng-if=\"currentItem.extends\"]').element(by.linkText(name)).click();\n  };\n\n  /**\n   * Gets the text of elements of the menu that aren't children of another\n   * element or titles (e.g. 'protractor' and 'by' but not 'getText')\n   */\n  this.getAdultNames = function() {\n    return element.all(by.repeater('item in items')).filter(function(elem) {\n      return elem.$$('[ng-switch-default]').count().then(function(count) {\n        return count == 1;\n      });\n    }).getText();\n  };\n};\n\n/** @type {ApiPage} */\nmodule.exports = new ApiPage();\n"
  },
  {
    "path": "website/test/e2e/api_spec.js",
    "content": "/** @type {ApiPage} */\nvar apiPage = require('./api-page');\n\ndescribe('Api', function() {\n  beforeEach(function() {\n    browser.get('#/api');\n  });\n\n  it('should navigate to the api page', function() {\n    expect($('#title').getText()).toMatch('Protractor API');\n    expect(apiPage.title.getText()).toBe('Protractor API Docs');\n  });\n\n  it('should navigate to function', function() {\n    // When you navigate to a url.\n    browser.get('#/api?view=ElementFinder.prototype.isElementPresent');\n\n    // Then ensure the function is shown.\n    expect(apiPage.title.getText()).\n        toBe('element(locator).isElementPresent View code');\n  });\n\n  it('should go to api home when url is incorrect', function() {\n    // When you navigate to a non-existent function.\n    browser.get('#/api?view=blah_blah_blah');\n\n    // Then ensure the default view is shown.\n    expect(apiPage.title.getText()).toBe('Protractor API Docs');\n  });\n\n  it('should search and find function', function() {\n    // When you search for the 'map' function.\n    apiPage.searchInput.sendKeys('map');\n\n    // Ensure the following elements are shown:\n    // element.all\n    // map\n    // ...\n    apiPage.getMenuItems().then(function(items) {\n      expect(items[0]).toBe('map');\n    });\n  });\n\n  it('should show item when you click on it', function() {\n    // When you click on element.all(locator).\n    apiPage.clickOnMenuItem('element.all(locator)');\n\n    // Then ensure the item is shown.\n    expect(apiPage.title.getText()).toBe('element.all(locator) View code');\n    expect(browser.getCurrentUrl()).toMatch(/api\\?view=ElementArrayFinder/);\n  });\n\n  it('should view item in param type link', function() {\n    // Given that you are viewing element.all.filter.\n    apiPage.clickOnMenuItem('filter');\n\n    // When you click on the ElementFinder type link of the params table.\n    apiPage.clickOnParamType('ElementFinder');\n\n    // Then ensure the type is shown.\n    expect(apiPage.title.getText()).toBe('element(locator) View code');\n    expect(browser.getCurrentUrl()).toMatch(/api\\?view=ElementFinder/);\n  });\n\n  it('should view item in returns link', function() {\n    // Given that you are viewing 'element.all(locator).first()'.\n    apiPage.clickOnMenuItem('first');\n\n    // When you click on the 'ElementFinder' link of the Returns table.\n    apiPage.clickOnReturnsType('ElementFinder');\n\n    // Then ensure the type is shown.\n    expect(apiPage.title.getText()).toBe('element(locator) View code');\n    expect(browser.getCurrentUrl()).toMatch(/api\\?view=ElementFinder/);\n  });\n\n  it('should show child functions', function() {\n    // Given that you go to element.all().\n    apiPage.clickOnMenuItem('element.all(locator)');\n\n    // When you click on 'first'.\n    apiPage.clickOnChildFunction('first');\n\n    // Then ensure the 'first' function is shown.\n    expect(apiPage.title.getText()).\n        toBe('element.all(locator).first() View code');\n    expect(browser.getCurrentUrl()).\n        toMatch(/api\\?view=ElementArrayFinder\\.prototype\\.first/)\n  });\n\n  it('should show element.all functions', function() {\n    // When you show element.all().\n    apiPage.clickOnMenuItem('element.all(locator)');\n\n    // Then ensure the child functions are shown.\n    expect(apiPage.getChildFunctionNames()).toEqual([\n      'clone', 'all', 'filter', 'get', 'first', 'last', '$$', 'count',\n      'isPresent', 'locator', 'then', 'each', 'map', 'reduce', 'evaluate',\n      'allowAnimations']);\n  });\n\n  it('should show element functions', function() {\n    // When you show element().\n    apiPage.clickOnMenuItem('element(locator)');\n\n    // Then ensure the child functions are shown.\n    expect(apiPage.getChildFunctionNames()).toEqual([\n      'clone', 'locator', 'getWebElement', 'all', 'element', '$$',\n      '$', 'isPresent', 'isElementPresent', 'evaluate', 'allowAnimations', 'equals']);\n  });\n\n  it('should show browser functions', function() {\n    // When you show browser.\n    apiPage.clickOnMenuItem('browser');\n\n    // Then ensure the child functions are shown.\n    expect(apiPage.getChildFunctionNames()).toEqual([\n      'angularAppRoot', 'waitForAngularEnabled', 'getProcessedConfig',\n      'forkNewDriverInstance', 'restart', 'restartSync',\n      'useAllAngular2AppRoots', 'waitForAngular', 'findElement', 'findElements',\n      'isElementPresent', 'addMockModule', 'clearMockModules',\n      'removeMockModule', 'getRegisteredMockModules', 'get', 'refresh',\n      'navigate', 'setLocation', 'getLocationAbsUrl', 'debugger', 'enterRepl',\n      'explore', 'pause', 'controlFlowIsEnabled']);\n  });\n\n  it('should view inherited function', function() {\n    // Given that you are viewing 'browser'.\n    apiPage.clickOnMenuItem('browser');\n\n    // When you click on the 'executeAsyncScript' item of the extends table.\n    apiPage.clickOnExtendsType('sleep');\n\n    // Then ensure the type is shown.\n    expect(apiPage.title.getText()).\n        toBe('webdriver.WebDriver.sleep');\n    expect(browser.getCurrentUrl()).\n        toMatch(/api\\?view=webdriver.WebDriver.prototype.sleep/);\n  });\n\n  it('should sort the menu to put webdriver docs next to the relevant ' +\n      'protractor objects', function() {\n        expect(apiPage.getAdultNames().then(function(names) {\n          return names[names.indexOf('browser') + 1];\n        })).toBe('ExtendedWebDriver');\n        expect(apiPage.getAdultNames().then(function(names) {\n          return names[names.indexOf('ExtendedWebDriver') + 1];\n        })).toBe('webdriver.WebDriver');\n  });\n});\n"
  },
  {
    "path": "website/test/e2e/menu-partial.js",
    "content": "/**\n * Page object for the top menu.\n * @constructor\n */\nvar MenuPartial = function() {\n  this.topMenuItems = $$('.navbar-nav > li > a');\n\n  /**\n   * Dropdown api. Used to click on an element under a dropdown or to get the\n   * item names under.\n   * @param {string} dropdownName\n   * @return {{item: Function, itemNames: Function}}\n   */\n  this.dropdown = function(dropdownName) {\n    return {\n      /**\n       * Open a dropdown given its name.\n       */\n      open: function() {\n        $('.navbar-nav')\n            .element(by.linkText(dropdownName))\n            .click();\n      },\n      /**\n       * Select an item under a menu dropdown.\n       * @param {string} itemName\n       */\n      item: function(itemName) {\n        this.open();\n\n        // Click on an element under the open dropdown.\n        $('.dropdown.open')\n            .element(by.linkText(itemName))\n            .click();\n      },\n      /**\n       * Get the names of the items under a dropdown menu.\n       */\n      itemNames: function() {\n        this.open();\n\n        return $$('.dropdown.open .dropdown-menu li a')\n            .getText();\n      }\n    };\n  };\n};\n\n/** @type {MenuPartial} */\nmodule.exports = new MenuPartial();\n"
  },
  {
    "path": "website/test/e2e/navigation_spec.js",
    "content": "/** @type {MenuPartial} */\nvar menu = require('./menu-partial');\n\ndescribe('Navigation', function() {\n  beforeEach(function() {\n    browser.get('#');\n  });\n\n  it('should go to home', function() {\n    menu.dropdown('Home').open();\n\n    expect($('.protractor-logo').isPresent()).toBe(true);\n  });\n\n  it('should go to tutorial', function() {\n    menu.dropdown('Quick Start').item('Tutorial');\n\n    expect($('h1').getText()).toBe('Tutorial');\n  });\n\n  describe('Menu items', function() {\n    it('should have menu items', function() {\n      // Make sure all the top level menu labels are present.\n      expect(menu.topMenuItems.getText()).toEqual([\n        'Home',\n        'Quick Start',\n        'Protractor Setup',\n        'Protractor Tests',\n        'Reference'\n      ]);\n    });\n\n    it('should have items under Quick Start', function() {\n      expect(menu.dropdown('Quick Start').itemNames()).toEqual([\n        'Tutorial'\n      ]);\n    });\n\n    it('should have items under Protractor Setup', function() {\n      expect(menu.dropdown('Protractor Setup').itemNames()).toEqual([\n          'Setting Up Protractor',\n          'Setting Up the Selenium Server',\n          'Setting Up the Browser',\n          'Choosing a Framework'\n      ]);\n    });\n\n    it('should have items under Protractor Tests', function() {\n      expect(menu.dropdown('Protractor Tests').itemNames()).toEqual([\n          'Getting Started',\n          'Tutorial',\n          'Working with Spec and Config Files',\n          'Setting Up the System Under Test',\n          'Using Locators',\n          'Using Page Objects to Organize Tests',\n          'Debugging Protractor Tests'\n      ]);\n    });\n\n    it('should have items under Reference', function() {\n      expect(menu.dropdown('Reference').itemNames()).toEqual([\n        'Configuration File',\n        'Protractor API',\n        'Style Guide',\n        'Protractor Syntax vs WebDriverJS Syntax',\n        'Browser Support',\n        'Plugins',\n        'Timeouts',\n        'The WebDriver Control Flow',\n        'Using TypeScript',\n        'Using async/await',\n        'How It Works',\n        'Upgrading to Jasmine 2.x',\n        'Mobile Setup',\n        'FAQ'\n      ]);\n    });\n  });\n\n  describe('Protractor Setup', function() {\n    it('should go to Setting Up Protractor', function() {\n      menu.dropdown('Protractor Setup').item('Setting Up Protractor');\n\n      expect($('h1').getText()).toBe('Setting Up Protractor');\n    });\n\n    it('should go to Setting Up the Selenium Server', function() {\n      menu.dropdown('Protractor Setup').item('Setting Up the Selenium Server');\n\n      expect($('h1').getText()).toBe('Setting Up the Selenium Server');\n    });\n\n    it('should go to Setting Up the Browser', function() {\n      menu.dropdown('Protractor Setup').item('Setting Up the Browser');\n\n      expect($('h1').getText()).toBe('Setting Up the Browser');\n    });\n\n    it('should go to Choosing a Framework', function() {\n      menu.dropdown('Protractor Setup').item('Choosing a Framework');\n\n      expect($('h1').getText()).toBe('Choosing a Framework');\n    });\n  });\n\n  describe('Protractor Tests', function() {\n    it('should go to Getting Started', function() {\n      menu.dropdown('Protractor Tests').item('Getting Started');\n\n      expect($('h1').getText()).toBe('Getting Started');\n    });\n\n    it('should go to Tutorial', function() {\n      menu.dropdown('Protractor Tests').item('Tutorial');\n\n      expect($('h1').getText()).toBe('Tutorial');\n    });\n\n    it('should go to Working with Spec and Config Files', function() {\n      menu.dropdown('Protractor Tests').item('Working with Spec and Config Files');\n\n      expect($$('h1').get(0).getText()).toBe('Working with Spec and Config Files');\n    });\n\n    it('should go to Setting Up the System Under Test', function() {\n      menu.dropdown('Protractor Tests').item('Setting Up the System Under Test');\n\n      expect($('h1').getText()).toBe('Setting Up the System Under Test');\n    });\n\n    it('should go to Using Locators', function() {\n      menu.dropdown('Protractor Tests').item('Using Locators');\n\n      expect($('h1').getText()).toBe('Using Locators');\n    });\n\n    it('should go to Using Page Objects to Organize Tests', function() {\n      menu.dropdown('Protractor Tests').item('Using Page Objects to Organize Tests');\n\n      expect($('h1').getText()).toBe('Using Page Objects to Organize Tests');\n    });\n\n    it('should go to Debugging Protractor Tests', function() {\n      menu.dropdown('Protractor Tests').item('Debugging Protractor Tests');\n\n      expect($('h1').getText()).toBe('Debugging Protractor Tests');\n    });\n  });\n\n  describe('Reference', function() {\n    it('should go to Protractor API', function() {\n      menu.dropdown('Reference').item('Protractor API');\n\n      expect($('#title').getText()).toMatch('Protractor API');\n    });\n\n    it('should go to Style Guide', function() {\n      menu.dropdown('Reference').item('Style Guide');\n\n      expect($('h1').getText()).toBe('Protractor style guide');\n    });\n\n    it('should go to Timeouts', function() {\n      menu.dropdown('Reference').item('Timeouts');\n\n      expect($('h1').getText()).toBe('Timeouts');\n    });\n\n    it('should go to Browser Support', function() {\n      menu.dropdown('Reference').item('Browser Support');\n\n      expect($('h1').getText()).toBe('Browser Support');\n    });\n\n    it('should go to Plugins', function() {\n      menu.dropdown('Reference').item('Plugins');\n\n      expect($('h1').getText()).toBe('Protractor Plugins');\n    });\n\n    it('should go to The WebDriver Control Flow', function() {\n      menu.dropdown('Reference').item('The WebDriver Control Flow');\n\n      expect($('h1').getText()).toBe('The WebDriver Control Flow');\n    });\n\n    it('should go to How It Works', function() {\n      menu.dropdown('Reference').item('How It Works');\n\n      expect($('h1').getText()).toBe('How It Works');\n    });\n\n    it('should go to Upgrading to Jasmine 2.x', function() {\n      menu.dropdown('Reference').item('Upgrading to Jasmine 2.x');\n\n      expect($('h1').getText()).toBe('Upgrading from Jasmine 1.3 to 2.x');\n    });\n\n    it('should go to Mobile Setup', function() {\n      menu.dropdown('Reference').item('Mobile Setup');\n\n      expect($('h1').getText()).toBe('Mobile Setup');\n    });\n\n    it('should go to FAQ', function() {\n      menu.dropdown('Reference').item('FAQ');\n\n      expect($('h1').getText()).toBe('FAQ');\n    });\n  });\n});\n"
  },
  {
    "path": "website/test/unit/api-controller-spec.js",
    "content": "'use strict';\n\ndescribe('ApiCtrl', function() {\n  var $controller_, $httpBackend, $location, $sce, $rootScope_, ctrl, scope;\n\n  beforeEach(module('protractorApp'));\n\n  beforeEach(inject(\n      function($controller, $rootScope, _$httpBackend_, _$location_, _$sce_) {\n        $controller_ = $controller;\n        $httpBackend = _$httpBackend_;\n        $location = _$location_;\n        $sce = _$sce_;\n        $rootScope_ = $rootScope;\n      }));\n\n  var createController = function(tocData) {\n    $httpBackend.expectGET('apiDocs/toc.json').respond(tocData);\n    $httpBackend.whenGET('partials/home.html').respond({});\n\n    scope = $rootScope_.$new();\n    ctrl = $controller_('ApiCtrl', {$scope: scope});\n    $httpBackend.flush();\n  };\n\n  var getSampleToc = function() {\n    return {\n      version: '1.0.0',\n      items: [\n        {\n          alias: 'element.all(locator)',\n          name: 'ElementArrayFinder',\n          fileName: 'protractor'\n        },\n        {\n          name: 'ElementArrayFinder.prototype.getWebElements',\n          fileName: 'protractor'\n        },\n        {\n          name: 'ElementArrayFinder.prototype.get',\n          fileName: 'protractor'\n        },\n        {\n          name: 'webdriver.WebDriver',\n          fileName: 'webdriver'\n        },\n        {\n          name: 'webdriver.WebDriver.createSession',\n          fileName: 'webdriver'\n        },\n        {\n          name: 'webdriver.WebDriver.prototype.controlFlow',\n          fileName: 'webdriver'\n        },\n        {\n          name: 'webdriver.WebDriver.Options',\n          fileName: 'webdriver'\n        },\n        {\n          name: 'webdriver.WebDriver.Options.prototype.addCookie',\n          fileName: 'webdriver'\n        }\n      ]\n    };\n  };\n\n  var findItemByName = function(displayName) {\n    return _.findWhere(scope.items, {displayName: displayName});\n  };\n\n  it('should fetch toc and populate items', function() {\n    createController(getSampleToc());\n    expect(scope.items.length).toBeGreaterThan(0);\n  });\n\n  it('should add title item at the top', function() {\n    createController(getSampleToc());\n    var firstItem = scope.items[0];\n\n    expect(firstItem.displayName).toBe('protractor');\n    expect(firstItem.isTitle).toBe(true);\n  });\n\n  it('should give title to items', function() {\n    createController({items: [\n      {name: 'foo1', alias: 'bar'},\n      {name: 'foo2'}\n    ]});\n\n    // Expect alias to be the title.\n    expect(scope.items[0].title).toBe('bar');\n    // Expect the name to be the title.\n    expect(scope.items[1].title).toBe('foo2');\n  });\n  \n  it('should use parent alias to generate title', function() {\n    // Given a parent item with alias.\n    createController({items: [\n      {name: 'name1', alias: 'parent'},\n      {name: 'name1.prototype.fn'}\n    ]});\n\n    // Expect the title to be parent + name.\n    expect(scope.items[1].title).toBe('parent.fn');\n  });\n\n  it('should assign type to each item', function() {\n    createController(getSampleToc());\n\n    var items = scope.items;\n    expect(items[0].type).toBe('title');\n    expect(items[1].type).toBeUndefined();\n    expect(items[2].type).toBe('child');\n  });\n\n  it('should add child type to children', function() {\n    createController(getSampleToc());\n    var items = _.map(scope.items, function(item) {\n      return _.pick(item, 'type', 'displayName');\n    });\n    expect(items).toEqual([\n      {type: 'title', displayName: 'protractor'},\n      {displayName: 'ElementArrayFinder'},\n      {type: 'child', displayName: 'getWebElements'},\n      {type: 'child', displayName: 'get'},\n      {type: 'title', displayName: 'webdriver'},\n      {displayName: 'webdriver.WebDriver'},\n      {type: 'child', displayName: 'createSession'},\n      {type: 'child', displayName: 'controlFlow'},\n      {type: 'child', displayName: 'Options'},\n      {type: 'child', displayName: 'addCookie'}\n    ]);\n  });\n\n  it('should assign display name', function() {\n    createController(getSampleToc());\n    expect(_.pluck(scope.items, 'displayName')).toEqual([\n      'protractor',\n      'ElementArrayFinder',\n      'getWebElements',\n      'get',\n      'webdriver',\n      'webdriver.WebDriver',\n      'createSession',\n      'controlFlow',\n      'Options',\n      'addCookie'\n    ]);\n  });\n\n  it('should add children to the parent', function() {\n    createController(getSampleToc());\n\n    // Ensure 'ElementArrayFinder' has children.\n    var parent = findItemByName('ElementArrayFinder');\n    expect(_.pluck(parent.children, 'displayName')).toEqual([\n      'getWebElements',\n      'get'\n    ]);\n  });\n\n  describe('Menu visibility', function() {\n    beforeEach(function() {\n      createController(getSampleToc());\n    });\n\n    it('should start with the nav bar hidden', function() {\n      expect(scope.isMenuVisible).toBe(false);\n    });\n\n    it('should toggle visibility', function() {\n      // Given that the menu is not visible.\n      expect(scope.isMenuVisible).toBe(false);\n\n      // When you toggle the menu.\n      scope.toggleMenu();\n\n      // Then ensure the menu is visible.\n      expect(scope.isMenuVisible).toBe(true);\n    });\n\n    it('should get toggle menu label', function() {\n      expect(scope.toggleMenuLabel()).toBe('Show list');\n\n      // When you toggle the menu.\n      scope.toggleMenu();\n\n      // Then ensure the label has changed.\n      expect(scope.toggleMenuLabel()).toBe('Hide list');\n    });\n  });\n\n  describe('Extends', function() {\n    it('should add parent reference', function() {\n      // When you have an items that extends.\n      createController({items: [\n        {name: 'name1', alias: 'parent'},\n        {name: 'name1.prototype.fn'},\n        {name: 'name2', extends: '{name1}'}\n      ]});\n      \n      // Then ensure the extending item has the functions of the parent.\n      var item = scope.items[0];\n      expect(item.title).toBe('name2');\n      expect(item.base.name).toBe('name1');\n      expect(item.base.children.length).toBe(1);\n    });\n  });\n\n  describe('trustHTML', function() {\n    it('should reject falsy html', function() {\n      expect(trustHTML($sce, false)).toBe(undefined);\n    });\n\n    it('should run truster', function() {\n      expect($sce.getTrustedHtml(trustHTML($sce, 'html'))).toBe('html');\n    });\n\n    it('should expand links', function() {\n      expect($sce.getTrustedHtml(trustHTML($sce, '[description](type)'))).toBe(\n          '<a href=\"#/api?view=type\">description</a>');\n    });\n  });\n});\n"
  },
  {
    "path": "website/test/unit/directives-spec.js",
    "content": "describe('Directives', function() {\n\n  var compile, element, scope;\n\n  beforeEach(module('protractorApp'));\n\n  beforeEach(inject(function($compile, $rootScope) {\n    scope = $rootScope.$new();\n    compile = $compile;\n  }));\n\n  var createDirective = function(html) {\n    element = angular.element(html);\n    compile(element)(scope);\n    scope.$digest();\n  };\n\n  it('should update markup when scope changes', function() {\n    // Given that you have a scope variable.\n    scope.currentItem = {\n      example: '123'\n    };\n\n    // When you create the directive.\n    createDirective('<pre ptor-code=\"currentItem.example\"></pre>');\n\n    // Then ensure the code got pretified.\n    expect(element.html()).toBe('<span class=\"lit\">123</span>');\n\n    // When you change the scope variable.\n    scope.$apply(function() {\n      scope.currentItem.example = '456';\n    });\n\n    // Then ensure the html changed.\n    expect(element.html()).toBe('<span class=\"lit\">456</span>');\n  });\n});\n"
  },
  {
    "path": "website/testapp/chat",
    "content": ""
  },
  {
    "path": "website/testapp/fastTemplateUrl",
    "content": "fast template contents\n"
  },
  {
    "path": "website/testapp/fastcall",
    "content": "done\n"
  },
  {
    "path": "website/testapp/slowTemplateUrl",
    "content": "slow template contents\n"
  },
  {
    "path": "website/testapp/slowcall",
    "content": "finally done\n"
  },
  {
    "path": "website/unit_test.json",
    "content": "{\n  \"spec_dir\": \"\",\n  \"spec_files\": [\n    \"docgen/spec/*.js\"\n  ]\n}\n"
  }
]