[
  {
    "path": ".clang-format",
    "content": "BasedOnStyle: Google\nAllowShortBlocksOnASingleLine: false\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortFunctionsOnASingleLine: None\nAllowShortIfStatementsOnASingleLine: false\nAllowShortLoopsOnASingleLine: false\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE",
    "content": "<!--\n  Thanks for the PR!\n\n  If this change has a user visible change (including\n  bug fixes, new features, etc) please describe the change in\n  CHANGELOG.md.\n\n  If the change is an entirely package-internal reshuffling/refactoring\n  should the change not be described in the CHANGELOG.\n\n  Consider also updating the README.\n\n  More info: http://keepachangelog.com/en/0.3.0/\n -->\n\n - [ ] CHANGELOG.md has been updated\n"
  },
  {
    "path": ".gitignore",
    "content": "# Update .npmignore whenever you update this file!\n\n.todo\n/bower_components\n/node_modules\npackage-lock.json\nnpm-debug.log\ntypings/\nrunner/*.js\nrunner/*.d.ts\nrunner/*.js.map\ntest/unit/*.js\ntest/unit/*.d.ts\ntest/unit/*.js.map\ntest/integration/*.js\ntest/integration/*.d.ts\ntest/integration/*.js.map\ntest/fixtures/integration/temp\n\nbrowser/*.js\nbrowser/*.js.map\nbrowser/environment/*.js*\nbrowser/mocha/*.js*\nbrowser/reporters/*.js*\n"
  },
  {
    "path": ".npmignore",
    "content": "# Update .gitignore whenever you update this file!\n\n.todo\nbower_components\nnode_modules\nnpm-debug.log\ntypings/\n\n# Don't ignore runner/*.js\n"
  },
  {
    "path": ".travis.yml",
    "content": "sudo: required\ndist: trusty\naddons:\n  firefox: latest\n  apt:\n    sources:\n    - google-chrome\n    packages:\n    - google-chrome-stable\n  sauce_connect: true\nlanguage: node_js\nnode_js:\n- '6'\n- '8'\nscript:\n- xvfb-run npm test\nenv:\n  global:\n  - MOCHA_TIMEOUT=300000\n  - secure: WqREPJCj0auiij7P86MfTpD5+buf7vlAuRiUILmcq0+rhVFvsgqG82NlKIjcQuCzM9WFMqBHxDyV0/iyfE5qiQsEXYd7g3SwMVFIAS3R8sKnMz/EMA0kCqFCGn14iwbakNduSuyjQhxHje72iczm/3S4nKFceOzDGFlnvGLxpTA=\n  - secure: D5P88V2PQF6mq/bPHlpzW1DgPSz4rKaoXJ1SNIsdeqUnd8xN+tmVUBO0/skunwIPIdws0y7cLfZOThEJuRMoDMUal0Fh81UcWna+yfM5N5NYziyCsOcf60UCZLpWdvS8QNEL0zGff6apO/TEaUkgYx7IMdzi0Bu+56v/iybwM3Q=\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "// Place your settings in this file to overwrite default and user settings.\n{\n  \"clang-format.style\": \"file\",\n  \"editor.formatOnSave\": true,\n  \"editor.formatOnType\": true,\n  \"files.exclude\": {\n    \"runner/*.js\": true,\n    \"runner/*.d.ts\": true,\n    \"runner/*.js.map\": true,\n    \"test/unit/*.js\": true,\n    \"test/unit/*.d.ts\": true,\n    \"test/unit/*.js.map\": true,\n    \"test/integration/*.js\": true,\n    \"test/integration/*.d.ts\": true,\n    \"test/integration/*.js.map\": true,\n    \"browser.js\": true,\n    \"browser.js.map\": true,\n    \"browser/**/*.js\": true,\n    \"browser/**/*.js.map\": true\n  },\n  \"typescript.tsdk\": \"./node_modules/typescript/lib\"\n}\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Change Log\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](http://keepachangelog.com/)\nand this project adheres to [Semantic Versioning](http://semver.org/).\n\n<!-- ## Unreleased -->\n<!-- Add new, unreleased items here. -->\n\n## 6.6.0-pre.5 - 2018-04-12\n* Restore missing dependency 'findup-sync'.\n\n## 6.6.0-pre.4 - 2018-04-11\n* Upgrade polyserve to pick up recent fixes.\n\n## 6.6.0-pre.3 - 2018-03-21\n* Upgrade polyserve to ^0.25.2 and add the --module-resolution flag\n* Fix #488 - Support .bowerrc directory name override of bower_components, including variants\n* Injected libraries are now resolved to correct paths while using the runner. Note: simply serving the test file and running it will attempt to find the files, but they may load the wrong versions (e.g. lodash 4 might load instead of 3)\n* Not published: 6.6.0-pre.1 & 6.6.0-pre.2\n\n## 6.5.0 - 2018-01-17\n* Upgrade wct-local to 2.1.0 to get support for headless Chrome.\n\n## 6.4.3 - 2018-01-11\n* web-component-tester: no longer injects `a11ySuite.js` script in `--npm` mode.\n* wct-browser-legacy: `a11ySuite.js` now exports a `a11ySuite` reference. Import this reference direction to use `a11ySuite()` in npm.\n\n## 6.4.2 - 2018-01-09\n* Upgrade wct-sauce to 2.0.0 to get updated browsers lists to include Safari 11 and Edge 15.\n* Fixed #523 WCT ignores the webserver hostname\n* Remove `overflow-y: auto` from test runner styling to increase performance.\n\n## 6.4.1 - 2017-11-20\n* Ensure that WCT is installed with compatible versions of wct-local and wct-sauce. This fixes a bug where – if incompatible versions are installed – they aren't able to coordinate shutdown, so WCT hangs after successfully completing a test run.\n\n## 6.4.0 - 2017-10-31 🎃\n\n* Updated package.json:\n  * Upgraded dependencies async, chai, cleankill, findup-sync, sinon, and socket.io.\n  * Upgraded devDependencies update-notifier\n* Added `define:webserver` hook to enable substitution of the generated express app for the webserver through a callback, to support use cases where a plugin might want to inject handlers or middleware in front of polyserve.\n* Added support for `proxy: {path: string, target: string}` config which is forwarded to `polyserve`.\n\n## 6.3.0 - 2017-10-02\n\n* Updated wct-browser-legacy to use a module version of a11ySuite to get access to Polymer.dom.flush.\n* Updated generated index for webserver to use a11ySuite as a module.\n* Updated polyserve to get support for on-the-fly module compilation and `<script type=module>` conversion for browsers that don't support modules.\n\n## 6.2.0 - 2017-09-19\n\n* Updated the browser.js file for npm case to use test-fixture as JS module instead of html import.\n* Updated the integration tests to support running on Sauce via wct-sauce plugin.\n* Updated polyserve to 0.22.1 for better compilation support and ES modules in HTML script tags and bug fixes.\n* Updated bower.json to reference newly published test-fixture version 3.0.0.\n* No longer require `?npm=true` url parameter, as `web-component-tester` and `wct-browser-legacy` versions of `browser.js` now contain explicit npm/non-npm directives of the form `window.__wctUseNpm` as a first step towards splitting out the client-side environment-specific config and behavior.\n* Added support for scoped package names under npm.\n\n## 6.1.5 - 2017-08-31\n\n* Removed reliance on `document.currentScript` in browser.js because IE11 doesn't have it. (Second reference found)\n\n## 6.1.4 - 2017-08-31\n\n* Removed reliance on `document.currentScript` in browser.js because IE11 doesn't have it.\n* To use WCT tests with NPM in browser without WCT (or polymer-cli) running on the commandline, you must include an `?npm=true` or `&npm=true` parameter in the URL for the test document; WCT does this automatically when opening its own browsers.\n\n## 6.1.3 - 2017-08-26\n\n* Added `sinon` as dependency of `sinon-chai` in web context to suppress the npm installation warning/error of unmet peer dependency, even though `@polymer/sinonjs` fulfills the runtime dependency and `sinon` will be unused.\n* Set `@polymer/test-fixture` back to ^0.0.3 because of dependency install errors related to yarn's \"flat\".\n\n## 6.1.2 - 2017-08-22\n\n* Updated npm browser dependency on `@polymer/test-fixture` v3.0.0-pre.1\n\n## 6.1.1 - 2017-08-21\n\n* Updated browser dependency to a browser-ready sinon npm package for `wct --npm` option.\n\n## 6.1.0 - 2017-08-17\n\n* Added an *experimental* `--npm` flag to support web packages which are installed in `node_modules` by npm (instead of installed in `bower_components` by Bower.\n* Builds a `wct-browser-legacy` package directory used to publish npm-specific client support package to npm, suitable for use with devDependencies of custom elements.\n* Added version flag to CLI. Available using `--version` or `-V`.\n\n## 6.0.1 - 2017-08-08\n\n* Updated package.json dependencies:\n  * Upgraded @types/gulp\n  * Moved all @types to devDependencies\n  * Removed PolymerElements/test-fixture from npm dependencies (is already installed by bower)\n* Undo fix for #505, as https://bugs.chromium.org/p/chromium/issues/detail?id=701601 has been fixed and shipped in M58 stable.\n\n## 6.0.0 - 2017-05-15\n\n* Major changes:\n  * In an effort to reduce magical behavior and make `wct` easier to understand, it no longer will automatically serve some resources from its own `npm` dependencies and some resources from the project under test. Instead, all resources are served out of the project under test. This gives the project under test control over its testing dependencies and their versions.\n    * As part of this, wct will also require that the project under test have an installation of the client side web-component-tester bower package.\n    * This release unifies the behavior of `wct` and `polyserve`, so if your code works without warnings in one it should work in the other.\n  * `webserver.webRunnerPath`, `webserver.webRunnerContent`, and `webserver.urlPrefix`, `webserver.staticContent` were internal properties that were exposed on the `config` object. They have been refactored and their replacement has been prefixed with an underscore to clarify that they're internal implementation details.\n  * Dropped support for node v4, added support for node v8. See our [node version support policy](https://www.polymer-project.org/2.0/docs/tools/node-support) for details.\n\n<details>\n  <summary>Click for full details</summary>\n\n## New in 6.0.0 stable\n\n* Update wct-local to remove deprecation warnings on install.\n* Remove warning when running wct if it's not installed into `node_modules`.\n\n## 6.0.0-prerelease.9 - 2017-04-19\n\n* The default `waitFor` doesn't rely on `HTMLImports.whenReady` or `Polymer.whenReady` timings, but only waits for the `WebComponentsReady` event to be fired. For a different wait time, set `WCT = { waitFor: function(cb){ cb(); }}`.\n\n## 6.0.0-prerelease.8 - 2017-04-13\n\n* [BREAKING] Dropped support for node v4, added support for node v8. See our [node version support policy](https://www.polymer-project.org/2.0/docs/tools/node-support) for details.\n\n## 6.0.0-prerelease.7 - 2017-03-15\n\n* Fixed #505 – Work around an issue in Chrome 57 where dynamically inserted HTML Imports did not block subsequent script execution. See https://bugs.chromium.org/p/chromium/issues/detail?id=701601\n\n## 6.0.0-prerelease.1 through 6.0.0-prerelease.6\n\n### Breaking change\n\n* In an effort to reduce magical behavior and make `wct` easier to understand, it no longer will automatically serve some resources from its own `npm` dependencies and some resources from the project under test. Instead, all resources are served out of the project under test. This gives the project under test control over its testing dependencies and their versions.\n  * As part of this, wct will also require that the project under test have an installation of the client side web-component-tester bower package. We recommend that all projects also have a dependency on the npm web-component-tester node module, and in a future release will will require it. This is to makes results more reproducible, and ensures that they'll be protected from future breaking changes.\n  * This release also unifies the behavior of `wct` and `polyserve`, so if your code works without warnings in one it should work in the other.\n  * Calling `replace(...)` will use sinon to stub `document.importNode` until `teardown` is called.\n\n### Added\n\n* Polymer.dom.flush() call in a11ySuite to ensure lazy dom is loaded\n* Added beforeEach parameter to a11ySuite\n* Added first pass of _variants_. Variants different configurations of testing the same code.\n  * Add support for _variant dependencies_.\n    * wct already supports loading dependencies from your `bower_components` directory, mapping them to `../` in your code. You can now add variant dependency directories named like `bower_components-foo`. When these are detected, tests will then run separately for each such dependency directory, mapping `../` appropriately. See README for more details.\n\n### Removed\n\n* `webserver.webRunnerPath`, `webserver.webRunnerContent`, and `webserver.urlPrefix`, `webserver.staticContent` were internal properties that were exposed on the `config` object. They have been refactored and their replacement has been prefixed with an underscore to clarify that they're internal implementation details.\n\n### Fixed\n\n* Fixed #392\n* Fixed #373 and #383 which were caused by `emitHook` not handling arguments correctly.\n* Fixed error log message for loading WCT config\n\n</details>\n\n## 5.0.1\n\n* Backport of fix for #505 – Work around an issue in Chrome 57 where dynamically inserted HTML Imports did not block subsequent script execution. See https://bugs.chromium.org/p/chromium/issues/detail?id=701601\n\n## 5.0.0\n* Mocha upgraded to `v3.1.2`. This shouldn't require any new code, but make sure your tests still pass as there were some more subtle changes made to Mocha behavior for v3 (Add IE7 support, update dependencies). See https://github.com/mochajs/mocha/pull/2350 for more info.\n\n## 4.2.2\n* Update bower dependencies to match node dependencies\n* Update rollup to 0.25\n* Update README to point to webcomponents-lite.js\n\n## 4.2.1\n* Fix `grep` for upstream mocha bug\n\n## 4.2.0\n* Add `httpbin` functionality to check `POST` requests\n  * `POST` to `/httpbin`, response will be contents of `POST`\n\n## 4.1.0\n* Add `ignoreRules` option to `a11ySuite`\n    * Array of a11ySuite rules to ignore for that suite\n    * Example: https://github.com/PolymerElements/paper-tooltip/commit/bf22b1dfaf7f47312ddb7f5415f75ae81fa467bf\n\n## 4.0.3\n* Fix npm 3 serving for lodash and sinon\n\n## 4.0.2\n* Fix serving from `node_modules` for npm 3\n\n## 4.0.1\n* Fix Polymer 0.5 testing\n\n## 4.0.0\n* Remove `bower` as a dependency, serve testing files out of `node_modules`\n* Upgrade to `wct-local` 2.0, which needs node 0.12+ for `launchpad` 0.5\n* Replace esperanto with rollup for building browser bundle\n\n# 3.x\n\n## 3.4.0\n* Integrate [test-fixture](https://github.com/PolymerElements/test-fixture)\n\n## 3.3.0\n* Add ability to cancel running tests from library\n\n## 3.2.0\n* Add accessibility testing with `a11ySuite` and\n    [accessibility-developer-tools](https://github.com/GoogleChrome/accessibility-developer-tools)\n\n## 3.1.3\n\n* `.bowerrc` included in the package to ensure that packages don't get placed in\n  unexpected locations.\n\n## 3.1.2\n\n* `--verbose` now includes logging from [`serve-waterfall`](https://github.com/PolymerLabs/serve-waterfall).\n\n## 3.1.1\n\n* WCT now depends on `wct-sauce ^1.5.0`\n\n## 3.1.0\n\n* WCT proper no longer periodically executes webdriver commands to ensure remote\n  keepalive. Logic has moved into `wct-sauce`.\n\n* Fix for verbose mode breaking IE10.\n\n## 3.0.7\n\n* Mixing TDD & BDD Mocha interfaces is now an error.\n\n* Calls to `console.error` now generate an error.\n\n* Errors that occur during WCT's initialization are more reliably reported.\n\n* WCT now treats dependencies installed into `bower_components/` as if they are\n  siblings of the current repo (much like polyserve).\n\n* Browser libraries are no longer bundled with WCT.\n\n  * They are now bower-managed, and by default installed to `bower_components/`\n    within `web-component-tester`.\n\n  * The libraries loaded can be configured via `WCT = {environmentScripts: []}`.\n\n  * Massive overhaul of `browser.js` to support this & `environment.js` no\n    longer exists.\n\n* Support for newer versions of webcomponents.js (also Polymer 0.8).\n\n* Mocha configuration can be specified by the `mochaOptions` key in client\n  options (i.e. `<script>WCT = {mochaOptions: {}};</script>`).\n\n* Browser options can be specified in `wct.conf.js` via the `clientOptions` key.\n\n* WCT now always generates an index when run via the command line.\n\n* `wct.conf.json` can be used as an alternative to `wct.conf.js`.\n\n## 3.0.0-3.0.6\n\nYanked. See `3.0.7` for rolled up notes.\n\n\n# 2.x\n\nThere were changes made, and @nevir failed to annotate them. What a slacker.\n\n\n# 1.x\n\nWhat are you, an archaeologist?\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2015 The Polymer Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "## 🚨 Moved to [`Polymer/tools/packages/web-component-tester`][1] 🚨\n\nThe [`Polymer/web-component-tester`][2] repo has been migrated to [`packages/web-component-tester`][1] folder of the [`Polymer/tools`][3] 🚝  *monorepo*.\n\nWe are *actively* working on migrating open Issues and PRs to the new repo. New Issues and PRs should be filed at [`Polymer/tools`][3].\n\n[1]: https://github.com/Polymer/tools/tree/master/packages/web-component-tester\n[2]: https://github.com/Polymer/web-component-tester\n[3]: https://github.com/Polymer/tools\n"
  },
  {
    "path": "bin/wct",
    "content": "#!/usr/bin/env node\n/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n */\nvar resolve = require('resolve');\n\nprocess.title = 'wct';\n\nresolve('web-component-tester', {basedir: process.cwd()}, function(error, path) {\n  var wct = path ? require(path) : require('..');\n  var promise = wct.cli.run(process.env, process.argv.slice(2), process.stdout, function (error) {\n    process.exit(error ? 1 : 0);\n  });\n  if (promise) {\n    promise.then(() => process.exit(0), () => process.exit(1));\n  }\n});\n"
  },
  {
    "path": "bin/wct-st",
    "content": "#!/usr/bin/env node\n/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n */\nvar resolve = require('resolve');\n\nprocess.title = 'wct-st';\n\nresolve('web-component-tester', {basedir: process.cwd()}, function(error, path) {\n  var wct = path ? require(path) : require('..');\n  var promise = wct.cli.runSauceTunnel(process.env, process.argv.slice(2), process.stdout, function (error) {\n    process.exit(error ? 1 : 0);\n  });\n  if (promise) {\n    promise.then(() => process.exit(0), () => process.exit(1));\n  }\n});\n"
  },
  {
    "path": "bower.json",
    "content": "{\n  \"name\": \"web-component-tester\",\n  \"description\": \"web-component-tester makes testing your web components a breeze!\",\n  \"version\": \"6.0.0\",\n  \"main\": [\n    \"browser.js\"\n  ],\n  \"license\": \"http://polymer.github.io/LICENSE.txt\",\n  \"ignore\": [\n    \"*\",\n    \"!/data/*\",\n    \"!/browser.js\",\n    \"!/browser.js.map\",\n    \"!/package.json\",\n    \"!/bower.json\"\n  ],\n  \"keywords\": [\n    \"browser\",\n    \"grunt\",\n    \"gruntplugin\",\n    \"gulp\",\n    \"polymer\",\n    \"test\",\n    \"testing\",\n    \"web component\",\n    \"web\"\n  ],\n  \"dependencies\": {\n    \"accessibility-developer-tools\": \"^2.10.0\",\n    \"async\": \"^1.5.0\",\n    \"chai\": \"^3.2.0\",\n    \"lodash\": \"^3.7.0\",\n    \"mocha\": \"^3.1.2\",\n    \"sinon-chai\": \"^2.7.0\",\n    \"sinonjs\": \"^1.14.1\",\n    \"stacky\": \"^1.3.0\",\n    \"test-fixture\": \"^3.0.0\"\n  },\n  \"devDependencies\": {\n    \"polymer\": \"Polymer/polymer#^1.5.0\",\n    \"webcomponentsjs\": \"webcomponents/webcomponentsjs#^0.7.22\"\n  }\n}\n"
  },
  {
    "path": "browser/childrunner.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt The complete set of authors may be found\n * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may\n * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by\n * Google as part of the polymer project is also subject to an additional IP\n * rights grant found at http://polymer.github.io/PATENTS.txt\n */\nimport * as util from './util.js';\n\n// TODO(thedeeno): Consider renaming subsuite. IIRC, childRunner is entirely\n// distinct from mocha suite, which tripped me up badly when trying to add\n// plugin support. Perhaps something like 'batch', or 'bundle'. Something that\n// has no mocha correlate. This may also eliminate the need for root/non-root\n// suite distinctions.\n\nexport interface SharedState {}\n\n/**\n * A Mocha suite (or suites) run within a child iframe, but reported as if they\n * are part of the current context.\n */\nexport default class ChildRunner {\n  private url: string;\n  parentScope: Window;\n  private state: 'initializing'|'loading'|'complete';\n  private iframe?: HTMLIFrameElement;\n  private onRunComplete: (error?: any) => void;\n  private timeoutId: undefined|number;\n  private share: SharedState;\n\n  constructor(url: string, parentScope: Window) {\n    const urlBits = util.parseUrl(url);\n    util.mergeParams(\n        urlBits.params, util.getParams(parentScope.location.search));\n    delete urlBits.params.cli_browser_id;\n\n    this.url = urlBits.base + util.paramsToQuery(urlBits.params);\n    this.parentScope = parentScope;\n\n    this.state = 'initializing';\n  }\n\n  // ChildRunners get a pretty generous load timeout by default.\n  static loadTimeout = 60000;\n\n  // We can't maintain properties on iframe elements in Firefox/Safari/???, so\n  // we track childRunners by URL.\n  static _byUrl: {[url: string]: undefined|ChildRunner} = {};\n\n  /**\n   * @return {ChildRunner} The `ChildRunner` that was registered for this\n   * window.\n   */\n  static current(): ChildRunner {\n    return ChildRunner.get(window);\n  }\n\n  /**\n   * @param {!Window} target A window to find the ChildRunner of.\n   * @param {boolean} traversal Whether this is a traversal from a child window.\n   * @return {ChildRunner} The `ChildRunner` that was registered for `target`.\n   */\n  static get(target: Window, traversal?: boolean): ChildRunner {\n    const childRunner = ChildRunner._byUrl[target.location.href];\n    if (childRunner) {\n      return childRunner;\n    }\n    if (window.parent === window) {  // Top window.\n      if (traversal) {\n        console.warn(\n            'Subsuite loaded but was never registered. This most likely is due to wonky history behavior. Reloading...');\n        window.location.reload();\n      }\n      return null;\n    }\n    // Otherwise, traverse.\n    return window.parent.WCT._ChildRunner.get(target, true);\n  }\n\n  /**\n   * Loads and runs the subsuite.\n   *\n   * @param {function} done Node-style callback.\n   */\n  run(done: (error?: any) => void) {\n    util.debug('ChildRunner#run', this.url);\n    this.state = 'loading';\n    this.onRunComplete = done;\n\n    this.iframe = document.createElement('iframe');\n    this.iframe.src = this.url;\n    this.iframe.classList.add('subsuite');\n\n    let container = document.getElementById('subsuites');\n    if (!container) {\n      container = document.createElement('div');\n      container.id = 'subsuites';\n      document.body.appendChild(container);\n    }\n    container.appendChild(this.iframe);\n\n    // let the iframe expand the URL for us.\n    this.url = this.iframe.src;\n    ChildRunner._byUrl[this.url] = this;\n\n    this.timeoutId = setTimeout(\n        this.loaded.bind(this, new Error('Timed out loading ' + this.url)),\n        ChildRunner.loadTimeout);\n\n    this.iframe.addEventListener(\n        'error',\n        this.loaded.bind(\n            this, new Error('Failed to load document ' + this.url)));\n\n    this.iframe.contentWindow.addEventListener(\n        'DOMContentLoaded', this.loaded.bind(this, null));\n  }\n\n  /**\n   * Called when the sub suite's iframe has loaded (or errored during load).\n   *\n   * @param {*} error The error that occured, if any.\n   */\n  loaded(error: any) {\n    util.debug('ChildRunner#loaded', this.url, error);\n\n    if (this.iframe.contentWindow == null && error) {\n      this.signalRunComplete(error);\n      this.done();\n      return;\n    }\n\n    // Not all targets have WCT loaded (compatiblity mode)\n    if (this.iframe.contentWindow.WCT) {\n      this.share = this.iframe.contentWindow.WCT.share;\n    }\n\n    if (error) {\n      this.signalRunComplete(error);\n      this.done();\n    }\n  }\n\n  /**\n   * Called in mocha/run.js when all dependencies have loaded, and the child is\n   * ready to start running tests\n   *\n   * @param {*} error The error that occured, if any.\n   */\n  ready(error?: any) {\n    util.debug('ChildRunner#ready', this.url, error);\n    if (this.timeoutId) {\n      clearTimeout(this.timeoutId);\n    }\n    if (error) {\n      this.signalRunComplete(error);\n      this.done();\n    }\n  }\n\n  /**\n   * Called when the sub suite's tests are complete, so that it can clean up.\n   */\n  done() {\n    util.debug('ChildRunner#done', this.url, arguments);\n\n    // make sure to clear that timeout\n    this.ready();\n    this.signalRunComplete();\n\n    if (!this.iframe)\n      return;\n    // Be safe and avoid potential browser crashes when logic attempts to\n    // interact with the removed iframe.\n    setTimeout(function() {\n      this.iframe.parentNode.removeChild(this.iframe);\n      this.iframe = null;\n      this.share = null;\n    }.bind(this), 1);\n  }\n\n  signalRunComplete(error?: any) {\n    if (!this.onRunComplete)\n      return;\n    this.state = 'complete';\n    this.onRunComplete(error);\n    this.onRunComplete = null;\n  }\n}\n"
  },
  {
    "path": "browser/clisocket.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt The complete set of authors may be found\n * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may\n * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by\n * Google as part of the polymer project is also subject to an additional IP\n * rights grant found at http://polymer.github.io/PATENTS.txt\n */\n\nimport ChildRunner from './childrunner.js';\nimport * as util from './util.js';\n\nconst SOCKETIO_ENDPOINT =\n    window.location.protocol + '//' + window.location.host;\nconst SOCKETIO_LIBRARY = SOCKETIO_ENDPOINT + '/socket.io/socket.io.js';\n\n/**\n * A socket for communication between the CLI and browser runners.\n *\n * @param {string} browserId An ID generated by the CLI runner.\n * @param {!io.Socket} socket The socket.io `Socket` to communicate over.\n */\nexport default class CLISocket {\n  private readonly socket: SocketIO.Socket;\n  private readonly browserId: string;\n  constructor(browserId: string, socket: SocketIO.Socket) {\n    this.browserId = browserId;\n    this.socket = socket;\n  }\n\n  /**\n   * @param {!Mocha.Runner} runner The Mocha `Runner` to observe, reporting\n   *     interesting events back to the CLI runner.\n   */\n  observe(runner: Mocha.IRunner) {\n    this.emitEvent('browser-start', {\n      url: window.location.toString(),\n    });\n\n    // We only emit a subset of events that we care about, and follow a more\n    // general event format that is hopefully applicable to test runners beyond\n    // mocha.\n    //\n    // For all possible mocha events, see:\n    // https://github.com/visionmedia/mocha/blob/master/lib/runner.js#L36\n    runner.on('test', (test: Mocha.IRunnable) => {\n      this.emitEvent('test-start', {test: getTitles(test)});\n    });\n\n    runner.on('test end', (test: Mocha.IRunnable) => {\n      this.emitEvent('test-end', {\n        state: getState(test),\n        test: getTitles(test),\n        duration: (test as any).duration,\n        error: (test as any).err,\n      });\n    });\n\n    runner.on('fail', (test, err) => {\n      // fail the test run if we catch errors outside of a test function\n      if (test.type !== 'test') {\n        this.emitEvent(\n            'browser-fail',\n            'Error thrown outside of test function: ' + err.stack);\n      }\n    });\n\n    runner.on('childRunner start', (childRunner) => {\n      this.emitEvent('sub-suite-start', childRunner.share);\n    });\n\n    runner.on('childRunner end', (childRunner) => {\n      this.emitEvent('sub-suite-end', childRunner.share);\n    });\n\n    runner.on('end', () => {\n      this.emitEvent('browser-end');\n    });\n  }\n\n  /**\n   * @param {string} event The name of the event to fire.\n   * @param {*} data Additional data to pass with the event.\n   */\n  emitEvent(event: string, data?: any) {\n    this.socket.emit('client-event', {\n      browserId: this.browserId,\n      event: event,\n      data: data,\n    });\n  }\n\n  /**\n   * Builds a `CLISocket` if we are within a CLI-run environment; short-circuits\n   * otherwise.\n   *\n   * @param {function(*, CLISocket)} done Node-style callback.\n   */\n  static init(done: (error?: any, socket?: CLISocket) => void) {\n    const browserId = util.getParam('cli_browser_id');\n    if (!browserId)\n      return done();\n    // Only fire up the socket for root runners.\n    if (ChildRunner.current())\n      return done();\n\n    util.loadScript(SOCKETIO_LIBRARY, function(error: any) {\n      if (error)\n        return done(error);\n\n      const socket = io(SOCKETIO_ENDPOINT);\n      socket.on('error', function(error?: any) {\n        socket.off();\n        done(error);\n      });\n\n      socket.on('connect', function() {\n        socket.off();\n        done(null, new CLISocket(browserId, socket as any));\n      });\n    });\n  }\n}\n\n// Misc Utility\n\n/**\n * @param {!Mocha.Runnable} runnable The test or suite to extract titles from.\n * @return {!Array.<string>} The titles of the runnable and its parents.\n */\nfunction getTitles(runnable: Mocha.IRunnable) {\n  const titles = [];\n  while (runnable && !runnable.root && runnable.title) {\n    titles.unshift(runnable.title);\n    runnable = runnable.parent as any;\n  }\n  return titles;\n}\n\n/**\n * @param {!Mocha.Runnable} runnable\n * @return {string}\n */\nfunction getState(runnable: Mocha.IRunnable) {\n  if (runnable.state === 'passed') {\n    return 'passing';\n  } else if (runnable.state === 'failed') {\n    return 'failing';\n  } else if (runnable.pending) {\n    return 'pending';\n  } else {\n    return 'unknown';\n  }\n}\n"
  },
  {
    "path": "browser/config.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt The complete set of authors may be found\n * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may\n * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by\n * Google as part of the polymer project is also subject to an additional IP\n * rights grant found at http://polymer.github.io/PATENTS.txt\n */\nimport ChildRunner from './childrunner.js';\nimport * as util from './util.js';\n\nexport interface Config {\n  /**\n   * `.js` scripts to be loaded (synchronously) before WCT starts in earnest.\n   *\n   * Paths are relative to `scriptPrefix`.\n   */\n  environmentScripts: string[];\n  environmentImports: string[];\n  /** Absolute root for client scripts. Detected in `setup()` if not set. */\n  root: null|string;\n  /** By default, we wait for any web component frameworks to load. */\n  waitForFrameworks: boolean;\n  /**\n   * Alternate callback for waiting for tests.\n   * `this` for the callback will be the window currently running tests.\n   */\n  waitFor: null|Function;\n  /** How many `.html` suites that can be concurrently loaded & run. */\n  numConcurrentSuites: number;\n  /** Whether `console.error` should be treated as a test failure. */\n  trackConsoleError: boolean;\n  /** Configuration passed to mocha.setup. */\n  mochaOptions: MochaSetupOptions;\n  /** Whether WCT should emit (extremely verbose) debugging log messages. */\n  verbose: boolean;\n}\n\n/**\n * The global configuration state for WCT's browser client.\n */\nexport let _config: Config = {\n  environmentScripts: !!window.__wctUseNpm ?\n      [\n        'stacky/browser.js', 'async/lib/async.js', 'lodash/index.js',\n        'mocha/mocha.js', 'chai/chai.js', '@polymer/sinonjs/sinon.js',\n        'sinon-chai/lib/sinon-chai.js',\n        'accessibility-developer-tools/dist/js/axs_testing.js',\n        '@polymer/test-fixture/test-fixture.js'\n      ] :\n      [\n        'stacky/browser.js', 'async/lib/async.js', 'lodash/lodash.js',\n        'mocha/mocha.js', 'chai/chai.js', 'sinonjs/sinon.js',\n        'sinon-chai/lib/sinon-chai.js',\n        'accessibility-developer-tools/dist/js/axs_testing.js'\n      ],\n\n  environmentImports: !!window.__wctUseNpm ? [] :\n                                             ['test-fixture/test-fixture.html'],\n  root: null as null | string,\n  waitForFrameworks: true,\n  waitFor: null as null | Function,\n  numConcurrentSuites: 1,\n  trackConsoleError: true,\n  mochaOptions: {timeout: 10 * 1000},\n  verbose: false,\n};\n\n/**\n * Merges initial `options` into WCT's global configuration.\n *\n * @param {Object} options The options to merge. See `browser/config.js` for a\n *     reference.\n */\nexport function setup(options: Config) {\n  const childRunner = ChildRunner.current();\n  if (childRunner) {\n    _deepMerge(_config, childRunner.parentScope.WCT._config);\n    // But do not force the mocha UI\n    delete _config.mochaOptions.ui;\n  }\n\n  if (options && typeof options === 'object') {\n    _deepMerge(_config, options);\n  }\n\n  if (!_config.root) {\n    // Sibling dependencies.\n    const root = util.scriptPrefix('browser.js');\n    _config.root = util.basePath(root.substr(0, root.length - 1));\n    if (!_config.root) {\n      throw new Error(\n          'Unable to detect root URL for WCT sources. Please set WCT.root before including browser.js');\n    }\n  }\n}\n\n/**\n * Retrieves a configuration value.\n */\nexport function get<K extends keyof Config>(key: K): Config[K] {\n  return _config[key];\n}\n\n// Internal\nfunction _deepMerge(target: Partial<Config>, source: Config) {\n  Object.keys(source).forEach(function(key) {\n    if (target[key] !== null && typeof target[key] === 'object' &&\n        !Array.isArray(target[key])) {\n      _deepMerge(target[key], source[key]);\n    } else {\n      target[key] = source[key];\n    }\n  });\n}\n"
  },
  {
    "path": "browser/declarations.ts",
    "content": "import * as ChaiStatic from 'chai';\nimport * as SinonStatic from 'sinon';\nimport * as SocketIOStatic from 'socket.io';\nimport * as StackyStatic from 'stacky';\n\nimport {default as ChildRunner, SharedState} from './childrunner.js';\nimport {Config} from './config.js';\nimport MultiReporter from './reporters/multi.js';\nimport * as suites from './suites.js';\n\ntype loadSuitesType = (typeof suites.loadSuites);\n\ndeclare global {\n  interface Window {\n    __wctUseNpm?: boolean;\n    WebComponents?: WebComponentsStatic;\n    Platform?: PlatformStatic;\n    Polymer?: PolymerStatic;\n    WCT: {\n      readonly _ChildRunner: typeof ChildRunner; //\n      readonly share: SharedState; //\n      readonly _config: Config; //\n      readonly loadSuites: loadSuitesType;\n      _reporter: MultiReporter;\n    };\n    mocha: typeof mocha;\n    Mocha: typeof Mocha;\n    __generatedByWct?: boolean;\n\n    chai: typeof ChaiStatic;\n    assert: typeof ChaiStatic.assert;\n    expect: typeof ChaiStatic.expect;\n  }\n  interface WebComponentsStatic {\n    ready?(): void;\n    flush?(): void;\n  }\n  interface PlatformStatic {\n    performMicrotaskCheckpoint(): void;\n  }\n  interface PolymerElement {\n    _stampTemplate?(): void;\n  }\n  interface PolymerElementConstructor {\n    prototype: PolymerElement;\n  }\n  interface PolymerStatic {\n    flush(): void;\n    dom: {flush(): void};\n    Element: PolymerElementConstructor;\n  }\n\n  interface Element {\n    isConnected?: boolean;\n  }\n\n  interface Mocha {\n    suite: Mocha.ISuite;\n  }\n\n  var Stacky: typeof StackyStatic;\n  var io: typeof SocketIOStatic;\n  var Platform: PlatformStatic;\n  var sinon: typeof SinonStatic;\n}\n"
  },
  {
    "path": "browser/environment/compatability.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt The complete set of authors may be found\n * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may\n * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by\n * Google as part of the polymer project is also subject to an additional IP\n * rights grant found at http://polymer.github.io/PATENTS.txt\n */\nimport ChildRunner from '../childrunner.js';\n\n// polymer-test-tools (and Polymer/tools) support HTML tests where each file is\n// expected to call `done()`, which posts a message to the parent window.\nwindow.addEventListener('message', function(event) {\n  if (!event.data || (event.data !== 'ok' && !event.data.error)) {\n    return;\n  }\n  const childRunner = ChildRunner.get(event.source);\n  if (!childRunner) {\n    return;\n  }\n\n  childRunner.ready();\n  // The name of the suite as exposed to the user.\n  const reporter = childRunner.parentScope.WCT._reporter;\n  const title = reporter.suiteTitle(event.source.location);\n  reporter.emitOutOfBandTest(\n      'page-wide tests via global done()', event.data.error, title, true);\n\n  childRunner.done();\n});\n\n// Attempt to ensure that we complete a test suite if it is interrupted by a\n// document unload.\nwindow.addEventListener('unload', function(_event: BeforeUnloadEvent) {\n  // Mocha's hook queue is asynchronous; but we want synchronous behavior if\n  // we've gotten to the point of unloading the document.\n  Mocha.Runner.immediately = function(callback: () => void) {\n    callback();\n  };\n});\n"
  },
  {
    "path": "browser/environment/errors.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt The complete set of authors may be found\n * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may\n * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by\n * Google as part of the polymer project is also subject to an additional IP\n * rights grant found at http://polymer.github.io/PATENTS.txt\n */\nimport * as config from '../config.js';\n\n// We may encounter errors during initialization (for example, syntax errors in\n// a test file). Hang onto those (and more) until we are ready to report them.\nexport let globalErrors: any[] = [];\n\n/**\n * Hook the environment to pick up on global errors.\n */\nexport function listenForErrors() {\n  window.addEventListener('error', function(event) {\n    globalErrors.push(event.error);\n  });\n\n  // Also, we treat `console.error` as a test failure. Unless you prefer not.\n  const origConsole = console;\n  const origError = console.error;\n  console.error = function wctShimmedError() {\n    origError.apply(origConsole, arguments);\n    if (config.get('trackConsoleError')) {\n      throw 'console.error: ' + Array.prototype.join.call(arguments, ' ');\n    }\n  };\n}\n"
  },
  {
    "path": "browser/environment/helpers.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt The complete set of authors may be found\n * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may\n * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by\n * Google as part of the polymer project is also subject to an additional IP\n * rights grant found at http://polymer.github.io/PATENTS.txt\n */\n\nexport {};\n\n// Make sure that we use native timers, in case they're being stubbed out.\nconst nativeSetInterval = window.setInterval;\nconst nativeSetTimeout = window.setTimeout;\nconst nativeRequestAnimationFrame = window.requestAnimationFrame;\n\n\n\n/**\n * Runs `stepFn`, catching any error and passing it to `callback` (Node-style).\n * Otherwise, calls `callback` with no arguments on success.\n *\n * @param {function()} callback\n * @param {function()} stepFn\n */\nfunction safeStep(callback: (error?: any) => void, stepFn: () => void) {\n  let err;\n  try {\n    stepFn();\n  } catch (error) {\n    err = error;\n  }\n  callback(err);\n}\n\n/**\n * Runs your test at declaration time (before Mocha has begun tests). Handy for\n * when you need to test document initialization.\n *\n * Be aware that any errors thrown asynchronously cannot be tied to your test.\n * You may want to catch them and pass them to the done event, instead. See\n * `safeStep`.\n *\n * @param {string} name The name of the test.\n * @param {function(?function())} testFn The test function. If an argument is\n *     accepted, the test will be treated as async, just like Mocha tests.\n */\nfunction testImmediate(name: string, testFn: Function) {\n  if (testFn.length > 0) {\n    return testImmediateAsync(name, testFn);\n  }\n\n  let err: any;\n  try {\n    testFn();\n  } catch (error) {\n    err = error;\n  }\n  test(name, function(done) {\n    done(err);\n  });\n}\n\n/**\n * An async-only variant of `testImmediate`.\n *\n * @param {string} name\n * @param {function(?function())} testFn\n */\nfunction testImmediateAsync(name: string, testFn: Function) {\n  let testComplete = false;\n  let err: any;\n\n  test(name, function(done) {\n    const intervalId = nativeSetInterval(function() {\n      if (!testComplete)\n        return;\n      clearInterval(intervalId);\n      done(err);\n    }, 10);\n  });\n\n  try {\n    testFn(function(error: any) {\n      if (error)\n        err = error;\n      testComplete = true;\n    });\n  } catch (error) {\n    err = error;\n    testComplete = true;\n  }\n}\n\n/**\n * Triggers a flush of any pending events, observations, etc and calls you back\n * after they have been processed.\n *\n * @param {function()} callback\n */\nfunction flush(callback: () => void) {\n  // Ideally, this function would be a call to Polymer.dom.flush, but that\n  // doesn't support a callback yet\n  // (https://github.com/Polymer/polymer-dev/issues/851),\n  // ...and there's cross-browser flakiness to deal with.\n\n  // Make sure that we're invoking the callback with no arguments so that the\n  // caller can pass Mocha callbacks, etc.\n  let done = function done() {\n    callback();\n  };\n\n  // Because endOfMicrotask is flaky for IE, we perform microtask checkpoints\n  // ourselves (https://github.com/Polymer/polymer-dev/issues/114):\n  const isIE = navigator.appName === 'Microsoft Internet Explorer';\n  if (isIE && window.Platform && window.Platform.performMicrotaskCheckpoint) {\n    const reallyDone = done;\n    done = function doneIE() {\n      Platform.performMicrotaskCheckpoint();\n      nativeSetTimeout(reallyDone, 0);\n    };\n  }\n\n  // Everyone else gets a regular flush.\n  let scope;\n  if (window.Polymer && window.Polymer.dom && window.Polymer.dom.flush) {\n    scope = window.Polymer.dom;\n  } else if (window.Polymer && window.Polymer.flush) {\n    scope = window.Polymer;\n  } else if (window.WebComponents && window.WebComponents.flush) {\n    scope = window.WebComponents;\n  }\n  if (scope) {\n    scope.flush();\n  }\n\n  // Ensure that we are creating a new _task_ to allow all active microtasks to\n  // finish (the code you're testing may be using endOfMicrotask, too).\n  nativeSetTimeout(done, 0);\n}\n\n/**\n * Advances a single animation frame.\n *\n * Calls `flush`, `requestAnimationFrame`, `flush`, and `callback` sequentially\n * @param {function()} callback\n */\nfunction animationFrameFlush(callback: () => void) {\n  flush(function() {\n    nativeRequestAnimationFrame(function() {\n      flush(callback);\n    });\n  });\n}\n\n/**\n * DEPRECATED: Use `flush`.\n * @param {function} callback\n */\nfunction asyncPlatformFlush(callback: () => void) {\n  console.warn(\n      'asyncPlatformFlush is deprecated in favor of the more terse flush()');\n  return window.flush(callback);\n}\n\nexport interface MutationEl {\n  onMutation(mutationEl: this, cb: () => void): void;\n}\n\n/**\n *\n */\nfunction waitFor(\n    fn: () => void, next: () => void, intervalOrMutationEl: number|MutationEl,\n    timeout: number, timeoutTime: number) {\n  timeoutTime = timeoutTime || Date.now() + (timeout || 1000);\n  intervalOrMutationEl = intervalOrMutationEl || 32;\n  try {\n    fn();\n  } catch (e) {\n    if (Date.now() > timeoutTime) {\n      throw e;\n    } else {\n      if (typeof intervalOrMutationEl !== 'number') {\n        intervalOrMutationEl.onMutation(intervalOrMutationEl, function() {\n          waitFor(fn, next, intervalOrMutationEl, timeout, timeoutTime);\n        });\n      } else {\n        nativeSetTimeout(function() {\n          waitFor(fn, next, intervalOrMutationEl, timeout, timeoutTime);\n        }, intervalOrMutationEl);\n      }\n      return;\n    }\n  }\n  next();\n}\n\ndeclare global {\n  interface Window {\n    safeStep: typeof safeStep;\n    testImmediate: typeof testImmediate;\n    testImmediateAsync: typeof testImmediateAsync;\n    flush: typeof flush;\n    animationFrameFlush: typeof animationFrameFlush;\n    asyncPlatformFlush: typeof asyncPlatformFlush;\n    waitFor: typeof waitFor;\n  }\n}\n\nwindow.safeStep = safeStep;\nwindow.testImmediate = testImmediate;\nwindow.testImmediateAsync = testImmediateAsync;\nwindow.flush = flush;\nwindow.animationFrameFlush = animationFrameFlush;\nwindow.asyncPlatformFlush = asyncPlatformFlush;\nwindow.waitFor = waitFor;\n"
  },
  {
    "path": "browser/environment.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt The complete set of authors may be found\n * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may\n * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by\n * Google as part of the polymer project is also subject to an additional IP\n * rights grant found at http://polymer.github.io/PATENTS.txt\n */\nimport * as config from './config.js';\nimport * as reporters from './reporters.js';\nimport * as util from './util.js';\n\n/**\n * Loads all environment scripts ...synchronously ...after us.\n */\nexport function loadSync() {\n  util.debug('Loading environment scripts:');\n  const a11ySuiteScriptPath = 'web-component-tester/data/a11ySuite.js';\n  const scripts = config.get('environmentScripts');\n  const a11ySuiteWillBeLoaded =\n      window.__generatedByWct || scripts.indexOf(a11ySuiteScriptPath) > -1;\n\n  // We can't inject a11ySuite when running the npm version because it is a\n  // module-based script that needs `<script type=module>` and compilation\n  // for browsers without module support.\n  if (!a11ySuiteWillBeLoaded && !window.__wctUseNpm) {\n    // wct is running as a bower dependency, load a11ySuite from data/\n    scripts.push(a11ySuiteScriptPath);\n  }\n  scripts.forEach(function(path) {\n    const url = util.expandUrl(path, config.get('root'));\n    util.debug('Loading environment script:', url);\n    // Synchronous load.\n    document.write(\n        '<script src=\"' + encodeURI(url) +\n        '\"></script>');  // jshint ignore:line\n  });\n  util.debug('Environment scripts loaded');\n\n  const imports = config.get('environmentImports');\n  imports.forEach(function(path) {\n    const url = util.expandUrl(path, config.get('root'));\n    util.debug('Loading environment import:', url);\n    // Synchronous load.\n    document.write(\n        '<link rel=\"import\" href=\"' + encodeURI(url) +\n        '\">');  // jshint ignore:line\n  });\n  util.debug('Environment imports loaded');\n}\n\n/**\n * We have some hard dependencies on things that should be loaded via\n * `environmentScripts`, so we assert that they're present here; and do any\n * post-facto setup.\n */\nexport function ensureDependenciesPresent() {\n  _ensureMocha();\n  _checkChai();\n}\n\nfunction _ensureMocha() {\n  const Mocha = window.Mocha;\n  if (!Mocha) {\n    throw new Error(\n        'WCT requires Mocha. Please ensure that it is present in WCT.environmentScripts, or that you load it before loading web-component-tester/browser.js');\n  }\n  reporters.injectMocha(Mocha);\n  // Magic loading of mocha's stylesheet\n  const mochaPrefix = util.scriptPrefix('mocha.js');\n  // only load mocha stylesheet for the test runner output\n  // Not the end of the world, if it doesn't load.\n  if (mochaPrefix && window.top === window.self) {\n    util.loadStyle(mochaPrefix + 'mocha.css');\n  }\n}\n\nfunction _checkChai() {\n  if (!window.chai) {\n    util.debug('Chai not present; not registering shorthands');\n    return;\n  }\n\n  window.assert = window.chai.assert;\n  window.expect = window.chai.expect;\n}\n"
  },
  {
    "path": "browser/index.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt The complete set of authors may be found\n * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may\n * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by\n * Google as part of the polymer project is also subject to an additional IP\n * rights grant found at http://polymer.github.io/PATENTS.txt\n */\n/**\n * This file is the entry point into `web-component-tester`'s browser client.\n */\n// Registers a bunch of globals:\nimport './environment/helpers.js';\n\nimport ChildRunner from './childrunner.js';\nimport CLISocket from './clisocket.js';\nimport * as config from './config.js';\nimport * as environment from './environment.js';\nimport * as errors from './environment/errors.js';\nimport * as mocha from './mocha.js';\nimport * as reporters from './reporters.js';\nimport MultiReporter from './reporters/multi.js';\nimport * as suites from './suites.js';\nimport * as util from './util.js';\n\n// You can configure WCT before it has loaded by assigning your custom\n// configuration to the global `WCT`.\nconfig.setup(window.WCT as any as config.Config);\n\n// Maybe some day we'll expose WCT as a module to whatever module registry you\n// are using (aka the UMD approach), or as an es6 module.\nconst WCT = window.WCT = {\n  // A generic place to hang data about the current suite. This object is\n  // reported\n  // back via the `sub-suite-start` and `sub-suite-end` events.\n  share: {},\n  // Until then, we get to rely on it to expose parent runners to their\n  // children.\n  _ChildRunner: ChildRunner,\n  _reporter: undefined as any,  // assigned below\n  _config: config._config,\n\n  // Public API\n\n  /**\n   * Loads suites of tests, supporting both `.js` and `.html` files.\n   *\n   * @param {!Array.<string>} files The files to load.\n   */\n  loadSuites: suites.loadSuites,\n};\n\n// Load Process\n\nerrors.listenForErrors();\nmocha.stubInterfaces();\nenvironment.loadSync();\n\n// Give any scripts on the page a chance to declare tests and muck with things.\ndocument.addEventListener('DOMContentLoaded', function() {\n  util.debug('DOMContentLoaded');\n\n  environment.ensureDependenciesPresent();\n\n  // We need the socket built prior to building its reporter.\n  CLISocket.init(function(error, socket) {\n    if (error)\n      throw error;\n\n    // Are we a child of another run?\n    const current = ChildRunner.current();\n    const parent = current && current.parentScope.WCT._reporter;\n    util.debug('parentReporter:', parent);\n\n    const childSuites = suites.activeChildSuites();\n    const reportersToUse = reporters.determineReporters(socket, parent);\n    // +1 for any local tests.\n    const reporter =\n        new MultiReporter(childSuites.length + 1, reportersToUse, parent);\n    WCT._reporter = reporter;  // For environment/compatibility.js\n\n    // We need the reporter so that we can report errors during load.\n    suites.loadJsSuites(reporter, function(error) {\n      // Let our parent know that we're about to start the tests.\n      if (current)\n        current.ready(error);\n      if (error)\n        throw error;\n\n      // Emit any errors we've encountered up til now\n      errors.globalErrors.forEach(function onError(error) {\n        reporter.emitOutOfBandTest('Test Suite Initialization', error);\n      });\n\n      suites.runSuites(reporter, childSuites, function(error) {\n        // Make sure to let our parent know that we're done.\n        if (current)\n          current.done();\n        if (error)\n          throw error;\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "browser/mocha/extend.ts",
    "content": "\nconst interfaceExtensions: Array<() => void> = [];\n\n/**\n * Registers an extension that extends the global `Mocha` implementation\n * with new helper methods. These helper methods will be added to the `window`\n * when tests run for both BDD and TDD interfaces.\n */\nexport function extendInterfaces(\n    helperName: string,\n    helperFactory: (\n        context: any, teardown: (cb: () => void) => void,\n        interfaceName: 'tdd'|'bdd') => void) {\n  interfaceExtensions.push(function() {\n    const Mocha = window.Mocha;\n    // For all Mocha interfaces (probably just TDD and BDD):\n    Object.keys((Mocha as any).interfaces)\n        .forEach(function(interfaceName: 'tdd'|'bdd') {\n          // This is the original callback that defines the interface (TDD or\n          // BDD):\n          const originalInterface = (Mocha as any).interfaces[interfaceName];\n          // This is the name of the \"teardown\" or \"afterEach\" property for the\n          // current interface:\n          const teardownProperty =\n              interfaceName === 'tdd' ? 'teardown' : 'afterEach';\n          // The original callback is monkey patched with a new one that appends\n          // to the global context however we want it to:\n          (Mocha as any).interfaces[interfaceName] = function(suite: any) {\n            // Call back to the original callback so that we get the base\n            // interface:\n            originalInterface.apply(this, arguments);\n            // Register a listener so that we can further extend the base\n            // interface:\n            suite.on(\n                'pre-require',\n                function(context: any, _file: string, _mocha: any) {\n                  // Capture a bound reference to the teardown function as a\n                  // convenience:\n                  const teardown = context[teardownProperty].bind(context);\n                  // Add our new helper to the testing context. The helper is\n                  // generated by a factory method that receives the context,\n                  // the teardown function and the interface name and returns\n                  // the new method to be added to that context:\n                  context[helperName] =\n                      helperFactory(context, teardown, interfaceName);\n                });\n          };\n        });\n  });\n}\n\n/**\n * Applies any registered interface extensions. The extensions will be applied\n * as many times as this function is called, so don't call it more than once.\n */\nexport function applyExtensions() {\n  interfaceExtensions.forEach(function(applyExtension) {\n    applyExtension();\n  });\n}\n"
  },
  {
    "path": "browser/mocha/fixture.ts",
    "content": "import {extendInterfaces} from './extend.js';\n\ninterface TestFixture extends HTMLElement {\n  create(model: object): HTMLElement;\n  restore(): void;\n}\n\nextendInterfaces('fixture', function(context, teardown) {\n\n  // Return context.fixture if it is already a thing, for backwards\n  // compatibility with `test-fixture-mocha.js`:\n  return context.fixture || function fixture(fixtureId: string, model: object) {\n\n    // Automatically register a teardown callback that will restore the\n    // test-fixture:\n    teardown(function() {\n      (document.getElementById(fixtureId) as TestFixture).restore();\n    });\n\n    // Find the test-fixture with the provided ID and create it, returning\n    // the results:\n    return (document.getElementById(fixtureId) as TestFixture).create(model);\n  };\n});\n"
  },
  {
    "path": "browser/mocha/replace.ts",
    "content": "import {extendInterfaces} from './extend.js';\n\n// replacement map stores what should be\nlet replacements = {};\nlet replaceTeardownAttached = false;\n\n/**\n * replace\n *\n * The replace addon allows the tester to replace all usages of one element with\n * another element within all Polymer elements created within the time span of\n * the test. Usage example:\n *\n * beforeEach(function() {\n *   replace('x-foo').with('x-fake-foo');\n * });\n *\n * All annotations and attributes will be set on the placement element the way\n * they were set for the original element.\n */\nextendInterfaces('replace', function(_context, teardown) {\n  return function replace(oldTagName: string) {\n    return {\n      with: function(tagName: string) {\n        // Standardizes our replacements map\n        oldTagName = oldTagName.toLowerCase();\n        tagName = tagName.toLowerCase();\n\n        replacements[oldTagName] = tagName;\n\n        // If the function is already a stub, restore it to original\n        if ((document.importNode as any).isSinonProxy) {\n          return;\n        }\n\n        if (!window.Polymer.Element) {\n          window.Polymer.Element = function() {};\n          window.Polymer.Element.prototype._stampTemplate = function() {};\n        }\n\n        // Keep a reference to the original `document.importNode`\n        // implementation for later:\n        const originalImportNode = document.importNode;\n\n        // Use Sinon to stub `document.ImportNode`:\n        sinon.stub(\n            document, 'importNode', function(origContent: any, deep: boolean) {\n              const templateClone = document.createElement('template');\n              const content = templateClone.content;\n              const inertDoc = content.ownerDocument;\n\n              // imports node from inertDoc which holds inert nodes.\n              templateClone.content.appendChild(\n                  inertDoc.importNode(origContent, true));\n\n              // optional arguments are not optional on IE.\n              const nodeIterator = document.createNodeIterator(\n                  content, NodeFilter.SHOW_ELEMENT, null, true);\n              let node;\n\n              // Traverses the tree. A recently-replaced node will be put next,\n              // so if a node is replaced, it will be checked if it needs to be\n              // replaced again.\n              while (node = nodeIterator.nextNode() as Element) {\n                let currentTagName = node.tagName.toLowerCase();\n\n                if (replacements.hasOwnProperty(currentTagName)) {\n                  currentTagName = replacements[currentTagName];\n\n                  // find the final tag name.\n                  while (replacements[currentTagName]) {\n                    currentTagName = replacements[currentTagName];\n                  }\n\n                  // Create a replacement:\n                  const replacement = document.createElement(currentTagName);\n\n                  // For all attributes in the original node..\n                  for (let index = 0; index < node.attributes.length; ++index) {\n                    // Set that attribute on the replacement:\n                    replacement.setAttribute(\n                        node.attributes[index].name,\n                        node.attributes[index].value);\n                  }\n\n                  // Replace the original node with the replacement node:\n                  node.parentNode.replaceChild(replacement, node);\n                }\n              }\n\n              return originalImportNode.call(this, content, deep);\n            });\n\n        if (!replaceTeardownAttached) {\n          // After each test...\n          teardown(function() {\n            replaceTeardownAttached = true;\n            // Restore the stubbed version of `document.importNode`:\n            const documentImportNode = document.importNode as any;\n            if (documentImportNode.isSinonProxy) {\n              documentImportNode.restore();\n            }\n\n            // Empty the replacement map\n            replacements = {};\n          });\n        }\n      }\n    };\n  };\n});\n"
  },
  {
    "path": "browser/mocha/stub.ts",
    "content": "import {extendInterfaces} from './extend.js';\n\n/**\n * stub\n *\n * The stub addon allows the tester to partially replace the implementation of\n * an element with some custom implementation. Usage example:\n *\n * beforeEach(function() {\n *   stub('x-foo', {\n *     attached: function() {\n *       // Custom implementation of the `attached` method of element `x-foo`..\n *     },\n *     otherMethod: function() {\n *       // More custom implementation..\n *     },\n *     getterSetterProperty: {\n *       get: function() {\n *         // Custom getter implementation..\n *       },\n *       set: function() {\n *         // Custom setter implementation..\n *       }\n *     },\n *     // etc..\n *   });\n * });\n */\nextendInterfaces('stub', function(_context, teardown) {\n\n  return function stub(tagName: string, implementation: object) {\n    // Find the prototype of the element being stubbed:\n    const proto = document.createElement(tagName).constructor.prototype;\n\n    // For all keys in the implementation to stub with..\n    const stubs = Object.keys(implementation).map(function(key) {\n      // Stub the method on the element prototype with Sinon:\n      return sinon.stub(proto, key, implementation[key]);\n    });\n\n    // After all tests..\n    teardown(function() {\n      stubs.forEach(function(stub) {\n        stub.restore();\n      });\n    });\n  };\n});\n"
  },
  {
    "path": "browser/mocha.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt The complete set of authors may be found\n * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may\n * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by\n * Google as part of the polymer project is also subject to an additional IP\n * rights grant found at http://polymer.github.io/PATENTS.txt\n */\nimport './mocha/fixture.js';\nimport './mocha/stub.js';\nimport './mocha/replace.js';\n\nimport * as config from './config.js';\nimport {applyExtensions} from './mocha/extend.js';\n\n// Mocha global helpers, broken out by testing method.\n//\n// Keys are the method for a particular interface; values are their analog in\n// the opposite interface.\nconst MOCHA_EXPORTS = {\n  // https://github.com/visionmedia/mocha/blob/master/lib/interfaces/tdd.js\n  tdd: {\n    'setup': '\"before\"',\n    'teardown': '\"after\"',\n    'suiteSetup': '\"beforeEach\"',\n    'suiteTeardown': '\"afterEach\"',\n    'suite': '\"describe\" or \"context\"',\n    'test': '\"it\" or \"specify\"',\n  },\n  // https://github.com/visionmedia/mocha/blob/master/lib/interfaces/bdd.js\n  bdd: {\n    'before': '\"setup\"',\n    'after': '\"teardown\"',\n    'beforeEach': '\"suiteSetup\"',\n    'afterEach': '\"suiteTeardown\"',\n    'describe': '\"suite\"',\n    'context': '\"suite\"',\n    'xdescribe': '\"suite.skip\"',\n    'xcontext': '\"suite.skip\"',\n    'it': '\"test\"',\n    'xit': '\"test.skip\"',\n    'specify': '\"test\"',\n    'xspecify': '\"test.skip\"',\n  },\n};\n\n/**\n * Exposes all Mocha methods up front, configuring and running mocha\n * automatically when you call them.\n *\n * The assumption is that it is a one-off (sub-)suite of tests being run.\n */\nexport function stubInterfaces() {\n  const keys = Object.keys(MOCHA_EXPORTS) as Array<keyof typeof MOCHA_EXPORTS>;\n  keys.forEach(function(ui) {\n    Object.keys(MOCHA_EXPORTS[ui]).forEach(function(key) {\n      window[key] = function wrappedMochaFunction() {\n        _setupMocha(ui, key, MOCHA_EXPORTS[ui][key]);\n        if (!window[key] || window[key] === wrappedMochaFunction) {\n          throw new Error('Expected mocha.setup to define ' + key);\n        }\n        window[key].apply(window, arguments);\n      };\n    });\n  });\n}\n\n// Whether we've called `mocha.setup`\nconst _mochaIsSetup = false;\n\n/**\n * @param {string} ui Sets up mocha to run `ui`-style tests.\n * @param {string} key The method called that triggered this.\n * @param {string} alternate The matching method in the opposite interface.\n */\nfunction _setupMocha(ui: 'tdd'|'bdd', key: string, alternate: 'string') {\n  const mochaOptions = config.get('mochaOptions');\n  if (mochaOptions.ui && mochaOptions.ui !== ui) {\n    const message = 'Mixing ' + mochaOptions.ui + ' and ' + ui +\n        ' Mocha styles is not supported. ' +\n        'You called \"' + key + '\". Did you mean ' + alternate + '?';\n    throw new Error(message);\n  }\n  if (_mochaIsSetup) {\n    return;\n  }\n\n  applyExtensions();\n  mochaOptions.ui = ui;\n  mocha.setup(mochaOptions);  // Note that the reporter is configured in run.js.\n}\n"
  },
  {
    "path": "browser/more-declarations.ts",
    "content": "declare namespace Mocha {\n  interface UtilsStatic {\n    highlightTags(somethingSomething: string): void;\n  }\n  let utils: UtilsStatic;\n  interface IRunner extends NodeJS.EventEmitter {\n    name?: string;\n    total: number;\n  }\n\n  interface IRunnable {\n    parent: ISuite;\n    root: boolean;\n    state: 'passed'|'failed'|undefined;\n    pending: boolean;\n  }\n\n  interface ISuite {\n    root: boolean;\n  }\n\n  let Runner: {prototype: IRunner; immediately(callback: () => void): void};\n}\n\ndeclare namespace SocketIO {\n  interface Server {\n    off(): void;\n  }\n}\n"
  },
  {
    "path": "browser/reporters/console.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt The complete set of authors may be found\n * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may\n * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by\n * Google as part of the polymer project is also subject to an additional IP\n * rights grant found at http://polymer.github.io/PATENTS.txt\n */\nimport * as util from '../util.js';\n\n// We capture console events when running tests; so make sure we have a\n// reference to the original one.\nconst console = window.console;\n\nconst FONT =\n    ';font: normal 13px \"Roboto\", \"Helvetica Neue\", \"Helvetica\", sans-serif;';\nconst STYLES = {\n  plain: FONT,\n  suite: 'color: #5c6bc0' + FONT,\n  test: FONT,\n  passing: 'color: #259b24' + FONT,\n  pending: 'color: #e65100' + FONT,\n  failing: 'color: #c41411' + FONT,\n  stack: 'color: #c41411',\n  results: FONT + 'font-size: 16px',\n};\n\n// I don't think we can feature detect this one...\nconst userAgent = navigator.userAgent.toLowerCase();\nconst CAN_STYLE_LOG = userAgent.match('firefox') || userAgent.match('webkit');\nconst CAN_STYLE_GROUP = userAgent.match('webkit');\n// Track the indent for faked `console.group`\nlet logIndent = '';\n\nfunction log(text: string, style?: keyof typeof STYLES) {\n  text = text.split('\\n')\n             .map(function(l) {\n               return logIndent + l;\n             })\n             .join('\\n');\n  if (CAN_STYLE_LOG) {\n    console.log('%c' + text, STYLES[style] || STYLES.plain);\n  } else {\n    console.log(text);\n  }\n}\n\nfunction logGroup(text: string, style?: keyof typeof STYLES) {\n  if (CAN_STYLE_GROUP) {\n    console.group('%c' + text, STYLES[style] || STYLES.plain);\n  } else if (console.group) {\n    console.group(text);\n  } else {\n    logIndent = logIndent + '  ';\n    log(text, style);\n  }\n}\n\nfunction logGroupEnd() {\n  if (console.groupEnd) {\n    console.groupEnd();\n  } else {\n    logIndent = logIndent.substr(0, logIndent.length - 2);\n  }\n}\n\nfunction logException(error: Error) {\n  log(error.stack || error.message || (error + ''), 'stack');\n}\n\n/**\n * A Mocha reporter that logs results out to the web `console`.\n */\nexport default class Console {\n  /**\n   * @param runner The runner that is being reported on.\n   */\n  constructor(runner: Mocha.IRunner) {\n    Mocha.reporters.Base.call(this, runner);\n\n    runner.on('suite', function(suite: Mocha.ISuite) {\n      if (suite.root) {\n        return;\n      }\n      logGroup(suite.title, 'suite');\n    }.bind(this));\n\n    runner.on('suite end', function(suite: Mocha.ISuite) {\n      if (suite.root) {\n        return;\n      }\n      logGroupEnd();\n    }.bind(this));\n\n    runner.on('test', function(test: Mocha.ITest) {\n      logGroup(test.title, 'test');\n    }.bind(this));\n\n    runner.on('pending', function(test: Mocha.ITest) {\n      logGroup(test.title, 'pending');\n    }.bind(this));\n\n    runner.on('fail', function(_test: Mocha.ITest, error: any) {\n      logException(error);\n    }.bind(this));\n\n    runner.on('test end', function(_test: Mocha.ITest) {\n      logGroupEnd();\n    }.bind(this));\n\n    runner.on('end', this.logSummary.bind(this));\n  }\n\n  /** Prints out a final summary of test results. */\n  logSummary() {\n    logGroup('Test Results', 'results');\n\n    if (this.stats.failures > 0) {\n      log(util.pluralizedStat(this.stats.failures, 'failing'), 'failing');\n    }\n    if (this.stats.pending > 0) {\n      log(util.pluralizedStat(this.stats.pending, 'pending'), 'pending');\n    }\n    log(util.pluralizedStat(this.stats.passes, 'passing'));\n\n    if (!this.stats.failures) {\n      log('test suite passed', 'passing');\n    }\n    log('Evaluated ' + this.stats.tests + ' tests in ' +\n        (this.stats as any).duration + 'ms.');\n    logGroupEnd();\n  }\n}\n\nexport default interface Console extends Mocha.reporters.Base {}\n"
  },
  {
    "path": "browser/reporters/html.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt The complete set of authors may be found\n * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may\n * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by\n * Google as part of the polymer project is also subject to an additional IP\n * rights grant found at http://polymer.github.io/PATENTS.txt\n */\n\n/**\n * WCT-specific behavior on top of Mocha's default HTML reporter.\n *\n * @param {!Mocha.Runner} runner The runner that is being reported on.\n */\nexport default function HTML(runner: Mocha.IRunner) {\n  const output = document.createElement('div');\n  output.id = 'mocha';\n  document.body.appendChild(output);\n\n  runner.on('suite', function(_test: any) {\n    this.total = runner.total;\n  }.bind(this));\n\n  Mocha.reporters.HTML.call(this, runner);\n}\n\n// Woo! What a hack. This just saves us from adding a bunch of complexity around\n// style loading.\nconst style = document.createElement('style');\nstyle.textContent = `\n    html, body {\n      position: relative;\n      height: 100%;\n      width:  100%;\n      min-width: 900px;\n    }\n    #mocha, #subsuites {\n      height: 100%;\n      position: absolute;\n      top: 0;\n    }\n    #mocha {\n      box-sizing: border-box;\n      margin: 0 !important;\n      padding: 60px 20px;\n      right: 0;\n      left: 500px;\n    }\n    #subsuites {\n      -ms-flex-direction: column;\n      -webkit-flex-direction: column;\n      display: -ms-flexbox;\n      display: -webkit-flex;\n      display: flex;\n      flex-direction: column;\n      left: 0;\n      width: 500px;\n    }\n    #subsuites .subsuite {\n      border: 0;\n      width: 100%;\n      height: 100%;\n    }\n    #mocha .test.pass .duration {\n      color: #555 !important;\n    }\n`;\ndocument.head.appendChild(style);\n"
  },
  {
    "path": "browser/reporters/multi.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt The complete set of authors may be found\n * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may\n * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by\n * Google as part of the polymer project is also subject to an additional IP\n * rights grant found at http://polymer.github.io/PATENTS.txt\n */\nimport * as util from '../util.js';\n\nconst STACKY_CONFIG = {\n  indent: '  ',\n  locationStrip: [\n    /^https?:\\/\\/[^\\/]+/,\n    /\\?.*$/,\n  ],\n  filter(line: {location: string}) {\n    return !!line.location.match(/\\/web-component-tester\\/[^\\/]+(\\?.*)?$/);\n  },\n};\n\n// https://github.com/visionmedia/mocha/blob/master/lib/runner.js#L36-46\nconst MOCHA_EVENTS = [\n  'start', 'end', 'suite', 'suite end', 'test', 'test end', 'hook', 'hook end',\n  'pass', 'fail', 'pending', 'childRunner end'\n];\n\n// Until a suite has loaded, we assume this many tests in it.\nconst ESTIMATED_TESTS_PER_SUITE = 3;\n\nexport interface Reporter {}\n\nexport interface ReporterFactory { new(parent: MultiReporter): Reporter; }\n\ninterface ExtendedTest extends Mocha.ITest {\n  err: any;\n}\n\n/**\n * A Mocha-like reporter that combines the output of multiple Mocha suites.\n */\nexport default class MultiReporter implements Reporter {\n  private readonly reporters: ReadonlyArray<Reporter>;\n  private readonly parent: MultiReporter|undefined;\n  private readonly basePath: string;\n  total: number;\n  private currentRunner: null|Mocha.IRunner;\n  /** Arguments that would be called on emit(). */\n  private pendingEvents: Array<any[]>;\n  private complete: boolean|undefined;\n\n  /**\n   * @param numSuites The number of suites that will be run, in order to\n   *     estimate the total number of tests that will be performed.\n   * @param reporters The set of reporters that\n   *     should receive the unified event stream.\n   * @param parent The parent reporter, if present.\n   */\n  constructor(\n      numSuites: number, reporters: ReporterFactory[],\n      parent: MultiReporter|undefined) {\n    this.reporters = reporters.map((reporter) => {\n      return new reporter(this);\n    });\n\n    this.parent = parent;\n    this.basePath = parent && parent.basePath || util.basePath(window.location);\n\n    this.total = numSuites * ESTIMATED_TESTS_PER_SUITE;\n    // Mocha reporters assume a stream of events, so we have to be careful to\n    // only report on one runner at a time...\n    this.currentRunner = null;\n    // ...while we buffer events for any other active runners.\n    this.pendingEvents = [];\n\n    this.emit('start');\n  }\n\n  /**\n   * @param location The location this reporter represents.\n   * @return A reporter-like \"class\" for each child suite\n   *     that should be passed to `mocha.run`.\n   */\n  childReporter(location: Location|string): ReporterFactory {\n    const name = this.suiteTitle(location);\n    // The reporter is used as a constructor, so we can't depend on `this` being\n    // properly bound.\n    const self = this;\n    return class ChildReporter {\n      constructor(runner: Mocha.IRunner) {\n        runner.name = window.name;\n        self.bindChildRunner(runner);\n      }\n\n      static title = window.name;\n    };\n  }\n\n  /** Must be called once all runners have finished. */\n  done() {\n    this.complete = true;\n    this.flushPendingEvents();\n    this.emit('end');\n  }\n\n  /**\n   * Emit a top level test that is not part of any suite managed by this\n   * reporter.\n   *\n   * Helpful for reporting on global errors, loading issues, etc.\n   *\n   * @param title The title of the test.\n   * @param error An error associated with this test. If falsy, test is\n   *     considered to be passing.\n   * @param suiteTitle Title for the suite that's wrapping the test.\n   * @param estimated If this test was included in the original\n   *     estimate of `numSuites`.\n   */\n  emitOutOfBandTest(\n      title: string, error?: any, suiteTitle?: string, estimated?: boolean) {\n    util.debug('MultiReporter#emitOutOfBandTest(', arguments, ')');\n    const root: Mocha.ISuite = new (Mocha as any).Suite(suiteTitle || '');\n    const test: ExtendedTest = new (Mocha as any).Test(title, function() {});\n    test.parent = root;\n    test.state = error ? 'failed' : 'passed';\n    test.err = error;\n\n    if (!estimated) {\n      this.total = this.total + ESTIMATED_TESTS_PER_SUITE;\n    }\n\n    const runner = {total: 1} as Mocha.IRunner;\n    this.proxyEvent('start', runner);\n    this.proxyEvent('suite', runner, root);\n    this.proxyEvent('test', runner, test);\n    if (error) {\n      this.proxyEvent('fail', runner, test, error);\n    } else {\n      this.proxyEvent('pass', runner, test);\n    }\n    this.proxyEvent('test end', runner, test);\n    this.proxyEvent('suite end', runner, root);\n    this.proxyEvent('end', runner);\n  }\n\n  /**\n   * @param {!Location|string} location\n   * @return {string}\n   */\n  suiteTitle(location: Location|string) {\n    let path = util.relativeLocation(location, this.basePath);\n    path = util.cleanLocation(path);\n    return path;\n  }\n\n  // Internal Interface\n\n  /** @param {!Mocha.runners.Base} runner The runner to listen to events for. */\n  private bindChildRunner(runner: Mocha.IRunner) {\n    MOCHA_EVENTS.forEach((eventName) => {\n      runner.on(eventName, this.proxyEvent.bind(this, eventName, runner));\n    });\n  }\n\n  /**\n   * Evaluates an event fired by `runner`, proxying it forward or buffering it.\n   *\n   * @param {string} eventName\n   * @param {!Mocha.runners.Base} runner The runner that emitted this event.\n   * @param {...*} var_args Any additional data passed as part of the event.\n   */\n  private proxyEvent(\n      eventName: string, runner: Mocha.IRunner, ..._args: any[]) {\n    const extraArgs = Array.prototype.slice.call(arguments, 2);\n    if (this.complete) {\n      console.warn(\n          'out of order Mocha event for ' + runner.name + ':', eventName,\n          extraArgs);\n      return;\n    }\n\n    if (this.currentRunner && runner !== this.currentRunner) {\n      this.pendingEvents.push(Array.prototype.slice.call(arguments));\n      return;\n    }\n    util.debug('MultiReporter#proxyEvent(', arguments, ')');\n\n    // This appears to be a Mocha bug: Tests failed by passing an error to their\n    // done function don't set `err` properly.\n    //\n    // TODO(nevir): Track down.\n    if (eventName === 'fail' && !extraArgs[0].err) {\n      extraArgs[0].err = extraArgs[1];\n    }\n\n    if (eventName === 'start') {\n      this.onRunnerStart(runner);\n    } else if (eventName === 'end') {\n      this.onRunnerEnd(runner);\n    } else {\n      this.cleanEvent(eventName, runner, extraArgs);\n      this.emit.apply(this, [eventName].concat(extraArgs));\n    }\n  }\n\n  /**\n   * Cleans or modifies an event if needed.\n   *\n   * @param eventName\n   * @param runner The runner that emitted this event.\n   * @param extraArgs\n   */\n  private cleanEvent(\n      eventName: string, _runner: Mocha.IRunner, extraArgs: any[]) {\n    // Suite hierarchy\n    if (extraArgs[0]) {\n      extraArgs[0] = this.showRootSuite(extraArgs[0]);\n    }\n\n    // Normalize errors\n    if (eventName === 'fail') {\n      extraArgs[1] = Stacky.normalize(extraArgs[1], STACKY_CONFIG);\n    }\n    if (extraArgs[0] && extraArgs[0].err) {\n      extraArgs[0].err = Stacky.normalize(extraArgs[0].err, STACKY_CONFIG);\n    }\n  }\n\n  /**\n   * We like to show the root suite's title, which requires a little bit of\n   * trickery in the suite hierarchy.\n   *\n   * @param {!Mocha.Runnable} node\n   */\n  private showRootSuite(node: Mocha.IRunnable) {\n    const leaf = node = Object.create(node);\n    while (node && node.parent) {\n      const wrappedParent = Object.create(node.parent);\n      node.parent = wrappedParent;\n      node = wrappedParent;\n    }\n    node.root = false;\n\n    return leaf;\n  }\n\n  /** @param {!Mocha.runners.Base} runner */\n  private onRunnerStart(runner: Mocha.IRunner) {\n    util.debug('MultiReporter#onRunnerStart:', runner.name);\n    this.total = this.total - ESTIMATED_TESTS_PER_SUITE + runner.total;\n    this.currentRunner = runner;\n  }\n\n  /** @param {!Mocha.runners.Base} runner */\n  private onRunnerEnd(runner: Mocha.IRunner) {\n    util.debug('MultiReporter#onRunnerEnd:', runner.name);\n    this.currentRunner = null;\n    this.flushPendingEvents();\n  }\n\n  /**\n   * Flushes any buffered events and runs them through `proxyEvent`. This will\n   * loop until all buffered runners are complete, or we have run out of\n   * buffered events.\n   */\n  private flushPendingEvents() {\n    const events = this.pendingEvents;\n    this.pendingEvents = [];\n    events.forEach((eventArgs) => {\n      this.proxyEvent.apply(this, eventArgs);\n    });\n  }\n}\n\nexport default interface MultiReporter extends Mocha.IRunner,\n                                               NodeJS.EventEmitter {}\n"
  },
  {
    "path": "browser/reporters/title.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt The complete set of authors may be found\n * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may\n * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by\n * Google as part of the polymer project is also subject to an additional IP\n * rights grant found at http://polymer.github.io/PATENTS.txt\n */\nimport * as util from '../util.js';\n\nconst ARC_OFFSET = 0;  // start at the right.\nconst ARC_WIDTH = 6;\n\n/**\n * A Mocha reporter that updates the document's title and favicon with\n * at-a-glance stats.\n *\n * @param {!Mocha.Runner} runner The runner that is being reported on.\n */\nexport default class Title {\n  runner: Mocha.IRunner;\n  constructor(runner: Mocha.IRunner) {\n    Mocha.reporters.Base.call(this, runner);\n\n    runner.on('test end', this.report.bind(this));\n  }\n\n  /** Reports current stats via the page title and favicon. */\n  report() {\n    this.updateTitle();\n    this.updateFavicon();\n  }\n\n  /** Updates the document title with a summary of current stats. */\n  updateTitle() {\n    if (this.stats.failures > 0) {\n      document.title = util.pluralizedStat(this.stats.failures, 'failing');\n    } else {\n      document.title = util.pluralizedStat(this.stats.passes, 'passing');\n    }\n  }\n\n  /** Updates the document's favicon w/ a summary of current stats. */\n  updateFavicon() {\n    const canvas = document.createElement('canvas');\n    canvas.height = canvas.width = 32;\n    const context = canvas.getContext('2d');\n\n    const passing = this.stats.passes;\n    const pending = this.stats.pending;\n    const failing = this.stats.failures;\n    const total = Math.max(this.runner.total, passing + pending + failing);\n    drawFaviconArc(context, total, 0, passing, '#0e9c57');\n    drawFaviconArc(context, total, passing, pending, '#f3b300');\n    drawFaviconArc(context, total, pending + passing, failing, '#ff5621');\n\n    this.setFavicon(canvas.toDataURL());\n  }\n\n  /** Sets the current favicon by URL. */\n  setFavicon(url: string) {\n    const current = document.head.querySelector('link[rel=\"icon\"]');\n    if (current) {\n      document.head.removeChild(current);\n    }\n\n    const link = document.createElement('link');\n    link.rel = 'icon';\n    link.type = 'image/x-icon';\n    link.href = url;\n    link.setAttribute('sizes', '32x32');\n    document.head.appendChild(link);\n  }\n}\n\n/**\n * Draws an arc for the favicon status, relative to the total number of tests.\n */\nfunction drawFaviconArc(\n    context: CanvasRenderingContext2D, total: number, start: number,\n    length: number, color: string) {\n  const arcStart = ARC_OFFSET + Math.PI * 2 * (start / total);\n  const arcEnd = ARC_OFFSET + Math.PI * 2 * ((start + length) / total);\n\n  context.beginPath();\n  context.strokeStyle = color;\n  context.lineWidth = ARC_WIDTH;\n  context.arc(16, 16, 16 - ARC_WIDTH / 2, arcStart, arcEnd);\n  context.stroke();\n}\n\nexport default interface Title extends Mocha.reporters.Base {}\n"
  },
  {
    "path": "browser/reporters.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt The complete set of authors may be found\n * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may\n * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by\n * Google as part of the polymer project is also subject to an additional IP\n * rights grant found at http://polymer.github.io/PATENTS.txt\n */\nimport CLISocket from './clisocket.js';\nimport ConsoleReporter from './reporters/console.js';\nimport HTMLReporter from './reporters/html.js';\nimport MultiReporter, {ReporterFactory} from './reporters/multi.js';\nimport TitleReporter from './reporters/title.js';\nimport * as suites from './suites.js';\n\nexport let htmlSuites: Array<undefined> = [];\nexport let jsSuites: Array<undefined> = [];\n\n/**\n * @param {CLISocket} socket The CLI socket, if present.\n * @param {MultiReporter} parent The parent reporter, if present.\n * @return {!Array.<!Mocha.reporters.Base} The reporters that should be used.\n */\nexport function determineReporters(\n    socket: CLISocket, parent: MultiReporter): ReporterFactory[] {\n  // Parents are greedy.\n  if (parent) {\n    return [parent.childReporter(window.location)];\n  }\n\n  // Otherwise, we get to run wild without any parental supervision!\n  const reporters: Array<ReporterFactory> = [TitleReporter, ConsoleReporter];\n\n  if (socket) {\n    reporters.push(function(runner: MultiReporter) {\n      socket.observe(runner);\n    } as any);\n  }\n\n  if (suites.htmlSuites.length > 0 || suites.jsSuites.length > 0) {\n    reporters.push(HTMLReporter as any);\n  }\n\n  return reporters;\n}\n\nexport type MochaStatic = typeof Mocha;\n/**\n * Yeah, hideous, but this allows us to be loaded before Mocha, which is handy.\n */\nexport function injectMocha(Mocha: MochaStatic) {\n  _injectPrototype(ConsoleReporter, Mocha.reporters.Base.prototype);\n  _injectPrototype(HTMLReporter, Mocha.reporters.HTML.prototype);\n  // Mocha doesn't expose its `EventEmitter` shim directly, so:\n  _injectPrototype(\n      MultiReporter, Object.getPrototypeOf(Mocha.Runner.prototype));\n}\n\nfunction _injectPrototype(klass: any, prototype: any) {\n  const newPrototype = Object.create(prototype);\n  // Only support\n  Object.keys(klass.prototype).forEach(function(key) {\n    newPrototype[key] = klass.prototype[key];\n  });\n\n  klass.prototype = newPrototype;\n}\n"
  },
  {
    "path": "browser/suites.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt The complete set of authors may be found\n * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may\n * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by\n * Google as part of the polymer project is also subject to an additional IP\n * rights grant found at http://polymer.github.io/PATENTS.txt\n */\nimport ChildRunner from './childrunner.js';\nimport * as config from './config.js';\nimport MultiReporter from './reporters/multi.js';\nimport * as util from './util.js';\n\nexport let htmlSuites: string[] = [];\nexport let jsSuites: string[] = [];\n\n// We process grep ourselves to avoid loading suites that will be filtered.\nlet GREP = util.getParam('grep');\n// work around mocha bug (https://github.com/mochajs/mocha/issues/2070)\nif (GREP) {\n  GREP = GREP.replace(/\\\\\\./g, '.');\n}\n\n/**\n * Loads suites of tests, supporting both `.js` and `.html` files.\n *\n * @param files The files to load.\n */\nexport function loadSuites(files: string[]) {\n  files.forEach(function(file) {\n    if (/\\.js(\\?.*)?$/.test(file)) {\n      jsSuites.push(file);\n    } else if (/\\.html(\\?.*)?$/.test(file)) {\n      htmlSuites.push(file);\n    } else {\n      throw new Error('Unknown resource type: ' + file);\n    }\n  });\n}\n\n/**\n * @return The child suites that should be loaded, ignoring\n *     those that would not match `GREP`.\n */\nexport function activeChildSuites(): string[] {\n  let subsuites = htmlSuites;\n  if (GREP) {\n    const cleanSubsuites = [];\n    for (let i = 0, subsuite; subsuite = subsuites[i]; i++) {\n      if (GREP.indexOf(util.cleanLocation(subsuite)) !== -1) {\n        cleanSubsuites.push(subsuite);\n      }\n    }\n    subsuites = cleanSubsuites;\n  }\n  return subsuites;\n}\n\n/**\n * Loads all `.js` sources requested by the current suite.\n */\nexport function loadJsSuites(\n    _reporter: MultiReporter, done: (error: Error) => void) {\n  util.debug('loadJsSuites', jsSuites);\n\n  const loaders = jsSuites.map(function(file) {\n    // We only support `.js` dependencies for now.\n    return util.loadScript.bind(util, file);\n  });\n\n  util.parallel(loaders, done);\n}\n\nexport function runSuites(\n    reporter: MultiReporter, childSuites: string[],\n    done: (error?: any) => void) {\n  util.debug('runSuites');\n\n  const suiteRunners: Array<(next: () => void) => void> = [\n    // Run the local tests (if any) first, not stopping on error;\n    _runMocha.bind(null, reporter),\n  ];\n\n  // As well as any sub suites. Again, don't stop on error.\n  childSuites.forEach(function(file) {\n    suiteRunners.push(function(next) {\n      const childRunner = new ChildRunner(file, window);\n      reporter.emit('childRunner start', childRunner);\n      childRunner.run(function(error) {\n        reporter.emit('childRunner end', childRunner);\n        if (error)\n          reporter.emitOutOfBandTest(file, error);\n        next();\n      });\n    });\n  });\n\n  util.parallel(\n      suiteRunners, config.get('numConcurrentSuites'), function(error) {\n        reporter.done();\n        done(error);\n      });\n}\n\n/**\n * Kicks off a mocha run, waiting for frameworks to load if necessary.\n *\n * @param {!MultiReporter} reporter Where to send Mocha's events.\n * @param {function} done A callback fired, _no error is passed_.\n */\nfunction _runMocha(reporter: MultiReporter, done: () => void, waited: boolean) {\n  if (config.get('waitForFrameworks') && !waited) {\n    const waitFor =\n        (config.get('waitFor') || util.whenFrameworksReady).bind(window);\n    waitFor(_runMocha.bind(null, reporter, done, true));\n    return;\n  }\n  util.debug('_runMocha');\n  const mocha = window.mocha;\n  const Mocha = window.Mocha;\n\n  mocha.reporter(reporter.childReporter(window.location));\n  mocha.suite.title = reporter.suiteTitle(window.location);\n  mocha.grep(GREP);\n\n  // We can't use `mocha.run` because it bashes over grep, invert, and friends.\n  // See https://github.com/visionmedia/mocha/blob/master/support/tail.js#L137\n  const runner = Mocha.prototype.run.call(mocha, function(_error: any) {\n    if (document.getElementById('mocha')) {\n      Mocha.utils.highlightTags('code');\n    }\n    done();  // We ignore the Mocha failure count.\n  });\n\n  // Mocha's default `onerror` handling strips the stack (to support really old\n  // browsers). We upgrade this to get better stacks for async errors.\n  //\n  // TODO(nevir): Can we expand support to other browsers?\n  if (navigator.userAgent.match(/chrome/i)) {\n    window.onerror = null;\n    window.addEventListener('error', function(event) {\n      if (!event.error)\n        return;\n      if (event.error.ignore)\n        return;\n      runner.uncaught(event.error);\n    });\n  }\n}\n"
  },
  {
    "path": "browser/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"module\": \"es6\",\n    \"moduleResolution\": \"node\",\n    \"isolatedModules\": false,\n    \"noImplicitAny\": true,\n    \"noUnusedLocals\": false,\n    \"noUnusedParameters\": true,\n    \"noImplicitThis\": false,\n    \"strictNullChecks\": false,\n    \"removeComments\": false,\n    \"preserveConstEnums\": true,\n    \"suppressImplicitAnyIndexErrors\": true,\n    \"lib\": [\n      \"es2017\",\n      \"dom\"\n    ],\n    \"sourceMap\": true,\n    \"pretty\": true\n  },\n  \"exclude\": [\n    \"node_modules\"\n  ],\n  \"include\": [\n    \"*.ts\",\n    \"**/*.ts\",\n    \"../custom_typings/*.d.ts\"\n  ]\n}\n"
  },
  {
    "path": "browser/util.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt The complete set of authors may be found\n * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may\n * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by\n * Google as part of the polymer project is also subject to an additional IP\n * rights grant found at http://polymer.github.io/PATENTS.txt\n */\n\nimport * as config from './config.js';\n\n/**\n * @param {function()} callback A function to call when the active web component\n *     frameworks have loaded.\n */\nexport function whenFrameworksReady(callback: () => void) {\n  debug('whenFrameworksReady');\n  const done = function() {\n    debug('whenFrameworksReady done');\n    callback();\n  };\n\n  // If webcomponents script is in the document, wait for WebComponentsReady.\n  if (window.WebComponents && !window.WebComponents.ready) {\n    debug('WebComponentsReady?');\n    window.addEventListener('WebComponentsReady', function wcReady() {\n      window.removeEventListener('WebComponentsReady', wcReady);\n      debug('WebComponentsReady');\n      done();\n    });\n  } else {\n    done();\n  }\n}\n\n/**\n * @return {string} '<count> <kind> tests' or '<count> <kind> test'.\n */\nexport function pluralizedStat(count: number, kind: string): string {\n  if (count === 1) {\n    return count + ' ' + kind + ' test';\n  } else {\n    return count + ' ' + kind + ' tests';\n  }\n}\n\n/**\n * @param {string} path The URI of the script to load.\n * @param {function} done\n */\nexport function loadScript(path: string, done: (error?: any) => void) {\n  const script = document.createElement('script');\n  script.src = path;\n  if (done) {\n    script.onload = done.bind(null, null);\n    script.onerror = done.bind(null, 'Failed to load script ' + script.src);\n  }\n  document.head.appendChild(script);\n}\n\n/**\n * @param {string} path The URI of the stylesheet to load.\n * @param {function} done\n */\nexport function loadStyle(path: string, done?: () => void) {\n  const link = document.createElement('link');\n  link.rel = 'stylesheet';\n  link.href = path;\n  if (done) {\n    link.onload = done.bind(null, null);\n    link.onerror = done.bind(null, 'Failed to load stylesheet ' + link.href);\n  }\n  document.head.appendChild(link);\n}\n\n/**\n * @param {...*} var_args Logs values to the console when the `debug`\n *     configuration option is true.\n */\nexport function debug(...var_args: any[]) {\n  if (!config.get('verbose')) {\n    return;\n  }\n  const args = [window.location.pathname, ...var_args];\n  (console.debug || console.log).apply(console, args);\n}\n\n// URL Processing\n\n/**\n * @param {string} url\n * @return {{base: string, params: string}}\n */\nexport function parseUrl(url: string) {\n  const parts = url.match(/^(.*?)(?:\\?(.*))?$/);\n  return {\n    base: parts[1],\n    params: getParams(parts[2] || ''),\n  };\n}\n\n/**\n * Expands a URL that may or may not be relative to `base`.\n *\n * @param {string} url\n * @param {string} base\n * @return {string}\n */\nexport function expandUrl(url: string, base: string) {\n  if (!base)\n    return url;\n  if (url.match(/^(\\/|https?:\\/\\/)/))\n    return url;\n  if (base.substr(base.length - 1) !== '/') {\n    base = base + '/';\n  }\n  return base + url;\n}\n\nexport interface Params { [param: string]: string[]; }\n\n/**\n * @param {string=} opt_query A query string to parse.\n * @return {!Object<string, !Array<string>>} All params on the URL's query.\n */\nexport function getParams(query?: string): Params {\n  query = typeof query === 'string' ? query : window.location.search;\n  if (query.substring(0, 1) === '?') {\n    query = query.substring(1);\n  }\n  // python's SimpleHTTPServer tacks a `/` on the end of query strings :(\n  if (query.slice(-1) === '/') {\n    query = query.substring(0, query.length - 1);\n  }\n  if (query === '')\n    return {};\n\n  const result: {[param: string]: string[]} = {};\n  query.split('&').forEach(function(part) {\n    const pair = part.split('=');\n    if (pair.length !== 2) {\n      console.warn('Invalid URL query part:', part);\n      return;\n    }\n    const key = decodeURIComponent(pair[0]);\n    const value = decodeURIComponent(pair[1]);\n\n    if (!result[key]) {\n      result[key] = [];\n    }\n    result[key].push(value);\n  });\n\n  return result;\n}\n\n/**\n * Merges params from `source` into `target` (mutating `target`).\n *\n * @param {!Object<string, !Array<string>>} target\n * @param {!Object<string, !Array<string>>} source\n */\nexport function mergeParams(target: Params, source: Params) {\n  Object.keys(source).forEach(function(key) {\n    if (!(key in target)) {\n      target[key] = [];\n    }\n    target[key] = target[key].concat(source[key]);\n  });\n}\n\n/**\n * @param {string} param The param to return a value for.\n * @return {?string} The first value for `param`, if found.\n */\nexport function getParam(param: string): string|null {\n  const params = getParams();\n  return params[param] ? params[param][0] : null;\n}\n\n/**\n * @param {!Object<string, !Array<string>>} params\n * @return {string} `params` encoded as a URI query.\n */\nexport function paramsToQuery(params: Params): string {\n  const pairs: string[] = [];\n  Object.keys(params).forEach(function(key) {\n    params[key].forEach(function(value) {\n      pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));\n    });\n  });\n  return (pairs.length > 0) ? ('?' + pairs.join('&')) : '';\n}\n\nfunction getPathName(location: Location|string): string {\n  return typeof location === 'string' ? location : location.pathname;\n}\n\nexport function basePath(location: Location|string) {\n  return getPathName(location).match(/^.*\\//)[0];\n}\n\nexport function relativeLocation(location: Location|string, basePath: string) {\n  let path = getPathName(location);\n  if (path.indexOf(basePath) === 0) {\n    path = path.substring(basePath.length);\n  }\n  return path;\n}\n\nexport function cleanLocation(location: Location|string) {\n  let path = getPathName(location);\n  if (path.slice(-11) === '/index.html') {\n    path = path.slice(0, path.length - 10);\n  }\n  return path;\n}\n\nexport type Runner = (f: Function) => void;\n\n/**\n * Like `async.parallelLimit`, but our own so that we don't force a dependency\n * on downstream code.\n *\n * @param runners Runners that call their given\n *     Node-style callback when done.\n * @param {number|function(*)} limit Maximum number of concurrent runners.\n *     (optional).\n * @param {?function(*)} done Callback that should be triggered once all runners\n *     have completed, or encountered an error.\n */\nexport function parallel(runners: Runner[], done: (error?: any) => void): void;\nexport function parallel(\n    runners: Runner[], limit: number, done: (error?: any) => void): void;\nexport function parallel(\n    runners: Runner[], maybeLimit: number|((error?: any) => void),\n    done?: (error?: any) => void) {\n  let limit: number;\n  if (typeof maybeLimit !== 'number') {\n    done = maybeLimit;\n    limit = 0;\n  } else {\n    limit = maybeLimit;\n  }\n  if (!runners.length) {\n    return done();\n  }\n\n  let called = false;\n  const total = runners.length;\n  let numActive = 0;\n  let numDone = 0;\n\n  function runnerDone(error: any) {\n    if (called) {\n      return;\n    }\n    numDone = numDone + 1;\n    numActive = numActive - 1;\n\n    if (error || numDone >= total) {\n      called = true;\n      done(error);\n    } else {\n      runOne();\n    }\n  }\n\n  function runOne() {\n    if (limit && numActive >= limit) {\n      return;\n    }\n    if (!runners.length) {\n      return;\n    }\n    numActive = numActive + 1;\n    runners.shift()(runnerDone);\n  }\n  runners.forEach(runOne);\n}\n\n/**\n * Finds the directory that a loaded script is hosted on.\n *\n * @param {string} filename\n * @return {string?}\n */\nexport function scriptPrefix(filename: string): string|null {\n  const scripts =\n      document.querySelectorAll('script[src*=\"' + filename + '\"]') as\n      NodeListOf<HTMLScriptElement>;\n  if (scripts.length !== 1) {\n    return null;\n  }\n  const script = scripts[0].src;\n  return script.substring(0, script.indexOf(filename));\n}\n"
  },
  {
    "path": "browser-js-header.txt",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n */\n \n/**\n * THIS FILE IS AUTOMATICALLY GENERATED! \n * To make changes to browser.js, please edit the source files in the repo's `browser/` directory!\n */\n"
  },
  {
    "path": "browser.js",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n */\n \n/**\n * THIS FILE IS AUTOMATICALLY GENERATED! \n * To make changes to browser.js, please edit the source files in the repo's `browser/` directory!\n */\n\n(function () {\n'use strict';\n\nwindow.__wctUseNpm = false;\n/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt The complete set of authors may be found\n * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may\n * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by\n * Google as part of the polymer project is also subject to an additional IP\n * rights grant found at http://polymer.github.io/PATENTS.txt\n */\n// Make sure that we use native timers, in case they're being stubbed out.\nvar nativeSetInterval = window.setInterval;\nvar nativeSetTimeout = window.setTimeout;\nvar nativeRequestAnimationFrame = window.requestAnimationFrame;\n/**\n * Runs `stepFn`, catching any error and passing it to `callback` (Node-style).\n * Otherwise, calls `callback` with no arguments on success.\n *\n * @param {function()} callback\n * @param {function()} stepFn\n */\nfunction safeStep(callback, stepFn) {\n    var err;\n    try {\n        stepFn();\n    }\n    catch (error) {\n        err = error;\n    }\n    callback(err);\n}\n/**\n * Runs your test at declaration time (before Mocha has begun tests). Handy for\n * when you need to test document initialization.\n *\n * Be aware that any errors thrown asynchronously cannot be tied to your test.\n * You may want to catch them and pass them to the done event, instead. See\n * `safeStep`.\n *\n * @param {string} name The name of the test.\n * @param {function(?function())} testFn The test function. If an argument is\n *     accepted, the test will be treated as async, just like Mocha tests.\n */\nfunction testImmediate(name, testFn) {\n    if (testFn.length > 0) {\n        return testImmediateAsync(name, testFn);\n    }\n    var err;\n    try {\n        testFn();\n    }\n    catch (error) {\n        err = error;\n    }\n    test(name, function (done) {\n        done(err);\n    });\n}\n/**\n * An async-only variant of `testImmediate`.\n *\n * @param {string} name\n * @param {function(?function())} testFn\n */\nfunction testImmediateAsync(name, testFn) {\n    var testComplete = false;\n    var err;\n    test(name, function (done) {\n        var intervalId = nativeSetInterval(function () {\n            if (!testComplete)\n                return;\n            clearInterval(intervalId);\n            done(err);\n        }, 10);\n    });\n    try {\n        testFn(function (error) {\n            if (error)\n                err = error;\n            testComplete = true;\n        });\n    }\n    catch (error) {\n        err = error;\n        testComplete = true;\n    }\n}\n/**\n * Triggers a flush of any pending events, observations, etc and calls you back\n * after they have been processed.\n *\n * @param {function()} callback\n */\nfunction flush(callback) {\n    // Ideally, this function would be a call to Polymer.dom.flush, but that\n    // doesn't support a callback yet\n    // (https://github.com/Polymer/polymer-dev/issues/851),\n    // ...and there's cross-browser flakiness to deal with.\n    // Make sure that we're invoking the callback with no arguments so that the\n    // caller can pass Mocha callbacks, etc.\n    var done = function done() {\n        callback();\n    };\n    // Because endOfMicrotask is flaky for IE, we perform microtask checkpoints\n    // ourselves (https://github.com/Polymer/polymer-dev/issues/114):\n    var isIE = navigator.appName === 'Microsoft Internet Explorer';\n    if (isIE && window.Platform && window.Platform.performMicrotaskCheckpoint) {\n        var reallyDone_1 = done;\n        done = function doneIE() {\n            Platform.performMicrotaskCheckpoint();\n            nativeSetTimeout(reallyDone_1, 0);\n        };\n    }\n    // Everyone else gets a regular flush.\n    var scope;\n    if (window.Polymer && window.Polymer.dom && window.Polymer.dom.flush) {\n        scope = window.Polymer.dom;\n    }\n    else if (window.Polymer && window.Polymer.flush) {\n        scope = window.Polymer;\n    }\n    else if (window.WebComponents && window.WebComponents.flush) {\n        scope = window.WebComponents;\n    }\n    if (scope) {\n        scope.flush();\n    }\n    // Ensure that we are creating a new _task_ to allow all active microtasks to\n    // finish (the code you're testing may be using endOfMicrotask, too).\n    nativeSetTimeout(done, 0);\n}\n/**\n * Advances a single animation frame.\n *\n * Calls `flush`, `requestAnimationFrame`, `flush`, and `callback` sequentially\n * @param {function()} callback\n */\nfunction animationFrameFlush(callback) {\n    flush(function () {\n        nativeRequestAnimationFrame(function () {\n            flush(callback);\n        });\n    });\n}\n/**\n * DEPRECATED: Use `flush`.\n * @param {function} callback\n */\nfunction asyncPlatformFlush(callback) {\n    console.warn('asyncPlatformFlush is deprecated in favor of the more terse flush()');\n    return window.flush(callback);\n}\n/**\n *\n */\nfunction waitFor(fn, next, intervalOrMutationEl, timeout, timeoutTime) {\n    timeoutTime = timeoutTime || Date.now() + (timeout || 1000);\n    intervalOrMutationEl = intervalOrMutationEl || 32;\n    try {\n        fn();\n    }\n    catch (e) {\n        if (Date.now() > timeoutTime) {\n            throw e;\n        }\n        else {\n            if (typeof intervalOrMutationEl !== 'number') {\n                intervalOrMutationEl.onMutation(intervalOrMutationEl, function () {\n                    waitFor(fn, next, intervalOrMutationEl, timeout, timeoutTime);\n                });\n            }\n            else {\n                nativeSetTimeout(function () {\n                    waitFor(fn, next, intervalOrMutationEl, timeout, timeoutTime);\n                }, intervalOrMutationEl);\n            }\n            return;\n        }\n    }\n    next();\n}\nwindow.safeStep = safeStep;\nwindow.testImmediate = testImmediate;\nwindow.testImmediateAsync = testImmediateAsync;\nwindow.flush = flush;\nwindow.animationFrameFlush = animationFrameFlush;\nwindow.asyncPlatformFlush = asyncPlatformFlush;\nwindow.waitFor = waitFor;\n\n/**\n * The global configuration state for WCT's browser client.\n */\nvar _config = {\n    environmentScripts: !!window.__wctUseNpm ?\n        [\n            'stacky/browser.js', 'async/lib/async.js', 'lodash/index.js',\n            'mocha/mocha.js', 'chai/chai.js', '@polymer/sinonjs/sinon.js',\n            'sinon-chai/lib/sinon-chai.js',\n            'accessibility-developer-tools/dist/js/axs_testing.js',\n            '@polymer/test-fixture/test-fixture.js'\n        ] :\n        [\n            'stacky/browser.js', 'async/lib/async.js', 'lodash/lodash.js',\n            'mocha/mocha.js', 'chai/chai.js', 'sinonjs/sinon.js',\n            'sinon-chai/lib/sinon-chai.js',\n            'accessibility-developer-tools/dist/js/axs_testing.js'\n        ],\n    environmentImports: !!window.__wctUseNpm ? [] :\n        ['test-fixture/test-fixture.html'],\n    root: null,\n    waitForFrameworks: true,\n    waitFor: null,\n    numConcurrentSuites: 1,\n    trackConsoleError: true,\n    mochaOptions: { timeout: 10 * 1000 },\n    verbose: false,\n};\n/**\n * Merges initial `options` into WCT's global configuration.\n *\n * @param {Object} options The options to merge. See `browser/config.js` for a\n *     reference.\n */\nfunction setup(options) {\n    var childRunner = ChildRunner.current();\n    if (childRunner) {\n        _deepMerge(_config, childRunner.parentScope.WCT._config);\n        // But do not force the mocha UI\n        delete _config.mochaOptions.ui;\n    }\n    if (options && typeof options === 'object') {\n        _deepMerge(_config, options);\n    }\n    if (!_config.root) {\n        // Sibling dependencies.\n        var root = scriptPrefix('browser.js');\n        _config.root = basePath(root.substr(0, root.length - 1));\n        if (!_config.root) {\n            throw new Error('Unable to detect root URL for WCT sources. Please set WCT.root before including browser.js');\n        }\n    }\n}\n/**\n * Retrieves a configuration value.\n */\nfunction get(key) {\n    return _config[key];\n}\n// Internal\nfunction _deepMerge(target, source) {\n    Object.keys(source).forEach(function (key) {\n        if (target[key] !== null && typeof target[key] === 'object' &&\n            !Array.isArray(target[key])) {\n            _deepMerge(target[key], source[key]);\n        }\n        else {\n            target[key] = source[key];\n        }\n    });\n}\n\n/**\n * @param {function()} callback A function to call when the active web component\n *     frameworks have loaded.\n */\nfunction whenFrameworksReady(callback) {\n    debug('whenFrameworksReady');\n    var done = function () {\n        debug('whenFrameworksReady done');\n        callback();\n    };\n    // If webcomponents script is in the document, wait for WebComponentsReady.\n    if (window.WebComponents && !window.WebComponents.ready) {\n        debug('WebComponentsReady?');\n        window.addEventListener('WebComponentsReady', function wcReady() {\n            window.removeEventListener('WebComponentsReady', wcReady);\n            debug('WebComponentsReady');\n            done();\n        });\n    }\n    else {\n        done();\n    }\n}\n/**\n * @return {string} '<count> <kind> tests' or '<count> <kind> test'.\n */\nfunction pluralizedStat(count, kind) {\n    if (count === 1) {\n        return count + ' ' + kind + ' test';\n    }\n    else {\n        return count + ' ' + kind + ' tests';\n    }\n}\n/**\n * @param {string} path The URI of the script to load.\n * @param {function} done\n */\nfunction loadScript(path, done) {\n    var script = document.createElement('script');\n    script.src = path;\n    if (done) {\n        script.onload = done.bind(null, null);\n        script.onerror = done.bind(null, 'Failed to load script ' + script.src);\n    }\n    document.head.appendChild(script);\n}\n/**\n * @param {string} path The URI of the stylesheet to load.\n * @param {function} done\n */\nfunction loadStyle(path, done) {\n    var link = document.createElement('link');\n    link.rel = 'stylesheet';\n    link.href = path;\n    if (done) {\n        link.onload = done.bind(null, null);\n        link.onerror = done.bind(null, 'Failed to load stylesheet ' + link.href);\n    }\n    document.head.appendChild(link);\n}\n/**\n * @param {...*} var_args Logs values to the console when the `debug`\n *     configuration option is true.\n */\nfunction debug() {\n    var var_args = [];\n    for (var _i = 0; _i < arguments.length; _i++) {\n        var_args[_i] = arguments[_i];\n    }\n    if (!get('verbose')) {\n        return;\n    }\n    var args = [window.location.pathname].concat(var_args);\n    (console.debug || console.log).apply(console, args);\n}\n// URL Processing\n/**\n * @param {string} url\n * @return {{base: string, params: string}}\n */\nfunction parseUrl(url) {\n    var parts = url.match(/^(.*?)(?:\\?(.*))?$/);\n    return {\n        base: parts[1],\n        params: getParams(parts[2] || ''),\n    };\n}\n/**\n * Expands a URL that may or may not be relative to `base`.\n *\n * @param {string} url\n * @param {string} base\n * @return {string}\n */\nfunction expandUrl(url, base) {\n    if (!base)\n        return url;\n    if (url.match(/^(\\/|https?:\\/\\/)/))\n        return url;\n    if (base.substr(base.length - 1) !== '/') {\n        base = base + '/';\n    }\n    return base + url;\n}\n/**\n * @param {string=} opt_query A query string to parse.\n * @return {!Object<string, !Array<string>>} All params on the URL's query.\n */\nfunction getParams(query) {\n    query = typeof query === 'string' ? query : window.location.search;\n    if (query.substring(0, 1) === '?') {\n        query = query.substring(1);\n    }\n    // python's SimpleHTTPServer tacks a `/` on the end of query strings :(\n    if (query.slice(-1) === '/') {\n        query = query.substring(0, query.length - 1);\n    }\n    if (query === '')\n        return {};\n    var result = {};\n    query.split('&').forEach(function (part) {\n        var pair = part.split('=');\n        if (pair.length !== 2) {\n            console.warn('Invalid URL query part:', part);\n            return;\n        }\n        var key = decodeURIComponent(pair[0]);\n        var value = decodeURIComponent(pair[1]);\n        if (!result[key]) {\n            result[key] = [];\n        }\n        result[key].push(value);\n    });\n    return result;\n}\n/**\n * Merges params from `source` into `target` (mutating `target`).\n *\n * @param {!Object<string, !Array<string>>} target\n * @param {!Object<string, !Array<string>>} source\n */\nfunction mergeParams(target, source) {\n    Object.keys(source).forEach(function (key) {\n        if (!(key in target)) {\n            target[key] = [];\n        }\n        target[key] = target[key].concat(source[key]);\n    });\n}\n/**\n * @param {string} param The param to return a value for.\n * @return {?string} The first value for `param`, if found.\n */\nfunction getParam(param) {\n    var params = getParams();\n    return params[param] ? params[param][0] : null;\n}\n/**\n * @param {!Object<string, !Array<string>>} params\n * @return {string} `params` encoded as a URI query.\n */\nfunction paramsToQuery(params) {\n    var pairs = [];\n    Object.keys(params).forEach(function (key) {\n        params[key].forEach(function (value) {\n            pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));\n        });\n    });\n    return (pairs.length > 0) ? ('?' + pairs.join('&')) : '';\n}\nfunction getPathName(location) {\n    return typeof location === 'string' ? location : location.pathname;\n}\nfunction basePath(location) {\n    return getPathName(location).match(/^.*\\//)[0];\n}\nfunction relativeLocation(location, basePath) {\n    var path = getPathName(location);\n    if (path.indexOf(basePath) === 0) {\n        path = path.substring(basePath.length);\n    }\n    return path;\n}\nfunction cleanLocation(location) {\n    var path = getPathName(location);\n    if (path.slice(-11) === '/index.html') {\n        path = path.slice(0, path.length - 10);\n    }\n    return path;\n}\nfunction parallel(runners, maybeLimit, done) {\n    var limit;\n    if (typeof maybeLimit !== 'number') {\n        done = maybeLimit;\n        limit = 0;\n    }\n    else {\n        limit = maybeLimit;\n    }\n    if (!runners.length) {\n        return done();\n    }\n    var called = false;\n    var total = runners.length;\n    var numActive = 0;\n    var numDone = 0;\n    function runnerDone(error) {\n        if (called) {\n            return;\n        }\n        numDone = numDone + 1;\n        numActive = numActive - 1;\n        if (error || numDone >= total) {\n            called = true;\n            done(error);\n        }\n        else {\n            runOne();\n        }\n    }\n    function runOne() {\n        if (limit && numActive >= limit) {\n            return;\n        }\n        if (!runners.length) {\n            return;\n        }\n        numActive = numActive + 1;\n        runners.shift()(runnerDone);\n    }\n    runners.forEach(runOne);\n}\n/**\n * Finds the directory that a loaded script is hosted on.\n *\n * @param {string} filename\n * @return {string?}\n */\nfunction scriptPrefix(filename) {\n    var scripts = document.querySelectorAll('script[src*=\"' + filename + '\"]');\n    if (scripts.length !== 1) {\n        return null;\n    }\n    var script = scripts[0].src;\n    return script.substring(0, script.indexOf(filename));\n}\n\n\nvar util = Object.freeze({\n    whenFrameworksReady: whenFrameworksReady,\n    pluralizedStat: pluralizedStat,\n    loadScript: loadScript,\n    loadStyle: loadStyle,\n    debug: debug,\n    parseUrl: parseUrl,\n    expandUrl: expandUrl,\n    getParams: getParams,\n    mergeParams: mergeParams,\n    getParam: getParam,\n    paramsToQuery: paramsToQuery,\n    basePath: basePath,\n    relativeLocation: relativeLocation,\n    cleanLocation: cleanLocation,\n    parallel: parallel,\n    scriptPrefix: scriptPrefix\n});\n\n/**\n * A Mocha suite (or suites) run within a child iframe, but reported as if they\n * are part of the current context.\n */\nvar ChildRunner = /** @class */ (function () {\n    function ChildRunner(url, parentScope) {\n        var urlBits = parseUrl(url);\n        mergeParams(urlBits.params, getParams(parentScope.location.search));\n        delete urlBits.params.cli_browser_id;\n        this.url = urlBits.base + paramsToQuery(urlBits.params);\n        this.parentScope = parentScope;\n        this.state = 'initializing';\n    }\n    /**\n     * @return {ChildRunner} The `ChildRunner` that was registered for this\n     * window.\n     */\n    ChildRunner.current = function () {\n        return ChildRunner.get(window);\n    };\n    /**\n     * @param {!Window} target A window to find the ChildRunner of.\n     * @param {boolean} traversal Whether this is a traversal from a child window.\n     * @return {ChildRunner} The `ChildRunner` that was registered for `target`.\n     */\n    ChildRunner.get = function (target, traversal) {\n        var childRunner = ChildRunner._byUrl[target.location.href];\n        if (childRunner) {\n            return childRunner;\n        }\n        if (window.parent === window) { // Top window.\n            if (traversal) {\n                console.warn('Subsuite loaded but was never registered. This most likely is due to wonky history behavior. Reloading...');\n                window.location.reload();\n            }\n            return null;\n        }\n        // Otherwise, traverse.\n        return window.parent.WCT._ChildRunner.get(target, true);\n    };\n    /**\n     * Loads and runs the subsuite.\n     *\n     * @param {function} done Node-style callback.\n     */\n    ChildRunner.prototype.run = function (done) {\n        debug('ChildRunner#run', this.url);\n        this.state = 'loading';\n        this.onRunComplete = done;\n        this.iframe = document.createElement('iframe');\n        this.iframe.src = this.url;\n        this.iframe.classList.add('subsuite');\n        var container = document.getElementById('subsuites');\n        if (!container) {\n            container = document.createElement('div');\n            container.id = 'subsuites';\n            document.body.appendChild(container);\n        }\n        container.appendChild(this.iframe);\n        // let the iframe expand the URL for us.\n        this.url = this.iframe.src;\n        ChildRunner._byUrl[this.url] = this;\n        this.timeoutId = setTimeout(this.loaded.bind(this, new Error('Timed out loading ' + this.url)), ChildRunner.loadTimeout);\n        this.iframe.addEventListener('error', this.loaded.bind(this, new Error('Failed to load document ' + this.url)));\n        this.iframe.contentWindow.addEventListener('DOMContentLoaded', this.loaded.bind(this, null));\n    };\n    /**\n     * Called when the sub suite's iframe has loaded (or errored during load).\n     *\n     * @param {*} error The error that occured, if any.\n     */\n    ChildRunner.prototype.loaded = function (error) {\n        debug('ChildRunner#loaded', this.url, error);\n        if (this.iframe.contentWindow == null && error) {\n            this.signalRunComplete(error);\n            this.done();\n            return;\n        }\n        // Not all targets have WCT loaded (compatiblity mode)\n        if (this.iframe.contentWindow.WCT) {\n            this.share = this.iframe.contentWindow.WCT.share;\n        }\n        if (error) {\n            this.signalRunComplete(error);\n            this.done();\n        }\n    };\n    /**\n     * Called in mocha/run.js when all dependencies have loaded, and the child is\n     * ready to start running tests\n     *\n     * @param {*} error The error that occured, if any.\n     */\n    ChildRunner.prototype.ready = function (error) {\n        debug('ChildRunner#ready', this.url, error);\n        if (this.timeoutId) {\n            clearTimeout(this.timeoutId);\n        }\n        if (error) {\n            this.signalRunComplete(error);\n            this.done();\n        }\n    };\n    /**\n     * Called when the sub suite's tests are complete, so that it can clean up.\n     */\n    ChildRunner.prototype.done = function () {\n        debug('ChildRunner#done', this.url, arguments);\n        // make sure to clear that timeout\n        this.ready();\n        this.signalRunComplete();\n        if (!this.iframe)\n            return;\n        // Be safe and avoid potential browser crashes when logic attempts to\n        // interact with the removed iframe.\n        setTimeout(function () {\n            this.iframe.parentNode.removeChild(this.iframe);\n            this.iframe = null;\n            this.share = null;\n        }.bind(this), 1);\n    };\n    ChildRunner.prototype.signalRunComplete = function (error) {\n        if (!this.onRunComplete)\n            return;\n        this.state = 'complete';\n        this.onRunComplete(error);\n        this.onRunComplete = null;\n    };\n    // ChildRunners get a pretty generous load timeout by default.\n    ChildRunner.loadTimeout = 60000;\n    // We can't maintain properties on iframe elements in Firefox/Safari/???, so\n    // we track childRunners by URL.\n    ChildRunner._byUrl = {};\n    return ChildRunner;\n}());\n\nvar SOCKETIO_ENDPOINT = window.location.protocol + '//' + window.location.host;\nvar SOCKETIO_LIBRARY = SOCKETIO_ENDPOINT + '/socket.io/socket.io.js';\n/**\n * A socket for communication between the CLI and browser runners.\n *\n * @param {string} browserId An ID generated by the CLI runner.\n * @param {!io.Socket} socket The socket.io `Socket` to communicate over.\n */\nvar CLISocket = /** @class */ (function () {\n    function CLISocket(browserId, socket) {\n        this.browserId = browserId;\n        this.socket = socket;\n    }\n    /**\n     * @param {!Mocha.Runner} runner The Mocha `Runner` to observe, reporting\n     *     interesting events back to the CLI runner.\n     */\n    CLISocket.prototype.observe = function (runner) {\n        var _this = this;\n        this.emitEvent('browser-start', {\n            url: window.location.toString(),\n        });\n        // We only emit a subset of events that we care about, and follow a more\n        // general event format that is hopefully applicable to test runners beyond\n        // mocha.\n        //\n        // For all possible mocha events, see:\n        // https://github.com/visionmedia/mocha/blob/master/lib/runner.js#L36\n        runner.on('test', function (test) {\n            _this.emitEvent('test-start', { test: getTitles(test) });\n        });\n        runner.on('test end', function (test) {\n            _this.emitEvent('test-end', {\n                state: getState(test),\n                test: getTitles(test),\n                duration: test.duration,\n                error: test.err,\n            });\n        });\n        runner.on('fail', function (test, err) {\n            // fail the test run if we catch errors outside of a test function\n            if (test.type !== 'test') {\n                _this.emitEvent('browser-fail', 'Error thrown outside of test function: ' + err.stack);\n            }\n        });\n        runner.on('childRunner start', function (childRunner) {\n            _this.emitEvent('sub-suite-start', childRunner.share);\n        });\n        runner.on('childRunner end', function (childRunner) {\n            _this.emitEvent('sub-suite-end', childRunner.share);\n        });\n        runner.on('end', function () {\n            _this.emitEvent('browser-end');\n        });\n    };\n    /**\n     * @param {string} event The name of the event to fire.\n     * @param {*} data Additional data to pass with the event.\n     */\n    CLISocket.prototype.emitEvent = function (event, data) {\n        this.socket.emit('client-event', {\n            browserId: this.browserId,\n            event: event,\n            data: data,\n        });\n    };\n    /**\n     * Builds a `CLISocket` if we are within a CLI-run environment; short-circuits\n     * otherwise.\n     *\n     * @param {function(*, CLISocket)} done Node-style callback.\n     */\n    CLISocket.init = function (done) {\n        var browserId = getParam('cli_browser_id');\n        if (!browserId)\n            return done();\n        // Only fire up the socket for root runners.\n        if (ChildRunner.current())\n            return done();\n        loadScript(SOCKETIO_LIBRARY, function (error) {\n            if (error)\n                return done(error);\n            var socket = io(SOCKETIO_ENDPOINT);\n            socket.on('error', function (error) {\n                socket.off();\n                done(error);\n            });\n            socket.on('connect', function () {\n                socket.off();\n                done(null, new CLISocket(browserId, socket));\n            });\n        });\n    };\n    return CLISocket;\n}());\n// Misc Utility\n/**\n * @param {!Mocha.Runnable} runnable The test or suite to extract titles from.\n * @return {!Array.<string>} The titles of the runnable and its parents.\n */\nfunction getTitles(runnable) {\n    var titles = [];\n    while (runnable && !runnable.root && runnable.title) {\n        titles.unshift(runnable.title);\n        runnable = runnable.parent;\n    }\n    return titles;\n}\n/**\n * @param {!Mocha.Runnable} runnable\n * @return {string}\n */\nfunction getState(runnable) {\n    if (runnable.state === 'passed') {\n        return 'passing';\n    }\n    else if (runnable.state === 'failed') {\n        return 'failing';\n    }\n    else if (runnable.pending) {\n        return 'pending';\n    }\n    else {\n        return 'unknown';\n    }\n}\n\n// We capture console events when running tests; so make sure we have a\n// reference to the original one.\nvar console$1 = window.console;\nvar FONT = ';font: normal 13px \"Roboto\", \"Helvetica Neue\", \"Helvetica\", sans-serif;';\nvar STYLES = {\n    plain: FONT,\n    suite: 'color: #5c6bc0' + FONT,\n    test: FONT,\n    passing: 'color: #259b24' + FONT,\n    pending: 'color: #e65100' + FONT,\n    failing: 'color: #c41411' + FONT,\n    stack: 'color: #c41411',\n    results: FONT + 'font-size: 16px',\n};\n// I don't think we can feature detect this one...\nvar userAgent = navigator.userAgent.toLowerCase();\nvar CAN_STYLE_LOG = userAgent.match('firefox') || userAgent.match('webkit');\nvar CAN_STYLE_GROUP = userAgent.match('webkit');\n// Track the indent for faked `console.group`\nvar logIndent = '';\nfunction log(text, style) {\n    text = text.split('\\n')\n        .map(function (l) {\n        return logIndent + l;\n    })\n        .join('\\n');\n    if (CAN_STYLE_LOG) {\n        console$1.log('%c' + text, STYLES[style] || STYLES.plain);\n    }\n    else {\n        console$1.log(text);\n    }\n}\nfunction logGroup(text, style) {\n    if (CAN_STYLE_GROUP) {\n        console$1.group('%c' + text, STYLES[style] || STYLES.plain);\n    }\n    else if (console$1.group) {\n        console$1.group(text);\n    }\n    else {\n        logIndent = logIndent + '  ';\n        log(text, style);\n    }\n}\nfunction logGroupEnd() {\n    if (console$1.groupEnd) {\n        console$1.groupEnd();\n    }\n    else {\n        logIndent = logIndent.substr(0, logIndent.length - 2);\n    }\n}\nfunction logException(error) {\n    log(error.stack || error.message || (error + ''), 'stack');\n}\n/**\n * A Mocha reporter that logs results out to the web `console`.\n */\nvar Console = /** @class */ (function () {\n    /**\n     * @param runner The runner that is being reported on.\n     */\n    function Console(runner) {\n        Mocha.reporters.Base.call(this, runner);\n        runner.on('suite', function (suite) {\n            if (suite.root) {\n                return;\n            }\n            logGroup(suite.title, 'suite');\n        }.bind(this));\n        runner.on('suite end', function (suite) {\n            if (suite.root) {\n                return;\n            }\n            logGroupEnd();\n        }.bind(this));\n        runner.on('test', function (test) {\n            logGroup(test.title, 'test');\n        }.bind(this));\n        runner.on('pending', function (test) {\n            logGroup(test.title, 'pending');\n        }.bind(this));\n        runner.on('fail', function (_test, error) {\n            logException(error);\n        }.bind(this));\n        runner.on('test end', function (_test) {\n            logGroupEnd();\n        }.bind(this));\n        runner.on('end', this.logSummary.bind(this));\n    }\n    /** Prints out a final summary of test results. */\n    Console.prototype.logSummary = function () {\n        logGroup('Test Results', 'results');\n        if (this.stats.failures > 0) {\n            log(pluralizedStat(this.stats.failures, 'failing'), 'failing');\n        }\n        if (this.stats.pending > 0) {\n            log(pluralizedStat(this.stats.pending, 'pending'), 'pending');\n        }\n        log(pluralizedStat(this.stats.passes, 'passing'));\n        if (!this.stats.failures) {\n            log('test suite passed', 'passing');\n        }\n        log('Evaluated ' + this.stats.tests + ' tests in ' +\n            this.stats.duration + 'ms.');\n        logGroupEnd();\n    };\n    return Console;\n}());\n\n/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt The complete set of authors may be found\n * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may\n * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by\n * Google as part of the polymer project is also subject to an additional IP\n * rights grant found at http://polymer.github.io/PATENTS.txt\n */\n/**\n * WCT-specific behavior on top of Mocha's default HTML reporter.\n *\n * @param {!Mocha.Runner} runner The runner that is being reported on.\n */\nfunction HTML(runner) {\n    var output = document.createElement('div');\n    output.id = 'mocha';\n    document.body.appendChild(output);\n    runner.on('suite', function (_test) {\n        this.total = runner.total;\n    }.bind(this));\n    Mocha.reporters.HTML.call(this, runner);\n}\n// Woo! What a hack. This just saves us from adding a bunch of complexity around\n// style loading.\nvar style = document.createElement('style');\nstyle.textContent = \"\\n    html, body {\\n      position: relative;\\n      height: 100%;\\n      width:  100%;\\n      min-width: 900px;\\n    }\\n    #mocha, #subsuites {\\n      height: 100%;\\n      position: absolute;\\n      top: 0;\\n    }\\n    #mocha {\\n      box-sizing: border-box;\\n      margin: 0 !important;\\n      padding: 60px 20px;\\n      right: 0;\\n      left: 500px;\\n    }\\n    #subsuites {\\n      -ms-flex-direction: column;\\n      -webkit-flex-direction: column;\\n      display: -ms-flexbox;\\n      display: -webkit-flex;\\n      display: flex;\\n      flex-direction: column;\\n      left: 0;\\n      width: 500px;\\n    }\\n    #subsuites .subsuite {\\n      border: 0;\\n      width: 100%;\\n      height: 100%;\\n    }\\n    #mocha .test.pass .duration {\\n      color: #555 !important;\\n    }\\n\";\ndocument.head.appendChild(style);\n\nvar STACKY_CONFIG = {\n    indent: '  ',\n    locationStrip: [\n        /^https?:\\/\\/[^\\/]+/,\n        /\\?.*$/,\n    ],\n    filter: function (line) {\n        return !!line.location.match(/\\/web-component-tester\\/[^\\/]+(\\?.*)?$/);\n    },\n};\n// https://github.com/visionmedia/mocha/blob/master/lib/runner.js#L36-46\nvar MOCHA_EVENTS = [\n    'start', 'end', 'suite', 'suite end', 'test', 'test end', 'hook', 'hook end',\n    'pass', 'fail', 'pending', 'childRunner end'\n];\n// Until a suite has loaded, we assume this many tests in it.\nvar ESTIMATED_TESTS_PER_SUITE = 3;\n/**\n * A Mocha-like reporter that combines the output of multiple Mocha suites.\n */\nvar MultiReporter = /** @class */ (function () {\n    /**\n     * @param numSuites The number of suites that will be run, in order to\n     *     estimate the total number of tests that will be performed.\n     * @param reporters The set of reporters that\n     *     should receive the unified event stream.\n     * @param parent The parent reporter, if present.\n     */\n    function MultiReporter(numSuites, reporters, parent) {\n        var _this = this;\n        this.reporters = reporters.map(function (reporter) {\n            return new reporter(_this);\n        });\n        this.parent = parent;\n        this.basePath = parent && parent.basePath || basePath(window.location);\n        this.total = numSuites * ESTIMATED_TESTS_PER_SUITE;\n        // Mocha reporters assume a stream of events, so we have to be careful to\n        // only report on one runner at a time...\n        this.currentRunner = null;\n        // ...while we buffer events for any other active runners.\n        this.pendingEvents = [];\n        this.emit('start');\n    }\n    /**\n     * @param location The location this reporter represents.\n     * @return A reporter-like \"class\" for each child suite\n     *     that should be passed to `mocha.run`.\n     */\n    MultiReporter.prototype.childReporter = function (location) {\n        var name = this.suiteTitle(location);\n        // The reporter is used as a constructor, so we can't depend on `this` being\n        // properly bound.\n        var self = this;\n        return _a = /** @class */ (function () {\n                function ChildReporter(runner) {\n                    runner.name = window.name;\n                    self.bindChildRunner(runner);\n                }\n                return ChildReporter;\n            }()),\n            _a.title = window.name,\n            _a;\n        var _a;\n    };\n    /** Must be called once all runners have finished. */\n    MultiReporter.prototype.done = function () {\n        this.complete = true;\n        this.flushPendingEvents();\n        this.emit('end');\n    };\n    /**\n     * Emit a top level test that is not part of any suite managed by this\n     * reporter.\n     *\n     * Helpful for reporting on global errors, loading issues, etc.\n     *\n     * @param title The title of the test.\n     * @param error An error associated with this test. If falsy, test is\n     *     considered to be passing.\n     * @param suiteTitle Title for the suite that's wrapping the test.\n     * @param estimated If this test was included in the original\n     *     estimate of `numSuites`.\n     */\n    MultiReporter.prototype.emitOutOfBandTest = function (title, error, suiteTitle, estimated) {\n        debug('MultiReporter#emitOutOfBandTest(', arguments, ')');\n        var root = new Mocha.Suite(suiteTitle || '');\n        var test = new Mocha.Test(title, function () { });\n        test.parent = root;\n        test.state = error ? 'failed' : 'passed';\n        test.err = error;\n        if (!estimated) {\n            this.total = this.total + ESTIMATED_TESTS_PER_SUITE;\n        }\n        var runner = { total: 1 };\n        this.proxyEvent('start', runner);\n        this.proxyEvent('suite', runner, root);\n        this.proxyEvent('test', runner, test);\n        if (error) {\n            this.proxyEvent('fail', runner, test, error);\n        }\n        else {\n            this.proxyEvent('pass', runner, test);\n        }\n        this.proxyEvent('test end', runner, test);\n        this.proxyEvent('suite end', runner, root);\n        this.proxyEvent('end', runner);\n    };\n    /**\n     * @param {!Location|string} location\n     * @return {string}\n     */\n    MultiReporter.prototype.suiteTitle = function (location) {\n        var path = relativeLocation(location, this.basePath);\n        path = cleanLocation(path);\n        return path;\n    };\n    // Internal Interface\n    /** @param {!Mocha.runners.Base} runner The runner to listen to events for. */\n    MultiReporter.prototype.bindChildRunner = function (runner) {\n        var _this = this;\n        MOCHA_EVENTS.forEach(function (eventName) {\n            runner.on(eventName, _this.proxyEvent.bind(_this, eventName, runner));\n        });\n    };\n    /**\n     * Evaluates an event fired by `runner`, proxying it forward or buffering it.\n     *\n     * @param {string} eventName\n     * @param {!Mocha.runners.Base} runner The runner that emitted this event.\n     * @param {...*} var_args Any additional data passed as part of the event.\n     */\n    MultiReporter.prototype.proxyEvent = function (eventName, runner) {\n        var _args = [];\n        for (var _i = 2; _i < arguments.length; _i++) {\n            _args[_i - 2] = arguments[_i];\n        }\n        var extraArgs = Array.prototype.slice.call(arguments, 2);\n        if (this.complete) {\n            console.warn('out of order Mocha event for ' + runner.name + ':', eventName, extraArgs);\n            return;\n        }\n        if (this.currentRunner && runner !== this.currentRunner) {\n            this.pendingEvents.push(Array.prototype.slice.call(arguments));\n            return;\n        }\n        debug('MultiReporter#proxyEvent(', arguments, ')');\n        // This appears to be a Mocha bug: Tests failed by passing an error to their\n        // done function don't set `err` properly.\n        //\n        // TODO(nevir): Track down.\n        if (eventName === 'fail' && !extraArgs[0].err) {\n            extraArgs[0].err = extraArgs[1];\n        }\n        if (eventName === 'start') {\n            this.onRunnerStart(runner);\n        }\n        else if (eventName === 'end') {\n            this.onRunnerEnd(runner);\n        }\n        else {\n            this.cleanEvent(eventName, runner, extraArgs);\n            this.emit.apply(this, [eventName].concat(extraArgs));\n        }\n    };\n    /**\n     * Cleans or modifies an event if needed.\n     *\n     * @param eventName\n     * @param runner The runner that emitted this event.\n     * @param extraArgs\n     */\n    MultiReporter.prototype.cleanEvent = function (eventName, _runner, extraArgs) {\n        // Suite hierarchy\n        if (extraArgs[0]) {\n            extraArgs[0] = this.showRootSuite(extraArgs[0]);\n        }\n        // Normalize errors\n        if (eventName === 'fail') {\n            extraArgs[1] = Stacky.normalize(extraArgs[1], STACKY_CONFIG);\n        }\n        if (extraArgs[0] && extraArgs[0].err) {\n            extraArgs[0].err = Stacky.normalize(extraArgs[0].err, STACKY_CONFIG);\n        }\n    };\n    /**\n     * We like to show the root suite's title, which requires a little bit of\n     * trickery in the suite hierarchy.\n     *\n     * @param {!Mocha.Runnable} node\n     */\n    MultiReporter.prototype.showRootSuite = function (node) {\n        var leaf = node = Object.create(node);\n        while (node && node.parent) {\n            var wrappedParent = Object.create(node.parent);\n            node.parent = wrappedParent;\n            node = wrappedParent;\n        }\n        node.root = false;\n        return leaf;\n    };\n    /** @param {!Mocha.runners.Base} runner */\n    MultiReporter.prototype.onRunnerStart = function (runner) {\n        debug('MultiReporter#onRunnerStart:', runner.name);\n        this.total = this.total - ESTIMATED_TESTS_PER_SUITE + runner.total;\n        this.currentRunner = runner;\n    };\n    /** @param {!Mocha.runners.Base} runner */\n    MultiReporter.prototype.onRunnerEnd = function (runner) {\n        debug('MultiReporter#onRunnerEnd:', runner.name);\n        this.currentRunner = null;\n        this.flushPendingEvents();\n    };\n    /**\n     * Flushes any buffered events and runs them through `proxyEvent`. This will\n     * loop until all buffered runners are complete, or we have run out of\n     * buffered events.\n     */\n    MultiReporter.prototype.flushPendingEvents = function () {\n        var _this = this;\n        var events = this.pendingEvents;\n        this.pendingEvents = [];\n        events.forEach(function (eventArgs) {\n            _this.proxyEvent.apply(_this, eventArgs);\n        });\n    };\n    return MultiReporter;\n}());\n\nvar ARC_OFFSET = 0; // start at the right.\nvar ARC_WIDTH = 6;\n/**\n * A Mocha reporter that updates the document's title and favicon with\n * at-a-glance stats.\n *\n * @param {!Mocha.Runner} runner The runner that is being reported on.\n */\nvar Title = /** @class */ (function () {\n    function Title(runner) {\n        Mocha.reporters.Base.call(this, runner);\n        runner.on('test end', this.report.bind(this));\n    }\n    /** Reports current stats via the page title and favicon. */\n    Title.prototype.report = function () {\n        this.updateTitle();\n        this.updateFavicon();\n    };\n    /** Updates the document title with a summary of current stats. */\n    Title.prototype.updateTitle = function () {\n        if (this.stats.failures > 0) {\n            document.title = pluralizedStat(this.stats.failures, 'failing');\n        }\n        else {\n            document.title = pluralizedStat(this.stats.passes, 'passing');\n        }\n    };\n    /** Updates the document's favicon w/ a summary of current stats. */\n    Title.prototype.updateFavicon = function () {\n        var canvas = document.createElement('canvas');\n        canvas.height = canvas.width = 32;\n        var context = canvas.getContext('2d');\n        var passing = this.stats.passes;\n        var pending = this.stats.pending;\n        var failing = this.stats.failures;\n        var total = Math.max(this.runner.total, passing + pending + failing);\n        drawFaviconArc(context, total, 0, passing, '#0e9c57');\n        drawFaviconArc(context, total, passing, pending, '#f3b300');\n        drawFaviconArc(context, total, pending + passing, failing, '#ff5621');\n        this.setFavicon(canvas.toDataURL());\n    };\n    /** Sets the current favicon by URL. */\n    Title.prototype.setFavicon = function (url) {\n        var current = document.head.querySelector('link[rel=\"icon\"]');\n        if (current) {\n            document.head.removeChild(current);\n        }\n        var link = document.createElement('link');\n        link.rel = 'icon';\n        link.type = 'image/x-icon';\n        link.href = url;\n        link.setAttribute('sizes', '32x32');\n        document.head.appendChild(link);\n    };\n    return Title;\n}());\n/**\n * Draws an arc for the favicon status, relative to the total number of tests.\n */\nfunction drawFaviconArc(context, total, start, length, color) {\n    var arcStart = ARC_OFFSET + Math.PI * 2 * (start / total);\n    var arcEnd = ARC_OFFSET + Math.PI * 2 * ((start + length) / total);\n    context.beginPath();\n    context.strokeStyle = color;\n    context.lineWidth = ARC_WIDTH;\n    context.arc(16, 16, 16 - ARC_WIDTH / 2, arcStart, arcEnd);\n    context.stroke();\n}\n\nvar htmlSuites$1 = [];\nvar jsSuites$1 = [];\n// We process grep ourselves to avoid loading suites that will be filtered.\nvar GREP = getParam('grep');\n// work around mocha bug (https://github.com/mochajs/mocha/issues/2070)\nif (GREP) {\n    GREP = GREP.replace(/\\\\\\./g, '.');\n}\n/**\n * Loads suites of tests, supporting both `.js` and `.html` files.\n *\n * @param files The files to load.\n */\nfunction loadSuites(files) {\n    files.forEach(function (file) {\n        if (/\\.js(\\?.*)?$/.test(file)) {\n            jsSuites$1.push(file);\n        }\n        else if (/\\.html(\\?.*)?$/.test(file)) {\n            htmlSuites$1.push(file);\n        }\n        else {\n            throw new Error('Unknown resource type: ' + file);\n        }\n    });\n}\n/**\n * @return The child suites that should be loaded, ignoring\n *     those that would not match `GREP`.\n */\nfunction activeChildSuites() {\n    var subsuites = htmlSuites$1;\n    if (GREP) {\n        var cleanSubsuites = [];\n        for (var i = 0, subsuite = void 0; subsuite = subsuites[i]; i++) {\n            if (GREP.indexOf(cleanLocation(subsuite)) !== -1) {\n                cleanSubsuites.push(subsuite);\n            }\n        }\n        subsuites = cleanSubsuites;\n    }\n    return subsuites;\n}\n/**\n * Loads all `.js` sources requested by the current suite.\n */\nfunction loadJsSuites(_reporter, done) {\n    debug('loadJsSuites', jsSuites$1);\n    var loaders = jsSuites$1.map(function (file) {\n        // We only support `.js` dependencies for now.\n        return loadScript.bind(util, file);\n    });\n    parallel(loaders, done);\n}\nfunction runSuites(reporter, childSuites, done) {\n    debug('runSuites');\n    var suiteRunners = [\n        // Run the local tests (if any) first, not stopping on error;\n        _runMocha.bind(null, reporter),\n    ];\n    // As well as any sub suites. Again, don't stop on error.\n    childSuites.forEach(function (file) {\n        suiteRunners.push(function (next) {\n            var childRunner = new ChildRunner(file, window);\n            reporter.emit('childRunner start', childRunner);\n            childRunner.run(function (error) {\n                reporter.emit('childRunner end', childRunner);\n                if (error)\n                    reporter.emitOutOfBandTest(file, error);\n                next();\n            });\n        });\n    });\n    parallel(suiteRunners, get('numConcurrentSuites'), function (error) {\n        reporter.done();\n        done(error);\n    });\n}\n/**\n * Kicks off a mocha run, waiting for frameworks to load if necessary.\n *\n * @param {!MultiReporter} reporter Where to send Mocha's events.\n * @param {function} done A callback fired, _no error is passed_.\n */\nfunction _runMocha(reporter, done, waited) {\n    if (get('waitForFrameworks') && !waited) {\n        var waitFor = (get('waitFor') || whenFrameworksReady).bind(window);\n        waitFor(_runMocha.bind(null, reporter, done, true));\n        return;\n    }\n    debug('_runMocha');\n    var mocha = window.mocha;\n    var Mocha = window.Mocha;\n    mocha.reporter(reporter.childReporter(window.location));\n    mocha.suite.title = reporter.suiteTitle(window.location);\n    mocha.grep(GREP);\n    // We can't use `mocha.run` because it bashes over grep, invert, and friends.\n    // See https://github.com/visionmedia/mocha/blob/master/support/tail.js#L137\n    var runner = Mocha.prototype.run.call(mocha, function (_error) {\n        if (document.getElementById('mocha')) {\n            Mocha.utils.highlightTags('code');\n        }\n        done(); // We ignore the Mocha failure count.\n    });\n    // Mocha's default `onerror` handling strips the stack (to support really old\n    // browsers). We upgrade this to get better stacks for async errors.\n    //\n    // TODO(nevir): Can we expand support to other browsers?\n    if (navigator.userAgent.match(/chrome/i)) {\n        window.onerror = null;\n        window.addEventListener('error', function (event) {\n            if (!event.error)\n                return;\n            if (event.error.ignore)\n                return;\n            runner.uncaught(event.error);\n        });\n    }\n}\n\n/**\n * @param {CLISocket} socket The CLI socket, if present.\n * @param {MultiReporter} parent The parent reporter, if present.\n * @return {!Array.<!Mocha.reporters.Base} The reporters that should be used.\n */\nfunction determineReporters(socket, parent) {\n    // Parents are greedy.\n    if (parent) {\n        return [parent.childReporter(window.location)];\n    }\n    // Otherwise, we get to run wild without any parental supervision!\n    var reporters = [Title, Console];\n    if (socket) {\n        reporters.push(function (runner) {\n            socket.observe(runner);\n        });\n    }\n    if (htmlSuites$1.length > 0 || jsSuites$1.length > 0) {\n        reporters.push(HTML);\n    }\n    return reporters;\n}\n/**\n * Yeah, hideous, but this allows us to be loaded before Mocha, which is handy.\n */\nfunction injectMocha(Mocha) {\n    _injectPrototype(Console, Mocha.reporters.Base.prototype);\n    _injectPrototype(HTML, Mocha.reporters.HTML.prototype);\n    // Mocha doesn't expose its `EventEmitter` shim directly, so:\n    _injectPrototype(MultiReporter, Object.getPrototypeOf(Mocha.Runner.prototype));\n}\nfunction _injectPrototype(klass, prototype) {\n    var newPrototype = Object.create(prototype);\n    // Only support\n    Object.keys(klass.prototype).forEach(function (key) {\n        newPrototype[key] = klass.prototype[key];\n    });\n    klass.prototype = newPrototype;\n}\n\n/**\n * Loads all environment scripts ...synchronously ...after us.\n */\nfunction loadSync() {\n    debug('Loading environment scripts:');\n    var a11ySuiteScriptPath = 'web-component-tester/data/a11ySuite.js';\n    var scripts = get('environmentScripts');\n    var a11ySuiteWillBeLoaded = window.__generatedByWct || scripts.indexOf(a11ySuiteScriptPath) > -1;\n    // We can't inject a11ySuite when running the npm version because it is a\n    // module-based script that needs `<script type=module>` and compilation\n    // for browsers without module support.\n    if (!a11ySuiteWillBeLoaded && !window.__wctUseNpm) {\n        // wct is running as a bower dependency, load a11ySuite from data/\n        scripts.push(a11ySuiteScriptPath);\n    }\n    scripts.forEach(function (path) {\n        var url = expandUrl(path, get('root'));\n        debug('Loading environment script:', url);\n        // Synchronous load.\n        document.write('<script src=\"' + encodeURI(url) +\n            '\"></script>'); // jshint ignore:line\n    });\n    debug('Environment scripts loaded');\n    var imports = get('environmentImports');\n    imports.forEach(function (path) {\n        var url = expandUrl(path, get('root'));\n        debug('Loading environment import:', url);\n        // Synchronous load.\n        document.write('<link rel=\"import\" href=\"' + encodeURI(url) +\n            '\">'); // jshint ignore:line\n    });\n    debug('Environment imports loaded');\n}\n/**\n * We have some hard dependencies on things that should be loaded via\n * `environmentScripts`, so we assert that they're present here; and do any\n * post-facto setup.\n */\nfunction ensureDependenciesPresent() {\n    _ensureMocha();\n    _checkChai();\n}\nfunction _ensureMocha() {\n    var Mocha = window.Mocha;\n    if (!Mocha) {\n        throw new Error('WCT requires Mocha. Please ensure that it is present in WCT.environmentScripts, or that you load it before loading web-component-tester/browser.js');\n    }\n    injectMocha(Mocha);\n    // Magic loading of mocha's stylesheet\n    var mochaPrefix = scriptPrefix('mocha.js');\n    // only load mocha stylesheet for the test runner output\n    // Not the end of the world, if it doesn't load.\n    if (mochaPrefix && window.top === window.self) {\n        loadStyle(mochaPrefix + 'mocha.css');\n    }\n}\nfunction _checkChai() {\n    if (!window.chai) {\n        debug('Chai not present; not registering shorthands');\n        return;\n    }\n    window.assert = window.chai.assert;\n    window.expect = window.chai.expect;\n}\n\n// We may encounter errors during initialization (for example, syntax errors in\n// a test file). Hang onto those (and more) until we are ready to report them.\nvar globalErrors = [];\n/**\n * Hook the environment to pick up on global errors.\n */\nfunction listenForErrors() {\n    window.addEventListener('error', function (event) {\n        globalErrors.push(event.error);\n    });\n    // Also, we treat `console.error` as a test failure. Unless you prefer not.\n    var origConsole = console;\n    var origError = console.error;\n    console.error = function wctShimmedError() {\n        origError.apply(origConsole, arguments);\n        if (get('trackConsoleError')) {\n            throw 'console.error: ' + Array.prototype.join.call(arguments, ' ');\n        }\n    };\n}\n\nvar interfaceExtensions = [];\n/**\n * Registers an extension that extends the global `Mocha` implementation\n * with new helper methods. These helper methods will be added to the `window`\n * when tests run for both BDD and TDD interfaces.\n */\nfunction extendInterfaces(helperName, helperFactory) {\n    interfaceExtensions.push(function () {\n        var Mocha = window.Mocha;\n        // For all Mocha interfaces (probably just TDD and BDD):\n        Object.keys(Mocha.interfaces)\n            .forEach(function (interfaceName) {\n            // This is the original callback that defines the interface (TDD or\n            // BDD):\n            var originalInterface = Mocha.interfaces[interfaceName];\n            // This is the name of the \"teardown\" or \"afterEach\" property for the\n            // current interface:\n            var teardownProperty = interfaceName === 'tdd' ? 'teardown' : 'afterEach';\n            // The original callback is monkey patched with a new one that appends\n            // to the global context however we want it to:\n            Mocha.interfaces[interfaceName] = function (suite) {\n                // Call back to the original callback so that we get the base\n                // interface:\n                originalInterface.apply(this, arguments);\n                // Register a listener so that we can further extend the base\n                // interface:\n                suite.on('pre-require', function (context, _file, _mocha) {\n                    // Capture a bound reference to the teardown function as a\n                    // convenience:\n                    var teardown = context[teardownProperty].bind(context);\n                    // Add our new helper to the testing context. The helper is\n                    // generated by a factory method that receives the context,\n                    // the teardown function and the interface name and returns\n                    // the new method to be added to that context:\n                    context[helperName] =\n                        helperFactory(context, teardown, interfaceName);\n                });\n            };\n        });\n    });\n}\n/**\n * Applies any registered interface extensions. The extensions will be applied\n * as many times as this function is called, so don't call it more than once.\n */\nfunction applyExtensions() {\n    interfaceExtensions.forEach(function (applyExtension) {\n        applyExtension();\n    });\n}\n\nextendInterfaces('fixture', function (context, teardown) {\n    // Return context.fixture if it is already a thing, for backwards\n    // compatibility with `test-fixture-mocha.js`:\n    return context.fixture || function fixture(fixtureId, model) {\n        // Automatically register a teardown callback that will restore the\n        // test-fixture:\n        teardown(function () {\n            document.getElementById(fixtureId).restore();\n        });\n        // Find the test-fixture with the provided ID and create it, returning\n        // the results:\n        return document.getElementById(fixtureId).create(model);\n    };\n});\n\n/**\n * stub\n *\n * The stub addon allows the tester to partially replace the implementation of\n * an element with some custom implementation. Usage example:\n *\n * beforeEach(function() {\n *   stub('x-foo', {\n *     attached: function() {\n *       // Custom implementation of the `attached` method of element `x-foo`..\n *     },\n *     otherMethod: function() {\n *       // More custom implementation..\n *     },\n *     getterSetterProperty: {\n *       get: function() {\n *         // Custom getter implementation..\n *       },\n *       set: function() {\n *         // Custom setter implementation..\n *       }\n *     },\n *     // etc..\n *   });\n * });\n */\nextendInterfaces('stub', function (_context, teardown) {\n    return function stub(tagName, implementation) {\n        // Find the prototype of the element being stubbed:\n        var proto = document.createElement(tagName).constructor.prototype;\n        // For all keys in the implementation to stub with..\n        var stubs = Object.keys(implementation).map(function (key) {\n            // Stub the method on the element prototype with Sinon:\n            return sinon.stub(proto, key, implementation[key]);\n        });\n        // After all tests..\n        teardown(function () {\n            stubs.forEach(function (stub) {\n                stub.restore();\n            });\n        });\n    };\n});\n\n// replacement map stores what should be\nvar replacements = {};\nvar replaceTeardownAttached = false;\n/**\n * replace\n *\n * The replace addon allows the tester to replace all usages of one element with\n * another element within all Polymer elements created within the time span of\n * the test. Usage example:\n *\n * beforeEach(function() {\n *   replace('x-foo').with('x-fake-foo');\n * });\n *\n * All annotations and attributes will be set on the placement element the way\n * they were set for the original element.\n */\nextendInterfaces('replace', function (_context, teardown) {\n    return function replace(oldTagName) {\n        return {\n            with: function (tagName) {\n                // Standardizes our replacements map\n                oldTagName = oldTagName.toLowerCase();\n                tagName = tagName.toLowerCase();\n                replacements[oldTagName] = tagName;\n                // If the function is already a stub, restore it to original\n                if (document.importNode.isSinonProxy) {\n                    return;\n                }\n                if (!window.Polymer.Element) {\n                    window.Polymer.Element = function () { };\n                    window.Polymer.Element.prototype._stampTemplate = function () { };\n                }\n                // Keep a reference to the original `document.importNode`\n                // implementation for later:\n                var originalImportNode = document.importNode;\n                // Use Sinon to stub `document.ImportNode`:\n                sinon.stub(document, 'importNode', function (origContent, deep) {\n                    var templateClone = document.createElement('template');\n                    var content = templateClone.content;\n                    var inertDoc = content.ownerDocument;\n                    // imports node from inertDoc which holds inert nodes.\n                    templateClone.content.appendChild(inertDoc.importNode(origContent, true));\n                    // optional arguments are not optional on IE.\n                    var nodeIterator = document.createNodeIterator(content, NodeFilter.SHOW_ELEMENT, null, true);\n                    var node;\n                    // Traverses the tree. A recently-replaced node will be put next,\n                    // so if a node is replaced, it will be checked if it needs to be\n                    // replaced again.\n                    while (node = nodeIterator.nextNode()) {\n                        var currentTagName = node.tagName.toLowerCase();\n                        if (replacements.hasOwnProperty(currentTagName)) {\n                            currentTagName = replacements[currentTagName];\n                            // find the final tag name.\n                            while (replacements[currentTagName]) {\n                                currentTagName = replacements[currentTagName];\n                            }\n                            // Create a replacement:\n                            var replacement = document.createElement(currentTagName);\n                            // For all attributes in the original node..\n                            for (var index = 0; index < node.attributes.length; ++index) {\n                                // Set that attribute on the replacement:\n                                replacement.setAttribute(node.attributes[index].name, node.attributes[index].value);\n                            }\n                            // Replace the original node with the replacement node:\n                            node.parentNode.replaceChild(replacement, node);\n                        }\n                    }\n                    return originalImportNode.call(this, content, deep);\n                });\n                if (!replaceTeardownAttached) {\n                    // After each test...\n                    teardown(function () {\n                        replaceTeardownAttached = true;\n                        // Restore the stubbed version of `document.importNode`:\n                        var documentImportNode = document.importNode;\n                        if (documentImportNode.isSinonProxy) {\n                            documentImportNode.restore();\n                        }\n                        // Empty the replacement map\n                        replacements = {};\n                    });\n                }\n            }\n        };\n    };\n});\n\n// Mocha global helpers, broken out by testing method.\n//\n// Keys are the method for a particular interface; values are their analog in\n// the opposite interface.\nvar MOCHA_EXPORTS = {\n    // https://github.com/visionmedia/mocha/blob/master/lib/interfaces/tdd.js\n    tdd: {\n        'setup': '\"before\"',\n        'teardown': '\"after\"',\n        'suiteSetup': '\"beforeEach\"',\n        'suiteTeardown': '\"afterEach\"',\n        'suite': '\"describe\" or \"context\"',\n        'test': '\"it\" or \"specify\"',\n    },\n    // https://github.com/visionmedia/mocha/blob/master/lib/interfaces/bdd.js\n    bdd: {\n        'before': '\"setup\"',\n        'after': '\"teardown\"',\n        'beforeEach': '\"suiteSetup\"',\n        'afterEach': '\"suiteTeardown\"',\n        'describe': '\"suite\"',\n        'context': '\"suite\"',\n        'xdescribe': '\"suite.skip\"',\n        'xcontext': '\"suite.skip\"',\n        'it': '\"test\"',\n        'xit': '\"test.skip\"',\n        'specify': '\"test\"',\n        'xspecify': '\"test.skip\"',\n    },\n};\n/**\n * Exposes all Mocha methods up front, configuring and running mocha\n * automatically when you call them.\n *\n * The assumption is that it is a one-off (sub-)suite of tests being run.\n */\nfunction stubInterfaces() {\n    var keys = Object.keys(MOCHA_EXPORTS);\n    keys.forEach(function (ui) {\n        Object.keys(MOCHA_EXPORTS[ui]).forEach(function (key) {\n            window[key] = function wrappedMochaFunction() {\n                _setupMocha(ui, key, MOCHA_EXPORTS[ui][key]);\n                if (!window[key] || window[key] === wrappedMochaFunction) {\n                    throw new Error('Expected mocha.setup to define ' + key);\n                }\n                window[key].apply(window, arguments);\n            };\n        });\n    });\n}\n// Whether we've called `mocha.setup`\nvar _mochaIsSetup = false;\n/**\n * @param {string} ui Sets up mocha to run `ui`-style tests.\n * @param {string} key The method called that triggered this.\n * @param {string} alternate The matching method in the opposite interface.\n */\nfunction _setupMocha(ui, key, alternate) {\n    var mochaOptions = get('mochaOptions');\n    if (mochaOptions.ui && mochaOptions.ui !== ui) {\n        var message = 'Mixing ' + mochaOptions.ui + ' and ' + ui +\n            ' Mocha styles is not supported. ' +\n            'You called \"' + key + '\". Did you mean ' + alternate + '?';\n        throw new Error(message);\n    }\n    if (_mochaIsSetup) {\n        return;\n    }\n    applyExtensions();\n    mochaOptions.ui = ui;\n    mocha.setup(mochaOptions); // Note that the reporter is configured in run.js.\n}\n\n// You can configure WCT before it has loaded by assigning your custom\n// configuration to the global `WCT`.\nsetup(window.WCT);\n// Maybe some day we'll expose WCT as a module to whatever module registry you\n// are using (aka the UMD approach), or as an es6 module.\nvar WCT = window.WCT = {\n    // A generic place to hang data about the current suite. This object is\n    // reported\n    // back via the `sub-suite-start` and `sub-suite-end` events.\n    share: {},\n    // Until then, we get to rely on it to expose parent runners to their\n    // children.\n    _ChildRunner: ChildRunner,\n    _reporter: undefined,\n    _config: _config,\n    // Public API\n    /**\n     * Loads suites of tests, supporting both `.js` and `.html` files.\n     *\n     * @param {!Array.<string>} files The files to load.\n     */\n    loadSuites: loadSuites,\n};\n// Load Process\nlistenForErrors();\nstubInterfaces();\nloadSync();\n// Give any scripts on the page a chance to declare tests and muck with things.\ndocument.addEventListener('DOMContentLoaded', function () {\n    debug('DOMContentLoaded');\n    ensureDependenciesPresent();\n    // We need the socket built prior to building its reporter.\n    CLISocket.init(function (error, socket) {\n        if (error)\n            throw error;\n        // Are we a child of another run?\n        var current = ChildRunner.current();\n        var parent = current && current.parentScope.WCT._reporter;\n        debug('parentReporter:', parent);\n        var childSuites = activeChildSuites();\n        var reportersToUse = determineReporters(socket, parent);\n        // +1 for any local tests.\n        var reporter = new MultiReporter(childSuites.length + 1, reportersToUse, parent);\n        WCT._reporter = reporter; // For environment/compatibility.js\n        // We need the reporter so that we can report errors during load.\n        loadJsSuites(reporter, function (error) {\n            // Let our parent know that we're about to start the tests.\n            if (current)\n                current.ready(error);\n            if (error)\n                throw error;\n            // Emit any errors we've encountered up til now\n            globalErrors.forEach(function onError(error) {\n                reporter.emitOutOfBandTest('Test Suite Initialization', error);\n            });\n            runSuites(reporter, childSuites, function (error) {\n                // Make sure to let our parent know that we're done.\n                if (current)\n                    current.done();\n                if (error)\n                    throw error;\n            });\n        });\n    });\n});\n\n}());\n//# sourceMappingURL=browser.js.map"
  },
  {
    "path": "custom_typings/bower-config.d.ts",
    "content": "declare module 'bower-config' {\n  interface Config {\n    directory: string;\n  }\n  function read(cwd: string, overrides?: boolean): Config;\n}\n"
  },
  {
    "path": "custom_typings/findup-sync.d.ts",
    "content": "declare module 'findup-sync' {\n  import * as minimatch from 'minimatch';\n\n  interface IOptions extends minimatch.IOptions {\n    cwd?: string;\n  }\n\n  function mod(pattern: string[]|string, opts?: IOptions): string;\n  namespace mod {}\n  export = mod;\n}\n"
  },
  {
    "path": "custom_typings/promisify-node.d.ts",
    "content": "declare module 'promisify-node' {\n  interface NodeCallback<T> {\n    (err: any, value: T): void;\n  }\n  function promisify<T>(f: (cb: NodeCallback<T>) => void): () => Promise<T>;\n  function promisify<A1, T>(f: (a: A1, cb: NodeCallback<T>) => void): (a: A1) =>\n      Promise<T>;\n  function promisify<A1, A2, T>(\n      f: (a: A1, a2: A2, cb: NodeCallback<T>) => void): (a: A1, a2: A2) =>\n      Promise<T>;\n  function promisify<A1, A2, A3, T>(\n      f: (a: A1, a2: A2, a3: A3, cb: NodeCallback<T>) =>\n          void): (a: A1, a2: A2, a3: A3) => Promise<T>;\n  namespace promisify {}\n  export = promisify;\n}\n"
  },
  {
    "path": "custom_typings/send.d.ts",
    "content": "declare module 'send' {\n  import * as http from 'http';\n  import * as events from 'events';\n\n\n  function send(req: http.IncomingMessage, path: string, options?: send.Options): send.SendStream;\n  namespace send {\n    export interface SendStream extends events.EventEmitter {\n      pipe(res: http.ServerResponse): void;\n    }\n\n    export interface Options {\n      dotfiles?: 'allow' | 'deny' | 'ignore';\n      end?: number;\n      etag?: boolean;\n      extensions?: string[];\n      index?: boolean|string|string[];\n      lastModified?: boolean;\n      maxAge?: number;\n      root?: string;\n      start?: number;\n    }\n  }\n  export = send;\n}\n"
  },
  {
    "path": "custom_typings/server-destroy.d.ts",
    "content": "declare module 'server-destroy' {\n  import * as http from 'http';\n\n  /**\n   * Monkey-patches the destroy() method onto the given server.\n   *\n   * It only accepts DestroyableServers as parameters to remind the user\n   * to update their type annotations elsewhere, as we can't express the\n   * mutation in the type system directly.\n   */\n  function enableDestroy(server: enableDestroy.DestroyableServer): void;\n  namespace enableDestroy {\n    interface DestroyableServer extends http.Server {\n      destroy(): void;\n    }\n  }\n  export = enableDestroy;\n}\n"
  },
  {
    "path": "custom_typings/stacky.d.ts",
    "content": "declare module 'stacky' {\n  interface ParsedStackFrame {\n    method: string;\n    location: string;\n    line: number;\n    column: number;\n  }\n  type StyleFunction = (part: string) => string;\n  interface Options {\n    maxMethodPadding?: number;\n    indent?: string;\n    methodPlaceholder?: string;\n    locationStrip?: (string|RegExp)[];\n    unimportantLocation?: (string|RegExp)[];\n    filter?: (line: ParsedStackFrame) => boolean;\n    styles?: {\n      method?: StyleFunction;\n      location?: StyleFunction;\n      line?: StyleFunction;\n      column?: StyleFunction;\n      unimportant?: StyleFunction;\n    };\n  }\n  export function clean(lines: ParsedStackFrame[], options: Options): void;\n  export function pretty(\n      errorStack: string|ParsedStackFrame[], options: Options): string;\n\n  export function normalize(error: Error, config: Options): Error;\n}\n"
  },
  {
    "path": "custom_typings/wd.d.ts",
    "content": "\n\ndeclare module 'wd' {\n  interface NodeCB<T> {\n    (err: any, value: T): void;\n  }\n  export interface Browser {\n    configureHttp(options: {\n      retries: number\n    }): void;\n    attach(sessionId: string, callback: NodeCB<Capabilities>): void;\n    init(capabilities: Capabilities, callback: NodeCB<string>): void;\n\n    get(url: string, callback: NodeCB<void>): void;\n    quit(callback: NodeCB<void>): void;\n\n    on(eventName: string, handler: Function): void;\n  }\n  export interface Capabilities {\n    /** The name of the browser being used */\n    browserName: 'android'|'chrome'|'firefox'|'htmlunit'|'internet explorer'|'iPhone'|'iPad'|'opera'|'safari';\n    /** The browser version, or the empty string if unknown. */\n    version: string;\n    /** A key specifying which platform the browser should be running on. */\n    platform: 'WINDOWS'|'XP'|'VISTA'|'MAC'|'LINUX'|'UNIX'|'ANDROID'|'ANY';\n\n    /** Whether the session can interact with modal popups,\n     *  such as window.alert and window.confirm. */\n    handlesAlerts: boolean;\n    /** Whether the session supports CSS selectors when searching for elements. */\n    cssSelectorsEnabled: boolean;\n\n    webdriver: {\n      remote: {\n        quietExceptions: boolean;\n      }\n    };\n\n    selenium: {\n      server: {\n        url: string;\n      }\n    };\n  }\n\n  export function remote(\n      hostnameOrUrl: string, port?: number,\n      username?: string, password?: string): Browser;\n  export function remote(\n      options: {hostname: string, port?: number,\n                auth?: string, path?: string, }\n      ): Browser;\n  export function remote(\n      options: {host: string, port?: number,\n                username?: string, accesskey?: string, path?: string, }\n      ): Browser;\n}\n"
  },
  {
    "path": "data/a11ySuite-npm-header.txt",
    "content": "import * as polymerDom from '../@polymer/polymer/lib/legacy/polymer.dom.js';\nconst Polymer = { dom: polymerDom };\nexport {a11ySuiteExport as a11ySuite};\n\n// wct-browser-legacy/a11ySuite.js is a generated file.  Source is in web-component-tester/data/a11ySuite.js\n"
  },
  {
    "path": "data/a11ySuite.js",
    "content": "/**\n * @license\n * Copyright (c) 2015 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n */\n\nvar a11ySuiteExport;\n\n(function(Mocha, chai, axs) {\n\n  Object.keys(Mocha.interfaces).forEach(function(iface) {\n    var orig = Mocha.interfaces[iface];\n\n    Mocha.interfaces[iface] = function(suite) {\n      orig.apply(this, arguments);\n\n      var Suite = Mocha.Suite;\n      var Test = Mocha.Test;\n\n      suite.on('pre-require', function(context, file, mocha) {\n\n        /**\n         * Runs the Chrome Accessibility Developer Tools Audit against a test-fixture\n         *\n         * @param {String} fixtureId ID of the fixture element in the document to use\n         * @param {Array?} ignoredRules Array of rules to ignore for this suite\n         * @param {Function?} beforeEach Function to be called before each test to ensure proper setup\n         */\n        a11ySuiteExport = context.a11ySuite = function(fixtureId, ignoredRules, beforeEach) {\n          // capture a reference to the fixture element early\n          var fixtureElement = document.getElementById(fixtureId);\n          if (!fixtureElement) {\n            return;\n          }\n\n          // build an audit config to disable certain ignorable tests\n          var axsConfig = new axs.AuditConfiguration();\n          axsConfig.scope = document.body;\n          axsConfig.showUnsupportedRulesWarning = false;\n          axsConfig.auditRulesToIgnore = ignoredRules;\n\n          // build mocha suite\n          var a11ySuite = Suite.create(suite, 'A11y Audit - Fixture: ' + fixtureId);\n\n          // override the `eachTest` function to hackily create the tests\n          //\n          // eachTest is called right before test runs to calculate the total\n          // number of tests\n          a11ySuite.eachTest = function() {\n            // instantiate fixture\n            fixtureElement.create();\n\n            // Make sure lazy-loaded dom is ready (eg <template is='dom-repeat'>)\n            Polymer.dom.flush();\n\n            // If we have a beforeEach function, call it\n            if (beforeEach) {\n              beforeEach();\n            }\n\n            // run audit\n            var auditResults = axs.Audit.run(axsConfig);\n\n            // create tests for audit results\n            auditResults.forEach(function(result, index) {\n              // only show applicable tests\n              if (result.result !== 'NA') {\n                var title = result.rule.heading;\n                // fail test if audit result is FAIL\n                var error = result.result === 'FAIL' ? axs.Audit.accessibilityErrorMessage(result) : null;\n                var test = new Test(title, function() {\n                  if (error) {\n                    throw new Error(error);\n                  }\n                });\n                test.file = file;\n                a11ySuite.addTest(test);\n              }\n            });\n\n            // teardown fixture\n            fixtureElement.restore();\n\n            suite.eachTest.apply(a11ySuite, arguments);\n            this.eachTest = suite.eachTest;\n          };\n\n          return a11ySuite;\n        };\n      });\n    };\n  });\n\n  chai.use(function(chai, util) {\n    var Assertion = chai.Assertion;\n\n    // assert\n    chai.assert.a11yLabel = function(node, exp, msg){\n      new Assertion(node).to.have.a11yLabel(exp, msg);\n    };\n\n    // expect / should\n    Assertion.addMethod('a11yLabel', function(str, msg) {\n      if (msg) {\n        util.flag(this, 'message', msg);\n      }\n\n      var node = this._obj;\n\n      // obj must be a Node\n      new Assertion(node).to.be.instanceOf(Node);\n\n      // vind the text alternative with the help of accessibility dev tools\n      var textAlternative = axs.properties.findTextAlternatives(node, {});\n\n      this.assert(\n        textAlternative === str,\n        'expected #{this} to have text alternative #{exp} but got #{act}',\n        'expected #{this} to not have text alternative #{act}',\n        str,\n        textAlternative,\n        true\n      );\n    });\n  });\n})(window.Mocha, window.chai, window.axs);\n"
  },
  {
    "path": "data/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script>\n    WCT = <%= JSON.stringify(clientOptions) %>;\n  </script>\n  <script>\n    window.__generatedByWct = true;\n  </script>\n  <script src=\"../<%= browserScript %>\"></script>\n  <% extraScripts.forEach(function(script) { %>\n    <script src=\"<%- script %>\"></script>\n  <% }); %>\n  <% if (typeof npm === 'undefined' || !npm) { %>\n    <script src=\"../<%= a11ySuiteScript %>\"></script>\n  <% } %>\n</head>\n\n<body>\n  <script>\n    WCT.loadSuites(<%= JSON.stringify(suites) %>);\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "gulpfile.js",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n */\n'use strict';\n\nconst concat = require('gulp-concat');\nconst depcheck = require('depcheck');\nconst fs = require('fs');\nconst glob = require('glob');\nconst gulp = require('gulp');\nconst bower = require('gulp-bower');\nconst mocha = require('gulp-spawn-mocha');\nconst tslint = require('gulp-tslint');\nconst ts = require('gulp-typescript');\nconst lazypipe = require('lazypipe');\nconst path = require('path');\nconst rollup = require('rollup');\nconst runSequence = require('run-sequence');\nconst typescript = require('typescript');\n\nconst mochaConfig = { reporter: 'spec' };\nif (process.env.MOCHA_TIMEOUT) {\n  mochaConfig.timeout = parseInt(process.env.MOCHA_TIMEOUT, 10);\n}\n\n// const commonTools = require('tools-common/gulpfile');\nconst commonTools = {\n  depcheck: commonDepCheck\n};\n\ngulp.task('lint', ['tslint', 'depcheck']);\n\n// Meta tasks\n\ngulp.task('default', ['test']);\n\nfunction removeFile(path) {\n  try {\n    fs.unlinkSync(path);\n    return;\n  } catch (e) {\n    try {\n      fs.statSync(path);\n    } catch (e) {\n      return;\n    }\n    throw new Error('Unable to remove file: ' + path);\n  }\n}\n\ngulp.task('clean', (done) => {\n  removeFile('browser.js');\n  removeFile('browser.js.map');\n  const patterns = ['runner/*.js', 'browser/**/*.js', 'browser/**/*.js.map'];\n  for (const pattern of patterns) {\n    glob(pattern, (err, files) => {\n      if (err) {\n        return done(err);\n      }\n      try {\n        for (const file of files) {\n          removeFile(file);\n        }\n      } catch (e) {\n        return done(e);\n      }\n    });\n  }\n  done();\n});\n\ngulp.task('test', function (done) {\n  runSequence(\n    'build:typescript-server',\n    'lint',\n    'test:unit',\n    'test:integration',\n    done);\n});\n\ngulp.task('build-all', (done) => {\n  runSequence('clean', 'lint', 'build', done);\n});\n\ngulp.task('build',\n  ['build:typescript-server', 'build:browser', 'build:wct-browser-legacy']);\n\nconst tsProject = ts.createProject('tsconfig.json', { typescript });\ngulp.task('build:typescript-server', function () {\n  // Ignore typescript errors, because gulp-typescript, like most things\n  // gulp, can't be trusted.\n  return tsProject.src().pipe(tsProject(ts.reporter.nullReporter())).js.pipe(gulp.dest('./'));\n});\n\nconst browserTsProject = ts.createProject('browser/tsconfig.json', {\n  typescript\n});\ngulp.task('build:typescript-browser', function () {\n  return browserTsProject.src().pipe(\n    browserTsProject(ts.reporter.nullReporter())).js.pipe(gulp.dest('./browser/'));\n});\n\n// Specific tasks\n\ngulp.task('build:browser', ['build:typescript-browser'], function (done) {\n  rollup.rollup({\n    entry: 'browser/index.js',\n  }).then(function (bundle) {\n    bundle.write({\n      indent: false,\n      format: 'iife',\n      banner: fs.readFileSync('browser-js-header.txt', 'utf-8'),\n      intro: 'window.__wctUseNpm = false;',\n      dest: 'browser.js',\n      sourceMap: true,\n      sourceMapFile: path.resolve('browser.js.map')\n    }).then(function () {\n      done();\n    });\n  }).catch(done);\n});\n\ngulp.task('build:wct-browser-legacy:a11ySuite', function () {\n  return gulp.src(['data/a11ySuite-npm-header.txt', 'data/a11ySuite.js'])\n    .pipe(concat('a11ySuite.js'))\n    .pipe(gulp.dest('wct-browser-legacy/'));\n});\n\ngulp.task('build:wct-browser-legacy:browser', ['build:typescript-browser'], function (done) {\n  rollup.rollup({\n    entry: 'browser/index.js',\n  }).then(function (bundle) {\n    bundle.write({\n      indent: false,\n      format: 'iife',\n      banner: fs.readFileSync('browser-js-header.txt', 'utf-8'),\n      intro: 'window.__wctUseNpm = true;',\n      dest: 'wct-browser-legacy/browser.js',\n      sourceMap: true,\n      sourceMapFile: path.resolve('browser.js.map')\n    }).then(function () {\n      done();\n    });\n  }).catch(done);\n});\n\ngulp.task('build:wct-browser-legacy', [\n  'build:wct-browser-legacy:a11ySuite',\n  'build:wct-browser-legacy:browser',\n]);\n\n\ngulp.task('test:unit', function () {\n  return gulp.src('test/unit/*.js', { read: false })\n    .pipe(mocha(mochaConfig));\n});\n\ngulp.task('bower', function () {\n  return bower();\n});\n\ngulp.task('test:integration', ['bower'], function () {\n  return gulp.src('test/integration/*.js', { read: false })\n    .pipe(mocha(mochaConfig));\n});\n\ngulp.task('tslint', () =>\n  gulp.src([\n    'runner/**/*.ts', '!runner/**/*.d.ts',\n    'test/**/*.ts', '!test/**/*.d.ts',\n    'custom_typings/*.d.ts', 'browser/**/*.ts', '!browser/**/*.ts'\n  ])\n    .pipe(tslint())\n    .pipe(tslint.report({ formatter: 'verbose' })));\n\n// Flows\n\ncommonTools.depcheck({\n  stickyDeps: new Set([\n    // Used in browser.js\n    'accessibility-developer-tools',\n    'mocha',\n    'test-fixture',\n    '@polymer/sinonjs',\n    '@polymer/test-fixture',\n    '@webcomponents/webcomponentsjs',\n    'async',\n    'findup-sync',\n\n    // Only included to satisfy peer dependency and suppress error on install\n    'sinon',\n\n    // Used in the wct binary\n    'resolve'\n  ])\n});\n\nfunction commonDepCheck(options) {\n  const defaultOptions = { stickyDeps: new Set() };\n  options = Object.assign({}, defaultOptions, options);\n\n  gulp.task('depcheck', () => {\n    return new Promise((resolve, reject) => {\n      depcheck(\n        __dirname, { ignoreDirs: [], ignoreMatches: ['@types/*'] }, resolve);\n    }).then((result) => {\n      const invalidFiles = Object.keys(result.invalidFiles) || [];\n      const invalidJsFiles = invalidFiles.filter((f) => f.endsWith('.js'));\n\n      if (invalidJsFiles.length > 0) {\n        console.log('Invalid files:', result.invalidFiles);\n        throw new Error('Invalid files');\n      }\n\n      const unused = new Set(result.dependencies);\n      for (const falseUnused of options.stickyDeps) {\n        unused.delete(falseUnused);\n      }\n      if (unused.size > 0) {\n        console.log('Unused dependencies:', unused);\n        throw new Error('Unused dependencies');\n      }\n    });\n  });\n}\n\ngulp.task('prepublish', function (done) {\n  // We can't run the integration tests here because on travis we may not\n  // be running with an x instance when we do `npm install`. We can change\n  // this to just `test` from `test:unit` once all supported npm versions\n  // no longer run `prepublish` on install.\n  runSequence('build-all', 'test:unit', done);\n});\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"web-component-tester\",\n  \"version\": \"6.6.0-pre.5\",\n  \"--private-wct--\": {\n    \"client-side-version-range\": \"4 - 6 || ^6.6.0-pre.1\",\n    \"wct-browser-legacy-version-range\": \"0.0.1-pre.1 || ^1.0.0\"\n  },\n  \"description\": \"web-component-tester makes testing your web components a breeze!\",\n  \"keywords\": [\n    \"browser\",\n    \"grunt\",\n    \"gruntplugin\",\n    \"gulp\",\n    \"polymer\",\n    \"test\",\n    \"testing\",\n    \"web component\",\n    \"web\"\n  ],\n  \"homepage\": \"https://github.com/Polymer/web-component-tester\",\n  \"bugs\": \"https://github.com/Polymer/web-component-tester/issues\",\n  \"license\": \"BSD-3-Clause\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/Polymer/web-component-tester.git\"\n  },\n  \"main\": \"runner.js\",\n  \"bin\": {\n    \"wct\": \"./bin/wct\",\n    \"wct-st\": \"./bin/wct-st\"\n  },\n  \"files\": [\n    \"bin/\",\n    \"data/\",\n    \"runner/\",\n    \"scripts/\",\n    \"tasks/\",\n    \".bowerrc\",\n    \"bower.json\",\n    \"browser.js\",\n    \"browser.js.map\",\n    \"package.json\",\n    \"LICENSE\",\n    \"README.md\",\n    \"runner.js\"\n  ],\n  \"scripts\": {\n    \"lint\": \"gulp lint\",\n    \"build\": \"tsc && gulp build\",\n    \"test\": \"tsc && gulp test\",\n    \"prepublishOnly\": \"gulp prepublish\",\n    \"test:watch\": \"watch 'gulp test:unit' runner/ browser/ bin/ test/ tasks/\",\n    \"format\": \"find runner test | grep '\\\\.js$\\\\|\\\\.ts$' | xargs ./node_modules/.bin/clang-format --style=file -i\"\n  },\n  \"dependencies\": {\n    \"@polymer/sinonjs\": \"^1.14.1\",\n    \"@polymer/test-fixture\": \"^0.0.3\",\n    \"@webcomponents/webcomponentsjs\": \"^1.0.7\",\n    \"accessibility-developer-tools\": \"^2.12.0\",\n    \"async\": \"^2.4.1\",\n    \"body-parser\": \"^1.17.2\",\n    \"bower-config\": \"^1.4.0\",\n    \"chai\": \"^4.0.2\",\n    \"chalk\": \"^1.1.3\",\n    \"cleankill\": \"^2.0.0\",\n    \"express\": \"^4.15.3\",\n    \"findup-sync\": \"^2.0.0\",\n    \"glob\": \"^7.1.2\",\n    \"lodash\": \"^3.10.1\",\n    \"mocha\": \"^3.4.2\",\n    \"multer\": \"^1.3.0\",\n    \"nomnom\": \"^1.8.1\",\n    \"polyserve\": \"^0.27.2\",\n    \"promisify-node\": \"^0.4.0\",\n    \"resolve\": \"^1.5.0\",\n    \"semver\": \"^5.3.0\",\n    \"send\": \"^0.11.1\",\n    \"server-destroy\": \"^1.0.1\",\n    \"sinon\": \"^2.3.5\",\n    \"sinon-chai\": \"^2.10.0\",\n    \"socket.io\": \"^2.0.3\",\n    \"stacky\": \"^1.3.1\",\n    \"wd\": \"^1.2.0\"\n  },\n  \"optionalDependencies\": {\n    \"update-notifier\": \"^2.2.0\",\n    \"wct-local\": \"^2.1.0\",\n    \"wct-sauce\": \"^2.0.0\"\n  },\n  \"devDependencies\": {\n    \"@types/body-parser\": \"0.0.33\",\n    \"@types/chai\": \"^3.4.34\",\n    \"@types/chalk\": \"^0.4.31\",\n    \"@types/express\": \"^4.0.33\",\n    \"@types/express-serve-static-core\": \"^4.0.39\",\n    \"@types/glob\": \"^5.0.30\",\n    \"@types/grunt\": \"^0.4.20\",\n    \"@types/gulp\": \"^3.8.8\",\n    \"@types/lodash\": \"^4.14.38\",\n    \"@types/mime\": \"0.0.29\",\n    \"@types/minimatch\": \"^2.0.29\",\n    \"@types/mocha\": \"^2.2.32\",\n    \"@types/multer\": \"0.0.32\",\n    \"@types/node\": \"^8.0.0\",\n    \"@types/nomnom\": \"0.0.28\",\n    \"@types/rimraf\": \"0.0.28\",\n    \"@types/semver\": \"^5.3.30\",\n    \"@types/sinon\": \"^1.16.31\",\n    \"@types/sinon-chai\": \"^2.7.27\",\n    \"@types/socket.io\": \"^1.4.27\",\n    \"bower\": \"^1.7.9\",\n    \"clang-format\": \"^1.0.43\",\n    \"depcheck\": \"^0.6.3\",\n    \"grunt\": \"^0.4.5\",\n    \"gulp\": \"^3.8.8\",\n    \"gulp-bower\": \"0.0.13\",\n    \"gulp-concat\": \"^2.6.1\",\n    \"gulp-spawn-mocha\": \"^3.1.0\",\n    \"gulp-tslint\": \"^8.1.2\",\n    \"gulp-typescript\": \"^3.1.2\",\n    \"lazypipe\": \"^1.0.1\",\n    \"rimraf\": \"^2.5.4\",\n    \"rollup\": \"^0.25.1\",\n    \"run-sequence\": \"^1.0.1\",\n    \"tslint\": \"^5.7.0\",\n    \"typescript\": \"^2.1.4\",\n    \"watch\": \"^0.18.0\"\n  },\n  \"engines\": {\n    \"node\": \">= 6.0\"\n  }\n}\n"
  },
  {
    "path": "runner/browserrunner.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\n\nimport * as chalk from 'chalk';\nimport * as cleankill from 'cleankill';\nimport * as _ from 'lodash';\nimport * as wd from 'wd';\nimport {Config} from './config';\n\nexport interface Stats {\n  status: string;\n  passing?: number;\n  pending?: number;\n  failing?: number;\n}\n\nexport interface BrowserDef extends wd.Capabilities {\n  id: number;\n  url: string;\n  sessionId: string;\n  deviceName?: string;\n  variant?: string;\n}\n\n// Browser abstraction, responsible for spinning up a browser instance via wd.js\n// and executing runner.html test files passed in options.files\nexport class BrowserRunner {\n  timeout: number;\n  browser: wd.Browser;\n  stats: Stats;\n  sessionId: string;\n  timeoutId: NodeJS.Timer;\n  emitter: NodeJS.EventEmitter;\n  def: BrowserDef;\n  options: Config;\n  donePromise: Promise<void>;\n\n  /**\n   * The url of the initial page to load in the browser when starting tests.\n   */\n  url: string;\n\n  private _resolve: () => void;\n  private _reject: (err: any) => void;\n\n  /**\n   * @param emitter The emitter to send updates about test progress to.\n   * @param def A BrowserDef describing and defining the browser to be run.\n   *     Includes both metadata and a method for connecting/launching the\n   *     browser.\n   * @param options WCT options.\n   * @param url The url of the generated index.html file that the browser should\n   *     point at.\n   * @param waitFor Optional. If given, we won't try to start/connect to the\n   *     browser until this promise resolves. Used for serializing access to\n   *     Safari webdriver, which can only have one instance running at once.\n   */\n  constructor(\n      emitter: NodeJS.EventEmitter, def: BrowserDef, options: Config,\n      url: string, waitFor?: Promise<void>) {\n    this.emitter = emitter;\n    this.def = def;\n    this.options = options;\n    this.timeout = options.testTimeout;\n    this.emitter = emitter;\n    this.url = url;\n\n    this.stats = {status: 'initializing'};\n\n    this.donePromise = new Promise<void>((resolve, reject) => {\n      this._resolve = resolve;\n      this._reject = reject;\n    });\n\n    waitFor = waitFor || Promise.resolve();\n    waitFor.then(() => {\n      this.browser = wd.remote(this.def.url);\n\n      // never retry selenium commands\n      this.browser.configureHttp({retries: -1});\n\n\n      cleankill.onInterrupt(() => {\n        return new Promise((resolve) => {\n          if (!this.browser) {\n            return resolve();\n          }\n\n          this.donePromise.then(() => resolve(), () => resolve());\n          this.done('Interrupting');\n        });\n      });\n\n      this.browser.on('command', (method: any, context: any) => {\n        emitter.emit('log:debug', this.def, chalk.cyan(method), context);\n      });\n\n      this.browser.on('http', (method: any, path: any, data: any) => {\n        if (data) {\n          emitter.emit(\n              'log:debug', this.def, chalk.magenta(method), chalk.cyan(path),\n              data);\n        } else {\n          emitter.emit(\n              'log:debug', this.def, chalk.magenta(method), chalk.cyan(path));\n        }\n      });\n\n      this.browser.on('connection', (code: any, message: any, error: any) => {\n        emitter.emit(\n            'log:warn', this.def, 'Error code ' + code + ':', message, error);\n      });\n\n      this.emitter.emit('browser-init', this.def, this.stats);\n\n      // Make sure that we are passing a pristine capabilities object to\n      // webdriver. None of our screwy custom properties!\n      const webdriverCapabilities = _.clone(this.def);\n      delete webdriverCapabilities.id;\n      delete webdriverCapabilities.url;\n      delete webdriverCapabilities.sessionId;\n\n      // Reusing a session?\n      if (this.def.sessionId) {\n        this.browser.attach(this.def.sessionId, (error) => {\n          this._init(error, this.def.sessionId);\n        });\n      } else {\n        this.browser.init(webdriverCapabilities, this._init.bind(this));\n      }\n    });\n  }\n\n  _init(error: any, sessionId: string) {\n    if (!this.browser) {\n      return;  // When interrupted.\n    }\n    if (error) {\n      // TODO(nevir): BEGIN TEMPORARY CHECK.\n      // https://github.com/Polymer/web-component-tester/issues/51\n      if (this.def.browserName === 'safari' && error.data) {\n        // debugger;\n        try {\n          const data = JSON.parse(error.data);\n          if (data.value && data.value.message &&\n              /Failed to connect to SafariDriver/i.test(data.value.message)) {\n            error = 'Until Selenium\\'s SafariDriver supports ' +\n                'Safari 6.2+, 7.1+, & 8.0+, you must\\n' +\n                'manually install it. Follow the steps at:\\n' +\n                'https://github.com/SeleniumHQ/selenium/' +\n                'wiki/SafariDriver#getting-started';\n          }\n        } catch (error) {\n          // Show the original error.\n        }\n      }\n      // END TEMPORARY CHECK\n      this.done(error.data || error);\n    } else {\n      this.sessionId = sessionId;\n      this.startTest();\n      this.extendTimeout();\n    }\n  }\n\n  startTest() {\n    const paramDelim = (this.url.indexOf('?') === -1 ? '?' : '&');\n    const extra = `${paramDelim}cli_browser_id=${this.def.id}`;\n    this.browser.get(this.url + extra, (error) => {\n      if (error) {\n        this.done(error.data || error);\n      } else {\n        this.extendTimeout();\n      }\n    });\n  }\n\n  onEvent(event: string, data: any) {\n    this.extendTimeout();\n    if (event === 'browser-start') {\n      // Always assign, to handle re-runs (no browser-init).\n      this.stats = {\n        status: 'running',\n        passing: 0,\n        pending: 0,\n        failing: 0,\n      };\n    } else if (event === 'test-end') {\n      this.stats[data.state] = this.stats[data.state] + 1;\n    }\n\n    if (event === 'browser-end' || event === 'browser-fail') {\n      this.done(data);\n    } else {\n      this.emitter.emit(event, this.def, data, this.stats, this.browser);\n    }\n  }\n\n  done(error: any) {\n    // No quitting for you!\n    if (this.options.persistent) {\n      return;\n    }\n\n    if (this.timeoutId) {\n      clearTimeout(this.timeoutId);\n    }\n    // Don't double-quit.\n    if (!this.browser) {\n      return;\n    }\n    const browser = this.browser;\n    this.browser = null;\n\n    this.stats.status = error ? 'error' : 'complete';\n    if (!error && this.stats.failing > 0) {\n      error = this.stats.failing + ' failed tests';\n    }\n\n    this.emitter.emit(\n        'browser-end', this.def, error, this.stats, this.sessionId, browser);\n\n    // Nothing to quit.\n    if (!this.sessionId) {\n      error ? this._reject(error) : this._resolve();\n    }\n\n    browser.quit((quitError) => {\n      if (quitError) {\n        this.emitter.emit(\n            'log:warn', this.def,\n            'Failed to quit:', quitError.data || quitError);\n      }\n      if (error) {\n        this._reject(error);\n      } else {\n        this._resolve();\n      }\n    });\n  }\n\n  extendTimeout() {\n    if (this.options.persistent) {\n      return;\n    }\n    if (this.timeoutId) {\n      clearTimeout(this.timeoutId);\n    }\n    this.timeoutId = setTimeout(() => {\n      this.done('Timed out');\n    }, this.timeout);\n  }\n\n  quit() {\n    this.done('quit was called');\n  }\n\n  // HACK\n  static BrowserRunner = BrowserRunner;\n}\n\nmodule.exports = BrowserRunner;\n"
  },
  {
    "path": "runner/cli.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\n\nimport * as chalk from 'chalk';\nimport * as events from 'events';\nimport * as _ from 'lodash';\n\nimport {CliReporter} from './clireporter';\nimport * as config from './config';\nimport {Context} from './context';\nimport {Plugin} from './plugin';\nimport {test} from './test';\n\nconst PACKAGE_INFO = require('../package.json');\nconst noopNotifier = {\n  notify: () => {}\n};\nlet updateNotifier = noopNotifier;\n\n(function() {\ntry {\n  updateNotifier = require('update-notifier')({pkg: PACKAGE_INFO});\n} catch (error) {\n  // S'ok if we don't have update-notifier. It's optional.\n}\n})();\n\nexport async function run(\n    _env: any, args: string[], output: NodeJS.WritableStream): Promise<void> {\n  await wrapResult(output, _run(args, output));\n}\n\nasync function _run(args: string[], output: NodeJS.WritableStream) {\n  // If the \"--version\" or \"-V\" flag is ever present, just print\n  // the current version. Useful for globally installed CLIs.\n  if (args.includes('--version') || args.includes('-V')) {\n    output.write(`${PACKAGE_INFO.version}\\n`);\n    return Promise.resolve();\n  }\n\n  // Options parsing is a two phase affair. First, we need an initial set of\n  // configuration so that we know which plugins to load, etc:\n  let options = config.preparseArgs(args) as config.Config;\n  // Depends on values from the initial merge:\n  options = config.merge(options, <config.Config>{\n    output: output,\n    ttyOutput: !process.env.CI && output['isTTY'] && !options.simpleOutput,\n  });\n  const context = new Context(options);\n\n  if (options.skipUpdateCheck) {\n    updateNotifier = noopNotifier;\n  }\n\n  // `parseArgs` merges any new configuration into `context.options`.\n  await config.parseArgs(context, args);\n  await test(context);\n}\n\n// Note that we're cheating horribly here. Ideally all of this logic is within\n// wct-sauce. The trouble is that we also want WCT's configuration lookup logic,\n// and that's not (yet) cleanly exposed.\nexport async function runSauceTunnel(\n    _env: any, args: string[], output: NodeJS.WritableStream): Promise<void> {\n  await wrapResult(output, _runSauceTunnel(args, output));\n}\n\nasync function _runSauceTunnel(args: string[], output: NodeJS.WritableStream) {\n  const cmdOptions = config.preparseArgs(args) as config.Config;\n  const context = new Context(cmdOptions);\n\n  const diskOptions = context.options;\n  const baseOptions: config.Config =\n      (diskOptions.plugins && diskOptions.plugins['sauce']) ||\n      diskOptions.sauce || {};\n\n  const plugin = await Plugin.get('sauce');\n  const parser = require('nomnom');\n  parser.script('wct-st');\n  parser.options(_.omit(plugin.cliConfig, 'browsers', 'tunnelId'));\n  const options = _.merge(baseOptions, parser.parse(args));\n\n  const wctSauce = require('wct-sauce');\n  wctSauce.expandOptions(options);\n\n  const emitter = new events.EventEmitter();\n  new CliReporter(emitter, output, <config.Config>{});\n  const tunnelId = await new Promise<string>((resolve, reject) => {\n    wctSauce.startTunnel(\n        options, emitter,\n        (error: any, tunnelId: string) =>\n            error ? reject(error) : resolve(tunnelId));\n  });\n\n  output.write('\\n');\n  output.write(\n      'The tunnel will remain active while this process is running.\\n');\n  output.write(\n      'To use this tunnel for other WCT runs, export the following:\\n');\n  output.write('\\n');\n  output.write(chalk.cyan('export SAUCE_TUNNEL_ID=' + tunnelId) + '\\n');\n}\n\nasync function wrapResult(\n    output: NodeJS.WritableStream, promise: Promise<void>) {\n  let error: any;\n  try {\n    await promise;\n  } catch (e) {\n    error = e;\n  }\n\n  if (!process.env.CI) {\n    updateNotifier.notify();\n  }\n\n  if (error) {\n    output.write('\\n');\n    output.write(chalk.red(error) + '\\n');\n    output.write('\\n');\n    throw error;\n  }\n}\n"
  },
  {
    "path": "runner/clireporter.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\n\nimport * as chalk from 'chalk';\nimport * as cleankill from 'cleankill';\nimport * as events from 'events';\nimport * as _ from 'lodash';\nimport * as stacky from 'stacky';\nimport * as tty from 'tty';\nimport * as util from 'util';\n\nimport {BrowserDef, Stats} from './browserrunner';\nimport * as config from './config';\n\nconst STACKY_CONFIG = {\n  indent: '    ',\n  locationStrip: [\n    /^https?:\\/\\/[^\\/]+/,\n    /\\?[\\d\\.]+$/,\n  ],\n  unimportantLocation: [\n    /^\\/web-component-tester\\//,\n  ]\n};\n\nexport type State = 'passing'|'pending'|'failing'|'unknown'|'error';\nexport type CompletedState = 'passing'|'failing'|'pending'|'unknown';\ntype Formatter = (value: string) => string;\n\nconst STATE_ICONS = {\n  passing: '✓',\n  pending: '✖',\n  failing: '✖',\n  unknown: '?',\n};\n\nconst STATE_COLORS: {[state: string]: Formatter} = {\n  passing: chalk.green,\n  pending: chalk.yellow,\n  failing: chalk.red,\n  unknown: chalk.red,\n  error: chalk.red,\n};\n\n\nconst SHORT = {\n  'internet explorer': 'IE',\n};\n\nconst BROWSER_PAD = 24;\nconst STATUS_PAD = 38;\n\n\nexport interface TestEndData {\n  state: CompletedState;\n  /**\n   * The titles of the tests that ran.\n   */\n  test: string[];\n  duration: number;\n  error: any;\n}\n\nexport class CliReporter {\n  prettyBrowsers: {[id: number]: string} = {};\n  browserStats: {[id: number]: Stats} = {};\n  emitter: events.EventEmitter;\n  stream: NodeJS.WritableStream;\n  options: config.Config;\n\n  /**\n   * The number of lines written the last time writeLines was called.\n   */\n  private linesWritten: number;\n\n  constructor(\n      emitter: events.EventEmitter, stream: NodeJS.WritableStream,\n      options: config.Config) {\n    this.emitter = emitter;\n    this.stream = stream;\n    this.options = options;\n    cleankill.onInterrupt(() => {\n      return new Promise((resolve) => {\n        this.flush();\n        resolve();\n      });\n    });\n\n    emitter.on('log:error', this.log.bind(this, chalk.red));\n\n    if (!this.options.quiet) {\n      emitter.on('log:warn', this.log.bind(this, chalk.yellow));\n      emitter.on('log:info', this.log.bind(this));\n      if (this.options.verbose) {\n        emitter.on('log:debug', this.log.bind(this, chalk.dim));\n      }\n    }\n\n    emitter.on('browser-init', (browser: BrowserDef, stats: Stats) => {\n      this.browserStats[browser.id] = stats;\n      this.prettyBrowsers[browser.id] = this.prettyBrowser(browser);\n      this.updateStatus();\n    });\n\n    emitter.on(\n        'browser-start',\n        (browser: BrowserDef, data: {url: string}, stats: Stats) => {\n          this.browserStats[browser.id] = stats;\n          this.log(browser, 'Beginning tests via', chalk.magenta(data.url));\n          this.updateStatus();\n        });\n\n    emitter.on(\n        'test-end', (browser: BrowserDef, data: TestEndData, stats: Stats) => {\n          this.browserStats[browser.id] = stats;\n          if (data.state === 'failing') {\n            this.writeTestError(browser, data);\n          } else if (this.options.expanded || this.options.verbose) {\n            this.log(\n                browser, this.stateIcon(data.state), this.prettyTest(data));\n          }\n\n          this.updateStatus();\n        });\n\n    emitter.on(\n        'browser-end', (browser: BrowserDef, error: any, stats: Stats) => {\n          this.browserStats[browser.id] = stats;\n          if (error) {\n            this.log(chalk.red, browser, 'Tests failed:', error);\n          } else {\n            this.log(chalk.green, browser, 'Tests passed');\n          }\n        });\n\n    emitter.on('run-end', (error: any) => {\n      if (error) {\n        this.log(chalk.red, 'Test run ended in failure:', error);\n      } else {\n        this.log(chalk.green, 'Test run ended with great success');\n      }\n\n      if (!this.options.ttyOutput) {\n        this.updateStatus(true);\n      }\n    });\n  }\n\n  // Specialized Reporting\n  updateStatus(force?: boolean) {\n    if (!this.options.ttyOutput && !force) {\n      return;\n    }\n    // EXTREME TERMINOLOGY FAIL, but here's a glossary:\n    //\n    // stats:  An object containing test stats (total, passing, failing, etc).\n    // state:  The state that the run is in (running, etc).\n    // status: A string representation of above.\n    const statuses = Object.keys(this.browserStats).map((browserIdStr) => {\n      const browserId = parseInt(browserIdStr, 10);\n      const pretty = this.prettyBrowsers[browserId];\n      const stats = this.browserStats[browserId];\n\n      let status = '';\n      const counts = [stats.passing, stats.pending, stats.failing];\n      if (counts[0] > 0 || counts[1] > 0 || counts[2] > 0) {\n        if (counts[0] > 0) {\n          counts[0] = <any>chalk.green(counts[0].toString());\n        }\n        if (counts[1] > 0) {\n          counts[1] = <any>chalk.yellow(counts[1].toString());\n        }\n        if (counts[2] > 0) {\n          counts[2] = <any>chalk.red(counts[2].toString());\n        }\n        status = counts.join('/');\n      }\n      if (stats.status === 'error') {\n        status = status + (status === '' ? '' : ' ') + chalk.red('error');\n      }\n\n      return padRight(pretty + ' (' + status + ')', STATUS_PAD);\n    });\n\n    this.writeWrapped(statuses, '  ');\n  }\n\n  writeTestError(browser: BrowserDef, data: TestEndData) {\n    this.log(browser, this.stateIcon(data.state), this.prettyTest(data));\n\n    const error = data.error || {};\n    this.write('\\n');\n\n    let prettyMessage = error.message || error;\n    if (typeof prettyMessage !== 'string') {\n      prettyMessage = util.inspect(prettyMessage);\n    }\n    this.write(chalk.red('  ' + prettyMessage));\n\n    if (error.stack) {\n      try {\n        this.write(stacky.pretty(data.error.stack, STACKY_CONFIG));\n      } catch (err) {\n        // If we couldn't extract a stack (i.e. there was no stack), the message\n        // is enough.\n      }\n    }\n    this.write('\\n');\n  }\n\n  // Object Formatting\n\n  stateIcon(state: State) {\n    const color = STATE_COLORS[state] || STATE_COLORS['unknown'];\n    return color(STATE_ICONS[state] || STATE_ICONS.unknown);\n  }\n\n  prettyTest(data: TestEndData) {\n    const color = STATE_COLORS[data.state] || STATE_COLORS['unknown'];\n    return color(data.test.join(' » ') || '<unknown test>');\n  }\n\n  prettyBrowser(browser: BrowserDef) {\n    const parts: string[] = [];\n\n    if (browser.platform && !browser.deviceName) {\n      parts.push(browser.platform);\n    }\n\n    const name = browser.deviceName || browser.browserName;\n    parts.push(SHORT[name] || name);\n\n    if (browser.version) {\n      parts.push(browser.version);\n    }\n\n    if (browser.variant) {\n      parts.push(`[${browser.variant}]`);\n    }\n\n    return chalk.blue(parts.join(' '));\n  }\n\n  // General Output Formatting\n\n  log(...values: any[]): void;\n  log() {\n    let values = Array.from(arguments);\n    let format: (line: string) => string;\n    if (_.isFunction(values[0])) {\n      format = values[0];\n      values = values.slice(1);\n    }\n    if (values[0] && values[0].browserName) {\n      values[0] = padRight(this.prettyBrowser(values[0]), BROWSER_PAD);\n    }\n\n    let line =\n        _.toArray(values)\n            .map((value) => _.isString(value) ? value : util.inspect(value))\n            .join(' ');\n    line = line.replace(/[\\s\\n\\r]+$/, '');\n    if (format) {\n      line = format(line);\n    }\n    this.write(line);\n  }\n\n  writeWrapped(blocks: string[], separator: string) {\n    if (blocks.length === 0) {\n      return;\n    }\n\n    const lines = [''];\n    const width = (<tty.WriteStream>this.stream).columns || 0;\n    for (const block of blocks) {\n      const line = lines[lines.length - 1];\n      const combined = line + separator + block;\n      if (line === '') {\n        lines[lines.length - 1] = block;\n      } else if (chalk.stripColor(combined).length <= width) {\n        lines[lines.length - 1] = combined;\n      } else {\n        lines.push(block);\n      }\n    }\n\n    this.writeLines(['\\n'].concat(lines));\n    if (this.options.ttyOutput) {\n      this.stream.write('\\r');\n      this.stream.write('\\u001b[' + (lines.length + 1) + 'A');\n    }\n  }\n\n  write(line: string) {\n    this.writeLines([line]);\n    this.updateStatus();\n  }\n\n  writeLines(lines: string[]) {\n    for (let line of lines) {\n      if (line[line.length - 1] !== '\\n') {\n        line = line + '\\n';\n      }\n      if (this.options.ttyOutput) {\n        line = '\\u001b[J' + line;\n      }\n      this.stream.write(line);\n    }\n    this.linesWritten = lines.length;\n  }\n\n  flush() {\n    if (!this.options.ttyOutput) {\n      return;\n    }\n    // Add an extra line for padding.\n    for (let i = 0; i <= this.linesWritten; i++) {\n      this.stream.write('\\n');\n    }\n  }\n\n  // HACK\n  static CliReporter = CliReporter;\n}\n// Yeah, yeah.\nfunction padRight(str: string, length: number) {\n  let currLength = chalk.stripColor(str).length;\n  while (currLength < length) {\n    currLength = currLength + 1;\n    str = str + ' ';\n  }\n  return str;\n}\n\n\nmodule.exports = CliReporter;\n"
  },
  {
    "path": "runner/config.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\n\nimport * as findup from 'findup-sync';\nimport * as fs from 'fs';\nimport * as _ from 'lodash';\nimport * as nomnom from 'nomnom';\nimport * as path from 'path';\nimport * as resolve from 'resolve';\nimport {Capabilities} from 'wd';\n\nimport {BrowserDef} from './browserrunner';\nimport {Context} from './context';\nimport * as paths from './paths';\nimport {Plugin} from './plugin';\n\n\nconst HOME_DIR = path.resolve(\n    process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE);\nconst JSON_MATCHER = 'wct.conf.json';\nconst CONFIG_MATCHER = 'wct.conf.*';\n\nexport type Browser = string|{browserName: string, platform: string};\n\nexport interface Config {\n  suites?: string[];\n  output?: NodeJS.WritableStream;\n  ttyOutput?: boolean;\n  verbose?: boolean;\n  quiet?: boolean;\n  expanded?: boolean;\n  root?: string;\n  testTimeout?: number;\n  persistent?: boolean;\n  extraScripts?: string[];\n  wctPackageName?: string;\n  clientOptions?:\n      {root?: string; verbose?: boolean; environmentScripts?: string[]};\n  activeBrowsers?: BrowserDef[];\n  browserOptions?: {[name: string]: Capabilities};\n  plugins?: (string|boolean)[]|{[key: string]: ({disabled: boolean} | boolean)};\n  registerHooks?: (wct: Context) => void;\n  enforceJsonConf?: boolean;\n  webserver?: {\n    // The port that the main webserver should run on. A port will be\n    // determined at runtime if none is provided.\n    port: number;\n\n    // The hostname used when generating URLs for the webdriver client.\n    hostname: string;\n\n    _generatedIndexContent?: string;\n    _servers?: {variant: string, url: string}[];\n  };\n  npm?: boolean;\n  moduleResolution?: 'none'|'node';\n  packageName?: string;\n  skipPlugins?: string[];\n  sauce?: {};\n  remote?: {};\n  origSuites?: string[];\n  compile?: 'auto'|'always'|'never';\n  skipCleanup?: boolean;\n  simpleOutput?: boolean;\n  skipUpdateCheck?: boolean;\n  configFile?: string;\n  proxy?: {\n    // Top-level path that should be redirected to the proxy-target.  E.g.\n    // `api/v1` when you want to redirect all requests of\n    // `https://localhost/api/v1/`.\n    path: string;\n    // Host URL to proxy to, for example `https://myredirect:8080/foo`.\n    target: string;\n  };\n  /** A deprecated option */\n  browsers?: Browser[]|Browser;\n}\n\nexport interface NPMPackage {\n  /**\n   * Name of the node package. e.g. '@polymer/polymer'\n   */\n  name: string;\n\n  /**\n   * JS entrypoints relative to packageName e.g. lodash/index.js would simply\n   * be ['index.js'] and myPackage/dist/addon.js and myPackage/lib/core.js would\n   * be ['dist/addon.js', 'lib/core.js']\n   */\n  jsEntrypoint: string;\n}\n\n/**\n * config helper: A basic function to synchronously read JSON,\n * log any errors, and return null if no file or invalid JSON\n * was found.\n */\nfunction readJsonSync(filename: string, dir?: string): any|null {\n  const configPath = path.resolve(dir || '', filename);\n  let config: any;\n  try {\n    config = fs.readFileSync(configPath, 'utf-8');\n  } catch (e) {\n    return null;\n  }\n  try {\n    return JSON.parse(config);\n  } catch (e) {\n    console.error(`Could not parse ${configPath} as JSON`);\n    console.error(e);\n  }\n  return null;\n}\n\n/**\n * Determines the package name by reading from the following sources:\n *\n * 1. `options.packageName`\n * 2. bower.json or package.json, depending on options.npm\n */\nexport function getPackageName(options: Config): string|undefined {\n  if (options.packageName) {\n    return options.packageName;\n  }\n  const manifestName = (options.npm ? 'package.json' : 'bower.json');\n  const manifest = readJsonSync(manifestName, options.root);\n  if (manifest !== null) {\n    return manifest.name;\n  }\n  const basename = path.basename(options.root);\n  console.warn(\n      `no ${manifestName} found, defaulting to packageName=${basename}`);\n  return basename;\n}\n\n/**\n * Truncates the path to the slash after the last occurrence of the given\n * package name.\n *\n * @param directory Name of directory\n * @param pathName Path to be truncated\n */\nexport function truncatePathToDir(directory: string, pathName: string): string|\n    null {\n  const delimitedDir = `/${directory}/`;\n  const lastDirOccurrence = pathName.lastIndexOf(delimitedDir);\n  if (lastDirOccurrence === -1) {\n    return null;\n  }\n  return pathName.substr(0, lastDirOccurrence + delimitedDir.length);\n}\n\n/**\n * Resolves npm paths from current config root to ScriptNames.\n *\n * e.g. a/b.js is actually resolved in directory c's node modules, it would\n * return c/node_modules/a/b.js. These dependencies must be direct dependencies\n * to WCT.\n *\n * @param config Current config / options / scope\n * @param npmPackages List of NPMScript objects to be resolved\n * @param wctPackageName Name of wct package with browser.js (Defaults to\n * wct-browser-legacy)\n */\nexport function resolveWctNpmEntrypointNames(\n    config: Config, npmPackages: NPMPackage[]): string[] {\n  // grab from CLI flag defaults to wct-browser-legacy\n  let wctPackageName = config.wctPackageName;\n\n  if (wctPackageName === undefined) {\n    wctPackageName = 'wct-browser-legacy';\n  }\n\n  let absoluteBrowserPath;\n\n  try {\n    absoluteBrowserPath = resolve.sync(wctPackageName, {basedir: config.root});\n  } catch {\n    throw new Error(\n        `${wctPackageName} not installed. Please change --wct-package-name` +\n        ` flag or install the package.`);\n  }\n\n  // We want to find and inject dependencies WCT relies on not the local\n  // package or its dependencies' dependencies\n  const absoluteWCTRoot =\n      truncatePathToDir(wctPackageName, absoluteBrowserPath);\n  const resolvedEntrypoints: string[] = [];\n\n  for (const npmPackage of npmPackages) {\n    const absoluteNpmMainPath =\n        resolve.sync(npmPackage.name, {basedir: absoluteWCTRoot});\n\n    const absoluteBasePath =\n        truncatePathToDir(npmPackage.name, absoluteNpmMainPath);\n\n    // Find path relative to our testing element's node_modules\n    const nodeModulesDir = path.posix.join(config.root, 'node_modules');\n    const relativeBasePath =\n        path.posix.relative(nodeModulesDir, absoluteBasePath);\n\n    resolvedEntrypoints.push(\n        path.posix.join(relativeBasePath, npmPackage.jsEntrypoint));\n  }\n\n  return resolvedEntrypoints;\n}\n\n// The full set of options, as a reference.\nexport function defaults(): Config {\n  return {\n    // The test suites that should be run.\n    suites: ['test/'],\n    // Output stream to write log messages to.\n    output: process.stdout,\n    // Whether the output stream should be treated as a TTY (and be given more\n    // complex output formatting). Defaults to `output.isTTY`.\n    ttyOutput: undefined,\n    // Spew all sorts of debugging messages.\n    verbose: false,\n    // Silence output\n    quiet: false,\n    // Display test results in expanded form. Verbose implies expanded.\n    expanded: false,\n    // The on-disk path where tests & static files should be served from. Paths\n    // (such as `suites`) are evaluated relative to this.\n    //\n    // Defaults to the project directory.\n    root: undefined,\n    // Idle timeout for tests.\n    testTimeout: 90 * 1000,\n    // Whether the browser should be closed after the tests run.\n    persistent: false,\n    // Additional .js files to include in *generated* test indexes.\n    extraScripts: [],\n    // Configuration options passed to the browser client.\n    clientOptions: {\n      root: '/components/',\n    },\n    compile: 'auto',\n\n    // Webdriver capabilities objects for each browser that should be run.\n    //\n    // Capabilities can also contain a `url` value which is either a string URL\n    // for the webdriver endpoint, or {hostname:, port:, user:, pwd:}.\n    //\n    // Most of the time you will want to rely on the WCT browser plugins to fill\n    // this in for you (e.g. via `--local`, `--sauce`, etc).\n    activeBrowsers: [],\n    // Default capabilities to use when constructing webdriver connections (for\n    // each browser specified in `activeBrowsers`). A handy place to hang common\n    // configuration.\n    //\n    // Selenium: https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities\n    // Sauce:    https://docs.saucelabs.com/reference/test-configuration/\n    browserOptions: {},\n    // The plugins that should be loaded, and their configuration.\n    //\n    // When an array, the named plugins will be loaded with their default\n    // configuration. When an object, each key maps to a plugin, and values are\n    // configuration values to be merged.\n    //\n    //   plugins: {\n    //     local: {browsers: ['firefox', 'chrome']},\n    //   }\n    //\n    plugins: ['local', 'sauce'],\n    // Callback that allows you to perform advanced configuration of the WCT\n    // runner.\n    //\n    // The hook is given the WCT context, and can generally be written like a\n    // plugin. For example, to serve custom content via the internal webserver:\n    //\n    //     registerHooks: function(wct) {\n    //       wct.hook('prepare:webserver', function(app) {\n    //         app.use(...);\n    //         return Promise.resolve();\n    //       });\n    //     }\n    //\n    registerHooks: function(_wct) {},\n    // Whether `wct.conf.*` is allowed, or only `wct.conf.json`.\n    //\n    // Handy for CI suites that want to be locked down.\n    enforceJsonConf: false,\n    // Configuration options for the webserver that serves up your test files\n    // and dependencies.\n    //\n    // Typically, you will not need to modify these values.\n    webserver: {\n      // The port that the webserver should run on. A port will be determined at\n      // runtime if none is provided.\n      port: undefined,\n      hostname: 'localhost',\n    },\n    // The name of the NPM package that is vending wct's browser.js\n    wctPackageName: 'wct-browser-legacy'\n  };\n}\n\n/**\n * nomnom configuration for command line arguments.\n *\n * This might feel like duplication with `defaults()`, and out of place (why not\n * in `cli.js`?). But, not every option matches a configurable value, and it is\n * best to keep the configuration for these together to help keep them in sync.\n */\nconst ARG_CONFIG = {\n  persistent: {\n    help: 'Keep browsers active (refresh to rerun tests).',\n    abbr: 'p',\n    flag: true,\n  },\n  root: {\n    help: 'The root directory to serve tests from.',\n    transform: path.resolve,\n  },\n  plugins: {\n    help: 'Plugins that should be loaded.',\n    metavar: 'NAME',\n    full: 'plugin',\n    list: true,\n  },\n  skipPlugins: {\n    help: 'Configured plugins that should _not_ be loaded.',\n    metavar: 'NAME',\n    full: 'skip-plugin',\n    list: true,\n  },\n  expanded: {\n    help: 'Log a status line for each test run.',\n    flag: true,\n  },\n  verbose: {\n    help: 'Turn on debugging output.',\n    flag: true,\n  },\n  quiet: {\n    help: 'Silence output.',\n    flag: true,\n  },\n  simpleOutput: {\n    help: 'Avoid fancy terminal output.',\n    flag: true,\n  },\n  skipUpdateCheck: {\n    help: 'Don\\'t check for updates.',\n    full: 'skip-update-check',\n    flag: true,\n  },\n  configFile: {\n    help: 'Config file that needs to be used by wct. ie: wct.config-sauce.js',\n    full: 'configFile',\n  },\n  npm: {\n    help: 'Use node_modules instead of bower_components for all browser ' +\n        'components and packages.  Uses polyserve with `--npm` flag.',\n    flag: true,\n  },\n  moduleResolution: {\n    // kebab case to match the polyserve flag\n    full: 'module-resolution',\n    help: 'Algorithm to use for resolving module specifiers in import ' +\n        'and export statements when rewriting them to be web-compatible. ' +\n        'Valid values are \"none\" and \"node\". \"none\" disables module ' +\n        'specifier rewriting. \"node\" uses Node.js resolution to find modules.',\n    // type: 'string',\n    choices: ['none', 'node'],\n  },\n  version: {\n    help: 'Display the current version of web-component-tester.  Ends ' +\n        'execution immediately (not useable with other options.)',\n    abbr: 'V',\n    flag: true,\n  },\n  'webserver.port': {\n    help: 'A port to use for the test webserver.',\n    full: 'webserver-port',\n  },\n  'webserver.hostname': {\n    full: 'webserver-hostname',\n    hidden: true,\n  },\n  // Managed by supports-color; let's not freak out if we see it.\n  color: {flag: true},\n\n  compile: {\n    help: 'Whether to compile ES2015 down to ES5. ' +\n        'Options: \"always\", \"never\", \"auto\". Auto means that we will ' +\n        'selectively compile based on the requesting user agent.'\n  },\n  wctPackageName: {\n    full: 'wct-package-name',\n    help: 'NPM package name that contains web-component-tester\\'s browser.js.' +\n        ' Defaults to wct-browser-legacy. This is only used in NPM projects.'\n  },\n\n  // Deprecated\n\n  browsers: {\n    abbr: 'b',\n    hidden: true,\n    list: true,\n  },\n  remote: {\n    abbr: 'r',\n    hidden: true,\n    flag: true,\n  },\n};\n\n// Values that should be extracted when pre-parsing args.\nconst PREPARSE_ARGS =\n    ['plugins', 'skipPlugins', 'simpleOutput', 'skipUpdateCheck', 'configFile'];\n\nexport interface PreparsedArgs {\n  plugins?: string[];\n  skipPlugins?: string[];\n  simpleOutput?: boolean;\n  skipUpdateCheck?: boolean;\n}\n\n/**\n * Discovers appropriate config files (global, and for the project), merging\n * them, and returning them.\n *\n * @param {string} matcher\n * @param {string} root\n * @return {!Object} The merged configuration.\n */\nexport function fromDisk(matcher: string, root?: string): Config {\n  const globalFile = path.join(HOME_DIR, matcher);\n  const projectFile = findup(matcher, {nocase: true, cwd: root});\n  // Load a shared config from the user's home dir, if they have one, and then\n  // try the project-specific path (starting at the current working directory).\n  const paths = _.union([globalFile, projectFile]);\n  const configs = _.filter(paths, fs.existsSync).map(loadProjectFile);\n  const options: Config = merge.apply(null, configs);\n\n  if (!options.root && projectFile && projectFile !== globalFile) {\n    options.root = path.dirname(projectFile);\n  }\n\n  return options;\n}\n\n/**\n * @param {string} file\n * @return {Object?}\n */\nfunction loadProjectFile(file: string) {\n  // If there are _multiple_ configs at this path, prefer `json`\n  if (path.extname(file) === '.js' && fs.existsSync(file + 'on')) {\n    file = file + 'on';\n  }\n\n  try {\n    if (path.extname(file) === '.json') {\n      return JSON.parse(fs.readFileSync(file, 'utf-8'));\n    } else {\n      return require(file);\n    }\n  } catch (error) {\n    throw new Error(`Failed to load WCT config \"${file}\": ${error.message}`);\n  }\n}\n\n/**\n * Runs a simplified options parse over the command line arguments, extracting\n * any values that are necessary for a full parse.\n *\n * See const: PREPARSE_ARGS for the values that are extracted.\n *\n * @param {!Array<string>} args\n * @return {!Object}\n */\nexport function preparseArgs(args: string[]): PreparsedArgs {\n  // Don't let it short circuit on help.\n  args = _.difference(args, ['--help', '-h']);\n\n  const parser = nomnom();\n  parser.options(<any>ARG_CONFIG);\n  parser.printer(function() {});  // No-op output & errors.\n  const options = parser.parse(args);\n\n  return _expandOptionPaths(_.pick(options, PREPARSE_ARGS));\n}\n\n/**\n * Runs a complete options parse over the args, respecting plugin options.\n *\n * @param {!Context} context The context, containing plugin state and any base\n *     options to merge into.\n * @param {!Array<string>} args The args to parse.\n */\nexport async function parseArgs(\n    context: Context, args: string[]): Promise<void> {\n  const parser = nomnom();\n  parser.script('wct');\n  parser.options(<any>ARG_CONFIG);\n\n  const plugins = await context.plugins();\n  plugins.forEach(_configurePluginOptions.bind(null, parser));\n  const options = <any>_expandOptionPaths(normalize(parser.parse(args)));\n  if (options._ && options._.length > 0) {\n    options.suites = options._;\n  }\n\n  context.options = merge(context.options, options);\n}\n\nfunction _configurePluginOptions(\n    parser: NomnomInternal.Parser, plugin: Plugin) {\n  /** HACK(rictic): this looks wrong, cliConfig shouldn't have a length. */\n  if (!plugin.cliConfig || (<any>plugin.cliConfig).length === 0) {\n    return;\n  }\n\n  // Group options per plugin. It'd be nice to also have a header, but that ends\n  // up shifting all the options over.\n  parser.option('plugins.' + plugin.name + '.', {string: ' '});\n\n  _.each(plugin.cliConfig, function(config, key) {\n    // Make sure that we don't expose the name prefixes.\n    if (!config['full']) {\n      config['full'] = key;\n    }\n    parser.option(\n        'plugins.' + plugin.name + '.' + key,\n        config as NomnomInternal.Parser.Option);\n  });\n}\n\nfunction _expandOptionPaths(options: {[key: string]: any}): any {\n  const result = {};\n  _.each(options, function(value, key) {\n    let target = result;\n    const parts = key.split('.');\n    for (const part of parts.slice(0, -1)) {\n      target = target[part] = target[part] || {};\n    }\n    target[_.last(parts)] = value;\n  });\n  return result;\n}\n\n/**\n * @param {!Object...} configs Configuration objects to merge.\n * @return {!Object} The merged configuration, where configuration objects\n *     specified later in the arguments list are given precedence.\n */\nexport function merge(...configs: Config[]): Config;\nexport function merge(): Config {\n  let configs: Config[] = Array.prototype.slice.call(arguments);\n  const result = <Config>{};\n  configs = configs.map(normalize);\n  _.merge.apply(_, [result].concat(configs));\n\n  // false plugin configs are preserved.\n  configs.forEach(function(config) {\n    _.each(config.plugins, function(value, key) {\n      if (typeof value === 'boolean' && value === false) {\n        result.plugins[key] = false;\n      }\n    });\n  });\n\n  return result;\n}\n\nexport function normalize(config: Config): Config {\n  if (_.isArray(config.plugins)) {\n    const pluginConfigs = <{[key: string]: {disabled: boolean}}>{};\n    for (let i = 0, name: string; name = <string>config.plugins[i]; i++) {\n      // A named plugin is explicitly enabled (e.g. --plugin foo).\n      pluginConfigs[name] = {disabled: false};\n    }\n    config.plugins = pluginConfigs;\n  }\n\n  // Always wins.\n  if (config.skipPlugins) {\n    config.plugins = config.plugins || {};\n    for (let i = 0, name: string; name = config.skipPlugins[i]; i++) {\n      config.plugins[name] = false;\n    }\n  }\n\n  return config;\n}\n\n/**\n * Expands values within the configuration based on the current environment.\n *\n * @param {!Context} context The context for the current run.\n */\nexport async function expand(context: Context): Promise<void> {\n  const options = context.options;\n  let root = context.options.root || process.cwd();\n  context.options.root = root = path.resolve(root);\n\n  options.origSuites = _.clone(options.suites);\n\n  expandDeprecated(context);\n\n  options.suites = await paths.expand(root, options.suites);\n}\n\n/**\n * Expands any options that have been deprecated, and warns about it.\n *\n * @param {!Context} context The context for the current run.\n */\nfunction expandDeprecated(context: Context) {\n  const options = context.options;\n  // We collect configuration fragments to be merged into the options object.\n  const fragments = [];\n\n  let browsers: Browser[] = <any>(\n      _.isArray(options.browsers) ? options.browsers : [options.browsers]);\n  browsers = <any>_.compact(<any>browsers);\n  if (browsers.length > 0) {\n    context.emit(\n        'log:warn',\n        'The --browsers flag/option is deprecated. Please use ' +\n            '--local and --sauce instead, or configure via plugins.' +\n            '[local|sauce].browsers.');\n    const fragment: {\n      plugins: {[name: string]: {browsers?: Browser[]}}\n    } = {plugins: {sauce: {}, local: {}}};\n    fragments.push(fragment);\n\n    for (const browser of browsers) {\n      const name = (<any>browser).browserName || browser;\n      const plugin = (<any>browser).platform || name.indexOf('/') !== -1 ?\n          'sauce' :\n          'local';\n      fragment.plugins[plugin].browsers =\n          fragment.plugins[plugin].browsers || [];\n      fragment.plugins[plugin].browsers.push(browser);\n    }\n\n    delete options.browsers;\n  }\n\n  if (options.sauce) {\n    context.emit(\n        'log:warn',\n        'The sauce configuration key is deprecated. Please use ' +\n            'plugins.sauce instead.');\n    fragments.push({\n      plugins: {sauce: options.sauce},\n    });\n    delete options.sauce;\n  }\n\n  if (options.remote) {\n    context.emit(\n        'log:warn',\n        'The --remote flag is deprecated. Please use ' +\n            '--sauce default instead.');\n    fragments.push({\n      plugins: {sauce: {browsers: ['default']}},\n    });\n    delete options.remote;\n  }\n\n  if (fragments.length > 0) {\n    // We are careful to modify context.options in place.\n    _.merge(context.options, merge.apply(null, fragments));\n  }\n}\n\n/**\n * @param {!Object} options The configuration to validate.\n */\nexport async function validate(options: Config): Promise<void> {\n  if (options['webRunner']) {\n    throw new Error(\n        'webRunner is no longer a supported configuration option. ' +\n        'Please list the files you wish to test as arguments, ' +\n        'or as `suites` in a configuration object.');\n  }\n  if (options['component']) {\n    throw new Error(\n        'component is no longer a supported configuration option. ' +\n        'Please list the files you wish to test as arguments, ' +\n        'or as `suites` in a configuration object.');\n  }\n\n  if (options.activeBrowsers.length === 0) {\n    throw new Error('No browsers configured to run');\n  }\n  if (options.suites.length === 0) {\n    const root = options.root || process.cwd();\n    const globs = options.origSuites.join(', ');\n    throw new Error(\n        'No test suites were found matching your configuration\\n' +\n        '\\n' +\n        '  WCT searched for .js and .html files matching: ' + globs + '\\n' +\n        '\\n' +\n        '  Relative paths were resolved against: ' + root);\n  }\n}\n"
  },
  {
    "path": "runner/context.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\n\nimport * as events from 'events';\nimport * as express from 'express';\nimport * as _ from 'lodash';\nimport {ExpressAppMapper, ServerOptions} from 'polyserve/lib/start_server';\nimport * as socketIO from 'socket.io';\nimport * as http from 'spdy';\nimport * as util from 'util';\n\nimport {BrowserRunner} from './browserrunner';\nimport * as config from './config';\nimport {Plugin} from './plugin';\n\nconst JSON_MATCHER = 'wct.conf.json';\nconst CONFIG_MATCHER = 'wct.conf.*';\n\nexport type Handler =\n    ((...args: any[]) => Promise<any>)|((done: (err?: any) => void) => void)|\n    ((arg1: any, done: (err?: any) => void) => void)|\n    ((arg1: any, arg2: any, done: (err?: any) => void) => void)|\n    ((arg1: any, arg2: any, arg3: any, done: (err?: any) => void) => void);\n\n/**\n * Exposes the current state of a WCT run, and emits events/hooks for anyone\n * downstream to listen to.\n *\n * TODO(rictic): break back-compat with plugins by moving hooks entirely away\n *     from callbacks to promises. Easiest way to do this would be to rename\n *     the hook-related methods on this object, so that downstream callers would\n *     break in obvious ways.\n *\n * @param {Object} options Any initially specified options.\n */\nexport class Context extends events.EventEmitter {\n  options: config.Config;\n  private _hookHandlers: {[key: string]: Handler[]} = {};\n  _socketIOServers: SocketIO.Server[];\n  _httpServers: http.Server[];\n  _testRunners: BrowserRunner[];\n\n  constructor(options?: config.Config) {\n    super();\n    options = options || {};\n\n    let matcher: string;\n    if (options.configFile) {\n      matcher = options.configFile;\n    } else if (options.enforceJsonConf) {\n      matcher = JSON_MATCHER;\n    } else {\n      matcher = CONFIG_MATCHER;\n    }\n\n    /**\n     * The configuration for the current WCT run.\n     *\n     * We guarantee that this object is never replaced (e.g. you are free to\n     * hold a reference to it, and make changes to it).\n     */\n    this.options = config.merge(\n        config.defaults(), config.fromDisk(matcher, options.root), options);\n  }\n\n  // Hooks\n  //\n  // In addition to emitting events, a context also exposes \"hooks\" that\n  // interested parties can use to inject behavior.\n\n  /**\n   * Registers a handler for a particular hook. Hooks are typically configured\n   * to run _before_ a particular behavior.\n   */\n  hook(name: string, handler: Handler) {\n    this._hookHandlers[name] = this._hookHandlers[name] || [];\n    this._hookHandlers[name].unshift(handler);\n  }\n\n  /**\n   * Registers a handler that will run after any handlers registered so far.\n   *\n   * @param {string} name\n   * @param {function(!Object, function(*))} handler\n   */\n  hookLate(name: string, handler: Handler) {\n    this._hookHandlers[name] = this._hookHandlers[name] || [];\n    this._hookHandlers[name].push(handler);\n  }\n\n  /**\n   * Once all registered handlers have run for the hook, your callback will be\n   * triggered. If any of the handlers indicates an error state, any subsequent\n   * handlers will be canceled, and the error will be passed to the callback for\n   * the hook.\n   *\n   * Any additional arguments passed between `name` and `done` will be passed to\n   * hooks (before the callback).\n   *\n   * @param {string} name\n   * @param {function(*)} done\n   * @return {!Context}\n   */\n  emitHook(\n      name: 'define:webserver', app: express.Express,\n      // The `mapper` param is a function the client of the hook uses to\n      // substitute a new app for the one given.  This enables, for example,\n      // mounting the polyserve app on a custom app to handle requests or mount\n      // middleware that needs to sit in front of polyserve's own handlers.\n      mapper: (app: Express.Application) => void, options: ServerOptions,\n      done?: (err?: any) => void): Promise<void>;\n  emitHook(\n      name: 'prepare:webserver', app: express.Express,\n      done?: (err?: any) => void): Promise<void>;\n  emitHook(name: 'configure', done?: (err?: any) => void): Promise<void>;\n  emitHook(name: 'prepare', done?: (err?: any) => void): Promise<void>;\n  emitHook(name: 'cleanup', done?: (err?: any) => void): Promise<void>;\n  emitHook(name: string, done?: (err?: any) => void): Promise<void>;\n  emitHook(name: string, ...args: any[]): Promise<void>;\n\n  async emitHook(name: string, ...args: any[]): Promise<void> {\n    this.emit('log:debug', 'hook:', name);\n\n    const hooks = (this._hookHandlers[name] || []);\n    type BoundHook = (cb: (err: any) => void) => (void|Promise<any>);\n    let boundHooks: BoundHook[];\n    let done: (err?: any) => void = (_err: any) => {};\n    let argsEnd = args.length - 1;\n    if (args[argsEnd] instanceof Function) {\n      done = args[argsEnd];\n      argsEnd = argsEnd--;\n    }\n    const hookArgs = args.slice(0, argsEnd + 1);\n    boundHooks =\n        hooks.map((hook) => hook.bind.apply(hook, [null].concat(hookArgs)));\n    if (!boundHooks) {\n      boundHooks = <any>hooks;\n    }\n\n    // A hook may return a promise or it may call a callback. We want to\n    // treat hooks as though they always return promises, so this converts.\n    const hookToPromise = (hook: BoundHook) => {\n      return new Promise((resolve, reject) => {\n        const maybePromise = hook((err) => {\n          if (err) {\n            reject(err);\n          } else {\n            resolve();\n          }\n        });\n        if (maybePromise) {\n          maybePromise.then(resolve, reject);\n        }\n      });\n    };\n\n    // We execute the handlers _sequentially_. This may be slower, but it gives\n    // us a lighter cognitive load and more obvious logs.\n    try {\n      for (const hook of boundHooks) {\n        await hookToPromise(hook);\n      }\n    } catch (err) {\n      // TODO(rictic): stop silently swallowing the error here and just below.\n      //     Looks like we'll need to track down some error being thrown from\n      //     deep inside the express router.\n      try {\n        done(err);\n      } catch (_) {\n      }\n      throw err;\n    }\n    try {\n      done();\n    } catch (_) {\n    }\n  }\n\n  /**\n   * @param {function(*, Array<!Plugin>)} done Asynchronously loads the plugins\n   *     requested by `options.plugins`.\n   */\n  async plugins(): Promise<Plugin[]> {\n    const plugins: Plugin[] = [];\n    for (const name of this.enabledPlugins()) {\n      plugins.push(await Plugin.get(name));\n    }\n    return plugins;\n  }\n\n  /**\n   * @return {!Array<string>} The names of enabled plugins.\n   */\n  enabledPlugins(): string[] {\n    // Plugins with falsy configuration or disabled: true are _not_ loaded.\n    const pairs = _.reject(\n        (<any>_).pairs(this.options.plugins),\n        (p: [string, {disabled: boolean}]) => !p[1] || p[1].disabled);\n    return _.map(pairs, (p) => p[0]);\n  }\n\n  /**\n   * @param {string} name\n   * @return {!Object}\n   */\n  pluginOptions(name: string) {\n    return this.options.plugins[Plugin.shortName(name)];\n  }\n\n  static Context = Context;\n}\n\nmodule.exports = Context;\n"
  },
  {
    "path": "runner/gulp.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\n\nimport * as chalk from 'chalk';\nimport {Gulp} from 'gulp';\n\nimport {Config} from './config';\nimport {test} from './test';\n\n\nexport function init(gulp: Gulp, dependencies?: string[]): void {\n  if (!dependencies) {\n    dependencies = [];\n  }\n\n  // TODO(nevir): Migrate fully to wct:local/etc.\n  gulp.task('test', ['wct:local']);\n  gulp.task('test:local', ['wct:local']);\n  gulp.task('test:remote', ['wct:sauce']);\n\n  gulp.task('wct', ['wct:local']);\n\n  gulp.task('wct:local', dependencies, () => {\n    return test(<any>{plugins: {local: {}, sauce: false}}).catch(cleanError);\n  });\n\n  gulp.task('wct:sauce', dependencies, () => {\n    return test(<any>{plugins: {local: false, sauce: {}}}).catch(cleanError);\n  });\n}\n\n// Utility\n\nfunction cleanError(error: any) {\n  // Pretty error for gulp.\n  error = new Error(chalk.red(error.message || error));\n  error.showStack = false;\n  throw error;\n}\n"
  },
  {
    "path": "runner/httpbin.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\n\n'use strict';\n\nimport * as bodyParser from 'body-parser';\nimport * as cleankill from 'cleankill';\nimport * as express from 'express';\nimport * as http from 'http';\nimport * as multer from 'multer';\nimport * as serverDestroy from 'server-destroy';\n\nimport {findPort} from './port-scanner';\nimport {Router} from 'express';\nexport {Router} from 'express';\n\nexport const httpbin: Router = express.Router();\n\nfunction capWords(s: string) {\n  return s.split('-')\n      .map((word) => word[0].toUpperCase() + word.slice(1))\n      .join('-');\n}\n\nfunction formatRequest(req: express.Request) {\n  const headers = {};\n  for (const key in req.headers) {\n    headers[capWords(key)] = req.headers[key];\n  }\n  const formatted = {\n    headers: headers,\n    url: req.originalUrl,\n    data: req.body,\n    files: (<any>req).files,\n    form: {},\n    json: {},\n  };\n  const contentType =\n      (headers['Content-Type'] || '').toLowerCase().split(';')[0];\n  const field = {\n    'application/json': 'json',\n    'application/x-www-form-urlencoded': 'form',\n    'multipart/form-data': 'form'\n  }[contentType];\n  if (field) {\n    formatted[field] = req.body;\n  }\n  return formatted;\n}\n\nhttpbin.use(bodyParser.urlencoded({extended: false}));\nhttpbin.use(bodyParser.json());\nconst storage = multer.memoryStorage();\nconst upload = multer({storage: storage});\nhttpbin.use(upload.any());\nhttpbin.use(bodyParser.text());\nhttpbin.use(bodyParser.text({type: 'html'}));\nhttpbin.use(bodyParser.text({type: 'xml'}));\n\nhttpbin.get('/delay/:seconds', function(req, res) {\n  setTimeout(function() {\n    res.json(formatRequest(req));\n  }, (req.params.seconds || 0) * 1000);\n});\n\nhttpbin.post('/post', function(req, res) {\n  res.json(formatRequest(req));\n});\n\n// Running this script directly with `node httpbin.js` will start up a server\n// that just serves out /httpbin/...\n// Useful for debugging only the httpbin functionality without the rest of\n// wct.\nasync function main() {\n  const app = express();\n  const server = http.createServer(app) as serverDestroy.DestroyableServer;\n\n  app.use('/httpbin', httpbin);\n\n\n  const port = await findPort([7777, 7000, 8000, 8080, 8888]);\n\n  server.listen(port);\n  (<any>server).port = port;\n  serverDestroy(server);\n  cleankill.onInterrupt(() => {\n    return new Promise((resolve) => {\n      server.destroy();\n      server.on('close', resolve);\n    });\n  });\n\n  console.log('Server running at http://localhost:' + port + '/httpbin/');\n}\n\nif (require.main === module) {\n  main().catch((err) => {\n    console.error(err);\n    process.exit(1);\n  });\n}\n"
  },
  {
    "path": "runner/paths.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\n\nimport * as fs from 'fs';\nimport * as glob from 'glob';\nimport * as _ from 'lodash';\nimport * as path from 'path';\nimport * as promisify from 'promisify-node';\n\n/**\n * Expands a series of path patterns (globs, files, directories) into a set of\n * files that represent those patterns.\n *\n * @param baseDir The directory that patterns are relative to.\n * @param patterns The patterns to expand.\n * @returns The expanded paths.\n */\nexport async function expand(\n    baseDir: string, patterns: string[]): Promise<string[]> {\n  return expandDirectories(baseDir, await unglob(baseDir, patterns));\n}\n\n/**\n * Expands any glob expressions in `patterns`.\n */\nasync function unglob(baseDir: string, patterns: string[]): Promise<string[]> {\n  const strs: string[][] = [];\n  const pGlob: any = promisify(glob);\n  for (const pattern of patterns) {\n    strs.push(await pGlob(String(pattern), {cwd: baseDir, root: baseDir}));\n  }\n\n  // for non-POSIX support, replacing path separators\n  return _.union(_.flatten(strs)).map((str) => str.replace(/\\//g, path.sep));\n}\n\n/**\n * Expands any directories in `patterns`, following logic similar to a web\n * server.\n *\n * If a pattern resolves to a directory, that directory is expanded. If the\n * directory contains an `index.html`, it is expanded to that. Otheriwse, the\n * it expands into its children (recursively).\n */\nasync function expandDirectories(baseDir: string, paths: string[]) {\n  const listsOfPaths: string[][] = [];\n  for (const aPath of paths) {\n    listsOfPaths.push(await expandDirectory(baseDir, aPath));\n  }\n\n  const files = _.union(_.flatten(listsOfPaths));\n  return files.filter((file) => /\\.(js|html)$/.test(file));\n}\n\nasync function expandDirectory(\n    baseDir: string, aPath: string): Promise<string[]> {\n  const stat = await promisify(fs.stat)(path.resolve(baseDir, aPath));\n  if (!stat.isDirectory()) {\n    return [aPath];\n  }\n  const files = await promisify(fs.readdir)(path.resolve(baseDir, aPath));\n  // We have an index; defer to that.\n  if (_.includes(files, 'index.html')) {\n    return [path.join(aPath, 'index.html')];\n  }\n  const children = await expandDirectories(path.join(baseDir, aPath), files);\n  return children.map((child) => path.join(aPath, child));\n}\n"
  },
  {
    "path": "runner/plugin.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\n\nimport * as _ from 'lodash';\nimport * as path from 'path';\n\nimport {Config} from './config';\nimport {Context} from './context';\n\n// Plugin module names can be prefixed by the following:\nconst PREFIXES = [\n  'web-component-tester-',\n  'wct-',\n];\n\nexport interface Metadata {}\n\n/**\n * A WCT plugin. This constructor is private. Plugins can be retrieved via\n * `Plugin.get`.\n */\nexport class Plugin {\n  name: string;\n  cliConfig: Config;\n  packageName: string;\n  metadata: Metadata;\n  constructor(packageName: string, metadata: Metadata) {\n    this.packageName = packageName;\n    this.metadata = metadata;\n    this.name = Plugin.shortName(packageName);\n\n    this.cliConfig = this.metadata['cli-options'] || {};\n  }\n\n  /**\n   * @param {!Context} context The context that this plugin should be evaluated\n   *     within.\n   */\n  async execute(context: Context): Promise<void> {\n    try {\n      const plugin = require(this.packageName);\n      plugin(context, context.pluginOptions(this.name), this);\n    } catch (error) {\n      throw `Failed to load plugin \"${this.name}\": ${error}`;\n    }\n  }\n\n  /**\n   * Retrieves a plugin by shorthand or module name (loading it as necessary).\n   *\n   * @param {string} name\n   */\n  static async get(name: string): Promise<Plugin> {\n    const shortName = Plugin.shortName(name);\n    if (_loadedPlugins[shortName]) {\n      return _loadedPlugins[shortName];\n    }\n\n    const names = [shortName].concat(PREFIXES.map((p) => p + shortName));\n    const loaded = _.compact(names.map(_tryLoadPluginPackage));\n    if (loaded.length > 1) {\n      const prettyNames = loaded.map((p) => p.packageName).join(' ');\n      throw `Loaded conflicting WCT plugin packages: ${prettyNames}`;\n    }\n    if (loaded.length < 1) {\n      throw `Could not find WCT plugin named \"${name}\"`;\n    }\n\n    return loaded[0];\n  }\n\n  /**\n   * @param {string} name\n   * @return {string} The short form of `name`.\n   */\n  static shortName(name: string) {\n    for (const prefix of PREFIXES) {\n      if (name.indexOf(prefix) === 0) {\n        return name.substr(prefix.length);\n      }\n    }\n    return name;\n  }\n\n  // HACK(rictic): Makes es6 style imports happy, so that we can do, e.g.\n  //     import {Plugin} from './plugin';\n  static Plugin = Plugin;\n}\n\n// Plugin Loading\n\n// We maintain an identity map of plugins, keyed by short name.\nconst _loadedPlugins: {[name: string]: Plugin} = {};\n\n/**\n * @param {string} packageName Attempts to load a package as a WCT plugin.\n * @return {Plugin}\n */\nfunction _tryLoadPluginPackage(packageName: string) {\n  let packageInfo: Object;\n  try {\n    packageInfo = require(path.join(packageName, 'package.json'));\n  } catch (error) {\n    if (error.code !== 'MODULE_NOT_FOUND') {\n      console.log(error);\n    }\n    return null;\n  }\n\n  // Plugins must have a (truthy) wct-plugin field.\n  if (!packageInfo['wct-plugin']) {\n    return null;\n  }\n  // Allow {\"wct-plugin\": true} as a shorthand.\n  const metadata =\n      _.isObject(packageInfo['wct-plugin']) ? packageInfo['wct-plugin'] : {};\n\n  return new Plugin(packageName, metadata);\n}\n\n\nmodule.exports = Plugin;\n"
  },
  {
    "path": "runner/port-scanner.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\n\nimport * as net from 'net';\n\nfunction checkPort(port: number): Promise<boolean> {\n  return new Promise<boolean>(function(resolve) {\n    const server = net.createServer();\n    let hasPort = false;\n\n    // if server is listening, we have the port!\n    server.on('listening', function(_err: any) {\n      hasPort = true;\n      server.close();\n    });\n\n    // callback on server close to free up the port before report it can be used\n    server.on('close', function(_err: any) {\n      resolve(hasPort);\n    });\n\n    // our port is busy, ignore it\n    server.on('error', function(_err: any) {\n      // docs say the server should close, this doesn't seem to be the case :(\n      server.close();\n    });\n\n    server.listen(port);\n  });\n}\n\ninterface PromiseGetter<T> {\n  (val: T): Promise<boolean>;\n}\n\nasync function detectSeries<T>(\n    values: T[], promiseGetter: PromiseGetter<T>): Promise<T> {\n  for (const value of values) {\n    if (await promiseGetter(value)) {\n      return value;\n    }\n  }\n  throw new Error('Couldn\\'t find a good value in detectSeries');\n}\n\nexport async function findPort(ports: number[]): Promise<number> {\n  try {\n    return await detectSeries(ports, checkPort);\n  } catch (error) {\n    throw new Error('no port found!');\n  }\n}\n"
  },
  {
    "path": "runner/steps.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\nimport * as http from 'http';\nimport * as _ from 'lodash';\nimport * as socketIO from 'socket.io';\n\nimport {BrowserRunner} from './browserrunner';\nimport * as config from './config';\nimport {Context} from './context';\nimport {Plugin} from './plugin';\nimport {webserver} from './webserver';\n\ninterface ClientMessage<T> {\n  browserId: number;\n  event: string;\n  data: T;\n}\n\n// Steps (& Hooks)\n\nexport async function setupOverrides(context: Context): Promise<void> {\n  if (context.options.registerHooks) {\n    context.options.registerHooks(context);\n  }\n}\n\nexport async function loadPlugins(context: Context): Promise<Plugin[]> {\n  context.emit('log:debug', 'step: loadPlugins');\n\n  const plugins = await context.plugins();\n\n  // built in quasi-plugin.\n  webserver(context);\n\n  // Actual plugins.\n  await Promise.all(plugins.map((plugin) => plugin.execute(context)));\n  return plugins;\n}\n\nexport async function configure(context: Context): Promise<void> {\n  context.emit('log:debug', 'step: configure');\n  const options = context.options;\n\n  await config.expand(context);\n\n  // Note that we trigger the configure hook _after_ filling in the `options`\n  // object.\n  //\n  // If you want to modify options prior to this; do it during plugin init.\n  await context.emitHook('configure');\n\n  // Even if the options don't validate; useful debugging info.\n  const cleanOptions = _.omit(options, 'output');\n  context.emit('log:debug', 'configuration:', cleanOptions);\n\n  await config.validate(options);\n}\n\n/**\n * The prepare step is where a lot of the runner's initialization occurs. This\n * is also typically where a plugin will want to spin up any long-running\n * process it requires.\n *\n * Note that some \"plugins\" are also built directly into WCT (webserver).\n */\nexport async function prepare(context: Context): Promise<void> {\n  await context.emitHook('prepare');\n}\n\nexport async function runTests(context: Context): Promise<void> {\n  context.emit('log:debug', 'step: runTests');\n\n  const result = runBrowsers(context);\n  const runners = result.runners;\n  context._testRunners = runners;\n\n  context._socketIOServers = context._httpServers.map((httpServer) => {\n    const socketIOServer = socketIO(httpServer);\n    socketIOServer.on('connection', function(socket) {\n      context.emit('log:debug', 'Test client opened sideband socket');\n      socket.on('client-event', function(data: ClientMessage<any>) {\n        const runner = runners[data.browserId];\n        if (!runner) {\n          throw new Error(\n              `Unable to find browser runner for ` +\n              `browser with id: ${data.browserId}`);\n        }\n        runner.onEvent(data.event, data.data);\n      });\n    });\n    return socketIOServer;\n  });\n\n  await result.completionPromise;\n}\n\nexport function cancelTests(context: Context): void {\n  if (!context._testRunners) {\n    return;\n  }\n  context._testRunners.forEach(function(tr) {\n    tr.quit();\n  });\n}\n\n// Helpers\n\nfunction runBrowsers(context: Context) {\n  const options = context.options;\n  const numActiveBrowsers = options.activeBrowsers.length;\n  if (numActiveBrowsers === 0) {\n    throw new Error('No browsers configured to run');\n  }\n\n  // TODO(nevir): validate browser definitions.\n\n  // Up the socket limit so that we can maintain more active requests.\n  // TODO(nevir): We should be queueing the browsers above some limit too.\n  http.globalAgent.maxSockets =\n      Math.max(http.globalAgent.maxSockets, numActiveBrowsers * 2);\n\n  context.emit('run-start', options);\n\n  const errors: any[] = [];\n\n  const promises: Promise<void>[] = [];\n\n  const runners: BrowserRunner[] = [];\n  let id = 0;\n  for (const originalBrowserDef of options.activeBrowsers) {\n    let waitFor: undefined|Promise<void> = undefined;\n    for (const server of options.webserver._servers) {\n      // Needed by both `BrowserRunner` and `CliReporter`.\n      const browserDef = _.clone(originalBrowserDef);\n      browserDef.id = id++;\n      browserDef.variant = server.variant;\n      _.defaultsDeep(browserDef, options.browserOptions);\n\n      const runner =\n          new BrowserRunner(context, browserDef, options, server.url, waitFor);\n      promises.push(runner.donePromise.then(\n          () => {\n            context.emit('log:debug', browserDef, 'BrowserRunner complete');\n          },\n          (error) => {\n            context.emit('log:debug', browserDef, 'BrowserRunner complete');\n            errors.push(error);\n          }));\n      runners.push(runner);\n      if (browserDef.browserName === 'safari') {\n        // Control to Safari must be serialized. We can't launch two instances\n        // simultaneously, because security lol.\n        // https://webkit.org/blog/6900/webdriver-support-in-safari-10/\n        waitFor = runner.donePromise.catch(() => {\n          // The next runner doesn't care about errors, just wants to know when\n          // it can start.\n          return undefined;\n        });\n      }\n    }\n  }\n\n  return {\n    runners,\n    completionPromise: (async function() {\n      await Promise.all(promises);\n      const error = errors.length > 0 ? _.union(errors).join(', ') : null;\n      context.emit('run-end', error);\n      // TODO(nevir): Better rationalize run-end and hook.\n      await context.emitHook('cleanup');\n\n      if (error) {\n        throw new Error(error);\n      }\n    }())\n  };\n}\n"
  },
  {
    "path": "runner/test.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\nimport * as cleankill from 'cleankill';\n\nimport {CliReporter} from './clireporter';\nimport {Config} from './config';\nimport {Context} from './context';\nimport * as steps from './steps';\n\n/**\n * Runs a suite of web component tests.\n *\n * The returned Context (a kind of EventEmitter) fires various events to allow\n * you to track the progress of the tests:\n *\n * Lifecycle Events:\n *\n * `run-start`\n *   WCT is ready to begin spinning up browsers.\n *\n * `browser-init` {browser} {stats}\n *   WCT is ready to begin spinning up browsers.\n *\n * `browser-start` {browser} {metadata} {stats}\n *   The browser has begun running tests. May fire multiple times (i.e. when\n *   manually refreshing the tests).\n *\n * `sub-suite-start` {browser} {sharedState} {stats}\n *   A suite file has begun running.\n *\n * `test-start` {browser} {test} {stats}\n *   A test has begun.\n *\n * `test-end` {browser} {test} {stats}\n *  A test has ended.\n *\n * `sub-suite-end` {browser} {sharedState} {stats}\n *   A suite file has finished running all of its tests.\n *\n * `browser-end` {browser} {error} {stats}\n *   The browser has completed, and it shutting down.\n *\n * `run-end` {error}\n *   WCT has run all browsers, and is shutting down.\n *\n * Generic Events:\n *\n *  * log:debug\n *  * log:info\n *  * log:warn\n *  * log:error\n *\n * @param {!Config|!Context} options The configuration or an already formed\n *     `Context` object.\n */\nexport async function test(options: Config|Context): Promise<void> {\n  const context = (options instanceof Context) ? options : new Context(options);\n\n  // We assume that any options related to logging are passed in via the initial\n  // `options`.\n  if (context.options.output) {\n    new CliReporter(context, context.options.output, context.options);\n  }\n\n  try {\n    await steps.setupOverrides(context);\n    await steps.loadPlugins(context);\n    await steps.configure(context);\n    await steps.prepare(context);\n    await steps.runTests(context);\n  } finally {\n    if (!context.options.skipCleanup) {\n      await cleankill.close();\n    }\n  }\n}\n\n// HACK\ntest['test'] = test;\n\nmodule.exports = test;\n"
  },
  {
    "path": "runner/webserver.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\n\nimport * as bowerConfig from 'bower-config';\nimport * as cleankill from 'cleankill';\nimport * as express from 'express';\nimport * as fs from 'fs';\nimport * as _ from 'lodash';\nimport * as path from 'path';\nimport {MainlineServer, PolyserveServer, RequestHandler, ServerOptions, startServers, VariantServer} from 'polyserve';\nimport * as semver from 'semver';\nimport * as send from 'send';\nimport * as serverDestroy from 'server-destroy';\n\nimport {getPackageName, NPMPackage, resolveWctNpmEntrypointNames} from './config';\nimport {Context} from './context';\n\n// Template for generated indexes.\nconst INDEX_TEMPLATE = _.template(fs.readFileSync(\n    path.resolve(__dirname, '../data/index.html'), {encoding: 'utf-8'}));\n\nconst DEFAULT_HEADERS = {\n  'Cache-Control': 'no-cache, no-store, must-revalidate',\n  'Pragma': 'no-cache',\n  'Expires': '0',\n};\n\n// scripts to be injected into the running test\nconst ENVIRONMENT_SCRIPTS: NPMPackage[] = [\n  {name: 'stacky', jsEntrypoint: 'browser.js'},\n  {name: 'async', jsEntrypoint: 'lib/async.js'},\n  {name: 'lodash', jsEntrypoint: 'index.js'},\n  {name: 'mocha', jsEntrypoint: 'mocha.js'},\n  {name: 'chai', jsEntrypoint: 'chai.js'},\n  {name: '@polymer/sinonjs', jsEntrypoint: 'sinon.js'},\n  {name: 'sinon-chai', jsEntrypoint: 'lib/sinon-chai.js'},\n  {\n    name: 'accessibility-developer-tools',\n    jsEntrypoint: 'dist/js/axs_testing.js'\n  },\n  {name: '@polymer/test-fixture', jsEntrypoint: 'test-fixture.js'},\n];\n\n/**\n * The webserver module is a quasi-plugin. This ensures that it is hooked in a\n * sane way (for other plugins), and just follows the same flow.\n *\n * It provides a static HTTP server for serving the desired tests and WCT's\n * `browser.js`/`environment.js`.\n */\nexport function webserver(wct: Context): void {\n  const options = wct.options;\n\n  wct.hook('configure', async function() {\n    // For now, you should treat all these options as an implementation detail\n    // of WCT. They may be opened up for public configuration, but we need to\n    // spend some time rationalizing interactions with external webservers.\n    options.webserver = _.merge(options.webserver, {});\n\n    if (options.verbose) {\n      options.clientOptions.verbose = true;\n    }\n\n    // Hacky workaround for Firefox + Windows issue where FF screws up pathing.\n    // Bug: https://github.com/Polymer/web-component-tester/issues/194\n    options.suites = options.suites.map((cv) => cv.replace(/\\\\/g, '/'));\n\n    // The generated index needs the correct \"browser.js\" script. When using\n    // npm, the wct-browser-legacy package may be used, so we test for that\n    // package and will use its \"browser.js\" if present.\n    let browserScript = 'web-component-tester/browser.js';\n    if (options.npm) {\n      try {\n        const wctBrowserLegacyPath =\n            path.join(options.root, 'node_modules', 'wct-browser-legacy');\n        const version =\n            require(path.join(wctBrowserLegacyPath, 'package.json')).version;\n        if (version) {\n          browserScript = 'wct-browser-legacy/browser.js';\n        }\n      } catch (e) {\n        // Safely ignore.\n      }\n      const packageName = getPackageName(options);\n      const isPackageScoped = packageName && packageName[0] === '@';\n\n      // concat options.clientOptions.environmentScripts with resolved\n      // ENVIRONMENT_SCRIPTS\n      options.clientOptions = options.clientOptions || {};\n      options.clientOptions.environmentScripts =\n          options.clientOptions.environmentScripts || [];\n      options.clientOptions.environmentScripts =\n          options.clientOptions.environmentScripts.concat(\n              resolveWctNpmEntrypointNames(options, ENVIRONMENT_SCRIPTS));\n\n      if (isPackageScoped) {\n        browserScript = `../${browserScript}`;\n      }\n    }\n    const a11ySuiteScript = 'web-component-tester/data/a11ySuite.js';\n    options.webserver._generatedIndexContent = INDEX_TEMPLATE(\n        Object.assign({browserScript, a11ySuiteScript}, options));\n  });\n\n  wct.hook('prepare', async function() {\n    const wsOptions = options.webserver;\n    const additionalRoutes = new Map<string, RequestHandler>();\n\n    const packageName = getPackageName(options);\n    let componentDir;\n\n    // Check for client-side compatibility.\n\n    // Non-npm case.\n    if (!options.npm) {\n      componentDir = bowerConfig.read(options.root).directory;\n      const pathToLocalWct =\n          path.join(options.root, componentDir, 'web-component-tester');\n      let version: string|undefined = undefined;\n      const mdFilenames = ['package.json', 'bower.json', '.bower.json'];\n      for (const mdFilename of mdFilenames) {\n        const pathToMetadata = path.join(pathToLocalWct, mdFilename);\n        try {\n          if (!version) {\n            version = require(pathToMetadata).version;\n          }\n        } catch (e) {\n          // Handled below, where we check if we found a version.\n        }\n      }\n      if (!version) {\n        throw new Error(`\nThe web-component-tester Bower package is not installed as a dependency of this project (${\n            packageName}).\n\nPlease run this command to install:\n    bower install --save-dev web-component-tester\n\nWeb Component Tester >=6.0 requires that support files needed in the browser are installed as part of the project's dependencies or dev-dependencies. This is to give projects greater control over the versions that are served, while also making Web Component Tester's behavior easier to understand.\n\nExpected to find a ${mdFilenames.join(' or ')} at: ${pathToLocalWct}/\n`);\n      }\n\n      const allowedRange =\n          require(path.join(\n              __dirname, '..', 'package.json'))['--private-wct--']\n                                               ['client-side-version-range'] as\n          string;\n      if (!semver.satisfies(version, allowedRange)) {\n        throw new Error(`\n    The web-component-tester Bower package installed is incompatible with the\n    wct node package you're using.\n\n    The test runner expects a version that satisfies ${allowedRange} but the\n    bower package you have installed is ${version}.\n`);\n      }\n\n      let hasWarnedBrowserJs = false;\n      additionalRoutes.set('/browser.js', function(request, response) {\n        if (!hasWarnedBrowserJs) {\n          console.warn(`\n\n          WARNING:\n          Loading WCT's browser.js from /browser.js is deprecated.\n\n          Instead load it from ../web-component-tester/browser.js\n          (or with the absolute url /components/web-component-tester/browser.js)\n        `);\n          hasWarnedBrowserJs = true;\n        }\n        const browserJsPath = path.join(pathToLocalWct, 'browser.js');\n        send(request, browserJsPath).pipe(response);\n      });\n    }\n\n    const pathToGeneratedIndex =\n        `/components/${packageName}/generated-index.html`;\n    additionalRoutes.set(pathToGeneratedIndex, (_request, response) => {\n      response.set(DEFAULT_HEADERS);\n      response.send(options.webserver._generatedIndexContent);\n    });\n\n    const appMapper = async (app: express.Express, options: ServerOptions) => {\n      // Using the define:webserver hook to provide a mapper function that\n      // allows user to substitute their own app for the generated polyserve\n      // app.\n      await wct.emitHook(\n          'define:webserver', app, (substitution: express.Express) => {\n            app = substitution;\n          }, options);\n      return app;\n    };\n\n    // Serve up project & dependencies via polyserve\n    const polyserveResult = await startServers(\n        {\n          root: options.root,\n          componentDir,\n          compile: options.compile,\n          hostname: options.webserver.hostname,\n          headers: DEFAULT_HEADERS,\n          packageName,\n          additionalRoutes,\n          npm: !!options.npm,\n          moduleResolution: options.moduleResolution,\n          proxy: options.proxy,\n        },\n        appMapper);\n\n    let servers: Array<MainlineServer|VariantServer>;\n\n    const onDestroyHandlers: Array<() => Promise<void>> = [];\n    const registerServerTeardown = (serverInfo: PolyserveServer) => {\n      const destroyableServer = serverInfo.server as any;\n      serverDestroy(destroyableServer);\n      onDestroyHandlers.push(() => {\n        destroyableServer.destroy();\n        return new Promise<void>(\n            (resolve) => serverInfo.server.on('close', () => resolve()));\n      });\n    };\n\n    if (polyserveResult.kind === 'mainline') {\n      servers = [polyserveResult];\n      registerServerTeardown(polyserveResult);\n      wsOptions.port = polyserveResult.server.address().port;\n    } else if (polyserveResult.kind === 'MultipleServers') {\n      servers = [polyserveResult.mainline];\n      servers = servers.concat(polyserveResult.variants);\n      wsOptions.port = polyserveResult.mainline.server.address().port;\n      for (const server of polyserveResult.servers) {\n        registerServerTeardown(server);\n      }\n    } else {\n      const never: never = polyserveResult;\n      throw new Error(\n          `Internal error: Got unknown response from polyserve.startServers:` +\n          `${never}`);\n    }\n\n    wct._httpServers = servers.map((s) => s.server);\n\n    // At this point, we allow other plugins to hook and configure the\n    // webservers as they please.\n    for (const server of servers) {\n      await wct.emitHook('prepare:webserver', server.app);\n    }\n\n    options.webserver._servers = servers.map((s) => {\n      const port = s.server.address().port;\n      const hostname = s.options.hostname;\n      const url = `http://${hostname}:${port}${pathToGeneratedIndex}`;\n      return {url, variant: s.kind === 'mainline' ? '' : s.variantName};\n    });\n\n    // TODO(rictic): re-enable this stuff. need to either move this code into\n    //     polyserve or let the polyserve API expose this stuff.\n    // app.use('/httpbin', httpbin.httpbin);\n\n    // app.get('/favicon.ico', function(request, response) {\n    //   response.end();\n    // });\n\n    // app.use(function(request, response, next) {\n    //   wct.emit('log:warn', '404', chalk.magenta(request.method),\n    //   request.url);\n    //   next();\n    // });\n\n    async function interruptHandler() {\n      // close the socket IO server directly if it is spun up\n      for (const io of (wct._socketIOServers || [])) {\n        // we will close the underlying server ourselves\n        (<any>io).httpServer = null;\n        io.close();\n      }\n      await Promise.all(onDestroyHandlers.map((f) => f()));\n    }\n    cleankill.onInterrupt(() => {\n      return new Promise((resolve) => {\n        interruptHandler().then(() => resolve(), resolve);\n      });\n    });\n  });\n}\n\nfunction exists(path: string): boolean {\n  try {\n    fs.statSync(path);\n    return true;\n  } catch (_err) {\n    return false;\n  }\n}\n\n// HACK(rictic): remove this ES6-compat hack and export webserver itself\nwebserver['webserver'] = webserver;\n\nmodule.exports = webserver;\n"
  },
  {
    "path": "runner.js",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n */\nmodule.exports = {\n  cli:    require('./runner/cli'),\n  config: require('./runner/config'),\n  gulp:   require('./runner/gulp'),\n  steps:  require('./runner/steps'),\n  test:   require('./runner/test'),\n};\n"
  },
  {
    "path": "tasks/test.js",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n */\nvar chalk  = require('chalk');\n\nvar test = require('../runner/test');\n\nmodule.exports = function(grunt) {\n  grunt.registerMultiTask('wct-test', 'Runs tests via web-component-tester', function() {\n    var done = this.async();\n    test(this.options()).then(() => done(), (error) => {\n      console.log(chalk.red(error));\n      // Grunt only errors on `false` and instances of `Error`.\n      done(new Error(error));\n    });\n  });\n};\n"
  },
  {
    "path": "test/fixtures/cli/conf/branch/leaf/thing.js",
    "content": ""
  },
  {
    "path": "test/fixtures/cli/conf/json/wct.conf.js",
    "content": "var path = require('path');\n\nmodule.exports = {\n  root: path.resolve(__dirname, '..'),\n  plugins: {\n    sauce: {\n      username: 'jsconf',\n    },\n  },\n};\n"
  },
  {
    "path": "test/fixtures/cli/conf/json/wct.conf.json",
    "content": "{\n  \"root\": \"..\",\n  \"plugins\": {\n    \"sauce\": {\n      \"username\": \"jsonconf\"\n    }\n  }\n}\n"
  },
  {
    "path": "test/fixtures/cli/conf/rooted/wct.conf.js",
    "content": "var path = require('path');\n\nmodule.exports = {\n  root: path.resolve(__dirname, '../../..'),\n  suites: ['cli/conf/test'],\n};\n"
  },
  {
    "path": "test/fixtures/cli/conf/test/foo.js",
    "content": ""
  },
  {
    "path": "test/fixtures/cli/conf/wct.conf.js",
    "content": "module.exports = {\n  plugins: {\n    sauce: {\n      username: 'abc123',\n    },\n  },\n};\n"
  },
  {
    "path": "test/fixtures/cli/standard/test/a.html",
    "content": ""
  },
  {
    "path": "test/fixtures/cli/standard/test/b.js",
    "content": ""
  },
  {
    "path": "test/fixtures/cli/standard/x-foo.html",
    "content": ""
  },
  {
    "path": "test/fixtures/early-failure/bower_components/web-component-tester/package.json",
    "content": "{\n  \"version\": \"0.0.1\"\n}\n"
  },
  {
    "path": "test/fixtures/early-failure/test/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></script>\n</head>\n\n<body>\n  <script>\n    test('inline passing test', function () {\n      assert.equal('yes', 'yes');\n    });\n  </script>\n</body>\n\n</html>"
  },
  {
    "path": "test/fixtures/fake-packages/duplicated-dep/package.json",
    "content": "{\n  \"name\": \"duplicated-dep\",\n  \"version\": \"1.0.0\"\n}"
  },
  {
    "path": "test/fixtures/fake-packages/singleton-dep/package.json",
    "content": "{\n  \"name\": \"singleton-dep\",\n  \"version\": \"1.0.0\"\n}"
  },
  {
    "path": "test/fixtures/integration/compilation/golden.json",
    "content": "{\n  \"passing\": 1,\n  \"pending\": 0,\n  \"failing\": 0,\n  \"status\": \"complete\",\n  \"tests\": {\n    \"test/\": {\n      \"ES6 works\": {\n        \"state\": \"passing\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "test/fixtures/integration/compilation/test/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></script>\n</head>\n\n<body>\n  <script>\n    test('ES6 works', function () {\n      // This doesn't work in Firefox 50, but it soon will...\n      // soon we'll need to test it with IE 11.\n      const funcs = [];\n      for (const foo of [1, 2, 3]) {\n        funcs.push(() => foo);\n      }\n\n      assert.deepEqual(funcs.map(f => f()), [1, 2, 3]);\n    });\n  </script>\n</body>\n\n</html>"
  },
  {
    "path": "test/fixtures/integration/components_dir/bower_components/foo-element/foo-element.js",
    "content": "window.fooElementLoaded = 'yes';"
  },
  {
    "path": "test/fixtures/integration/components_dir/golden.json",
    "content": "{\n  \"passing\": 1,\n  \"pending\": 0,\n  \"failing\": 0,\n  \"status\": \"complete\",\n  \"tests\": {\n    \"test/\": {\n      \"inline passing test\": {\"state\": \"passing\"}\n    }\n  }\n}"
  },
  {
    "path": "test/fixtures/integration/components_dir/test/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></script>\n</head>\n\n<body>\n  <script src=\"../../foo-element/foo-element.js\"></script>\n  <script>\n    test('inline passing test', function () {\n      assert.equal(window.fooElementLoaded, 'yes');\n    });\n  </script>\n</body>\n\n</html>"
  },
  {
    "path": "test/fixtures/integration/components_dir/wct.conf.json",
    "content": "{\n  \"plugins\": {\n    \"local\": {\n      \"browsers\": [\n        \"chrome\"\n      ],\n      \"skipSeleniumInstall\": true\n    }\n  }\n}\n"
  },
  {
    "path": "test/fixtures/integration/custom-components_dir/.bowerrc",
    "content": "{\n  \"directory\": \"my_components/\"\n}"
  },
  {
    "path": "test/fixtures/integration/custom-components_dir/golden.json",
    "content": "{\n  \"passing\": 1,\n  \"pending\": 0,\n  \"failing\": 0,\n  \"status\": \"complete\",\n  \"tests\": {\n    \"test/\": {\n      \"inline passing test\": {\"state\": \"passing\"}\n    }\n  }\n}"
  },
  {
    "path": "test/fixtures/integration/custom-components_dir/my_components/bar-element/bar-element.js",
    "content": "window.barElementLoaded = 'yes';"
  },
  {
    "path": "test/fixtures/integration/custom-components_dir/test/index.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <script src=\"/components/web-component-tester/browser.js\"></script>\n  </head>\n  <body>\n    <script src=\"../../bar-element/bar-element.js\"></script>\n    <script>\n      test('inline passing test', function() {\n        assert.equal(window.barElementLoaded, 'yes');\n      });\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/fixtures/integration/custom-multiple-component_dirs/.bowerrc",
    "content": "{\n  \"directory\": \"my_components\"\n}"
  },
  {
    "path": "test/fixtures/integration/custom-multiple-component_dirs/golden.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"passing\": 1,\n      \"pending\": 0,\n      \"failing\": 0,\n      \"status\": \"complete\",\n      \"tests\": {\n        \"test/\": {\n          \"only works with mainline components\": {\"state\": \"passing\"}\n        }\n      }\n    },\n    \"foo\": {\n      \"passing\": 0,\n      \"pending\": 0,\n      \"failing\": 1,\n      \"status\": \"complete\",\n      \"tests\": {\n        \"test/\": {\n          \"only works with mainline components\": {\"state\": \"failing\"}\n        }\n      },\n      \"errors\": {\n        \"test/\": {\n          \"only works with mainline components\": [\n              \"expected 'foo' to equal 'mainline'\",\n              \"at index\\\\.html:11\"\n          ]\n        }\n      }\n    },\n    \"bar\": {\n      \"passing\": 0,\n      \"pending\": 0,\n      \"failing\": 1,\n      \"status\": \"complete\",\n      \"tests\": {\n        \"test/\": {\n          \"only works with mainline components\": {\"state\": \"failing\"}\n        }\n      },\n      \"errors\": {\n        \"test/\": {\n          \"only works with mainline components\": [\n              \"expected 'bar' to equal 'mainline'\",\n              \"at index\\\\.html:11\"\n          ]\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "test/fixtures/integration/custom-multiple-component_dirs/my_components/package/index.js",
    "content": "window.nameOfThing = 'mainline';"
  },
  {
    "path": "test/fixtures/integration/custom-multiple-component_dirs/my_components-bar/package/index.js",
    "content": "window.nameOfThing = 'bar';"
  },
  {
    "path": "test/fixtures/integration/custom-multiple-component_dirs/my_components-foo/package/index.js",
    "content": "window.nameOfThing = 'foo';"
  },
  {
    "path": "test/fixtures/integration/custom-multiple-component_dirs/test/index.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <script src=\"/components/web-component-tester/browser.js\"></script>\n  </head>\n  <body>\n    <script src=\"../../package/index.js\"></script>\n    <script>\n      test('only works with mainline components', function() {\n        expect(window.nameOfThing).to.be.equals('mainline');\n      });\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/fixtures/integration/define-webserver-hook/golden.json",
    "content": "{\n  \"passing\": 2,\n  \"pending\": 0,\n  \"failing\": 0,\n  \"status\": \"complete\",\n  \"tests\": {\n    \"test/tests.html\": {\n      \"suite\": {\n        \"nested test\": {\n          \"state\": \"passing\"\n        }\n      },\n      \"test\": {\n        \"state\": \"passing\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "test/fixtures/integration/define-webserver-hook/test/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></script>\n</head>\n\n<body>\n  <script>\n    WCT.loadSuites(['tests.html']);\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "test/fixtures/integration/define-webserver-hook/test/tests.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></script>\n</head>\n\n<body>\n  <script>\n    suite('suite', function () {\n      test('nested test', function () { });\n    });\n    test('test', function () { });\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "test/fixtures/integration/failing/golden.json",
    "content": "{\n  \"passing\": 3,\n  \"pending\": 0,\n  \"failing\": 3,\n  \"status\": \"complete\",\n  \"tests\": {\n    \"test/\": {\n      \"failing test\": {\n        \"state\": \"failing\"\n      },\n      \"inline failing test\": {\n        \"state\": \"failing\"\n      },\n      \"inline passing test\": {\n        \"state\": \"passing\"\n      },\n      \"passing test\": {\n        \"state\": \"passing\"\n      }\n    },\n    \"test/tests.html\": {\n      \"failing test\": {\n        \"state\": \"failing\"\n      },\n      \"passing test\": {\n        \"state\": \"passing\"\n      }\n    }\n  },\n  \"errors\": {\n    \"test/\": {\n      \"inline failing test\": [\n        \"expected false to be true\",\n        \"at index\\\\.html:(12|15)(:|$)\"\n      ],\n      \"failing test\": [\n        \"expected false to be true\",\n        \"at tests\\\\.js:3(:|$)\"\n      ]\n    },\n    \"test/tests.html\": {\n      \"failing test\": [\n        \"expected false to be true\",\n        \"at tests\\\\.html:(10|13)(:|$)\"\n      ]\n    }\n  }\n}"
  },
  {
    "path": "test/fixtures/integration/failing/test/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></script>\n</head>\n\n<body>\n  <script>\n    WCT.loadSuites(['tests.html', 'tests.js']);\n\n    test('inline passing test', function () { });\n    test('inline failing test', function () {\n      assert.isTrue(false);\n    });\n  </script>\n</body>\n\n</html>"
  },
  {
    "path": "test/fixtures/integration/failing/test/tests.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></script>\n</head>\n\n<body>\n  <script>\n    test('passing test', function () { });\n    test('failing test', function () {\n      assert.isTrue(false);\n    });\n  </script>\n</body>\n\n</html>"
  },
  {
    "path": "test/fixtures/integration/failing/test/tests.js",
    "content": "test('passing test', function() {});\ntest('failing test', function() {\n  assert.isTrue(false);\n});\n"
  },
  {
    "path": "test/fixtures/integration/missing/golden.json",
    "content": "{\n  \"passing\": 0,\n  \"pending\": 0,\n  \"failing\": 0,\n  \"status\": \"error\"\n}"
  },
  {
    "path": "test/fixtures/integration/missing/test/missing.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></script>\n</head>\n\n<body>\n  <script>\n    WCT.loadSuites(['tests.html', 'tests.js']);\n  </script>\n</body>\n\n</html>"
  },
  {
    "path": "test/fixtures/integration/mixed-suites/golden.json",
    "content": "{\n  \"passing\": 10,\n  \"pending\": 0,\n  \"failing\": 0,\n  \"status\": \"complete\",\n  \"tests\": {\n    \"test/\": {\n      \"inline suite\": {\n        \"inline nested test\": {\n          \"state\": \"passing\"\n        }\n      },\n      \"inline test\": {\n        \"state\": \"passing\"\n      },\n      \"suite 1\": {\n        \"nested test 1\": {\n          \"state\": \"passing\"\n        }\n      },\n      \"test 1\": {\n        \"state\": \"passing\"\n      },\n      \"suite 2\": {\n        \"nested test 2\": {\n          \"state\": \"passing\"\n        }\n      },\n      \"test 2\": {\n        \"state\": \"passing\"\n      }\n    },\n    \"test/one.html\": {\n      \"suite 1\": {\n        \"nested test 1\": {\n          \"state\": \"passing\"\n        }\n      },\n      \"test 1\": {\n        \"state\": \"passing\"\n      }\n    },\n    \"test/two.html\": {\n      \"suite 2\": {\n        \"nested test 2\": {\n          \"state\": \"passing\"\n        }\n      },\n      \"test 2\": {\n        \"state\": \"passing\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "test/fixtures/integration/mixed-suites/test/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></script>\n</head>\n\n<body>\n  <script>\n    WCT.loadSuites(['one.html', 'two.html', 'one.js', 'two.js']);\n\n    suite('inline suite', function () {\n      test('inline nested test', function () { });\n    });\n    test('inline test', function () { });\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "test/fixtures/integration/mixed-suites/test/one.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></script>\n</head>\n\n<body>\n  <script>\n    suite('suite 1', function () {\n      test('nested test 1', function () { });\n    });\n    test('test 1', function () { });\n  </script>\n</body>\n\n</html>"
  },
  {
    "path": "test/fixtures/integration/mixed-suites/test/one.js",
    "content": "suite('suite 1', function() {\n  test('nested test 1', function() {});\n});\ntest('test 1', function() {});\n"
  },
  {
    "path": "test/fixtures/integration/mixed-suites/test/two.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></script>\n</head>\n\n<body>\n  <script>\n    suite('suite 2', function () {\n      test('nested test 2', function () { });\n    });\n    test('test 2', function () { });\n  </script>\n</body>\n\n</html>"
  },
  {
    "path": "test/fixtures/integration/mixed-suites/test/two.js",
    "content": "suite('suite 2', function() {\n  test('nested test 2', function() {});\n});\ntest('test 2', function() {});\n"
  },
  {
    "path": "test/fixtures/integration/multiple-component_dirs/bower_components/package/index.js",
    "content": "window.nameOfThing = 'mainline';"
  },
  {
    "path": "test/fixtures/integration/multiple-component_dirs/bower_components-bar/package/index.js",
    "content": "window.nameOfThing = 'bar';"
  },
  {
    "path": "test/fixtures/integration/multiple-component_dirs/bower_components-foo/package/index.js",
    "content": "window.nameOfThing = 'foo';"
  },
  {
    "path": "test/fixtures/integration/multiple-component_dirs/golden.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"passing\": 1,\n      \"pending\": 0,\n      \"failing\": 0,\n      \"status\": \"complete\",\n      \"tests\": {\n        \"test/\": {\n          \"only works with mainline components\": {\n            \"state\": \"passing\"\n          }\n        }\n      }\n    },\n    \"foo\": {\n      \"passing\": 0,\n      \"pending\": 0,\n      \"failing\": 1,\n      \"status\": \"complete\",\n      \"tests\": {\n        \"test/\": {\n          \"only works with mainline components\": {\n            \"state\": \"failing\"\n          }\n        }\n      },\n      \"errors\": {\n        \"test/\": {\n          \"only works with mainline components\": [\n            \"expected 'foo' to equal 'mainline'\",\n            \"at index\\\\.html:(10|13)\"\n          ]\n        }\n      }\n    },\n    \"bar\": {\n      \"passing\": 0,\n      \"pending\": 0,\n      \"failing\": 1,\n      \"status\": \"complete\",\n      \"tests\": {\n        \"test/\": {\n          \"only works with mainline components\": {\n            \"state\": \"failing\"\n          }\n        }\n      },\n      \"errors\": {\n        \"test/\": {\n          \"only works with mainline components\": [\n            \"expected 'bar' to equal 'mainline'\",\n            \"at index\\\\.html:(10|13)\"\n          ]\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "test/fixtures/integration/multiple-component_dirs/test/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></script>\n</head>\n\n<body>\n  <script src=\"../../package/index.js\"></script>\n  <script>\n    test('only works with mainline components', function () {\n      expect(window.nameOfThing).to.be.equals('mainline');\n    });\n  </script>\n</body>\n\n</html>"
  },
  {
    "path": "test/fixtures/integration/multiple-replace/dom-if-element.html",
    "content": "<link rel=\"import\" href=\"../polymer/polymer.html\">\n<link rel=\"import\" href=\"./normal-element.html\">\n<link rel=\"import\" href=\"./exception-element.html\">\n\n<dom-module id=\"dom-if-element\">\n  <template>\n    <normal-element id=\"outside\"></normal-element>\n\n    <template is=\"dom-if\" if=\"{{isStamped}}\">\n      <exception-element id=\"inside\"></exception-element>\n    </template>\n  </template>\n\n  <script>\n    Polymer({\n      is: 'dom-if-element',\n\n      properties: {\n        isStamped: {\n          type: Boolean,\n          value: false,\n          notifies: true\n        }\n      }\n    });\n  </script>\n</dom-module>\n"
  },
  {
    "path": "test/fixtures/integration/multiple-replace/dom-repeat-fixture.html",
    "content": "<link rel=\"import\" href=\"../polymer/polymer.html\">\n<link rel=\"import\" href=\"./exception-element.html\">\n\n<dom-module id=\"binding-element\">\n  <template>\n    <div>\n      <span>{{boundValue}}</span>\n    </div>\n  </template>\n  <script>\n    Polymer({\n      is: 'binding-element',\n      properties: {\n        boundValue: String\n      }\n    });\n  </script>\n</dom-module>\n\n<dom-module id=\"dom-repeat-fixture\">\n  <template>\n    <template is=\"dom-repeat\" items=\"{{list}}\">\n      <binding-element bound-value=\"[[item]]\"></binding-element>\n    </template>\n  </template>\n  <script>\n    Polymer({\n      is: 'dom-repeat-fixture',\n\n      properties: {\n        list: {\n          type: Array\n        }\n      },\n\n      ready: function() {\n        this.list = [1,2,3,4,5];\n      }\n    });\n  </script>\n</dom-module>\n"
  },
  {
    "path": "test/fixtures/integration/multiple-replace/exception-element.html",
    "content": "<link rel=\"import\" href=\"../polymer/polymer.html\">\n<dom-module id=\"exception-element\">\n  <template>\n  </template>\n  <script>\n    Polymer({\n      is: 'exception-element',\n\n      created: function() {\n        throw new Error('exception-element created');\n      }\n    });\n  </script>\n</dom-module>\n"
  },
  {
    "path": "test/fixtures/integration/multiple-replace/exception-fixture.html",
    "content": "<link rel=\"import\" href=\"../polymer/polymer.html\">\n<link rel=\"import\" href=\"./normal-element.html\">\n<link rel=\"import\" href=\"./exception-element.html\">\n\n<dom-module id=\"exception-fixture\">\n  <template>\n    <normal-element id=\"normal\"></normal-element>\n    <exception-element id=\"exception\"></exception-element>\n  </template>\n\n  <script>\n    Polymer({\n      is: 'exception-fixture'\n    });\n  </script>\n</dom-module>\n"
  },
  {
    "path": "test/fixtures/integration/multiple-replace/golden.json",
    "content": "{\n  \"passing\": 10,\n  \"pending\": 0,\n  \"failing\": 0,\n  \"status\": \"complete\",\n  \"tests\": {\n    \"test/tests.html\": {\n      \"testing projected element replacement\": {\n        \"projection replace test\": {\"state\": \"passing\"}\n      },\n      \"testing standard multiple replace\": {\n        \"double replace test\": {\"state\": \"passing\"}\n      },\n      \"testing template bindings\": {\n        \"dom-repeat bindings exist\": {\"state\": \"passing\"}\n      },\n      \"testing template reset after test\": {\n        \"checking template is reset after replace test\": {\"state\": \"passing\"}\n      },\n      \"testing templatized multiple replace\": {\n        \"dom-if test\": {\"state\": \"passing\"}\n      }\n    }\n  }\n}"
  },
  {
    "path": "test/fixtures/integration/multiple-replace/normal-element.html",
    "content": "<link rel=\"import\" href=\"../polymer/polymer.html\">\n\n<dom-module id=\"normal-element\">\n  <template>\n    <div>\n      Hello world!\n    </div>\n    <a id=\"element1\"></a>\n    <b id=\"element2\"></b>\n\n    <d id=\"element3\"></d>\n    <e id=\"element4\"></e>\n  </template>\n\n  <script>\n    Polymer({\n      is: 'normal-element'\n    });\n  </script>\n</dom-module>\n"
  },
  {
    "path": "test/fixtures/integration/multiple-replace/projection-element-2.html",
    "content": "<link rel=\"import\" href=\"../polymer/polymer.html\">\n\n<dom-module id=\"projection-element-2\">\n  <template>\n    <div id=\"content\">two</div>\n  </template>\n  <script>\n    Polymer({\n      is: 'projection-element-2'\n    });\n  </script>\n</dom-module>\n"
  },
  {
    "path": "test/fixtures/integration/multiple-replace/projection-element-3.html",
    "content": "<link rel=\"import\" href=\"../polymer/polymer.html\">\n\n<dom-module id=\"projection-element-3\">\n  <template>\n    <div id=\"content\">three</div>\n  </template>\n  <script>\n    Polymer({\n      is: 'projection-element-3'\n    });\n  </script>\n</dom-module>\n"
  },
  {
    "path": "test/fixtures/integration/multiple-replace/projection-element-4.html",
    "content": "<link rel=\"import\" href=\"../polymer/polymer.html\">\n\n<dom-module id=\"projection-element-4\">\n  <template>\n    <content select=\".selector\"></content>\n  </template>\n  <script>\n    Polymer({\n      is: 'projection-element-4'\n    });\n  </script>\n</dom-module>\n"
  },
  {
    "path": "test/fixtures/integration/multiple-replace/projection-element.html",
    "content": "<link rel=\"import\" href=\"../polymer/polymer.html\">\n<link rel=\"import\" href=\"./projection-element-2.html\">\n<link rel=\"import\" href=\"./projection-element-3.html\">\n<link rel=\"import\" href=\"./projection-element-4.html\">\n\n<dom-module id=\"projection-element\">\n  <template>\n    <projection-element-2 id=\"replaced\"></projection-element-2>\n    <projection-element-4 id=\"projected\">\n      <div id=\"content\" class=\"selector\">four</div>\n    </projection-element-4>\n  </template>\n  <script>\n    Polymer({\n      is: 'projection-element'\n    });\n  </script>\n</dom-module>\n"
  },
  {
    "path": "test/fixtures/integration/multiple-replace/test/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"../../webcomponentsjs/webcomponents-lite.js\"></script>\n  <script src=\"../../web-component-tester/browser.js\"></script>\n</head>\n\n<body>\n  <script>\n    WCT.loadSuites([\n      'tests.html?dom=shadow',\n      'tests.html?wc-ce=true&wc-shadydom=true',\n    ]);\n  </script>\n</body>\n\n</html>"
  },
  {
    "path": "test/fixtures/integration/multiple-replace/test/tests.html",
    "content": "<html>\n\n<head>\n  <link rel=\"import\" href=\"../../test-fixture/test-fixture.html\">\n  <script src=\"../../webcomponentsjs/webcomponents-lite.js\"></script>\n  <link rel=\"import\" href=\"../../polymer/polymer.html\">\n  <script src=\"../../web-component-tester/browser.js\"></script>\n  <link rel=\"import\" href=\"../dom-if-element.html\">\n  <link rel=\"import\" href=\"../exception-fixture.html\">\n  <link rel=\"import\" href=\"../projection-element.html\">\n  <link rel=\"import\" href=\"../dom-repeat-fixture.html\">\n</head>\n\n<body>\n  <test-fixture id=\"DomIfFixture\">\n    <template>\n      <dom-if-element></dom-if-element>\n    </template>\n  </test-fixture>\n\n  <test-fixture id=\"ExceptionFixture\">\n    <template>\n      <exception-fixture></exception-fixture>\n    </template>\n  </test-fixture>\n\n  <test-fixture id=\"ProjectionFixture\">\n    <template>\n      <projection-element>\n      </projection-element>\n    </template>\n  </test-fixture>\n\n  <test-fixture id=\"DomRepeatFixture\">\n    <template>\n      <dom-repeat-fixture>\n      </dom-repeat-fixture>\n    </template>\n  </test-fixture>\n\n  <script>\n    'use strict';\n    window.addEventListener('WebComponentsReady', function () {\n      var Flusher = Polymer.Templatizer.flush ? Polymer.Templatizer : Polymer.dom;\n\n      describe('testing templatized multiple replace', function () {\n        var domIfElement;\n        var insideElement;\n        var outsideElement;\n\n        before(function () {\n          replace('normal-element').with('changed-element-name');\n          replace('exception-element').with('original-template-failure');\n\n          domIfElement = fixture('DomIfFixture');\n          domIfElement.isStamped = true;\n\n          Flusher.flush();\n\n          insideElement = domIfElement.$$('#inside');\n          outsideElement = domIfElement.$.outside;\n          domIfElement.isStamped = false;\n\n          Flusher.flush();\n        });\n\n        it('dom-if test', function () {\n          assert.equal(outsideElement.nodeName, 'CHANGED-ELEMENT-NAME');\n          assert.equal(insideElement.nodeName, 'ORIGINAL-TEMPLATE-FAILURE');\n        });\n      });\n\n      describe('testing standard multiple replace', function () {\n        var setupCorrectly = true;\n        var element1;\n        var element2;\n        var element3;\n        var element4;\n\n        before(function () {\n          try {\n            replace('a').with('b');\n            replace('b').with('c');\n\n            replace('e').with('f');\n            replace('d').with('e');\n\n            replace('exception-element').with('dummy-element');\n          } catch (err) {\n            setupCorrectly = false;\n          }\n\n          var template = fixture('ExceptionFixture');\n          element1 = template.$.normal.$$('#element1');\n          element2 = template.$.normal.$$('#element2');\n          element3 = template.$.normal.$$('#element3');\n          element4 = template.$.normal.$$('#element4');\n        });\n\n        after(function () {\n\n        });\n\n        it('double replace test', function () {\n          assert.isOk(setupCorrectly);\n          assert.equal(element1.nodeName, 'C');\n          assert.equal(element2.nodeName, 'C');\n\n          assert.equal(element3.nodeName, 'F');\n          assert.equal(element4.nodeName, 'F');\n        });\n      });\n\n      describe('testing projected element replacement', function () {\n        var setupCorrectly = true;\n        var pE3Content;\n        var pE4Content;\n\n        before(function () {\n          try {\n            replace('projection-element-2').with('projection-element-3');\n          } catch (err) {\n            setupCorrectly = false;\n          }\n\n          var template = fixture('ProjectionFixture');\n          pE3Content = template.$.replaced.$.content.innerText;\n          pE4Content =\n            Polymer.dom(template.$.projected).querySelector('#content').innerText;\n\n        });\n\n        after(function () {\n\n        });\n\n        it('projection replace test', function () {\n          assert.isOk(setupCorrectly);\n          assert.equal(pE3Content, 'three');\n          assert.equal(pE4Content, 'four');\n        });\n      });\n\n      describe('testing template reset after test', function () {\n        var setupCorrectly = true;\n        var insideElement;\n\n        before(function () {\n          try {\n            replace('exception-element').with('original-template-success');\n          } catch (err) {\n            setupCorrectly = false;\n          }\n\n          var template = fixture('DomIfFixture');\n          template.isStamped = true;\n\n          Flusher.flush();\n\n          insideElement = template.$$('#inside');\n        });\n\n        after(function () {\n\n        });\n\n        it('checking template is reset after replace test', function () {\n          assert.isOk(setupCorrectly);\n\n          assert.equal(insideElement.nodeName, 'ORIGINAL-TEMPLATE-SUCCESS');\n        });\n      });\n\n      describe('testing template bindings', function () {\n        var setupCorrectly = true;\n        var insideElement;\n        var domRepeat;\n        var annotationsBefore;\n        var annotationsAfter;\n\n        before(function () {\n          domRepeat = fixture('DomRepeatFixture');\n\n          Flusher.flush();\n\n          var notes = domRepeat._notes || domRepeat._template.content._notes;\n          annotationsBefore = JSON.stringify(notes);\n\n          var bindingElements = Polymer.dom(domRepeat.root)\n            .querySelectorAll('binding-element');\n\n          for (var i = 0; i < bindingElements.length; i++) {\n            var element = bindingElements[i];\n\n            annotationsBefore += JSON.stringify(element._notes);\n          }\n\n          try {\n            replace('div').with('span');\n          } catch (err) {\n            setupCorrectly = false;\n          }\n\n          domRepeat = fixture('DomRepeatFixture');\n\n          Flusher.flush();\n\n          var notes = domRepeat._notes || domRepeat._template.content._notes;\n          annotationsAfter = JSON.stringify(notes);\n\n          bindingElements = Polymer.dom(domRepeat.root)\n            .querySelectorAll('binding-element');\n\n          for (var i = 0; i < bindingElements.length; i++) {\n            var element = bindingElements[i];\n\n            annotationsAfter += JSON.stringify(element._notes);\n          }\n        });\n\n        after(function () {\n\n        });\n\n        it('dom-repeat bindings exist', function () {\n          assert.isOk(setupCorrectly);\n\n          assert.equal(annotationsBefore, annotationsAfter);\n        });\n      });\n    });\n  </script>\n\n</body>\n\n</html>"
  },
  {
    "path": "test/fixtures/integration/nested/golden.json",
    "content": "{\n  \"passing\": 4,\n  \"pending\": 0,\n  \"failing\": 0,\n  \"status\": \"complete\",\n  \"tests\": {\n    \"test/\": {\"js test\": {\"state\": \"passing\"}},\n    \"test/one/tests.html\": {\"test\": {\"state\": \"passing\"}},\n    \"test/two/\": {\"inline test\": {\"state\": \"passing\"}},\n    \"test/leaf.html\": {\"test\": {\"state\": \"passing\"}}\n  }\n}\n"
  },
  {
    "path": "test/fixtures/integration/nested/test/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></script>\n</head>\n\n<body>\n  <script>\n    WCT.loadSuites(['one/index.html', 'two/index.html', 'leaf.html', 'leaf.js']);\n  </script>\n</body>\n\n</html>"
  },
  {
    "path": "test/fixtures/integration/nested/test/leaf.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></script>\n</head>\n\n<body>\n  <script>\n    test('test', function () { });\n  </script>\n</body>\n\n</html>"
  },
  {
    "path": "test/fixtures/integration/nested/test/leaf.js",
    "content": "test('js test', function() {});\n"
  },
  {
    "path": "test/fixtures/integration/nested/test/one/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></script>\n</head>\n\n<body>\n  <script>\n    WCT.loadSuites(['tests.html']);\n  </script>\n</body>\n\n</html>"
  },
  {
    "path": "test/fixtures/integration/nested/test/one/tests.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></script>\n</head>\n\n<body>\n  <script>\n    test('test', function () { });\n  </script>\n</body>\n\n</html>"
  },
  {
    "path": "test/fixtures/integration/nested/test/two/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></script>\n</head>\n\n<body>\n  <script>\n    test('inline test', function () { });\n  </script>\n</body>\n\n</html>"
  },
  {
    "path": "test/fixtures/integration/no-tests/golden.json",
    "content": "{\n  \"passing\": 0,\n  \"pending\": 0,\n  \"failing\": 0,\n  \"status\": \"complete\"\n}"
  },
  {
    "path": "test/fixtures/integration/no-tests/test/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></script>\n</head>\n\n<body>\n  <script>\n    WCT.loadSuites([]);\n  </script>\n</body>\n\n</html>"
  },
  {
    "path": "test/fixtures/integration/query-string/golden.json",
    "content": "{\n  \"passing\": 3,\n  \"pending\": 0,\n  \"failing\": 0,\n  \"status\": \"complete\",\n  \"tests\": {\n    \"test/tests.html\": {\n      \"preserves query strings\": {\n        \"state\": \"passing\"\n      }\n    },\n    \"test/\": {\n      \"preserves query strings (?fizz=buzz&foo=bar)\": {\n        \"state\": \"passing\"\n      },\n      \"preserves query strings (?fizz=buzz)\": {\n        \"state\": \"passing\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "test/fixtures/integration/query-string/test/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></script>\n</head>\n\n<body>\n  <script>\n    WCT.loadSuites([\n      'tests.html?foo=bar',\n      'tests.js?fizz=buzz',\n      // TODO(usergenic):  Uncomment the following after `new URL()`\n      // and `document.currentScript` polyfills are added for IE support.\n      // 'tests.js?fizz=buzz&foo=bar',\n    ]);\n  </script>\n</body>\n\n</html>"
  },
  {
    "path": "test/fixtures/integration/query-string/test/tests.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></script>\n</head>\n\n<body>\n  <script>\n    test('preserves query strings', function () {\n      expect(window.location.search).to.eq('?foo=bar');\n    });\n  </script>\n</body>\n\n</html>"
  },
  {
    "path": "test/fixtures/integration/query-string/test/tests.js",
    "content": "// TODO(usergenic): Figure out a reasonable solution to get URL() and\n// document.currentScript to work in IE11 and then put these tests back\n// in commission.\n//\n// See https://github.com/PolymerElements/iron-location/blob/3ef6d758514d7cb80a3297f8ef5208774d486e88/iron-location.html#L65\n// as a possible solution.\n/*\nvar url = new URL(document.currentScript.src);\n\ntest('preserves query strings (' + url.search + ')', function () {\n  expect(url.search).to.match(/\\?fizz=buzz/);\n});\n*/\ntest('preserves query strings (?fizz=buzz&foo=bar)', function () { });\ntest('preserves query strings (?fizz=buzz)', function () { });\n"
  },
  {
    "path": "test/fixtures/paths/bar/a.js",
    "content": ""
  },
  {
    "path": "test/fixtures/paths/bar/index.html",
    "content": ""
  },
  {
    "path": "test/fixtures/paths/bar/index.js",
    "content": ""
  },
  {
    "path": "test/fixtures/paths/baz/a/fizz.html",
    "content": ""
  },
  {
    "path": "test/fixtures/paths/baz/a.html",
    "content": ""
  },
  {
    "path": "test/fixtures/paths/baz/b/deep/index.html",
    "content": ""
  },
  {
    "path": "test/fixtures/paths/baz/b/deep/stuff.html",
    "content": ""
  },
  {
    "path": "test/fixtures/paths/baz/b/deep/stuff.js",
    "content": ""
  },
  {
    "path": "test/fixtures/paths/baz/b/index.html",
    "content": ""
  },
  {
    "path": "test/fixtures/paths/baz/b/one.js",
    "content": ""
  },
  {
    "path": "test/fixtures/paths/baz/b.js",
    "content": ""
  },
  {
    "path": "test/fixtures/paths/foo/one.js",
    "content": ""
  },
  {
    "path": "test/fixtures/paths/foo/three.css",
    "content": ""
  },
  {
    "path": "test/fixtures/paths/foo/two.html",
    "content": ""
  },
  {
    "path": "test/fixtures/paths/foo.html",
    "content": ""
  },
  {
    "path": "test/fixtures/paths/foo.js",
    "content": ""
  },
  {
    "path": "test/integration/browser.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\nimport {expect} from 'chai';\nimport * as express from 'express';\nimport * as fs from 'fs';\nimport * as lodash from 'lodash';\nimport * as path from 'path';\n\nimport {ExpressAppMapper, ServerOptions} from 'polyserve/lib/start_server';\nimport {BrowserDef, Stats} from '../../runner/browserrunner';\nimport {CompletedState, TestEndData} from '../../runner/clireporter';\nimport * as config from '../../runner/config';\nimport {Context} from '../../runner/context';\nimport {test} from '../../runner/test';\nimport {makeProperTestDir} from './setup_test_dir';\n\nfunction parseList(stringList?: string): string[] {\n  return (stringList || '')\n      .split(',')\n      .map((item) => item.trim())\n      .filter((item) => !!item);\n}\n\nfunction loadOptionsFile(dir: string): config.Config {\n  const filename = path.join(dir, 'wct.conf.json');\n  try {\n    const jsonOptions = fs.readFileSync(filename, 'utf-8').toString();\n    const parsedOptions = JSON.parse(jsonOptions);\n    if (parsedOptions !== null && typeof parsedOptions === 'object') {\n      return parsedOptions;\n    } else {\n      return {};\n    }\n  } catch (e) {\n    return {};\n  }\n}\n\nconst testLocalBrowsers = !process.env.SKIP_LOCAL_BROWSERS;\nconst testLocalBrowsersList = parseList(process.env.TEST_LOCAL_BROWSERS);\nconst testRemoteBrowsers = !process.env.SKIP_REMOTE_BROWSERS &&\n    process.env.SAUCE_USERNAME && process.env.SAUCE_ACCESS_KEY;\nconst testRemoteBrowsersList = parseList(process.env.TEST_REMOTE_BROWSERS);\nif (testRemoteBrowsersList.length === 0) {\n  testRemoteBrowsersList.push('default');\n}\n\ninterface TestErrorExpectation {\n  [fileName: string]: {\n    // The test name mapped to the error\n    [testName: string]: [string, string];\n  };\n}\n\ntype Golden = VariantsGolden|VariantResultGolden;\n\nfunction isVariantsGolden(golden: Golden): golden is VariantsGolden {\n  return !!golden['variants'];\n}\n\ninterface VariantsGolden {\n  variants: {[variantName: string]: VariantResultGolden};\n}\n\n\ninterface VariantResultGolden {\n  passing: number;\n  pending: number;\n  failing: number;\n  status: string;\n  tests: TestNode;\n  errors: TestErrorExpectation;\n}\ntype TestNode = {\n  state?: CompletedState; [subTestName: string]: TestNode | CompletedState;\n};\n\nclass TestResults {\n  variants: {[variantName: string]: VariantResults} = {};\n  runError: any = null;\n  testRunnerError: any = null;\n\n  getVariantResults(variantName: string): VariantResults {\n    this.variants[variantName] =\n        this.variants[variantName] || new VariantResults();\n    return this.variants[variantName];\n  }\n}\n\nclass VariantResults {\n  tests: TestNode = {};\n  testErrors: TestNode = {};\n  stats: {[browserName: string]: Stats} = {};\n  errors: {[browserName: string]: any} = {};\n}\n\n// Tests\n\n/** Describes all suites, mixed into the environments being run. */\nfunction runsAllIntegrationSuites(options: config.Config = {}) {\n  const integrationDirnames =\n      fs.readdirSync(integrationDir).filter((fn) => fn !== 'temp');\n  // Overwrite integrationDirnames to run tests in isolation while developing:\n  // integrationDirnames = ['components_dir'];\n\n  // TODO(#421): `missing` correctly fails, but currently it times out which\n  //     takes ~2 minutes.\n  const suitesToSkip = new Set(['missing']);\n\n  for (const fn of integrationDirnames) {\n    runIntegrationSuiteForDir(fn, options, suitesToSkip.has(fn));\n  }\n}\n\n\nfunction runIntegrationSuiteForDir(\n    dirname: string, options: config.Config, skip: boolean) {\n  runsIntegrationSuite(dirname, options, skip, function(testResults) {\n    const golden: Golden = JSON.parse(fs.readFileSync(\n        path.join(integrationDir, dirname, 'golden.json'), 'utf-8'));\n\n    let variantsGolden: VariantsGolden;\n    if (isVariantsGolden(golden)) {\n      variantsGolden = golden;\n    } else {\n      variantsGolden = {variants: {'': golden}};\n    }\n\n    it('ran the correct variants', function() {\n      expect(Object.keys(testResults.variants).sort())\n          .to.deep.equal(Object.keys(variantsGolden.variants).sort());\n    });\n    for (const variantName in variantsGolden.variants) {\n      const run = () => assertVariantResultsConformToGolden(\n          variantsGolden.variants[variantName],\n          testResults.getVariantResults(variantName));\n      if (variantName !== '') {\n        describe(`the variant with bower_components-${variantName}`, run);\n      } else {\n        run();\n      }\n    }\n  });\n}\n\n\n\nconst integrationDir = path.resolve(__dirname, '../fixtures/integration');\n/**\n * Creates a mocha context that runs an integration suite (once), and hangs onto\n * the output for tests.\n */\nfunction runsIntegrationSuite(\n    dirName: string, options: config.Config, skip: boolean,\n    contextFunction: (context: TestResults) => void) {\n  const suiteName = `integration fixture dir '${dirName}'`;\n  let describer: (suiteName: string, spec: () => void) => void = describe;\n  if (skip) {\n    describer = describe.skip;\n  }\n  describer(suiteName, function() {\n    const log: string[] = [];\n    const testResults = new TestResults();\n\n    before(async function() {\n      const suiteRoot = await makeProperTestDir(dirName);\n      const suiteOptions = <any>loadOptionsFile(\n          path.join('test', 'fixtures', 'integration', dirName));\n      // Filter the list of browsers within the suite's options by the global\n      // overrides if they are present.\n      if (suiteOptions.plugins !== undefined) {\n        if (testLocalBrowsersList.length > 0 &&\n            !testLocalBrowsersList.includes('default') &&\n            suiteOptions.plugins.local !== undefined &&\n            suiteOptions.plugins.local.browsers !== undefined) {\n          suiteOptions.plugins.local.browsers =\n              suiteOptions.plugins.local.browsers.filter(\n                  (b: string) => testLocalBrowsersList.includes(b));\n        }\n        if (testRemoteBrowsersList.length > 0 &&\n            suiteOptions.plugins.sauce !== undefined &&\n            suiteOptions.plugins.sauce.browsers !== undefined) {\n          suiteOptions.plugins.sauce.browsers =\n              suiteOptions.plugins.sauce.browsers.filter(\n                  (b: string) => testRemoteBrowsersList.includes(b));\n        }\n      }\n      const allOptions: config.Config = Object.assign(\n          {\n            output: <any>{write: log.push.bind(log)},\n            ttyOutput: false,\n            root: suiteRoot,\n            browserOptions: <any>{\n              name: 'web-component-tester',\n              tags: ['org:Polymer', 'repo:web-component-tester'],\n            },\n          },\n          options, suiteOptions);\n      const context = new Context(allOptions);\n\n      const addEventHandler = (name: string, handler: Function) => {\n        context.on(name, function() {\n          try {\n            handler.apply(null, arguments);\n          } catch (error) {\n            console.error(`Error inside ${name} handler in integration tests:`);\n            console.error(error.stack);\n          }\n        });\n      };\n\n      addEventHandler(\n          'test-end',\n          (browserDef: BrowserDef, data: TestEndData, stats: Stats) => {\n            const variantResults =\n                testResults.getVariantResults(browserDef.variant || '');\n            const browserName = getBrowserName(browserDef);\n            variantResults.stats[browserName] = stats;\n\n            let testNode = <TestNode>(\n                variantResults.tests[browserName] =\n                    variantResults.tests[browserName] || {});\n            let errorNode = variantResults.testErrors[browserName] =\n                variantResults.testErrors[browserName] || {};\n            for (let i = 0; i < data.test.length; i++) {\n              const name = data.test[i];\n              testNode = <TestNode>(testNode[name] = testNode[name] || {});\n              if (i < data.test.length - 1) {\n                errorNode = errorNode[name] = errorNode[name] || {};\n              } else if (data.error) {\n                errorNode[name] = data.error;\n              }\n            }\n            testNode.state = data.state;\n          });\n\n      addEventHandler(\n          'browser-end', (browserDef: BrowserDef, error: any, stats: Stats) => {\n            const variantResults =\n                testResults.getVariantResults(browserDef.variant || '');\n            const browserName = getBrowserName(browserDef);\n            variantResults.stats[browserName] = stats;\n            variantResults.errors[browserName] = error || null;\n          });\n\n      addEventHandler('run-end', (error: any) => {\n        testResults.runError = error;\n      });\n\n      // Don't fail the integration suite on test errors.\n      try {\n        await test(context);\n      } catch (error) {\n        testResults.testRunnerError = error.message;\n      }\n    });\n\n    afterEach(function() {\n      if (this.currentTest.state === 'failed') {\n        process.stderr.write(\n            `\\n    Output of wct for integration suite named \\`${dirName}\\`` +\n            `\\n` +\n            `    ======================================================\\n\\n`);\n        for (const line of log.join('').split('\\n')) {\n          process.stderr.write(`    ${line}\\n`);\n        }\n        process.stderr.write(\n            `\\n    ======================================================\\n\\n`);\n      }\n    });\n\n    contextFunction(testResults);\n  });\n}\n\nif (testLocalBrowsers || testRemoteBrowsers) {\n  describe('Browser Tests', function() {\n    const pluginConfig = <any>{};\n    if (testLocalBrowsers) {\n      pluginConfig.local = {\n        browsers: testLocalBrowsersList,\n        skipSeleniumInstall: true,\n      };\n    }\n    if (testRemoteBrowsers) {\n      pluginConfig.sauce = {\n        browsers: testRemoteBrowsersList,\n      };\n    }\n    runsAllIntegrationSuites({\n      plugins: pluginConfig,\n    });\n  });\n}\n\n/** Assert that all browsers passed. */\nfunction assertPassed(context: TestResults) {\n  if (context.runError) {\n    console.error(\n        context.runError.stack || context.runError.message || context.runError);\n  }\n  if (context.testRunnerError) {\n    console.error(\n        context.testRunnerError.stack || context.testRunnerError.message ||\n        context.testRunnerError);\n  }\n  expect(context.runError).to.not.be.ok;\n  expect(context.testRunnerError).to.not.be.ok;\n  // expect(context.errors).to.deep.equal(repeatBrowsers(context, null));\n}\n\nfunction assertFailed(context: VariantResults, expectedError: string) {\n  // expect(context.runError).to.eq(expectedError);\n  // expect(context.testRunnerError).to.be.eq(expectedError);\n  expect(context.errors).to.deep.equal(repeatBrowsers(context, expectedError));\n}\n\n/** Asserts that all browsers match the given stats. */\nfunction assertStats(\n    context: VariantResults, passing: number, pending: number, failing: number,\n    status: 'complete') {\n  const expected: Stats = {passing, pending, failing, status};\n  expect(context.stats).to.deep.equal(repeatBrowsers(context, expected));\n}\n\n/** Asserts that all browsers match the given test layout. */\nfunction assertTests(context: VariantResults, expected: TestNode) {\n  expect(context.tests).to.deep.equal(repeatBrowsers(context, expected));\n}\n\n\n/** Asserts that all browsers emitted the given errors. */\nfunction assertTestErrors(\n    context: VariantResults, expected: TestErrorExpectation) {\n  lodash.each(context.testErrors, function(actual: any, browser) {\n    expect(Object.keys(expected))\n        .to.have.members(\n            Object.keys(actual),\n            'Test file mismatch for ' + browser +\n                `: expected ${JSON.stringify(Object.keys(expected))} - got ${\n                    JSON.stringify(Object.keys(actual))}`);\n\n    lodash.each(actual, function(errors: any, file: any) {\n      const expectedErrors = expected[file];\n      // Currently very dumb for simplicity: We don't support suites.\n      expect(Object.keys(expectedErrors))\n          .to.have.members(\n              Object.keys(errors),\n              `Test failure mismatch for ${file} on ${browser}`);\n\n      lodash.each(errors, function(error: Error, test: string) {\n        const locationInfo = `for ${file} - \"${test}\" on ${browser}`;\n        const expectedError = expectedErrors[test];\n        const stackLines = error.stack.split('\\n');\n        expect(error.message)\n            .to.eq(expectedError[0], `Error message mismatch ${locationInfo}`);\n\n        // Chai fails to emit stacks for Firefox.\n        // https://github.com/chaijs/chai/issues/100\n        if (browser.match(/firefox|internet explorer 11/)) {\n          return;\n        }\n\n        const expectedErrorText = expectedError[0];\n        const stackTraceMatcher = expectedError[1];\n        expect(stackLines[0]).to.eq(expectedErrorText);\n        expect(stackLines[stackLines.length - 1])\n            .to.match(\n                new RegExp(stackTraceMatcher), `error.stack=\"${error.stack}\"`);\n      });\n    });\n  });\n}\n\nfunction assertVariantResultsConformToGolden(\n    golden: VariantResultGolden, variantResults: VariantResults) {\n  // const variantResults = testResults.getVariantResults('');\n  it('records the correct result stats', function() {\n    try {\n      assertStats(\n          variantResults, golden.passing, golden.pending, golden.failing,\n          <any>golden.status);\n    } catch (_) {\n      // mocha reports twice the failures because reasons\n      // https://github.com/mochajs/mocha/issues/2083\n      assertStats(\n          variantResults, golden.passing, golden.pending, golden.failing * 2,\n          <any>golden.status);\n    }\n  });\n\n  if (golden.passing + golden.pending + golden.failing === 0 && !golden.tests) {\n    return;\n  }\n\n  it('runs the correct tests', function() {\n    assertTests(variantResults, golden.tests);\n  });\n\n  if (golden.errors || golden.failing > 0) {\n    it('emits well formed errors', function() {\n      assertTestErrors(variantResults, golden.errors);\n    });\n  }\n  // it('passed the test', function() {\n  //   assertPassed(testResults);\n  // });\n}\n\nfunction getBrowserName(browser: BrowserDef) {\n  const parts: string[] = [];\n  if (browser.platform && !browser.deviceName) {\n    parts.push(browser.platform);\n  }\n\n  parts.push(browser.deviceName || browser.browserName);\n\n  if (browser.version) {\n    parts.push(browser.version);\n  }\n\n  if (browser.variant) {\n    parts.push(`[${browser.variant}]`);\n  }\n\n  return parts.join(' ');\n}\n\nfunction repeatBrowsers<T>(\n    context: VariantResults, data: T): {[browserId: string]: T} {\n  expect(Object.keys(context.stats).length)\n      .to.be.greaterThan(0, 'No browsers were run. Bad environment?');\n  return lodash.mapValues(context.stats, () => data);\n}\n\ndescribe('define:webserver hook', () => {\n  it('supports substituting given app', async function() {\n    const suiteRoot = await makeProperTestDir('define-webserver-hook');\n    const log: string[] = [];\n    const requestedUrls: string[] = [];\n    const options: config.Config = {\n      output: <any>{write: log.push.bind(log)},\n      ttyOutput: false,\n      root: suiteRoot,\n      browserOptions: <any>{\n        name: 'web-component-tester',\n        tags: ['org:Polymer', 'repo:web-component-tester'],\n      },\n      plugins: <any>{\n        local: {\n          browsers: testLocalBrowsersList,\n          skipSeleniumInstall: true,\n        }\n      },\n    };\n    const context = new Context(options);\n    context.hook(\n        'define:webserver',\n        (app: express.Application, assign: (sub: express.Express) => void,\n         _options: ServerOptions, done: (err?: any) => void) => {\n          const newApp = express();\n          newApp.get('*', (request, _response, next) => {\n            requestedUrls.push(request.url);\n            next();\n          });\n          newApp.use(app);\n          assign(newApp);\n          done();\n        });\n    await test(context);\n\n    // Our middleware records all the requested urls into this requestedUrls\n    // array, so we can test that the middleware works by inspecting it for\n    // expected tests.html file which should be loaded by index.html\n    expect(requestedUrls)\n        .to.include('/components/define-webserver-hook/test/tests.html');\n    return true;\n  });\n});\n\ndescribe('early failures', () => {\n  it(`wct doesn't start testing if it's not bower installed locally`,\n     async function() {\n       const log: string[] = [];\n       const options: config.Config = {\n         output: <any>{write: log.push.bind(log)},\n         ttyOutput: false,\n         root: path.join(\n             __dirname, '..', 'fixtures', 'integration', 'components_dir'),\n         browserOptions: <any>{\n           name: 'web-component-tester',\n           tags: ['org:Polymer', 'repo:web-component-tester'],\n         },\n         plugins: <any>{\n           local: {browsers: testLocalBrowsersList, skipSeleniumInstall: true},\n         },\n       };\n       const context = new Context(options);\n       try {\n         await test(context);\n         throw new Error('Expected test() to fail!');\n       } catch (e) {\n         expect(e.message).to.match(\n             /The web-component-tester Bower package is not installed as a dependency of this project/);\n       }\n     });\n\n  it('fails if the client side library is out of allowed version range',\n     async function() {\n       const log: string[] = [];\n       const options: config.Config = {\n         output: <any>{write: log.push.bind(log)},\n         ttyOutput: false,\n         root: path.join(__dirname, '..', 'fixtures', 'early-failure'),\n         browserOptions: <any>{\n           name: 'web-component-tester',\n           tags: ['org:Polymer', 'repo:web-component-tester'],\n         },\n         plugins: <any>{\n           local: {browsers: testLocalBrowsersList, skipSeleniumInstall: true},\n         },\n       };\n       const context = new Context(options);\n       try {\n         await test(context);\n         throw new Error('Expected test() to fail!');\n       } catch (e) {\n         expect(e.message).to.match(\n             /The web-component-tester Bower package installed is incompatible with the\\n\\s*wct node package you're using/);\n       }\n     });\n});\n"
  },
  {
    "path": "test/integration/setup_test_dir.ts",
    "content": "import * as fs from 'fs';\nimport * as path from 'path';\nimport * as rimraf from 'rimraf';\nimport * as bowerConfig from 'bower-config';\n\nconst baseDir = path.join(__dirname, '..', 'fixtures', 'integration');\n\n/**\n * Sets up the given integration fixture with proper bower components.\n *\n * For wct to work it needs to be installed in the bower_components directory\n * (or, with variants, in each variant directory). So this copies the given\n * integration test fixture, then sets up symlinks from\n * bower_components/web-component-tester/browser.js to the browser.js of this\n * repo. It also makes symlinks for each of wct's bower dependencies into the\n * integration tests' bower_components dir.\n *\n * @param dirname The basename of an integration fixture directory.\n * @return A fully resolved path to a copy of the fixture directory with\n *   a proper bower_components directory.\n */\nexport async function makeProperTestDir(dirname: string) {\n  const startingDir = path.join(baseDir, dirname);\n  const tempDir = path.join(baseDir, 'temp');\n  if (await exists(tempDir)) {\n    await new Promise((resolve, reject) => {\n      rimraf(tempDir, (err) => err ? reject(err) : resolve());\n    });\n  }\n  fs.mkdirSync(tempDir);\n\n  // copy dir\n  const pathToTestDir = await copyDir(startingDir, tempDir);\n  const bowerDir = bowerConfig.read(pathToTestDir).directory;\n\n  fs.mkdirSync(path.join(pathToTestDir, 'node_modules'));\n  fs.mkdirSync(\n      path.join(pathToTestDir, 'node_modules', 'web-component-tester'));\n\n  // set up symlinks into component dirs for browser.js, data/, and wct's\n  // dependencies (like mocha, sinon, etc)\n  const componentsDirs = new Set([bowerDir]);\n  for (const baseFile of fs.readdirSync(startingDir)) {\n    if (new RegExp(`${bowerDir}(-|$)`).test(baseFile)) {\n      componentsDirs.add(baseFile);\n    }\n  }\n\n  for (const baseComponentsDir of componentsDirs) {\n    const componentsDir = path.join(pathToTestDir, baseComponentsDir);\n    if (!await exists(componentsDir)) {\n      fs.mkdirSync(componentsDir);\n    }\n    // all of wct's bower deps should be present in the project under tests'\n    // components dir\n    const bowerDeps =\n        fs.readdirSync(path.join(__dirname, '../../bower_components'));\n    for (const baseFile of bowerDeps) {\n      fs.symlinkSync(\n          path.join('../../../../../../bower_components', baseFile),\n          path.join(componentsDir, baseFile));\n    }\n    // Also set up a web-component-tester dir with symlinks into our own\n    // client-side files.\n    const wctDir = path.join(componentsDir, 'web-component-tester');\n    fs.mkdirSync(wctDir);\n    fs.symlinkSync(\n        '../../../../../../../browser.js', path.join(wctDir, 'browser.js'),\n        'file');\n    fs.symlinkSync(\n        '../../../../../../../package.json', path.join(wctDir, 'package.json'),\n        'file');\n    fs.symlinkSync(\n        '../../../../../../../data', path.join(wctDir, 'data'), 'dir');\n  }\n\n  return pathToTestDir;\n}\n\nasync function copyDir(from: string, to: string) {\n  const newDir = path.join(to, path.basename(from));\n  fs.mkdirSync(newDir);\n  for (const baseFile of fs.readdirSync(from)) {\n    const file = path.join(from, baseFile);\n    if (fs.statSync(file).isDirectory()) {\n      await copyDir(file, newDir);\n    } else {\n      const newFile = path.join(newDir, baseFile);\n      fs.writeFileSync(newFile, fs.readFileSync(file));\n    }\n  }\n  return newDir;\n}\n\nasync function exists(fn: string) {\n  return new Promise((resolve) => fs.stat(fn, (err) => resolve(!err)));\n}\n"
  },
  {
    "path": "test/unit/cli.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\n\nimport * as chai from 'chai';\nimport * as _ from 'lodash';\nimport * as path from 'path';\nimport * as sinon from 'sinon';\n\nimport * as cli from '../../runner/cli';\nimport * as context from '../../runner/context';\nimport * as steps from '../../runner/steps';\n\nconst expect = chai.expect;\n\nconst wctLocalBrowsers = require('wct-local/lib/browsers');\n\nconst FIXTURES = path.resolve(__dirname, '../fixtures/cli');\n\nconst LOCAL_BROWSERS = {\n  aurora: {browserName: 'aurora', version: '1'},\n  canary: {browserName: 'canary', version: '2'},\n  chrome: {browserName: 'chrome', version: '3'},\n  firefox: {browserName: 'firefox', version: '4'},\n};\n\ndescribe('cli', () => {\n\n  let sandbox: sinon.SinonSandbox;\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    sandbox.stub(steps, 'prepare').callsFake(async(): Promise<void> => undefined);\n    sandbox.stub(steps, 'runTests').callsFake(async(): Promise<void> => undefined);\n\n    sandbox.stub(wctLocalBrowsers, 'detect')\n      .callsFake(\n        async () => _.omit(LOCAL_BROWSERS, 'aurora'));\n    sandbox.stub(wctLocalBrowsers, 'supported').callsFake(() => _.keys(LOCAL_BROWSERS));\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('.run', () => {\n    const expectRun = async (args: string[], logInput?: string[]) => {\n      const log = logInput || [];\n      const stream = <NodeJS.WritableStream><any>{write: log.push.bind(log)};\n      try {\n        await cli.run({}, args, stream);\n      } catch (error) {\n        log.forEach((line) => process.stderr.write(line));\n        throw error;\n      }\n      const call = <{args: [context.Context]}>steps.runTests['getCall'](0);\n      return call.args[0];\n    };\n\n    it('honors --version flags', async () => {\n      const version: String = require('../../package.json').version;\n      let output: String;\n\n      await cli.run({}, ['--version'], <any>{write: (o: String) => output = o});\n      expect(output).to.eq(`${version}\\n`);\n      await cli.run({}, ['-V'], <any>{write: (o: String) => output = o});\n      expect(output).to.eq(`${version}\\n`);\n    });\n\n    it('expands test/ by default, ' +\n           'and serves from /components/<basename>',\n       async () => {\n         process.chdir(path.join(FIXTURES, 'standard'));\n         const options = (await expectRun([])).options;\n         expect(options.suites).to.have.members([\n           'test/a.html',\n           'test/b.js',\n         ]);\n         expect(options.root).to.equal(path.join(FIXTURES, 'standard'));\n       });\n\n    it('honors globs', async () => {\n      process.chdir(path.join(FIXTURES, 'standard'));\n\n      const options = (await expectRun(['**/*.html'])).options;\n      expect(options.suites).to.have.members([\n        'test/a.html',\n        'x-foo.html',\n      ]);\n    });\n\n    it('honors expanded files', async () => {\n      process.chdir(path.join(FIXTURES, 'standard'));\n\n      const options = (await expectRun(['test/b.js', 'x-foo.html'])).options;\n      expect(options.suites).to.have.members([\n        'test/b.js',\n        'x-foo.html',\n      ]);\n    });\n\n    it('honors --root with no specified suites', async () => {\n      process.chdir(__dirname);\n\n      const root = path.join(FIXTURES, 'standard');\n      const options = (await expectRun(['--root', root])).options;\n      expect(options.suites).to.have.members([\n        'test/a.html',\n        'test/b.js',\n      ]);\n      expect(options.root).to.equal(root);\n    });\n\n    it('honors --root with specified suites', async () => {\n      process.chdir(__dirname);\n\n      const root = path.join(FIXTURES, 'standard');\n      const options = (await expectRun(['--root', root, '**/*.html'])).options;\n      expect(options.suites).to.have.members([\n        'test/a.html',\n        'x-foo.html',\n      ]);\n      expect(options.root).to.equal(root);\n    });\n\n    it('throws an error if no suites could be found', async () => {\n      try {\n        await cli.run({}, ['404'], <any>{write: () => {}});\n      } catch (error) {\n        expect(error).to.match(/no.*suites.*found/i);\n        return;\n      }\n      throw new Error('cli.run should have failed');\n    });\n\n    it('loads the local and sauce plugins by default', async () => {\n      process.chdir(path.join(FIXTURES, 'standard'));\n\n      const context = await expectRun([]);\n      expect(context.enabledPlugins()).to.have.members(['local', 'sauce']);\n    });\n\n    it('allows plugins to be diabled via --skip-plugin', async () => {\n      process.chdir(path.join(FIXTURES, 'standard'));\n\n      const context = await expectRun(['--skip-plugin', 'sauce']);\n      expect(context.enabledPlugins()).to.have.members(['local']);\n    });\n\n    // TODO(nevir): Remove after deprecation period.\n    it('throws an error when --webRunner is set', () => {\n      return cli.run({}, ['--webRunner', 'foo'], <any>{write: () => {}})\n          .then(\n              () => {\n                throw new Error('cli.run should have failed');\n              },\n              (error) => {\n                expect(error.message).to.include('webRunner');\n                expect(error.message).to.include('suites');\n              });\n    });\n\n    describe('with wct.conf.js', () => {\n      const ROOT = path.join(FIXTURES, 'conf');\n\n      it('serves from /components/<basename>', async () => {\n        process.chdir(ROOT);\n\n        const options = (await expectRun([])).options;\n        expect(options.suites).to.have.members([\n          'test/foo.js',\n        ]);\n        expect(options.root).to.equal(ROOT);\n      });\n\n      it('walks the ancestry', async () => {\n        process.chdir(path.join(ROOT, 'branch/leaf'));\n\n        const options = (await expectRun([])).options;\n        expect(options.suites).to.have.members([\n          'test/foo.js',\n        ]);\n        expect(options.root).to.equal(ROOT);\n      });\n\n      it('honors specified values', async () => {\n        process.chdir(ROOT);\n\n        const options = (await expectRun([])).options;\n        expect(options.plugins['sauce'].username).to.eq('abc123');\n      });\n\n      it('honors root', async () => {\n        process.chdir(path.join(ROOT, 'rooted'));\n\n        const options = (await expectRun([])).options;\n        expect(options.suites).to.have.members([\n          'cli/conf/test/foo.js',\n        ]);\n        expect(options.root).to.equal(path.dirname(FIXTURES));\n      });\n    });\n\n    describe('deprecated flags', () => {\n\n      beforeEach(() => {\n        process.chdir(path.join(FIXTURES, 'standard'));\n      });\n\n      describe('--browsers', () => {\n\n        it('warns when used', async () => {\n          const log: string[] = [];\n          await expectRun(['--browsers', 'firefox'], log);\n          const hasWarning =\n              _.some(log, (l) => /--browsers.*deprecated/i.test(l));\n          expect(hasWarning)\n              .to.eq(true, 'Expected a warning that --browsers is deprecated');\n        });\n\n        // Semi-integration test.\n        // This also checks that wct-local (mostly) works.\n        it('supports local browsers', async () => {\n          const args = ['--browsers', 'firefox', '-b', 'chrome'];\n          const options = (await expectRun(args)).options;\n          const names =\n              options.activeBrowsers.map((browser) => browser.browserName);\n          expect(names).to.have.members(['firefox', 'chrome']);\n        });\n\n        // Semi-integration test.\n        // This also checks that wct-sauce (mostly) works.\n        it('supports sauce browsers', async () => {\n          const args = ['--browsers', 'linux/firefox', '-b', 'linux/chrome'];\n          const options = (await expectRun(args)).options;\n          const names =\n              options.activeBrowsers.map((browser) => browser.browserName);\n          expect(names).to.have.members(['firefox', 'chrome']);\n        });\n\n      });\n\n      describe('--remote', () => {\n\n        it('warns when used', async () => {\n          const log: string[] = [];\n          await expectRun(['--remote'], log);\n          const hasWarning = _.some(log, (l) => /--remote.*--sauce/.test(l));\n          expect(hasWarning)\n              .to.eq(true, 'Expected a warning that --remote is deprecated');\n        });\n\n        // Semi-integration test.\n        // This also checks that wct-sauce (mostly) works.\n        it('sets up default sauce browsers', async () => {\n          const options = (await expectRun(['--remote'])).options;\n          const platforms =\n              options.activeBrowsers.map((browser) => browser.platform);\n          expect(_.compact(platforms).length).to.be.gt(0);\n        });\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/unit/config.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\nimport * as chai from 'chai';\nimport * as path from 'path';\n\nimport * as config from '../../runner/config';\nimport {Context} from '../../runner/context';\n\nconst expect = chai.expect;\n\ndescribe('config', function() {\n\n  describe('.merge', function() {\n\n    it('avoids modifying the input', function() {\n      const one = <any>{foo: 1};\n      const two = <any>{foo: 2};\n      const merged = <any>config.merge(one, two);\n\n      expect(one.foo).to.eq(1);\n      expect(two.foo).to.eq(2);\n      expect(merged.foo).to.eq(2);\n      expect(merged).to.not.equal(two);\n    });\n\n    it('honors false as an explicit blacklisting', function() {\n      const merged = config.merge(\n          <any>{plugins: {foo: {}}}, <any>{plugins: {foo: false}},\n          <any>{plugins: {foo: {}, bar: {}}});\n\n      expect(merged).to.deep.equal({plugins: {foo: false, bar: {}}});\n    });\n\n  });\n\n  describe('.expand', function() {\n\n    describe('deprecated options', function() {\n\n      it('expands local string browsers', function() {\n        const context = new Context(<any>{browsers: ['chrome']});\n        return config.expand(context).then(() => {\n          expect(context.options.plugins['local'].browsers).to.have.members([\n            'chrome'\n          ]);\n        });\n      });\n\n      it('expands sauce string browsers', function() {\n        const context = new Context(<any>{browsers: ['linux/firefox']});\n        return config.expand(context).then(() => {\n          expect(context.options.plugins['sauce'].browsers).to.have.members([\n            'linux/firefox'\n          ]);\n        });\n      });\n\n      it('expands local object browsers', function() {\n        const context =\n            new Context(<any>{browsers: [{browserName: 'firefox'}]});\n        return config.expand(context).then(() => {\n          expect(context.options.plugins['local'].browsers)\n              .to.deep['have']\n              .members([{browserName: 'firefox'}]);\n        });\n      });\n\n      it('expands sauce object browsers', function() {\n        const context = new Context(\n            <any>{browsers: [{browserName: 'safari', platform: 'OS X'}]});\n        return config.expand(context).then(() => {\n          expect(context.options.plugins['sauce'].browsers)\n              .to.deep['have']\n              .members([{browserName: 'safari', platform: 'OS X'}]);\n        });\n      });\n    });\n\n  });\n\n  describe('npm pathing', function() {\n    describe('Resolves simple names to paths', function() {\n      const localPackagePath =\n          path.join(__dirname, '../fixtures/fake-packages/singleton-dep');\n      const options = <config.Config>{root: localPackagePath};\n      const npmPackages: config.NPMPackage[] = [\n        {name: 'dependency', jsEntrypoint: 'index.js'},\n        {name: 'dependency', jsEntrypoint: 'arbitraryJsFile.js'}\n      ];\n      const resolvedEntrypoints =\n          config.resolveWctNpmEntrypointNames(options, npmPackages);\n\n      expect(resolvedEntrypoints[0]).to.equal('dependency/index.js');\n      expect(resolvedEntrypoints[1]).to.equal('dependency/arbitraryJsFile.js');\n    });\n\n    it('Resolves duplicated names to paths', function() {\n      const localPackagePath =\n          path.join(__dirname, '../fixtures/fake-packages/duplicated-dep');\n      const options = <config.Config>{root: localPackagePath};\n      const npmPackages: config.NPMPackage[] = [\n        {name: 'dependency', jsEntrypoint: 'index.js'},\n        {name: 'dependency', jsEntrypoint: 'arbitraryJsFile.js'}\n      ];\n      const resolvedEntrypoints =\n          config.resolveWctNpmEntrypointNames(options, npmPackages);\n\n      expect(resolvedEntrypoints[0])\n          .to.equal('wct-browser-legacy/node_modules/dependency/index.js');\n      expect(resolvedEntrypoints[1])\n          .to.equal(\n              'wct-browser-legacy/node_modules/dependency/arbitraryJsFile.js');\n    });\n  });\n\n});\n"
  },
  {
    "path": "test/unit/context.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\nimport * as chai from 'chai';\nimport * as sinon from 'sinon';\nimport * as sinonChai from 'sinon-chai';\n\nimport {Context} from '../../runner/context';\nimport {Plugin} from '../../runner/plugin';\n\nconst expect = chai.expect;\nchai.use(sinonChai);\n\ndescribe('Context', () => {\n\n  let sandbox: sinon.SinonSandbox;\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('.plugins', () => {\n\n    it('excludes plugins with a falsy config', async () => {\n      const context = new Context(<any>{plugins: {local: false, sauce: {}}});\n      const stub = sandbox.stub(Plugin, 'get').callsFake((name: string) => {\n        return Promise.resolve(name);\n      });\n\n      const plugins = await context.plugins();\n      expect(stub).to.have.been.calledOnce;\n      expect(stub).to.have.been.calledWith('sauce');\n      expect(plugins).to.have.members(['sauce']);\n    });\n\n    it('excludes plugins disabled: true', async () => {\n      const context =\n          new Context(<any>{plugins: {local: {}, sauce: {disabled: true}}});\n      const stub = sandbox.stub(Plugin, 'get').callsFake((name: string) => {\n        return Promise.resolve(name);\n      });\n\n      const plugins = await context.plugins();\n      expect(stub).to.have.been.calledOnce;\n      expect(stub).to.have.been.calledWith('local');\n      expect(plugins).to.have.members(['local']);\n    });\n\n    describe('hook handlers with non-callback second argument', async () => {\n      it('are passed the \"done\" callback function instead of the argument passed to emitHook',\n         async () => {\n           const context = new Context();\n           context.hook('foo', function(arg1: any, done: () => void) {\n             expect(arg1).to.eq('hookArg');\n             done();\n           });\n           await context.emitHook('foo', 'hookArg');\n         });\n    });\n\n    describe('hook handlers written to call callbacks', () => {\n      it('passes additional arguments through', async () => {\n        const context = new Context();\n        context.hook(\n            'foo',\n            (arg1: string, arg2: number, hookDone: (err?: any) => void) => {\n              expect(arg1).to.eq('one');\n              expect(arg2).to.eq(2);\n              hookDone();\n            });\n\n        // Tests the promise form of emitHook.\n        await context.emitHook('foo', 'one', 2);\n\n        // Tests the callback form of emitHook.\n        const error = await new Promise((resolve) => {\n          context.emitHook('foo', 'one', 2, resolve);\n        });\n        expect(error).to.not.be.ok;\n      });\n\n      it('halts on error', async () => {\n        const context = new Context();\n        context.hook('bar', function(hookDone: (err?: any) => void) {\n          hookDone('nope');\n        });\n\n        // Tests the promise form of emitHook.\n        try {\n          await context.emitHook('bar');\n          throw new Error('emitHook should have thrown');\n        } catch (error) {\n          expect(error).to.eq('nope');\n        }\n\n        // Tests the callback form of emitHook.\n        const error = await new Promise((resolve) => {\n          context.emitHook('bar', resolve);\n        });\n        expect(error).to.eq('nope');\n      });\n    });\n\n    describe('hooks handlers written to return promises', () => {\n      it('passes additional arguments through', async () => {\n        const context = new Context();\n        context.hook('foo', async function(arg1: any, arg2: any) {\n          expect(arg1).to.eq('one');\n          expect(arg2).to.eq(2);\n        });\n\n        await context.emitHook('foo', 'one', 2);\n        const error = await new Promise((resolve) => {\n          context.emitHook('foo', 'one', 2, resolve);\n        });\n        expect(error).to.not.be.ok;\n      });\n\n      it('halts on error', async () => {\n        const context = new Context();\n        context.hook('bar', async () => {\n          throw 'nope';\n        });\n\n        // Tests the promise form of emitHook.\n        try {\n          await context.emitHook('bar');\n          throw new Error('emitHook should have thrown');\n        } catch (error) {\n          expect(error).to.eq('nope');\n        }\n\n        // Tests the callback form of emitHook.\n        const error = await new Promise((resolve) => {\n          context.emitHook('bar', resolve);\n        });\n        expect(error).to.eq('nope');\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/unit/grunt.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\nimport * as chai from 'chai';\nimport * as grunt from 'grunt';\nimport * as _ from 'lodash';\nimport * as path from 'path';\nimport * as sinon from 'sinon';\n\nimport {Context} from '../../runner/context';\nimport * as steps from '../../runner/steps';\n\n\nconst wctLocalBrowsers = require('wct-local/lib/browsers');\nconst expect = chai.expect;\nchai.use(require('sinon-chai'));\n\nconst LOCAL_BROWSERS = {\n  aurora: {browserName: 'aurora', version: '1'},\n  canary: {browserName: 'canary', version: '2'},\n  chrome: {browserName: 'chrome', version: '3'},\n  firefox: {browserName: 'firefox', version: '4'},\n};\n\ndescribe('grunt', function() {\n\n  // Sinon doesn't stub process.env very well.\n  let origEnv: any, origArgv: any;\n  beforeEach(function() {\n    origEnv = _.clone(process.env);\n    origArgv = process.argv;\n  });\n  afterEach(function() {\n    _.assign(process.env, origEnv);\n    _.difference(_.keys(process.env), _.keys(origEnv)).forEach(function(key) {\n      delete process.env[key];\n    });\n    process.argv = origArgv;\n  });\n\n  before(function() {\n    grunt.initConfig({\n      'wct-test': {\n        'passthrough': {\n          options: {foo: 1, bar: 'asdf'},\n        },\n        'override': {\n          options: {sauce: {username: '--real-sauce--'}},\n        },\n      },\n    });\n    grunt.loadTasks(path.resolve(__dirname, '../../tasks'));\n  });\n\n  async function runTask(task: string) {\n    await new Promise((resolve, reject) => {\n      grunt.task['options']({error: reject, done: resolve});\n      grunt.task.run('wct-test:' + task)['start']();\n    });\n    // We shouldn't error before hitting it.\n    expect(steps.runTests).to.have.been.calledOnce;\n    return <{args: [Context]}>steps.runTests['getCall'](0);\n  }\n\n  describe('wct-test', function() {\n\n    let sandbox: sinon.SinonSandbox;\n    beforeEach(function() {\n      sandbox = sinon.sandbox.create();\n      sandbox.stub(steps, 'prepare')\n        .callsFake(\n          async(_context: Context): Promise<void> => undefined);\n\n      sandbox.stub(wctLocalBrowsers, 'detect').callsFake(async () => LOCAL_BROWSERS);\n      sandbox.stub(wctLocalBrowsers, 'supported').callsFake(() => _.keys(LOCAL_BROWSERS));\n\n      process.chdir(path.resolve(__dirname, '../fixtures/cli/standard'));\n    });\n\n    afterEach(function() {\n      sandbox.restore();\n    });\n\n    describe('with a passing suite', function() {\n\n      beforeEach(function() {\n        sandbox.stub(steps, 'runTests').callsFake(async(): Promise<void> => undefined);\n      });\n\n      it('passes configuration through', async () => {\n        const call = await runTask('passthrough');\n        expect(call.args[0].options).to.include({foo: 1, bar: 'asdf'});\n      });\n    });\n\n    describe('with a failing suite', function() {\n      beforeEach(function() {\n        sandbox.stub(steps, 'runTests').callsFake(async () => {\n          throw 'failures';\n        });\n      });\n\n      it('passes errors out', async () => {\n        try {\n          await runTask('passthrough');\n        } catch (error) {\n          return;  // All's well!\n        }\n        throw new Error('Expected runTask to fail!');\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/unit/gulp.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\nimport * as chai from 'chai';\nimport * as gulp from 'gulp';\nimport * as path from 'path';\nimport * as sinon from 'sinon';\n\nimport {Config} from '../../runner/config';\nimport {Context} from '../../runner/context';\nimport * as wctGulp from '../../runner/gulp';\nimport {Plugin} from '../../runner/plugin';\nimport * as steps from '../../runner/steps';\n\nconst expect = chai.expect;\nchai.use(require('sinon-chai'));\n\nconst FIXTURES = path.resolve(__dirname, '../fixtures/cli');\n\ndescribe('gulp', function() {\n\n  let pluginsCalled: string[];\n  let sandbox: sinon.SinonSandbox;\n  let orch: gulp.Gulp;\n  let options: Config;\n  beforeEach(function() {\n    orch = new gulp['Gulp']();\n    wctGulp.init(orch);\n\n    sandbox = sinon.sandbox.create();\n    sandbox.stub(\n        steps, 'prepare').callsFake(async(_context: Context): Promise<void> => undefined);\n    sandbox.stub(steps, 'runTests').callsFake(async (context: Context) => {\n      options = context.options;\n    });\n\n    pluginsCalled = [];\n    sandbox.stub(Plugin.prototype, 'execute').callsFake(async function(context: Context) {\n      pluginsCalled.push(this.name);\n      context.options.activeBrowsers.push(\n          <any>{browserName: 'fake for ' + this.name});\n    });\n  });\n\n  afterEach(function() {\n    sandbox.restore();\n  });\n\n  async function runGulpTask(name: string) {\n    await new Promise((resolve, reject) => {\n      orch.start(name, (error) => error ? reject(error) : resolve());\n    });\n  }\n\n  it('honors wcf.conf.js', async () => {\n    process.chdir(path.join(FIXTURES, 'conf'));\n    await runGulpTask('wct:sauce');\n    expect(options.plugins['sauce'].username).to.eq('abc123');\n  });\n\n  it('prefers wcf.conf.json', async () => {\n    process.chdir(path.join(FIXTURES, 'conf', 'json'));\n    await runGulpTask('wct:sauce');\n    expect(options.plugins['sauce'].username).to.eq('jsonconf');\n  });\n\n  describe('wct:local', function() {\n\n    it('kicks off local tests', async () => {\n      await runGulpTask('wct:local');\n      expect(steps.runTests).to.have.been.calledOnce;\n      expect(pluginsCalled).to.have.members(['local']);\n    });\n\n  });\n\n  describe('wct:sauce', function() {\n\n    it('kicks off sauce tests', async () => {\n      await runGulpTask('wct:sauce');\n      expect(steps.runTests).to.have.been.calledOnce;\n      expect(pluginsCalled).to.have.members(['sauce']);\n    });\n\n  });\n\n});\n"
  },
  {
    "path": "test/unit/paths.ts",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\nimport {expect} from 'chai';\nimport * as path from 'path';\n\nimport * as paths from '../../runner/paths';\n\ndescribe('paths', function() {\n\n  describe('.expand', function() {\n    const baseDir = path.resolve(__dirname, '../fixtures/paths');\n\n    async function expectExpands(patterns: string[], expected: string[]) {\n      const actual = await paths.expand(baseDir, patterns);\n\n      // for non-POSIX support\n      expected = expected.map((str) => str.replace(/\\//g, path.sep));\n      expect(actual).to.have.members(expected);\n    }\n\n    it('is ok with an empty list', async () => {\n      await expectExpands([], []);\n    });\n\n    it('ignores explicit files that are missing', async () => {\n      await expectExpands(['404.js'], []);\n      await expectExpands(['404.js', 'foo.html'], ['foo.html']);\n    });\n\n    it('does not expand explicit files', async () => {\n      await expectExpands(['foo.js'], ['foo.js']);\n      await expectExpands(['foo.html'], ['foo.html']);\n      await expectExpands(['foo.js', 'foo.html'], ['foo.js', 'foo.html']);\n    });\n\n    it('expands directories into their files', async () => {\n      await expectExpands(['foo'], ['foo/one.js', 'foo/two.html']);\n      await expectExpands(['foo/'], ['foo/one.js', 'foo/two.html']);\n    });\n\n    it('expands directories into index.html when present', async () => {\n      await expectExpands(['bar'], ['bar/index.html']);\n      await expectExpands(['bar/'], ['bar/index.html']);\n    });\n\n    it('expands directories recursively, honoring all rules', async () => {\n      await expectExpands(['baz'], [\n        'baz/a/fizz.html',\n        'baz/b/index.html',\n        'baz/a.html',\n        'baz/b.js',\n      ]);\n    });\n\n    it('accepts globs for explicit file matches', async () => {\n      await expectExpands(['baz/*.js'], ['baz/b.js']);\n      await expectExpands(['baz/*.html'], ['baz/a.html']);\n      await expectExpands(['baz/**/*.js'], [\n        'baz/b/deep/stuff.js',\n        'baz/b/one.js',\n        'baz/b.js',\n      ]);\n      await expectExpands(['baz/**/*.html'], [\n        'baz/a/fizz.html',\n        'baz/b/deep/index.html',\n        'baz/b/deep/stuff.html',\n        'baz/b/index.html',\n        'baz/a.html',\n      ]);\n    });\n\n    it('accepts globs for directories, honoring directory behavior',\n       async () => {\n         await expectExpands(['*'], [\n           'bar/index.html',\n           'baz/a/fizz.html',\n           'baz/b/index.html',\n           'baz/a.html',\n           'baz/b.js',\n           'foo/one.js',\n           'foo/two.html',\n           'foo.html',\n           'foo.js',\n         ]);\n         await expectExpands(['baz/*'], [\n           'baz/a/fizz.html',\n           'baz/b/index.html',\n           'baz/a.html',\n           'baz/b.js',\n         ]);\n       });\n\n    it('deduplicates', async () => {\n      await expectExpands(['bar/a.js', 'bar/*.js', 'bar', 'bar/*.html'], [\n        'bar/a.js',\n        'bar/index.html',\n        'bar/index.js',\n      ]);\n    });\n  });\n});\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"target\": \"es6\",\n        \"module\": \"commonjs\",\n        \"moduleResolution\": \"node\",\n        \"isolatedModules\": false,\n        \"noImplicitAny\": true,\n        \"noUnusedLocals\": false,\n        \"noUnusedParameters\": true,\n        \"noImplicitThis\": false,\n        \"strictNullChecks\": false,\n        \"removeComments\": false,\n        \"preserveConstEnums\": true,\n        \"suppressImplicitAnyIndexErrors\": true,\n        \"lib\": [\"es2017\"],\n        \"declaration\": true,\n        \"sourceMap\": true,\n        \"pretty\": true\n    },\n    \"exclude\": [\n        \"node_modules\"\n    ],\n    \"include\": [\n        \"runner/*.ts\",\n        \"test/**/*.ts\",\n        \"custom_typings/*.d.ts\",\n        \"node_modules/@types/mocha/index.d.ts\"\n    ]\n}\n"
  },
  {
    "path": "tslint.json",
    "content": "{\n    \"rules\": {\n        \"arrow-parens\": true,\n        \"class-name\": true,\n        \"indent\": [\n            true,\n            \"spaces\"\n        ],\n        \"prefer-const\": true,\n        \"no-duplicate-variable\": true,\n        \"no-eval\": true,\n        \"no-internal-module\": true,\n        \"no-trailing-whitespace\": true,\n        \"no-var-keyword\": true,\n        \"one-line\": [\n            true,\n            \"check-open-brace\",\n            \"check-whitespace\"\n        ],\n        \"quotemark\": [\n            true,\n            \"single\",\n            \"avoid-escape\"\n        ],\n        \"semicolon\": [\n            true,\n            \"always\"\n        ],\n        \"trailing-comma\": [\n            true,\n            \"multiline\"\n        ],\n        \"triple-equals\": [\n            true,\n            \"allow-null-check\"\n        ],\n        \"typedef-whitespace\": [\n            true,\n            {\n                \"call-signature\": \"nospace\",\n                \"index-signature\": \"nospace\",\n                \"parameter\": \"nospace\",\n                \"property-declaration\": \"nospace\",\n                \"variable-declaration\": \"nospace\"\n            }\n        ],\n        \"variable-name\": [\n            true,\n            \"ban-keywords\"\n        ],\n        \"whitespace\": [\n            true,\n            \"check-branch\",\n            \"check-decl\",\n            \"check-operator\",\n            \"check-separator\",\n            \"check-type\"\n        ]\n    }\n}\n"
  },
  {
    "path": "wct-browser-legacy/a11ySuite.js",
    "content": "import * as polymerDom from '../@polymer/polymer/lib/legacy/polymer.dom.js';\nconst Polymer = { dom: polymerDom };\nexport {a11ySuiteExport as a11ySuite};\n\n// wct-browser-legacy/a11ySuite.js is a generated file.  Source is in web-component-tester/data/a11ySuite.js\n\n/**\n * @license\n * Copyright (c) 2015 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n */\n\nvar a11ySuiteExport;\n\n(function(Mocha, chai, axs) {\n\n  Object.keys(Mocha.interfaces).forEach(function(iface) {\n    var orig = Mocha.interfaces[iface];\n\n    Mocha.interfaces[iface] = function(suite) {\n      orig.apply(this, arguments);\n\n      var Suite = Mocha.Suite;\n      var Test = Mocha.Test;\n\n      suite.on('pre-require', function(context, file, mocha) {\n\n        /**\n         * Runs the Chrome Accessibility Developer Tools Audit against a test-fixture\n         *\n         * @param {String} fixtureId ID of the fixture element in the document to use\n         * @param {Array?} ignoredRules Array of rules to ignore for this suite\n         * @param {Function?} beforeEach Function to be called before each test to ensure proper setup\n         */\n        a11ySuiteExport = context.a11ySuite = function(fixtureId, ignoredRules, beforeEach) {\n          // capture a reference to the fixture element early\n          var fixtureElement = document.getElementById(fixtureId);\n          if (!fixtureElement) {\n            return;\n          }\n\n          // build an audit config to disable certain ignorable tests\n          var axsConfig = new axs.AuditConfiguration();\n          axsConfig.scope = document.body;\n          axsConfig.showUnsupportedRulesWarning = false;\n          axsConfig.auditRulesToIgnore = ignoredRules;\n\n          // build mocha suite\n          var a11ySuite = Suite.create(suite, 'A11y Audit - Fixture: ' + fixtureId);\n\n          // override the `eachTest` function to hackily create the tests\n          //\n          // eachTest is called right before test runs to calculate the total\n          // number of tests\n          a11ySuite.eachTest = function() {\n            // instantiate fixture\n            fixtureElement.create();\n\n            // Make sure lazy-loaded dom is ready (eg <template is='dom-repeat'>)\n            Polymer.dom.flush();\n\n            // If we have a beforeEach function, call it\n            if (beforeEach) {\n              beforeEach();\n            }\n\n            // run audit\n            var auditResults = axs.Audit.run(axsConfig);\n\n            // create tests for audit results\n            auditResults.forEach(function(result, index) {\n              // only show applicable tests\n              if (result.result !== 'NA') {\n                var title = result.rule.heading;\n                // fail test if audit result is FAIL\n                var error = result.result === 'FAIL' ? axs.Audit.accessibilityErrorMessage(result) : null;\n                var test = new Test(title, function() {\n                  if (error) {\n                    throw new Error(error);\n                  }\n                });\n                test.file = file;\n                a11ySuite.addTest(test);\n              }\n            });\n\n            // teardown fixture\n            fixtureElement.restore();\n\n            suite.eachTest.apply(a11ySuite, arguments);\n            this.eachTest = suite.eachTest;\n          };\n\n          return a11ySuite;\n        };\n      });\n    };\n  });\n\n  chai.use(function(chai, util) {\n    var Assertion = chai.Assertion;\n\n    // assert\n    chai.assert.a11yLabel = function(node, exp, msg){\n      new Assertion(node).to.have.a11yLabel(exp, msg);\n    };\n\n    // expect / should\n    Assertion.addMethod('a11yLabel', function(str, msg) {\n      if (msg) {\n        util.flag(this, 'message', msg);\n      }\n\n      var node = this._obj;\n\n      // obj must be a Node\n      new Assertion(node).to.be.instanceOf(Node);\n\n      // vind the text alternative with the help of accessibility dev tools\n      var textAlternative = axs.properties.findTextAlternatives(node, {});\n\n      this.assert(\n        textAlternative === str,\n        'expected #{this} to have text alternative #{exp} but got #{act}',\n        'expected #{this} to not have text alternative #{act}',\n        str,\n        textAlternative,\n        true\n      );\n    });\n  });\n})(window.Mocha, window.chai, window.axs);\n"
  },
  {
    "path": "wct-browser-legacy/browser.js",
    "content": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n */\n \n/**\n * THIS FILE IS AUTOMATICALLY GENERATED! \n * To make changes to browser.js, please edit the source files in the repo's `browser/` directory!\n */\n\n(function () {\n'use strict';\n\nwindow.__wctUseNpm = true;\n/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt The complete set of authors may be found\n * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may\n * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by\n * Google as part of the polymer project is also subject to an additional IP\n * rights grant found at http://polymer.github.io/PATENTS.txt\n */\n// Make sure that we use native timers, in case they're being stubbed out.\nvar nativeSetInterval = window.setInterval;\nvar nativeSetTimeout = window.setTimeout;\nvar nativeRequestAnimationFrame = window.requestAnimationFrame;\n/**\n * Runs `stepFn`, catching any error and passing it to `callback` (Node-style).\n * Otherwise, calls `callback` with no arguments on success.\n *\n * @param {function()} callback\n * @param {function()} stepFn\n */\nfunction safeStep(callback, stepFn) {\n    var err;\n    try {\n        stepFn();\n    }\n    catch (error) {\n        err = error;\n    }\n    callback(err);\n}\n/**\n * Runs your test at declaration time (before Mocha has begun tests). Handy for\n * when you need to test document initialization.\n *\n * Be aware that any errors thrown asynchronously cannot be tied to your test.\n * You may want to catch them and pass them to the done event, instead. See\n * `safeStep`.\n *\n * @param {string} name The name of the test.\n * @param {function(?function())} testFn The test function. If an argument is\n *     accepted, the test will be treated as async, just like Mocha tests.\n */\nfunction testImmediate(name, testFn) {\n    if (testFn.length > 0) {\n        return testImmediateAsync(name, testFn);\n    }\n    var err;\n    try {\n        testFn();\n    }\n    catch (error) {\n        err = error;\n    }\n    test(name, function (done) {\n        done(err);\n    });\n}\n/**\n * An async-only variant of `testImmediate`.\n *\n * @param {string} name\n * @param {function(?function())} testFn\n */\nfunction testImmediateAsync(name, testFn) {\n    var testComplete = false;\n    var err;\n    test(name, function (done) {\n        var intervalId = nativeSetInterval(function () {\n            if (!testComplete)\n                return;\n            clearInterval(intervalId);\n            done(err);\n        }, 10);\n    });\n    try {\n        testFn(function (error) {\n            if (error)\n                err = error;\n            testComplete = true;\n        });\n    }\n    catch (error) {\n        err = error;\n        testComplete = true;\n    }\n}\n/**\n * Triggers a flush of any pending events, observations, etc and calls you back\n * after they have been processed.\n *\n * @param {function()} callback\n */\nfunction flush(callback) {\n    // Ideally, this function would be a call to Polymer.dom.flush, but that\n    // doesn't support a callback yet\n    // (https://github.com/Polymer/polymer-dev/issues/851),\n    // ...and there's cross-browser flakiness to deal with.\n    // Make sure that we're invoking the callback with no arguments so that the\n    // caller can pass Mocha callbacks, etc.\n    var done = function done() {\n        callback();\n    };\n    // Because endOfMicrotask is flaky for IE, we perform microtask checkpoints\n    // ourselves (https://github.com/Polymer/polymer-dev/issues/114):\n    var isIE = navigator.appName === 'Microsoft Internet Explorer';\n    if (isIE && window.Platform && window.Platform.performMicrotaskCheckpoint) {\n        var reallyDone_1 = done;\n        done = function doneIE() {\n            Platform.performMicrotaskCheckpoint();\n            nativeSetTimeout(reallyDone_1, 0);\n        };\n    }\n    // Everyone else gets a regular flush.\n    var scope;\n    if (window.Polymer && window.Polymer.dom && window.Polymer.dom.flush) {\n        scope = window.Polymer.dom;\n    }\n    else if (window.Polymer && window.Polymer.flush) {\n        scope = window.Polymer;\n    }\n    else if (window.WebComponents && window.WebComponents.flush) {\n        scope = window.WebComponents;\n    }\n    if (scope) {\n        scope.flush();\n    }\n    // Ensure that we are creating a new _task_ to allow all active microtasks to\n    // finish (the code you're testing may be using endOfMicrotask, too).\n    nativeSetTimeout(done, 0);\n}\n/**\n * Advances a single animation frame.\n *\n * Calls `flush`, `requestAnimationFrame`, `flush`, and `callback` sequentially\n * @param {function()} callback\n */\nfunction animationFrameFlush(callback) {\n    flush(function () {\n        nativeRequestAnimationFrame(function () {\n            flush(callback);\n        });\n    });\n}\n/**\n * DEPRECATED: Use `flush`.\n * @param {function} callback\n */\nfunction asyncPlatformFlush(callback) {\n    console.warn('asyncPlatformFlush is deprecated in favor of the more terse flush()');\n    return window.flush(callback);\n}\n/**\n *\n */\nfunction waitFor(fn, next, intervalOrMutationEl, timeout, timeoutTime) {\n    timeoutTime = timeoutTime || Date.now() + (timeout || 1000);\n    intervalOrMutationEl = intervalOrMutationEl || 32;\n    try {\n        fn();\n    }\n    catch (e) {\n        if (Date.now() > timeoutTime) {\n            throw e;\n        }\n        else {\n            if (typeof intervalOrMutationEl !== 'number') {\n                intervalOrMutationEl.onMutation(intervalOrMutationEl, function () {\n                    waitFor(fn, next, intervalOrMutationEl, timeout, timeoutTime);\n                });\n            }\n            else {\n                nativeSetTimeout(function () {\n                    waitFor(fn, next, intervalOrMutationEl, timeout, timeoutTime);\n                }, intervalOrMutationEl);\n            }\n            return;\n        }\n    }\n    next();\n}\nwindow.safeStep = safeStep;\nwindow.testImmediate = testImmediate;\nwindow.testImmediateAsync = testImmediateAsync;\nwindow.flush = flush;\nwindow.animationFrameFlush = animationFrameFlush;\nwindow.asyncPlatformFlush = asyncPlatformFlush;\nwindow.waitFor = waitFor;\n\n/**\n * The global configuration state for WCT's browser client.\n */\nvar _config = {\n    environmentScripts: !!window.__wctUseNpm ?\n        [\n            'stacky/browser.js', 'async/lib/async.js', 'lodash/index.js',\n            'mocha/mocha.js', 'chai/chai.js', '@polymer/sinonjs/sinon.js',\n            'sinon-chai/lib/sinon-chai.js',\n            'accessibility-developer-tools/dist/js/axs_testing.js',\n            '@polymer/test-fixture/test-fixture.js'\n        ] :\n        [\n            'stacky/browser.js', 'async/lib/async.js', 'lodash/lodash.js',\n            'mocha/mocha.js', 'chai/chai.js', 'sinonjs/sinon.js',\n            'sinon-chai/lib/sinon-chai.js',\n            'accessibility-developer-tools/dist/js/axs_testing.js'\n        ],\n    environmentImports: !!window.__wctUseNpm ? [] :\n        ['test-fixture/test-fixture.html'],\n    root: null,\n    waitForFrameworks: true,\n    waitFor: null,\n    numConcurrentSuites: 1,\n    trackConsoleError: true,\n    mochaOptions: { timeout: 10 * 1000 },\n    verbose: false,\n};\n/**\n * Merges initial `options` into WCT's global configuration.\n *\n * @param {Object} options The options to merge. See `browser/config.js` for a\n *     reference.\n */\nfunction setup(options) {\n    var childRunner = ChildRunner.current();\n    if (childRunner) {\n        _deepMerge(_config, childRunner.parentScope.WCT._config);\n        // But do not force the mocha UI\n        delete _config.mochaOptions.ui;\n    }\n    if (options && typeof options === 'object') {\n        _deepMerge(_config, options);\n    }\n    if (!_config.root) {\n        // Sibling dependencies.\n        var root = scriptPrefix('browser.js');\n        _config.root = basePath(root.substr(0, root.length - 1));\n        if (!_config.root) {\n            throw new Error('Unable to detect root URL for WCT sources. Please set WCT.root before including browser.js');\n        }\n    }\n}\n/**\n * Retrieves a configuration value.\n */\nfunction get(key) {\n    return _config[key];\n}\n// Internal\nfunction _deepMerge(target, source) {\n    Object.keys(source).forEach(function (key) {\n        if (target[key] !== null && typeof target[key] === 'object' &&\n            !Array.isArray(target[key])) {\n            _deepMerge(target[key], source[key]);\n        }\n        else {\n            target[key] = source[key];\n        }\n    });\n}\n\n/**\n * @param {function()} callback A function to call when the active web component\n *     frameworks have loaded.\n */\nfunction whenFrameworksReady(callback) {\n    debug('whenFrameworksReady');\n    var done = function () {\n        debug('whenFrameworksReady done');\n        callback();\n    };\n    // If webcomponents script is in the document, wait for WebComponentsReady.\n    if (window.WebComponents && !window.WebComponents.ready) {\n        debug('WebComponentsReady?');\n        window.addEventListener('WebComponentsReady', function wcReady() {\n            window.removeEventListener('WebComponentsReady', wcReady);\n            debug('WebComponentsReady');\n            done();\n        });\n    }\n    else {\n        done();\n    }\n}\n/**\n * @return {string} '<count> <kind> tests' or '<count> <kind> test'.\n */\nfunction pluralizedStat(count, kind) {\n    if (count === 1) {\n        return count + ' ' + kind + ' test';\n    }\n    else {\n        return count + ' ' + kind + ' tests';\n    }\n}\n/**\n * @param {string} path The URI of the script to load.\n * @param {function} done\n */\nfunction loadScript(path, done) {\n    var script = document.createElement('script');\n    script.src = path;\n    if (done) {\n        script.onload = done.bind(null, null);\n        script.onerror = done.bind(null, 'Failed to load script ' + script.src);\n    }\n    document.head.appendChild(script);\n}\n/**\n * @param {string} path The URI of the stylesheet to load.\n * @param {function} done\n */\nfunction loadStyle(path, done) {\n    var link = document.createElement('link');\n    link.rel = 'stylesheet';\n    link.href = path;\n    if (done) {\n        link.onload = done.bind(null, null);\n        link.onerror = done.bind(null, 'Failed to load stylesheet ' + link.href);\n    }\n    document.head.appendChild(link);\n}\n/**\n * @param {...*} var_args Logs values to the console when the `debug`\n *     configuration option is true.\n */\nfunction debug() {\n    var var_args = [];\n    for (var _i = 0; _i < arguments.length; _i++) {\n        var_args[_i] = arguments[_i];\n    }\n    if (!get('verbose')) {\n        return;\n    }\n    var args = [window.location.pathname].concat(var_args);\n    (console.debug || console.log).apply(console, args);\n}\n// URL Processing\n/**\n * @param {string} url\n * @return {{base: string, params: string}}\n */\nfunction parseUrl(url) {\n    var parts = url.match(/^(.*?)(?:\\?(.*))?$/);\n    return {\n        base: parts[1],\n        params: getParams(parts[2] || ''),\n    };\n}\n/**\n * Expands a URL that may or may not be relative to `base`.\n *\n * @param {string} url\n * @param {string} base\n * @return {string}\n */\nfunction expandUrl(url, base) {\n    if (!base)\n        return url;\n    if (url.match(/^(\\/|https?:\\/\\/)/))\n        return url;\n    if (base.substr(base.length - 1) !== '/') {\n        base = base + '/';\n    }\n    return base + url;\n}\n/**\n * @param {string=} opt_query A query string to parse.\n * @return {!Object<string, !Array<string>>} All params on the URL's query.\n */\nfunction getParams(query) {\n    query = typeof query === 'string' ? query : window.location.search;\n    if (query.substring(0, 1) === '?') {\n        query = query.substring(1);\n    }\n    // python's SimpleHTTPServer tacks a `/` on the end of query strings :(\n    if (query.slice(-1) === '/') {\n        query = query.substring(0, query.length - 1);\n    }\n    if (query === '')\n        return {};\n    var result = {};\n    query.split('&').forEach(function (part) {\n        var pair = part.split('=');\n        if (pair.length !== 2) {\n            console.warn('Invalid URL query part:', part);\n            return;\n        }\n        var key = decodeURIComponent(pair[0]);\n        var value = decodeURIComponent(pair[1]);\n        if (!result[key]) {\n            result[key] = [];\n        }\n        result[key].push(value);\n    });\n    return result;\n}\n/**\n * Merges params from `source` into `target` (mutating `target`).\n *\n * @param {!Object<string, !Array<string>>} target\n * @param {!Object<string, !Array<string>>} source\n */\nfunction mergeParams(target, source) {\n    Object.keys(source).forEach(function (key) {\n        if (!(key in target)) {\n            target[key] = [];\n        }\n        target[key] = target[key].concat(source[key]);\n    });\n}\n/**\n * @param {string} param The param to return a value for.\n * @return {?string} The first value for `param`, if found.\n */\nfunction getParam(param) {\n    var params = getParams();\n    return params[param] ? params[param][0] : null;\n}\n/**\n * @param {!Object<string, !Array<string>>} params\n * @return {string} `params` encoded as a URI query.\n */\nfunction paramsToQuery(params) {\n    var pairs = [];\n    Object.keys(params).forEach(function (key) {\n        params[key].forEach(function (value) {\n            pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));\n        });\n    });\n    return (pairs.length > 0) ? ('?' + pairs.join('&')) : '';\n}\nfunction getPathName(location) {\n    return typeof location === 'string' ? location : location.pathname;\n}\nfunction basePath(location) {\n    return getPathName(location).match(/^.*\\//)[0];\n}\nfunction relativeLocation(location, basePath) {\n    var path = getPathName(location);\n    if (path.indexOf(basePath) === 0) {\n        path = path.substring(basePath.length);\n    }\n    return path;\n}\nfunction cleanLocation(location) {\n    var path = getPathName(location);\n    if (path.slice(-11) === '/index.html') {\n        path = path.slice(0, path.length - 10);\n    }\n    return path;\n}\nfunction parallel(runners, maybeLimit, done) {\n    var limit;\n    if (typeof maybeLimit !== 'number') {\n        done = maybeLimit;\n        limit = 0;\n    }\n    else {\n        limit = maybeLimit;\n    }\n    if (!runners.length) {\n        return done();\n    }\n    var called = false;\n    var total = runners.length;\n    var numActive = 0;\n    var numDone = 0;\n    function runnerDone(error) {\n        if (called) {\n            return;\n        }\n        numDone = numDone + 1;\n        numActive = numActive - 1;\n        if (error || numDone >= total) {\n            called = true;\n            done(error);\n        }\n        else {\n            runOne();\n        }\n    }\n    function runOne() {\n        if (limit && numActive >= limit) {\n            return;\n        }\n        if (!runners.length) {\n            return;\n        }\n        numActive = numActive + 1;\n        runners.shift()(runnerDone);\n    }\n    runners.forEach(runOne);\n}\n/**\n * Finds the directory that a loaded script is hosted on.\n *\n * @param {string} filename\n * @return {string?}\n */\nfunction scriptPrefix(filename) {\n    var scripts = document.querySelectorAll('script[src*=\"' + filename + '\"]');\n    if (scripts.length !== 1) {\n        return null;\n    }\n    var script = scripts[0].src;\n    return script.substring(0, script.indexOf(filename));\n}\n\n\nvar util = Object.freeze({\n    whenFrameworksReady: whenFrameworksReady,\n    pluralizedStat: pluralizedStat,\n    loadScript: loadScript,\n    loadStyle: loadStyle,\n    debug: debug,\n    parseUrl: parseUrl,\n    expandUrl: expandUrl,\n    getParams: getParams,\n    mergeParams: mergeParams,\n    getParam: getParam,\n    paramsToQuery: paramsToQuery,\n    basePath: basePath,\n    relativeLocation: relativeLocation,\n    cleanLocation: cleanLocation,\n    parallel: parallel,\n    scriptPrefix: scriptPrefix\n});\n\n/**\n * A Mocha suite (or suites) run within a child iframe, but reported as if they\n * are part of the current context.\n */\nvar ChildRunner = /** @class */ (function () {\n    function ChildRunner(url, parentScope) {\n        var urlBits = parseUrl(url);\n        mergeParams(urlBits.params, getParams(parentScope.location.search));\n        delete urlBits.params.cli_browser_id;\n        this.url = urlBits.base + paramsToQuery(urlBits.params);\n        this.parentScope = parentScope;\n        this.state = 'initializing';\n    }\n    /**\n     * @return {ChildRunner} The `ChildRunner` that was registered for this\n     * window.\n     */\n    ChildRunner.current = function () {\n        return ChildRunner.get(window);\n    };\n    /**\n     * @param {!Window} target A window to find the ChildRunner of.\n     * @param {boolean} traversal Whether this is a traversal from a child window.\n     * @return {ChildRunner} The `ChildRunner` that was registered for `target`.\n     */\n    ChildRunner.get = function (target, traversal) {\n        var childRunner = ChildRunner._byUrl[target.location.href];\n        if (childRunner) {\n            return childRunner;\n        }\n        if (window.parent === window) { // Top window.\n            if (traversal) {\n                console.warn('Subsuite loaded but was never registered. This most likely is due to wonky history behavior. Reloading...');\n                window.location.reload();\n            }\n            return null;\n        }\n        // Otherwise, traverse.\n        return window.parent.WCT._ChildRunner.get(target, true);\n    };\n    /**\n     * Loads and runs the subsuite.\n     *\n     * @param {function} done Node-style callback.\n     */\n    ChildRunner.prototype.run = function (done) {\n        debug('ChildRunner#run', this.url);\n        this.state = 'loading';\n        this.onRunComplete = done;\n        this.iframe = document.createElement('iframe');\n        this.iframe.src = this.url;\n        this.iframe.classList.add('subsuite');\n        var container = document.getElementById('subsuites');\n        if (!container) {\n            container = document.createElement('div');\n            container.id = 'subsuites';\n            document.body.appendChild(container);\n        }\n        container.appendChild(this.iframe);\n        // let the iframe expand the URL for us.\n        this.url = this.iframe.src;\n        ChildRunner._byUrl[this.url] = this;\n        this.timeoutId = setTimeout(this.loaded.bind(this, new Error('Timed out loading ' + this.url)), ChildRunner.loadTimeout);\n        this.iframe.addEventListener('error', this.loaded.bind(this, new Error('Failed to load document ' + this.url)));\n        this.iframe.contentWindow.addEventListener('DOMContentLoaded', this.loaded.bind(this, null));\n    };\n    /**\n     * Called when the sub suite's iframe has loaded (or errored during load).\n     *\n     * @param {*} error The error that occured, if any.\n     */\n    ChildRunner.prototype.loaded = function (error) {\n        debug('ChildRunner#loaded', this.url, error);\n        if (this.iframe.contentWindow == null && error) {\n            this.signalRunComplete(error);\n            this.done();\n            return;\n        }\n        // Not all targets have WCT loaded (compatiblity mode)\n        if (this.iframe.contentWindow.WCT) {\n            this.share = this.iframe.contentWindow.WCT.share;\n        }\n        if (error) {\n            this.signalRunComplete(error);\n            this.done();\n        }\n    };\n    /**\n     * Called in mocha/run.js when all dependencies have loaded, and the child is\n     * ready to start running tests\n     *\n     * @param {*} error The error that occured, if any.\n     */\n    ChildRunner.prototype.ready = function (error) {\n        debug('ChildRunner#ready', this.url, error);\n        if (this.timeoutId) {\n            clearTimeout(this.timeoutId);\n        }\n        if (error) {\n            this.signalRunComplete(error);\n            this.done();\n        }\n    };\n    /**\n     * Called when the sub suite's tests are complete, so that it can clean up.\n     */\n    ChildRunner.prototype.done = function () {\n        debug('ChildRunner#done', this.url, arguments);\n        // make sure to clear that timeout\n        this.ready();\n        this.signalRunComplete();\n        if (!this.iframe)\n            return;\n        // Be safe and avoid potential browser crashes when logic attempts to\n        // interact with the removed iframe.\n        setTimeout(function () {\n            this.iframe.parentNode.removeChild(this.iframe);\n            this.iframe = null;\n            this.share = null;\n        }.bind(this), 1);\n    };\n    ChildRunner.prototype.signalRunComplete = function (error) {\n        if (!this.onRunComplete)\n            return;\n        this.state = 'complete';\n        this.onRunComplete(error);\n        this.onRunComplete = null;\n    };\n    // ChildRunners get a pretty generous load timeout by default.\n    ChildRunner.loadTimeout = 60000;\n    // We can't maintain properties on iframe elements in Firefox/Safari/???, so\n    // we track childRunners by URL.\n    ChildRunner._byUrl = {};\n    return ChildRunner;\n}());\n\nvar SOCKETIO_ENDPOINT = window.location.protocol + '//' + window.location.host;\nvar SOCKETIO_LIBRARY = SOCKETIO_ENDPOINT + '/socket.io/socket.io.js';\n/**\n * A socket for communication between the CLI and browser runners.\n *\n * @param {string} browserId An ID generated by the CLI runner.\n * @param {!io.Socket} socket The socket.io `Socket` to communicate over.\n */\nvar CLISocket = /** @class */ (function () {\n    function CLISocket(browserId, socket) {\n        this.browserId = browserId;\n        this.socket = socket;\n    }\n    /**\n     * @param {!Mocha.Runner} runner The Mocha `Runner` to observe, reporting\n     *     interesting events back to the CLI runner.\n     */\n    CLISocket.prototype.observe = function (runner) {\n        var _this = this;\n        this.emitEvent('browser-start', {\n            url: window.location.toString(),\n        });\n        // We only emit a subset of events that we care about, and follow a more\n        // general event format that is hopefully applicable to test runners beyond\n        // mocha.\n        //\n        // For all possible mocha events, see:\n        // https://github.com/visionmedia/mocha/blob/master/lib/runner.js#L36\n        runner.on('test', function (test) {\n            _this.emitEvent('test-start', { test: getTitles(test) });\n        });\n        runner.on('test end', function (test) {\n            _this.emitEvent('test-end', {\n                state: getState(test),\n                test: getTitles(test),\n                duration: test.duration,\n                error: test.err,\n            });\n        });\n        runner.on('fail', function (test, err) {\n            // fail the test run if we catch errors outside of a test function\n            if (test.type !== 'test') {\n                _this.emitEvent('browser-fail', 'Error thrown outside of test function: ' + err.stack);\n            }\n        });\n        runner.on('childRunner start', function (childRunner) {\n            _this.emitEvent('sub-suite-start', childRunner.share);\n        });\n        runner.on('childRunner end', function (childRunner) {\n            _this.emitEvent('sub-suite-end', childRunner.share);\n        });\n        runner.on('end', function () {\n            _this.emitEvent('browser-end');\n        });\n    };\n    /**\n     * @param {string} event The name of the event to fire.\n     * @param {*} data Additional data to pass with the event.\n     */\n    CLISocket.prototype.emitEvent = function (event, data) {\n        this.socket.emit('client-event', {\n            browserId: this.browserId,\n            event: event,\n            data: data,\n        });\n    };\n    /**\n     * Builds a `CLISocket` if we are within a CLI-run environment; short-circuits\n     * otherwise.\n     *\n     * @param {function(*, CLISocket)} done Node-style callback.\n     */\n    CLISocket.init = function (done) {\n        var browserId = getParam('cli_browser_id');\n        if (!browserId)\n            return done();\n        // Only fire up the socket for root runners.\n        if (ChildRunner.current())\n            return done();\n        loadScript(SOCKETIO_LIBRARY, function (error) {\n            if (error)\n                return done(error);\n            var socket = io(SOCKETIO_ENDPOINT);\n            socket.on('error', function (error) {\n                socket.off();\n                done(error);\n            });\n            socket.on('connect', function () {\n                socket.off();\n                done(null, new CLISocket(browserId, socket));\n            });\n        });\n    };\n    return CLISocket;\n}());\n// Misc Utility\n/**\n * @param {!Mocha.Runnable} runnable The test or suite to extract titles from.\n * @return {!Array.<string>} The titles of the runnable and its parents.\n */\nfunction getTitles(runnable) {\n    var titles = [];\n    while (runnable && !runnable.root && runnable.title) {\n        titles.unshift(runnable.title);\n        runnable = runnable.parent;\n    }\n    return titles;\n}\n/**\n * @param {!Mocha.Runnable} runnable\n * @return {string}\n */\nfunction getState(runnable) {\n    if (runnable.state === 'passed') {\n        return 'passing';\n    }\n    else if (runnable.state === 'failed') {\n        return 'failing';\n    }\n    else if (runnable.pending) {\n        return 'pending';\n    }\n    else {\n        return 'unknown';\n    }\n}\n\n// We capture console events when running tests; so make sure we have a\n// reference to the original one.\nvar console$1 = window.console;\nvar FONT = ';font: normal 13px \"Roboto\", \"Helvetica Neue\", \"Helvetica\", sans-serif;';\nvar STYLES = {\n    plain: FONT,\n    suite: 'color: #5c6bc0' + FONT,\n    test: FONT,\n    passing: 'color: #259b24' + FONT,\n    pending: 'color: #e65100' + FONT,\n    failing: 'color: #c41411' + FONT,\n    stack: 'color: #c41411',\n    results: FONT + 'font-size: 16px',\n};\n// I don't think we can feature detect this one...\nvar userAgent = navigator.userAgent.toLowerCase();\nvar CAN_STYLE_LOG = userAgent.match('firefox') || userAgent.match('webkit');\nvar CAN_STYLE_GROUP = userAgent.match('webkit');\n// Track the indent for faked `console.group`\nvar logIndent = '';\nfunction log(text, style) {\n    text = text.split('\\n')\n        .map(function (l) {\n        return logIndent + l;\n    })\n        .join('\\n');\n    if (CAN_STYLE_LOG) {\n        console$1.log('%c' + text, STYLES[style] || STYLES.plain);\n    }\n    else {\n        console$1.log(text);\n    }\n}\nfunction logGroup(text, style) {\n    if (CAN_STYLE_GROUP) {\n        console$1.group('%c' + text, STYLES[style] || STYLES.plain);\n    }\n    else if (console$1.group) {\n        console$1.group(text);\n    }\n    else {\n        logIndent = logIndent + '  ';\n        log(text, style);\n    }\n}\nfunction logGroupEnd() {\n    if (console$1.groupEnd) {\n        console$1.groupEnd();\n    }\n    else {\n        logIndent = logIndent.substr(0, logIndent.length - 2);\n    }\n}\nfunction logException(error) {\n    log(error.stack || error.message || (error + ''), 'stack');\n}\n/**\n * A Mocha reporter that logs results out to the web `console`.\n */\nvar Console = /** @class */ (function () {\n    /**\n     * @param runner The runner that is being reported on.\n     */\n    function Console(runner) {\n        Mocha.reporters.Base.call(this, runner);\n        runner.on('suite', function (suite) {\n            if (suite.root) {\n                return;\n            }\n            logGroup(suite.title, 'suite');\n        }.bind(this));\n        runner.on('suite end', function (suite) {\n            if (suite.root) {\n                return;\n            }\n            logGroupEnd();\n        }.bind(this));\n        runner.on('test', function (test) {\n            logGroup(test.title, 'test');\n        }.bind(this));\n        runner.on('pending', function (test) {\n            logGroup(test.title, 'pending');\n        }.bind(this));\n        runner.on('fail', function (_test, error) {\n            logException(error);\n        }.bind(this));\n        runner.on('test end', function (_test) {\n            logGroupEnd();\n        }.bind(this));\n        runner.on('end', this.logSummary.bind(this));\n    }\n    /** Prints out a final summary of test results. */\n    Console.prototype.logSummary = function () {\n        logGroup('Test Results', 'results');\n        if (this.stats.failures > 0) {\n            log(pluralizedStat(this.stats.failures, 'failing'), 'failing');\n        }\n        if (this.stats.pending > 0) {\n            log(pluralizedStat(this.stats.pending, 'pending'), 'pending');\n        }\n        log(pluralizedStat(this.stats.passes, 'passing'));\n        if (!this.stats.failures) {\n            log('test suite passed', 'passing');\n        }\n        log('Evaluated ' + this.stats.tests + ' tests in ' +\n            this.stats.duration + 'ms.');\n        logGroupEnd();\n    };\n    return Console;\n}());\n\n/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt The complete set of authors may be found\n * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may\n * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by\n * Google as part of the polymer project is also subject to an additional IP\n * rights grant found at http://polymer.github.io/PATENTS.txt\n */\n/**\n * WCT-specific behavior on top of Mocha's default HTML reporter.\n *\n * @param {!Mocha.Runner} runner The runner that is being reported on.\n */\nfunction HTML(runner) {\n    var output = document.createElement('div');\n    output.id = 'mocha';\n    document.body.appendChild(output);\n    runner.on('suite', function (_test) {\n        this.total = runner.total;\n    }.bind(this));\n    Mocha.reporters.HTML.call(this, runner);\n}\n// Woo! What a hack. This just saves us from adding a bunch of complexity around\n// style loading.\nvar style = document.createElement('style');\nstyle.textContent = \"\\n    html, body {\\n      position: relative;\\n      height: 100%;\\n      width:  100%;\\n      min-width: 900px;\\n    }\\n    #mocha, #subsuites {\\n      height: 100%;\\n      position: absolute;\\n      top: 0;\\n    }\\n    #mocha {\\n      box-sizing: border-box;\\n      margin: 0 !important;\\n      padding: 60px 20px;\\n      right: 0;\\n      left: 500px;\\n    }\\n    #subsuites {\\n      -ms-flex-direction: column;\\n      -webkit-flex-direction: column;\\n      display: -ms-flexbox;\\n      display: -webkit-flex;\\n      display: flex;\\n      flex-direction: column;\\n      left: 0;\\n      width: 500px;\\n    }\\n    #subsuites .subsuite {\\n      border: 0;\\n      width: 100%;\\n      height: 100%;\\n    }\\n    #mocha .test.pass .duration {\\n      color: #555 !important;\\n    }\\n\";\ndocument.head.appendChild(style);\n\nvar STACKY_CONFIG = {\n    indent: '  ',\n    locationStrip: [\n        /^https?:\\/\\/[^\\/]+/,\n        /\\?.*$/,\n    ],\n    filter: function (line) {\n        return !!line.location.match(/\\/web-component-tester\\/[^\\/]+(\\?.*)?$/);\n    },\n};\n// https://github.com/visionmedia/mocha/blob/master/lib/runner.js#L36-46\nvar MOCHA_EVENTS = [\n    'start', 'end', 'suite', 'suite end', 'test', 'test end', 'hook', 'hook end',\n    'pass', 'fail', 'pending', 'childRunner end'\n];\n// Until a suite has loaded, we assume this many tests in it.\nvar ESTIMATED_TESTS_PER_SUITE = 3;\n/**\n * A Mocha-like reporter that combines the output of multiple Mocha suites.\n */\nvar MultiReporter = /** @class */ (function () {\n    /**\n     * @param numSuites The number of suites that will be run, in order to\n     *     estimate the total number of tests that will be performed.\n     * @param reporters The set of reporters that\n     *     should receive the unified event stream.\n     * @param parent The parent reporter, if present.\n     */\n    function MultiReporter(numSuites, reporters, parent) {\n        var _this = this;\n        this.reporters = reporters.map(function (reporter) {\n            return new reporter(_this);\n        });\n        this.parent = parent;\n        this.basePath = parent && parent.basePath || basePath(window.location);\n        this.total = numSuites * ESTIMATED_TESTS_PER_SUITE;\n        // Mocha reporters assume a stream of events, so we have to be careful to\n        // only report on one runner at a time...\n        this.currentRunner = null;\n        // ...while we buffer events for any other active runners.\n        this.pendingEvents = [];\n        this.emit('start');\n    }\n    /**\n     * @param location The location this reporter represents.\n     * @return A reporter-like \"class\" for each child suite\n     *     that should be passed to `mocha.run`.\n     */\n    MultiReporter.prototype.childReporter = function (location) {\n        var name = this.suiteTitle(location);\n        // The reporter is used as a constructor, so we can't depend on `this` being\n        // properly bound.\n        var self = this;\n        return _a = /** @class */ (function () {\n                function ChildReporter(runner) {\n                    runner.name = window.name;\n                    self.bindChildRunner(runner);\n                }\n                return ChildReporter;\n            }()),\n            _a.title = window.name,\n            _a;\n        var _a;\n    };\n    /** Must be called once all runners have finished. */\n    MultiReporter.prototype.done = function () {\n        this.complete = true;\n        this.flushPendingEvents();\n        this.emit('end');\n    };\n    /**\n     * Emit a top level test that is not part of any suite managed by this\n     * reporter.\n     *\n     * Helpful for reporting on global errors, loading issues, etc.\n     *\n     * @param title The title of the test.\n     * @param error An error associated with this test. If falsy, test is\n     *     considered to be passing.\n     * @param suiteTitle Title for the suite that's wrapping the test.\n     * @param estimated If this test was included in the original\n     *     estimate of `numSuites`.\n     */\n    MultiReporter.prototype.emitOutOfBandTest = function (title, error, suiteTitle, estimated) {\n        debug('MultiReporter#emitOutOfBandTest(', arguments, ')');\n        var root = new Mocha.Suite(suiteTitle || '');\n        var test = new Mocha.Test(title, function () { });\n        test.parent = root;\n        test.state = error ? 'failed' : 'passed';\n        test.err = error;\n        if (!estimated) {\n            this.total = this.total + ESTIMATED_TESTS_PER_SUITE;\n        }\n        var runner = { total: 1 };\n        this.proxyEvent('start', runner);\n        this.proxyEvent('suite', runner, root);\n        this.proxyEvent('test', runner, test);\n        if (error) {\n            this.proxyEvent('fail', runner, test, error);\n        }\n        else {\n            this.proxyEvent('pass', runner, test);\n        }\n        this.proxyEvent('test end', runner, test);\n        this.proxyEvent('suite end', runner, root);\n        this.proxyEvent('end', runner);\n    };\n    /**\n     * @param {!Location|string} location\n     * @return {string}\n     */\n    MultiReporter.prototype.suiteTitle = function (location) {\n        var path = relativeLocation(location, this.basePath);\n        path = cleanLocation(path);\n        return path;\n    };\n    // Internal Interface\n    /** @param {!Mocha.runners.Base} runner The runner to listen to events for. */\n    MultiReporter.prototype.bindChildRunner = function (runner) {\n        var _this = this;\n        MOCHA_EVENTS.forEach(function (eventName) {\n            runner.on(eventName, _this.proxyEvent.bind(_this, eventName, runner));\n        });\n    };\n    /**\n     * Evaluates an event fired by `runner`, proxying it forward or buffering it.\n     *\n     * @param {string} eventName\n     * @param {!Mocha.runners.Base} runner The runner that emitted this event.\n     * @param {...*} var_args Any additional data passed as part of the event.\n     */\n    MultiReporter.prototype.proxyEvent = function (eventName, runner) {\n        var _args = [];\n        for (var _i = 2; _i < arguments.length; _i++) {\n            _args[_i - 2] = arguments[_i];\n        }\n        var extraArgs = Array.prototype.slice.call(arguments, 2);\n        if (this.complete) {\n            console.warn('out of order Mocha event for ' + runner.name + ':', eventName, extraArgs);\n            return;\n        }\n        if (this.currentRunner && runner !== this.currentRunner) {\n            this.pendingEvents.push(Array.prototype.slice.call(arguments));\n            return;\n        }\n        debug('MultiReporter#proxyEvent(', arguments, ')');\n        // This appears to be a Mocha bug: Tests failed by passing an error to their\n        // done function don't set `err` properly.\n        //\n        // TODO(nevir): Track down.\n        if (eventName === 'fail' && !extraArgs[0].err) {\n            extraArgs[0].err = extraArgs[1];\n        }\n        if (eventName === 'start') {\n            this.onRunnerStart(runner);\n        }\n        else if (eventName === 'end') {\n            this.onRunnerEnd(runner);\n        }\n        else {\n            this.cleanEvent(eventName, runner, extraArgs);\n            this.emit.apply(this, [eventName].concat(extraArgs));\n        }\n    };\n    /**\n     * Cleans or modifies an event if needed.\n     *\n     * @param eventName\n     * @param runner The runner that emitted this event.\n     * @param extraArgs\n     */\n    MultiReporter.prototype.cleanEvent = function (eventName, _runner, extraArgs) {\n        // Suite hierarchy\n        if (extraArgs[0]) {\n            extraArgs[0] = this.showRootSuite(extraArgs[0]);\n        }\n        // Normalize errors\n        if (eventName === 'fail') {\n            extraArgs[1] = Stacky.normalize(extraArgs[1], STACKY_CONFIG);\n        }\n        if (extraArgs[0] && extraArgs[0].err) {\n            extraArgs[0].err = Stacky.normalize(extraArgs[0].err, STACKY_CONFIG);\n        }\n    };\n    /**\n     * We like to show the root suite's title, which requires a little bit of\n     * trickery in the suite hierarchy.\n     *\n     * @param {!Mocha.Runnable} node\n     */\n    MultiReporter.prototype.showRootSuite = function (node) {\n        var leaf = node = Object.create(node);\n        while (node && node.parent) {\n            var wrappedParent = Object.create(node.parent);\n            node.parent = wrappedParent;\n            node = wrappedParent;\n        }\n        node.root = false;\n        return leaf;\n    };\n    /** @param {!Mocha.runners.Base} runner */\n    MultiReporter.prototype.onRunnerStart = function (runner) {\n        debug('MultiReporter#onRunnerStart:', runner.name);\n        this.total = this.total - ESTIMATED_TESTS_PER_SUITE + runner.total;\n        this.currentRunner = runner;\n    };\n    /** @param {!Mocha.runners.Base} runner */\n    MultiReporter.prototype.onRunnerEnd = function (runner) {\n        debug('MultiReporter#onRunnerEnd:', runner.name);\n        this.currentRunner = null;\n        this.flushPendingEvents();\n    };\n    /**\n     * Flushes any buffered events and runs them through `proxyEvent`. This will\n     * loop until all buffered runners are complete, or we have run out of\n     * buffered events.\n     */\n    MultiReporter.prototype.flushPendingEvents = function () {\n        var _this = this;\n        var events = this.pendingEvents;\n        this.pendingEvents = [];\n        events.forEach(function (eventArgs) {\n            _this.proxyEvent.apply(_this, eventArgs);\n        });\n    };\n    return MultiReporter;\n}());\n\nvar ARC_OFFSET = 0; // start at the right.\nvar ARC_WIDTH = 6;\n/**\n * A Mocha reporter that updates the document's title and favicon with\n * at-a-glance stats.\n *\n * @param {!Mocha.Runner} runner The runner that is being reported on.\n */\nvar Title = /** @class */ (function () {\n    function Title(runner) {\n        Mocha.reporters.Base.call(this, runner);\n        runner.on('test end', this.report.bind(this));\n    }\n    /** Reports current stats via the page title and favicon. */\n    Title.prototype.report = function () {\n        this.updateTitle();\n        this.updateFavicon();\n    };\n    /** Updates the document title with a summary of current stats. */\n    Title.prototype.updateTitle = function () {\n        if (this.stats.failures > 0) {\n            document.title = pluralizedStat(this.stats.failures, 'failing');\n        }\n        else {\n            document.title = pluralizedStat(this.stats.passes, 'passing');\n        }\n    };\n    /** Updates the document's favicon w/ a summary of current stats. */\n    Title.prototype.updateFavicon = function () {\n        var canvas = document.createElement('canvas');\n        canvas.height = canvas.width = 32;\n        var context = canvas.getContext('2d');\n        var passing = this.stats.passes;\n        var pending = this.stats.pending;\n        var failing = this.stats.failures;\n        var total = Math.max(this.runner.total, passing + pending + failing);\n        drawFaviconArc(context, total, 0, passing, '#0e9c57');\n        drawFaviconArc(context, total, passing, pending, '#f3b300');\n        drawFaviconArc(context, total, pending + passing, failing, '#ff5621');\n        this.setFavicon(canvas.toDataURL());\n    };\n    /** Sets the current favicon by URL. */\n    Title.prototype.setFavicon = function (url) {\n        var current = document.head.querySelector('link[rel=\"icon\"]');\n        if (current) {\n            document.head.removeChild(current);\n        }\n        var link = document.createElement('link');\n        link.rel = 'icon';\n        link.type = 'image/x-icon';\n        link.href = url;\n        link.setAttribute('sizes', '32x32');\n        document.head.appendChild(link);\n    };\n    return Title;\n}());\n/**\n * Draws an arc for the favicon status, relative to the total number of tests.\n */\nfunction drawFaviconArc(context, total, start, length, color) {\n    var arcStart = ARC_OFFSET + Math.PI * 2 * (start / total);\n    var arcEnd = ARC_OFFSET + Math.PI * 2 * ((start + length) / total);\n    context.beginPath();\n    context.strokeStyle = color;\n    context.lineWidth = ARC_WIDTH;\n    context.arc(16, 16, 16 - ARC_WIDTH / 2, arcStart, arcEnd);\n    context.stroke();\n}\n\nvar htmlSuites$1 = [];\nvar jsSuites$1 = [];\n// We process grep ourselves to avoid loading suites that will be filtered.\nvar GREP = getParam('grep');\n// work around mocha bug (https://github.com/mochajs/mocha/issues/2070)\nif (GREP) {\n    GREP = GREP.replace(/\\\\\\./g, '.');\n}\n/**\n * Loads suites of tests, supporting both `.js` and `.html` files.\n *\n * @param files The files to load.\n */\nfunction loadSuites(files) {\n    files.forEach(function (file) {\n        if (/\\.js(\\?.*)?$/.test(file)) {\n            jsSuites$1.push(file);\n        }\n        else if (/\\.html(\\?.*)?$/.test(file)) {\n            htmlSuites$1.push(file);\n        }\n        else {\n            throw new Error('Unknown resource type: ' + file);\n        }\n    });\n}\n/**\n * @return The child suites that should be loaded, ignoring\n *     those that would not match `GREP`.\n */\nfunction activeChildSuites() {\n    var subsuites = htmlSuites$1;\n    if (GREP) {\n        var cleanSubsuites = [];\n        for (var i = 0, subsuite = void 0; subsuite = subsuites[i]; i++) {\n            if (GREP.indexOf(cleanLocation(subsuite)) !== -1) {\n                cleanSubsuites.push(subsuite);\n            }\n        }\n        subsuites = cleanSubsuites;\n    }\n    return subsuites;\n}\n/**\n * Loads all `.js` sources requested by the current suite.\n */\nfunction loadJsSuites(_reporter, done) {\n    debug('loadJsSuites', jsSuites$1);\n    var loaders = jsSuites$1.map(function (file) {\n        // We only support `.js` dependencies for now.\n        return loadScript.bind(util, file);\n    });\n    parallel(loaders, done);\n}\nfunction runSuites(reporter, childSuites, done) {\n    debug('runSuites');\n    var suiteRunners = [\n        // Run the local tests (if any) first, not stopping on error;\n        _runMocha.bind(null, reporter),\n    ];\n    // As well as any sub suites. Again, don't stop on error.\n    childSuites.forEach(function (file) {\n        suiteRunners.push(function (next) {\n            var childRunner = new ChildRunner(file, window);\n            reporter.emit('childRunner start', childRunner);\n            childRunner.run(function (error) {\n                reporter.emit('childRunner end', childRunner);\n                if (error)\n                    reporter.emitOutOfBandTest(file, error);\n                next();\n            });\n        });\n    });\n    parallel(suiteRunners, get('numConcurrentSuites'), function (error) {\n        reporter.done();\n        done(error);\n    });\n}\n/**\n * Kicks off a mocha run, waiting for frameworks to load if necessary.\n *\n * @param {!MultiReporter} reporter Where to send Mocha's events.\n * @param {function} done A callback fired, _no error is passed_.\n */\nfunction _runMocha(reporter, done, waited) {\n    if (get('waitForFrameworks') && !waited) {\n        var waitFor = (get('waitFor') || whenFrameworksReady).bind(window);\n        waitFor(_runMocha.bind(null, reporter, done, true));\n        return;\n    }\n    debug('_runMocha');\n    var mocha = window.mocha;\n    var Mocha = window.Mocha;\n    mocha.reporter(reporter.childReporter(window.location));\n    mocha.suite.title = reporter.suiteTitle(window.location);\n    mocha.grep(GREP);\n    // We can't use `mocha.run` because it bashes over grep, invert, and friends.\n    // See https://github.com/visionmedia/mocha/blob/master/support/tail.js#L137\n    var runner = Mocha.prototype.run.call(mocha, function (_error) {\n        if (document.getElementById('mocha')) {\n            Mocha.utils.highlightTags('code');\n        }\n        done(); // We ignore the Mocha failure count.\n    });\n    // Mocha's default `onerror` handling strips the stack (to support really old\n    // browsers). We upgrade this to get better stacks for async errors.\n    //\n    // TODO(nevir): Can we expand support to other browsers?\n    if (navigator.userAgent.match(/chrome/i)) {\n        window.onerror = null;\n        window.addEventListener('error', function (event) {\n            if (!event.error)\n                return;\n            if (event.error.ignore)\n                return;\n            runner.uncaught(event.error);\n        });\n    }\n}\n\n/**\n * @param {CLISocket} socket The CLI socket, if present.\n * @param {MultiReporter} parent The parent reporter, if present.\n * @return {!Array.<!Mocha.reporters.Base} The reporters that should be used.\n */\nfunction determineReporters(socket, parent) {\n    // Parents are greedy.\n    if (parent) {\n        return [parent.childReporter(window.location)];\n    }\n    // Otherwise, we get to run wild without any parental supervision!\n    var reporters = [Title, Console];\n    if (socket) {\n        reporters.push(function (runner) {\n            socket.observe(runner);\n        });\n    }\n    if (htmlSuites$1.length > 0 || jsSuites$1.length > 0) {\n        reporters.push(HTML);\n    }\n    return reporters;\n}\n/**\n * Yeah, hideous, but this allows us to be loaded before Mocha, which is handy.\n */\nfunction injectMocha(Mocha) {\n    _injectPrototype(Console, Mocha.reporters.Base.prototype);\n    _injectPrototype(HTML, Mocha.reporters.HTML.prototype);\n    // Mocha doesn't expose its `EventEmitter` shim directly, so:\n    _injectPrototype(MultiReporter, Object.getPrototypeOf(Mocha.Runner.prototype));\n}\nfunction _injectPrototype(klass, prototype) {\n    var newPrototype = Object.create(prototype);\n    // Only support\n    Object.keys(klass.prototype).forEach(function (key) {\n        newPrototype[key] = klass.prototype[key];\n    });\n    klass.prototype = newPrototype;\n}\n\n/**\n * Loads all environment scripts ...synchronously ...after us.\n */\nfunction loadSync() {\n    debug('Loading environment scripts:');\n    var a11ySuiteScriptPath = 'web-component-tester/data/a11ySuite.js';\n    var scripts = get('environmentScripts');\n    var a11ySuiteWillBeLoaded = window.__generatedByWct || scripts.indexOf(a11ySuiteScriptPath) > -1;\n    // We can't inject a11ySuite when running the npm version because it is a\n    // module-based script that needs `<script type=module>` and compilation\n    // for browsers without module support.\n    if (!a11ySuiteWillBeLoaded && !window.__wctUseNpm) {\n        // wct is running as a bower dependency, load a11ySuite from data/\n        scripts.push(a11ySuiteScriptPath);\n    }\n    scripts.forEach(function (path) {\n        var url = expandUrl(path, get('root'));\n        debug('Loading environment script:', url);\n        // Synchronous load.\n        document.write('<script src=\"' + encodeURI(url) +\n            '\"></script>'); // jshint ignore:line\n    });\n    debug('Environment scripts loaded');\n    var imports = get('environmentImports');\n    imports.forEach(function (path) {\n        var url = expandUrl(path, get('root'));\n        debug('Loading environment import:', url);\n        // Synchronous load.\n        document.write('<link rel=\"import\" href=\"' + encodeURI(url) +\n            '\">'); // jshint ignore:line\n    });\n    debug('Environment imports loaded');\n}\n/**\n * We have some hard dependencies on things that should be loaded via\n * `environmentScripts`, so we assert that they're present here; and do any\n * post-facto setup.\n */\nfunction ensureDependenciesPresent() {\n    _ensureMocha();\n    _checkChai();\n}\nfunction _ensureMocha() {\n    var Mocha = window.Mocha;\n    if (!Mocha) {\n        throw new Error('WCT requires Mocha. Please ensure that it is present in WCT.environmentScripts, or that you load it before loading web-component-tester/browser.js');\n    }\n    injectMocha(Mocha);\n    // Magic loading of mocha's stylesheet\n    var mochaPrefix = scriptPrefix('mocha.js');\n    // only load mocha stylesheet for the test runner output\n    // Not the end of the world, if it doesn't load.\n    if (mochaPrefix && window.top === window.self) {\n        loadStyle(mochaPrefix + 'mocha.css');\n    }\n}\nfunction _checkChai() {\n    if (!window.chai) {\n        debug('Chai not present; not registering shorthands');\n        return;\n    }\n    window.assert = window.chai.assert;\n    window.expect = window.chai.expect;\n}\n\n// We may encounter errors during initialization (for example, syntax errors in\n// a test file). Hang onto those (and more) until we are ready to report them.\nvar globalErrors = [];\n/**\n * Hook the environment to pick up on global errors.\n */\nfunction listenForErrors() {\n    window.addEventListener('error', function (event) {\n        globalErrors.push(event.error);\n    });\n    // Also, we treat `console.error` as a test failure. Unless you prefer not.\n    var origConsole = console;\n    var origError = console.error;\n    console.error = function wctShimmedError() {\n        origError.apply(origConsole, arguments);\n        if (get('trackConsoleError')) {\n            throw 'console.error: ' + Array.prototype.join.call(arguments, ' ');\n        }\n    };\n}\n\nvar interfaceExtensions = [];\n/**\n * Registers an extension that extends the global `Mocha` implementation\n * with new helper methods. These helper methods will be added to the `window`\n * when tests run for both BDD and TDD interfaces.\n */\nfunction extendInterfaces(helperName, helperFactory) {\n    interfaceExtensions.push(function () {\n        var Mocha = window.Mocha;\n        // For all Mocha interfaces (probably just TDD and BDD):\n        Object.keys(Mocha.interfaces)\n            .forEach(function (interfaceName) {\n            // This is the original callback that defines the interface (TDD or\n            // BDD):\n            var originalInterface = Mocha.interfaces[interfaceName];\n            // This is the name of the \"teardown\" or \"afterEach\" property for the\n            // current interface:\n            var teardownProperty = interfaceName === 'tdd' ? 'teardown' : 'afterEach';\n            // The original callback is monkey patched with a new one that appends\n            // to the global context however we want it to:\n            Mocha.interfaces[interfaceName] = function (suite) {\n                // Call back to the original callback so that we get the base\n                // interface:\n                originalInterface.apply(this, arguments);\n                // Register a listener so that we can further extend the base\n                // interface:\n                suite.on('pre-require', function (context, _file, _mocha) {\n                    // Capture a bound reference to the teardown function as a\n                    // convenience:\n                    var teardown = context[teardownProperty].bind(context);\n                    // Add our new helper to the testing context. The helper is\n                    // generated by a factory method that receives the context,\n                    // the teardown function and the interface name and returns\n                    // the new method to be added to that context:\n                    context[helperName] =\n                        helperFactory(context, teardown, interfaceName);\n                });\n            };\n        });\n    });\n}\n/**\n * Applies any registered interface extensions. The extensions will be applied\n * as many times as this function is called, so don't call it more than once.\n */\nfunction applyExtensions() {\n    interfaceExtensions.forEach(function (applyExtension) {\n        applyExtension();\n    });\n}\n\nextendInterfaces('fixture', function (context, teardown) {\n    // Return context.fixture if it is already a thing, for backwards\n    // compatibility with `test-fixture-mocha.js`:\n    return context.fixture || function fixture(fixtureId, model) {\n        // Automatically register a teardown callback that will restore the\n        // test-fixture:\n        teardown(function () {\n            document.getElementById(fixtureId).restore();\n        });\n        // Find the test-fixture with the provided ID and create it, returning\n        // the results:\n        return document.getElementById(fixtureId).create(model);\n    };\n});\n\n/**\n * stub\n *\n * The stub addon allows the tester to partially replace the implementation of\n * an element with some custom implementation. Usage example:\n *\n * beforeEach(function() {\n *   stub('x-foo', {\n *     attached: function() {\n *       // Custom implementation of the `attached` method of element `x-foo`..\n *     },\n *     otherMethod: function() {\n *       // More custom implementation..\n *     },\n *     getterSetterProperty: {\n *       get: function() {\n *         // Custom getter implementation..\n *       },\n *       set: function() {\n *         // Custom setter implementation..\n *       }\n *     },\n *     // etc..\n *   });\n * });\n */\nextendInterfaces('stub', function (_context, teardown) {\n    return function stub(tagName, implementation) {\n        // Find the prototype of the element being stubbed:\n        var proto = document.createElement(tagName).constructor.prototype;\n        // For all keys in the implementation to stub with..\n        var stubs = Object.keys(implementation).map(function (key) {\n            // Stub the method on the element prototype with Sinon:\n            return sinon.stub(proto, key, implementation[key]);\n        });\n        // After all tests..\n        teardown(function () {\n            stubs.forEach(function (stub) {\n                stub.restore();\n            });\n        });\n    };\n});\n\n// replacement map stores what should be\nvar replacements = {};\nvar replaceTeardownAttached = false;\n/**\n * replace\n *\n * The replace addon allows the tester to replace all usages of one element with\n * another element within all Polymer elements created within the time span of\n * the test. Usage example:\n *\n * beforeEach(function() {\n *   replace('x-foo').with('x-fake-foo');\n * });\n *\n * All annotations and attributes will be set on the placement element the way\n * they were set for the original element.\n */\nextendInterfaces('replace', function (_context, teardown) {\n    return function replace(oldTagName) {\n        return {\n            with: function (tagName) {\n                // Standardizes our replacements map\n                oldTagName = oldTagName.toLowerCase();\n                tagName = tagName.toLowerCase();\n                replacements[oldTagName] = tagName;\n                // If the function is already a stub, restore it to original\n                if (document.importNode.isSinonProxy) {\n                    return;\n                }\n                if (!window.Polymer.Element) {\n                    window.Polymer.Element = function () { };\n                    window.Polymer.Element.prototype._stampTemplate = function () { };\n                }\n                // Keep a reference to the original `document.importNode`\n                // implementation for later:\n                var originalImportNode = document.importNode;\n                // Use Sinon to stub `document.ImportNode`:\n                sinon.stub(document, 'importNode', function (origContent, deep) {\n                    var templateClone = document.createElement('template');\n                    var content = templateClone.content;\n                    var inertDoc = content.ownerDocument;\n                    // imports node from inertDoc which holds inert nodes.\n                    templateClone.content.appendChild(inertDoc.importNode(origContent, true));\n                    // optional arguments are not optional on IE.\n                    var nodeIterator = document.createNodeIterator(content, NodeFilter.SHOW_ELEMENT, null, true);\n                    var node;\n                    // Traverses the tree. A recently-replaced node will be put next,\n                    // so if a node is replaced, it will be checked if it needs to be\n                    // replaced again.\n                    while (node = nodeIterator.nextNode()) {\n                        var currentTagName = node.tagName.toLowerCase();\n                        if (replacements.hasOwnProperty(currentTagName)) {\n                            currentTagName = replacements[currentTagName];\n                            // find the final tag name.\n                            while (replacements[currentTagName]) {\n                                currentTagName = replacements[currentTagName];\n                            }\n                            // Create a replacement:\n                            var replacement = document.createElement(currentTagName);\n                            // For all attributes in the original node..\n                            for (var index = 0; index < node.attributes.length; ++index) {\n                                // Set that attribute on the replacement:\n                                replacement.setAttribute(node.attributes[index].name, node.attributes[index].value);\n                            }\n                            // Replace the original node with the replacement node:\n                            node.parentNode.replaceChild(replacement, node);\n                        }\n                    }\n                    return originalImportNode.call(this, content, deep);\n                });\n                if (!replaceTeardownAttached) {\n                    // After each test...\n                    teardown(function () {\n                        replaceTeardownAttached = true;\n                        // Restore the stubbed version of `document.importNode`:\n                        var documentImportNode = document.importNode;\n                        if (documentImportNode.isSinonProxy) {\n                            documentImportNode.restore();\n                        }\n                        // Empty the replacement map\n                        replacements = {};\n                    });\n                }\n            }\n        };\n    };\n});\n\n// Mocha global helpers, broken out by testing method.\n//\n// Keys are the method for a particular interface; values are their analog in\n// the opposite interface.\nvar MOCHA_EXPORTS = {\n    // https://github.com/visionmedia/mocha/blob/master/lib/interfaces/tdd.js\n    tdd: {\n        'setup': '\"before\"',\n        'teardown': '\"after\"',\n        'suiteSetup': '\"beforeEach\"',\n        'suiteTeardown': '\"afterEach\"',\n        'suite': '\"describe\" or \"context\"',\n        'test': '\"it\" or \"specify\"',\n    },\n    // https://github.com/visionmedia/mocha/blob/master/lib/interfaces/bdd.js\n    bdd: {\n        'before': '\"setup\"',\n        'after': '\"teardown\"',\n        'beforeEach': '\"suiteSetup\"',\n        'afterEach': '\"suiteTeardown\"',\n        'describe': '\"suite\"',\n        'context': '\"suite\"',\n        'xdescribe': '\"suite.skip\"',\n        'xcontext': '\"suite.skip\"',\n        'it': '\"test\"',\n        'xit': '\"test.skip\"',\n        'specify': '\"test\"',\n        'xspecify': '\"test.skip\"',\n    },\n};\n/**\n * Exposes all Mocha methods up front, configuring and running mocha\n * automatically when you call them.\n *\n * The assumption is that it is a one-off (sub-)suite of tests being run.\n */\nfunction stubInterfaces() {\n    var keys = Object.keys(MOCHA_EXPORTS);\n    keys.forEach(function (ui) {\n        Object.keys(MOCHA_EXPORTS[ui]).forEach(function (key) {\n            window[key] = function wrappedMochaFunction() {\n                _setupMocha(ui, key, MOCHA_EXPORTS[ui][key]);\n                if (!window[key] || window[key] === wrappedMochaFunction) {\n                    throw new Error('Expected mocha.setup to define ' + key);\n                }\n                window[key].apply(window, arguments);\n            };\n        });\n    });\n}\n// Whether we've called `mocha.setup`\nvar _mochaIsSetup = false;\n/**\n * @param {string} ui Sets up mocha to run `ui`-style tests.\n * @param {string} key The method called that triggered this.\n * @param {string} alternate The matching method in the opposite interface.\n */\nfunction _setupMocha(ui, key, alternate) {\n    var mochaOptions = get('mochaOptions');\n    if (mochaOptions.ui && mochaOptions.ui !== ui) {\n        var message = 'Mixing ' + mochaOptions.ui + ' and ' + ui +\n            ' Mocha styles is not supported. ' +\n            'You called \"' + key + '\". Did you mean ' + alternate + '?';\n        throw new Error(message);\n    }\n    if (_mochaIsSetup) {\n        return;\n    }\n    applyExtensions();\n    mochaOptions.ui = ui;\n    mocha.setup(mochaOptions); // Note that the reporter is configured in run.js.\n}\n\n// You can configure WCT before it has loaded by assigning your custom\n// configuration to the global `WCT`.\nsetup(window.WCT);\n// Maybe some day we'll expose WCT as a module to whatever module registry you\n// are using (aka the UMD approach), or as an es6 module.\nvar WCT = window.WCT = {\n    // A generic place to hang data about the current suite. This object is\n    // reported\n    // back via the `sub-suite-start` and `sub-suite-end` events.\n    share: {},\n    // Until then, we get to rely on it to expose parent runners to their\n    // children.\n    _ChildRunner: ChildRunner,\n    _reporter: undefined,\n    _config: _config,\n    // Public API\n    /**\n     * Loads suites of tests, supporting both `.js` and `.html` files.\n     *\n     * @param {!Array.<string>} files The files to load.\n     */\n    loadSuites: loadSuites,\n};\n// Load Process\nlistenForErrors();\nstubInterfaces();\nloadSync();\n// Give any scripts on the page a chance to declare tests and muck with things.\ndocument.addEventListener('DOMContentLoaded', function () {\n    debug('DOMContentLoaded');\n    ensureDependenciesPresent();\n    // We need the socket built prior to building its reporter.\n    CLISocket.init(function (error, socket) {\n        if (error)\n            throw error;\n        // Are we a child of another run?\n        var current = ChildRunner.current();\n        var parent = current && current.parentScope.WCT._reporter;\n        debug('parentReporter:', parent);\n        var childSuites = activeChildSuites();\n        var reportersToUse = determineReporters(socket, parent);\n        // +1 for any local tests.\n        var reporter = new MultiReporter(childSuites.length + 1, reportersToUse, parent);\n        WCT._reporter = reporter; // For environment/compatibility.js\n        // We need the reporter so that we can report errors during load.\n        loadJsSuites(reporter, function (error) {\n            // Let our parent know that we're about to start the tests.\n            if (current)\n                current.ready(error);\n            if (error)\n                throw error;\n            // Emit any errors we've encountered up til now\n            globalErrors.forEach(function onError(error) {\n                reporter.emitOutOfBandTest('Test Suite Initialization', error);\n            });\n            runSuites(reporter, childSuites, function (error) {\n                // Make sure to let our parent know that we're done.\n                if (current)\n                    current.done();\n                if (error)\n                    throw error;\n            });\n        });\n    });\n});\n\n}());\n//# sourceMappingURL=browser.js.map"
  },
  {
    "path": "wct-browser-legacy/package.json",
    "content": "{\n  \"name\": \"wct-browser-legacy\",\n  \"version\": \"0.0.1-pre.12\",\n  \"description\": \"Client-side dependencies for web-component-tester tests installed via npm.\",\n  \"main\": \"browser.js\",\n  \"license\": \"http://polymer.github.io/LICENSE.txt\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/polymer/web-component-tester.git\"\n  },\n  \"keywords\": [\n    \"browser\",\n    \"gulp\",\n    \"polymer\",\n    \"test\",\n    \"testing\",\n    \"web\",\n    \"web component\"\n  ],\n  \"bugs\": {\n    \"url\": \"https://github.com/polymer/web-component-tester/issues\"\n  },\n  \"homepage\": \"https://github.com/polymer/web-component-tester#readme\",\n  \"dependencies\": {\n    \"@polymer/polymer\": \"^3.0.0-pre.1\",\n    \"@polymer/sinonjs\": \"^1.14.1\",\n    \"@polymer/test-fixture\": \"^3.0.0-pre.1\",\n    \"@webcomponents/webcomponentsjs\": \"^1.0.7\",\n    \"accessibility-developer-tools\": \"^2.12.0\",\n    \"async\": \"^1.5.2\",\n    \"chai\": \"^3.5.0\",\n    \"lodash\": \"^3.10.1\",\n    \"mocha\": \"^3.4.2\",\n    \"sinon\": \"^1.17.1\",\n    \"sinon-chai\": \"^2.10.0\",\n    \"stacky\": \"^1.3.1\"\n  }\n}\n"
  }
]