Full Code of Polymer/web-component-tester for AI

master e68b2cb5842f cached
167 files
409.9 KB
106.9k tokens
403 symbols
1 requests
Download .txt
Showing preview only (454K chars total). Download the full file or copy to clipboard to get everything.
Repository: Polymer/web-component-tester
Branch: master
Commit: e68b2cb5842f
Files: 167
Total size: 409.9 KB

Directory structure:
gitextract_nxebno92/

├── .clang-format
├── .github/
│   └── PULL_REQUEST_TEMPLATE
├── .gitignore
├── .npmignore
├── .travis.yml
├── .vscode/
│   └── settings.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── bin/
│   ├── wct
│   └── wct-st
├── bower.json
├── browser/
│   ├── childrunner.ts
│   ├── clisocket.ts
│   ├── config.ts
│   ├── declarations.ts
│   ├── environment/
│   │   ├── compatability.ts
│   │   ├── errors.ts
│   │   └── helpers.ts
│   ├── environment.ts
│   ├── index.ts
│   ├── mocha/
│   │   ├── extend.ts
│   │   ├── fixture.ts
│   │   ├── replace.ts
│   │   └── stub.ts
│   ├── mocha.ts
│   ├── more-declarations.ts
│   ├── reporters/
│   │   ├── console.ts
│   │   ├── html.ts
│   │   ├── multi.ts
│   │   └── title.ts
│   ├── reporters.ts
│   ├── suites.ts
│   ├── tsconfig.json
│   └── util.ts
├── browser-js-header.txt
├── browser.js
├── custom_typings/
│   ├── bower-config.d.ts
│   ├── findup-sync.d.ts
│   ├── promisify-node.d.ts
│   ├── send.d.ts
│   ├── server-destroy.d.ts
│   ├── stacky.d.ts
│   └── wd.d.ts
├── data/
│   ├── a11ySuite-npm-header.txt
│   ├── a11ySuite.js
│   └── index.html
├── gulpfile.js
├── package.json
├── runner/
│   ├── browserrunner.ts
│   ├── cli.ts
│   ├── clireporter.ts
│   ├── config.ts
│   ├── context.ts
│   ├── gulp.ts
│   ├── httpbin.ts
│   ├── paths.ts
│   ├── plugin.ts
│   ├── port-scanner.ts
│   ├── steps.ts
│   ├── test.ts
│   └── webserver.ts
├── runner.js
├── tasks/
│   └── test.js
├── test/
│   ├── fixtures/
│   │   ├── cli/
│   │   │   ├── conf/
│   │   │   │   ├── branch/
│   │   │   │   │   └── leaf/
│   │   │   │   │       └── thing.js
│   │   │   │   ├── json/
│   │   │   │   │   ├── wct.conf.js
│   │   │   │   │   └── wct.conf.json
│   │   │   │   ├── rooted/
│   │   │   │   │   └── wct.conf.js
│   │   │   │   ├── test/
│   │   │   │   │   └── foo.js
│   │   │   │   └── wct.conf.js
│   │   │   └── standard/
│   │   │       ├── test/
│   │   │       │   ├── a.html
│   │   │       │   └── b.js
│   │   │       └── x-foo.html
│   │   ├── early-failure/
│   │   │   ├── bower_components/
│   │   │   │   └── web-component-tester/
│   │   │   │       └── package.json
│   │   │   └── test/
│   │   │       └── index.html
│   │   ├── fake-packages/
│   │   │   ├── duplicated-dep/
│   │   │   │   └── package.json
│   │   │   └── singleton-dep/
│   │   │       └── package.json
│   │   ├── integration/
│   │   │   ├── compilation/
│   │   │   │   ├── golden.json
│   │   │   │   └── test/
│   │   │   │       └── index.html
│   │   │   ├── components_dir/
│   │   │   │   ├── bower_components/
│   │   │   │   │   └── foo-element/
│   │   │   │   │       └── foo-element.js
│   │   │   │   ├── golden.json
│   │   │   │   ├── test/
│   │   │   │   │   └── index.html
│   │   │   │   └── wct.conf.json
│   │   │   ├── custom-components_dir/
│   │   │   │   ├── .bowerrc
│   │   │   │   ├── golden.json
│   │   │   │   ├── my_components/
│   │   │   │   │   └── bar-element/
│   │   │   │   │       └── bar-element.js
│   │   │   │   └── test/
│   │   │   │       └── index.html
│   │   │   ├── custom-multiple-component_dirs/
│   │   │   │   ├── .bowerrc
│   │   │   │   ├── golden.json
│   │   │   │   ├── my_components/
│   │   │   │   │   └── package/
│   │   │   │   │       └── index.js
│   │   │   │   ├── my_components-bar/
│   │   │   │   │   └── package/
│   │   │   │   │       └── index.js
│   │   │   │   ├── my_components-foo/
│   │   │   │   │   └── package/
│   │   │   │   │       └── index.js
│   │   │   │   └── test/
│   │   │   │       └── index.html
│   │   │   ├── define-webserver-hook/
│   │   │   │   ├── golden.json
│   │   │   │   └── test/
│   │   │   │       ├── index.html
│   │   │   │       └── tests.html
│   │   │   ├── failing/
│   │   │   │   ├── golden.json
│   │   │   │   └── test/
│   │   │   │       ├── index.html
│   │   │   │       ├── tests.html
│   │   │   │       └── tests.js
│   │   │   ├── missing/
│   │   │   │   ├── golden.json
│   │   │   │   └── test/
│   │   │   │       └── missing.html
│   │   │   ├── mixed-suites/
│   │   │   │   ├── golden.json
│   │   │   │   └── test/
│   │   │   │       ├── index.html
│   │   │   │       ├── one.html
│   │   │   │       ├── one.js
│   │   │   │       ├── two.html
│   │   │   │       └── two.js
│   │   │   ├── multiple-component_dirs/
│   │   │   │   ├── bower_components/
│   │   │   │   │   └── package/
│   │   │   │   │       └── index.js
│   │   │   │   ├── bower_components-bar/
│   │   │   │   │   └── package/
│   │   │   │   │       └── index.js
│   │   │   │   ├── bower_components-foo/
│   │   │   │   │   └── package/
│   │   │   │   │       └── index.js
│   │   │   │   ├── golden.json
│   │   │   │   └── test/
│   │   │   │       └── index.html
│   │   │   ├── multiple-replace/
│   │   │   │   ├── dom-if-element.html
│   │   │   │   ├── dom-repeat-fixture.html
│   │   │   │   ├── exception-element.html
│   │   │   │   ├── exception-fixture.html
│   │   │   │   ├── golden.json
│   │   │   │   ├── normal-element.html
│   │   │   │   ├── projection-element-2.html
│   │   │   │   ├── projection-element-3.html
│   │   │   │   ├── projection-element-4.html
│   │   │   │   ├── projection-element.html
│   │   │   │   └── test/
│   │   │   │       ├── index.html
│   │   │   │       └── tests.html
│   │   │   ├── nested/
│   │   │   │   ├── golden.json
│   │   │   │   └── test/
│   │   │   │       ├── index.html
│   │   │   │       ├── leaf.html
│   │   │   │       ├── leaf.js
│   │   │   │       ├── one/
│   │   │   │       │   ├── index.html
│   │   │   │       │   └── tests.html
│   │   │   │       └── two/
│   │   │   │           └── index.html
│   │   │   ├── no-tests/
│   │   │   │   ├── golden.json
│   │   │   │   └── test/
│   │   │   │       └── index.html
│   │   │   └── query-string/
│   │   │       ├── golden.json
│   │   │       └── test/
│   │   │           ├── index.html
│   │   │           ├── tests.html
│   │   │           └── tests.js
│   │   └── paths/
│   │       ├── bar/
│   │       │   ├── a.js
│   │       │   ├── index.html
│   │       │   └── index.js
│   │       ├── baz/
│   │       │   ├── a/
│   │       │   │   └── fizz.html
│   │       │   ├── a.html
│   │       │   ├── b/
│   │       │   │   ├── deep/
│   │       │   │   │   ├── index.html
│   │       │   │   │   ├── stuff.html
│   │       │   │   │   └── stuff.js
│   │       │   │   ├── index.html
│   │       │   │   └── one.js
│   │       │   └── b.js
│   │       ├── foo/
│   │       │   ├── one.js
│   │       │   ├── three.css
│   │       │   └── two.html
│   │       ├── foo.html
│   │       └── foo.js
│   ├── integration/
│   │   ├── browser.ts
│   │   └── setup_test_dir.ts
│   └── unit/
│       ├── cli.ts
│       ├── config.ts
│       ├── context.ts
│       ├── grunt.ts
│       ├── gulp.ts
│       └── paths.ts
├── tsconfig.json
├── tslint.json
└── wct-browser-legacy/
    ├── a11ySuite.js
    ├── browser.js
    └── package.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .clang-format
================================================
BasedOnStyle: Google
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false


================================================
FILE: .github/PULL_REQUEST_TEMPLATE
================================================
<!--
  Thanks for the PR!

  If this change has a user visible change (including
  bug fixes, new features, etc) please describe the change in
  CHANGELOG.md.

  If the change is an entirely package-internal reshuffling/refactoring
  should the change not be described in the CHANGELOG.

  Consider also updating the README.

  More info: http://keepachangelog.com/en/0.3.0/
 -->

 - [ ] CHANGELOG.md has been updated


================================================
FILE: .gitignore
================================================
# Update .npmignore whenever you update this file!

.todo
/bower_components
/node_modules
package-lock.json
npm-debug.log
typings/
runner/*.js
runner/*.d.ts
runner/*.js.map
test/unit/*.js
test/unit/*.d.ts
test/unit/*.js.map
test/integration/*.js
test/integration/*.d.ts
test/integration/*.js.map
test/fixtures/integration/temp

browser/*.js
browser/*.js.map
browser/environment/*.js*
browser/mocha/*.js*
browser/reporters/*.js*


================================================
FILE: .npmignore
================================================
# Update .gitignore whenever you update this file!

.todo
bower_components
node_modules
npm-debug.log
typings/

# Don't ignore runner/*.js


================================================
FILE: .travis.yml
================================================
sudo: required
dist: trusty
addons:
  firefox: latest
  apt:
    sources:
    - google-chrome
    packages:
    - google-chrome-stable
  sauce_connect: true
language: node_js
node_js:
- '6'
- '8'
script:
- xvfb-run npm test
env:
  global:
  - MOCHA_TIMEOUT=300000
  - secure: WqREPJCj0auiij7P86MfTpD5+buf7vlAuRiUILmcq0+rhVFvsgqG82NlKIjcQuCzM9WFMqBHxDyV0/iyfE5qiQsEXYd7g3SwMVFIAS3R8sKnMz/EMA0kCqFCGn14iwbakNduSuyjQhxHje72iczm/3S4nKFceOzDGFlnvGLxpTA=
  - secure: D5P88V2PQF6mq/bPHlpzW1DgPSz4rKaoXJ1SNIsdeqUnd8xN+tmVUBO0/skunwIPIdws0y7cLfZOThEJuRMoDMUal0Fh81UcWna+yfM5N5NYziyCsOcf60UCZLpWdvS8QNEL0zGff6apO/TEaUkgYx7IMdzi0Bu+56v/iybwM3Q=


================================================
FILE: .vscode/settings.json
================================================
// Place your settings in this file to overwrite default and user settings.
{
  "clang-format.style": "file",
  "editor.formatOnSave": true,
  "editor.formatOnType": true,
  "files.exclude": {
    "runner/*.js": true,
    "runner/*.d.ts": true,
    "runner/*.js.map": true,
    "test/unit/*.js": true,
    "test/unit/*.d.ts": true,
    "test/unit/*.js.map": true,
    "test/integration/*.js": true,
    "test/integration/*.d.ts": true,
    "test/integration/*.js.map": true,
    "browser.js": true,
    "browser.js.map": true,
    "browser/**/*.js": true,
    "browser/**/*.js.map": true
  },
  "typescript.tsdk": "./node_modules/typescript/lib"
}


================================================
FILE: CHANGELOG.md
================================================
# Change Log

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

<!-- ## Unreleased -->
<!-- Add new, unreleased items here. -->

## 6.6.0-pre.5 - 2018-04-12
* Restore missing dependency 'findup-sync'.

## 6.6.0-pre.4 - 2018-04-11
* Upgrade polyserve to pick up recent fixes.

## 6.6.0-pre.3 - 2018-03-21
* Upgrade polyserve to ^0.25.2 and add the --module-resolution flag
* Fix #488 - Support .bowerrc directory name override of bower_components, including variants
* 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)
* Not published: 6.6.0-pre.1 & 6.6.0-pre.2

## 6.5.0 - 2018-01-17
* Upgrade wct-local to 2.1.0 to get support for headless Chrome.

## 6.4.3 - 2018-01-11
* web-component-tester: no longer injects `a11ySuite.js` script in `--npm` mode.
* wct-browser-legacy: `a11ySuite.js` now exports a `a11ySuite` reference. Import this reference direction to use `a11ySuite()` in npm.

## 6.4.2 - 2018-01-09
* Upgrade wct-sauce to 2.0.0 to get updated browsers lists to include Safari 11 and Edge 15.
* Fixed #523 WCT ignores the webserver hostname
* Remove `overflow-y: auto` from test runner styling to increase performance.

## 6.4.1 - 2017-11-20
* 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.

## 6.4.0 - 2017-10-31 🎃

* Updated package.json:
  * Upgraded dependencies async, chai, cleankill, findup-sync, sinon, and socket.io.
  * Upgraded devDependencies update-notifier
* 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.
* Added support for `proxy: {path: string, target: string}` config which is forwarded to `polyserve`.

## 6.3.0 - 2017-10-02

* Updated wct-browser-legacy to use a module version of a11ySuite to get access to Polymer.dom.flush.
* Updated generated index for webserver to use a11ySuite as a module.
* Updated polyserve to get support for on-the-fly module compilation and `<script type=module>` conversion for browsers that don't support modules.

## 6.2.0 - 2017-09-19

* Updated the browser.js file for npm case to use test-fixture as JS module instead of html import.
* Updated the integration tests to support running on Sauce via wct-sauce plugin.
* Updated polyserve to 0.22.1 for better compilation support and ES modules in HTML script tags and bug fixes.
* Updated bower.json to reference newly published test-fixture version 3.0.0.
* 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.
* Added support for scoped package names under npm.

## 6.1.5 - 2017-08-31

* Removed reliance on `document.currentScript` in browser.js because IE11 doesn't have it. (Second reference found)

## 6.1.4 - 2017-08-31

* Removed reliance on `document.currentScript` in browser.js because IE11 doesn't have it.
* 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.

## 6.1.3 - 2017-08-26

* 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.
* Set `@polymer/test-fixture` back to ^0.0.3 because of dependency install errors related to yarn's "flat".

## 6.1.2 - 2017-08-22

* Updated npm browser dependency on `@polymer/test-fixture` v3.0.0-pre.1

## 6.1.1 - 2017-08-21

* Updated browser dependency to a browser-ready sinon npm package for `wct --npm` option.

## 6.1.0 - 2017-08-17

* 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.
* 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.
* Added version flag to CLI. Available using `--version` or `-V`.

## 6.0.1 - 2017-08-08

* Updated package.json dependencies:
  * Upgraded @types/gulp
  * Moved all @types to devDependencies
  * Removed PolymerElements/test-fixture from npm dependencies (is already installed by bower)
* Undo fix for #505, as https://bugs.chromium.org/p/chromium/issues/detail?id=701601 has been fixed and shipped in M58 stable.

## 6.0.0 - 2017-05-15

* Major changes:
  * 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.
    * 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.
    * This release unifies the behavior of `wct` and `polyserve`, so if your code works without warnings in one it should work in the other.
  * `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.
  * 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.

<details>
  <summary>Click for full details</summary>

## New in 6.0.0 stable

* Update wct-local to remove deprecation warnings on install.
* Remove warning when running wct if it's not installed into `node_modules`.

## 6.0.0-prerelease.9 - 2017-04-19

* 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(); }}`.

## 6.0.0-prerelease.8 - 2017-04-13

* [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.

## 6.0.0-prerelease.7 - 2017-03-15

* 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

## 6.0.0-prerelease.1 through 6.0.0-prerelease.6

### Breaking change

* 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.
  * 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.
  * 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.
  * Calling `replace(...)` will use sinon to stub `document.importNode` until `teardown` is called.

### Added

* Polymer.dom.flush() call in a11ySuite to ensure lazy dom is loaded
* Added beforeEach parameter to a11ySuite
* Added first pass of _variants_. Variants different configurations of testing the same code.
  * Add support for _variant dependencies_.
    * 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.

### Removed

* `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.

### Fixed

* Fixed #392
* Fixed #373 and #383 which were caused by `emitHook` not handling arguments correctly.
* Fixed error log message for loading WCT config

</details>

## 5.0.1

* 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

## 5.0.0
* 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.

## 4.2.2
* Update bower dependencies to match node dependencies
* Update rollup to 0.25
* Update README to point to webcomponents-lite.js

## 4.2.1
* Fix `grep` for upstream mocha bug

## 4.2.0
* Add `httpbin` functionality to check `POST` requests
  * `POST` to `/httpbin`, response will be contents of `POST`

## 4.1.0
* Add `ignoreRules` option to `a11ySuite`
    * Array of a11ySuite rules to ignore for that suite
    * Example: https://github.com/PolymerElements/paper-tooltip/commit/bf22b1dfaf7f47312ddb7f5415f75ae81fa467bf

## 4.0.3
* Fix npm 3 serving for lodash and sinon

## 4.0.2
* Fix serving from `node_modules` for npm 3

## 4.0.1
* Fix Polymer 0.5 testing

## 4.0.0
* Remove `bower` as a dependency, serve testing files out of `node_modules`
* Upgrade to `wct-local` 2.0, which needs node 0.12+ for `launchpad` 0.5
* Replace esperanto with rollup for building browser bundle

# 3.x

## 3.4.0
* Integrate [test-fixture](https://github.com/PolymerElements/test-fixture)

## 3.3.0
* Add ability to cancel running tests from library

## 3.2.0
* Add accessibility testing with `a11ySuite` and
    [accessibility-developer-tools](https://github.com/GoogleChrome/accessibility-developer-tools)

## 3.1.3

* `.bowerrc` included in the package to ensure that packages don't get placed in
  unexpected locations.

## 3.1.2

* `--verbose` now includes logging from [`serve-waterfall`](https://github.com/PolymerLabs/serve-waterfall).

## 3.1.1

* WCT now depends on `wct-sauce ^1.5.0`

## 3.1.0

* WCT proper no longer periodically executes webdriver commands to ensure remote
  keepalive. Logic has moved into `wct-sauce`.

* Fix for verbose mode breaking IE10.

## 3.0.7

* Mixing TDD & BDD Mocha interfaces is now an error.

* Calls to `console.error` now generate an error.

* Errors that occur during WCT's initialization are more reliably reported.

* WCT now treats dependencies installed into `bower_components/` as if they are
  siblings of the current repo (much like polyserve).

* Browser libraries are no longer bundled with WCT.

  * They are now bower-managed, and by default installed to `bower_components/`
    within `web-component-tester`.

  * The libraries loaded can be configured via `WCT = {environmentScripts: []}`.

  * Massive overhaul of `browser.js` to support this & `environment.js` no
    longer exists.

* Support for newer versions of webcomponents.js (also Polymer 0.8).

* Mocha configuration can be specified by the `mochaOptions` key in client
  options (i.e. `<script>WCT = {mochaOptions: {}};</script>`).

* Browser options can be specified in `wct.conf.js` via the `clientOptions` key.

* WCT now always generates an index when run via the command line.

* `wct.conf.json` can be used as an alternative to `wct.conf.js`.

## 3.0.0-3.0.6

Yanked. See `3.0.7` for rolled up notes.


# 2.x

There were changes made, and @nevir failed to annotate them. What a slacker.


# 1.x

What are you, an archaeologist?


================================================
FILE: LICENSE
================================================
Copyright (c) 2015 The Polymer Authors. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

   * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
   * Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
   * Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: README.md
================================================
## 🚨 Moved to [`Polymer/tools/packages/web-component-tester`][1] 🚨

The [`Polymer/web-component-tester`][2] repo has been migrated to [`packages/web-component-tester`][1] folder of the [`Polymer/tools`][3] 🚝  *monorepo*.

We are *actively* working on migrating open Issues and PRs to the new repo. New Issues and PRs should be filed at [`Polymer/tools`][3].

[1]: https://github.com/Polymer/tools/tree/master/packages/web-component-tester
[2]: https://github.com/Polymer/web-component-tester
[3]: https://github.com/Polymer/tools


================================================
FILE: bin/wct
================================================
#!/usr/bin/env node
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
 * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
 * Code distributed by Google as part of the polymer project is also
 * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
 */
var resolve = require('resolve');

process.title = 'wct';

resolve('web-component-tester', {basedir: process.cwd()}, function(error, path) {
  var wct = path ? require(path) : require('..');
  var promise = wct.cli.run(process.env, process.argv.slice(2), process.stdout, function (error) {
    process.exit(error ? 1 : 0);
  });
  if (promise) {
    promise.then(() => process.exit(0), () => process.exit(1));
  }
});


================================================
FILE: bin/wct-st
================================================
#!/usr/bin/env node
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
 * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
 * Code distributed by Google as part of the polymer project is also
 * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
 */
var resolve = require('resolve');

process.title = 'wct-st';

resolve('web-component-tester', {basedir: process.cwd()}, function(error, path) {
  var wct = path ? require(path) : require('..');
  var promise = wct.cli.runSauceTunnel(process.env, process.argv.slice(2), process.stdout, function (error) {
    process.exit(error ? 1 : 0);
  });
  if (promise) {
    promise.then(() => process.exit(0), () => process.exit(1));
  }
});


================================================
FILE: bower.json
================================================
{
  "name": "web-component-tester",
  "description": "web-component-tester makes testing your web components a breeze!",
  "version": "6.0.0",
  "main": [
    "browser.js"
  ],
  "license": "http://polymer.github.io/LICENSE.txt",
  "ignore": [
    "*",
    "!/data/*",
    "!/browser.js",
    "!/browser.js.map",
    "!/package.json",
    "!/bower.json"
  ],
  "keywords": [
    "browser",
    "grunt",
    "gruntplugin",
    "gulp",
    "polymer",
    "test",
    "testing",
    "web component",
    "web"
  ],
  "dependencies": {
    "accessibility-developer-tools": "^2.10.0",
    "async": "^1.5.0",
    "chai": "^3.2.0",
    "lodash": "^3.7.0",
    "mocha": "^3.1.2",
    "sinon-chai": "^2.7.0",
    "sinonjs": "^1.14.1",
    "stacky": "^1.3.0",
    "test-fixture": "^3.0.0"
  },
  "devDependencies": {
    "polymer": "Polymer/polymer#^1.5.0",
    "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.22"
  }
}


================================================
FILE: browser/childrunner.ts
================================================
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
 * Google as part of the polymer project is also subject to an additional IP
 * rights grant found at http://polymer.github.io/PATENTS.txt
 */
import * as util from './util.js';

// TODO(thedeeno): Consider renaming subsuite. IIRC, childRunner is entirely
// distinct from mocha suite, which tripped me up badly when trying to add
// plugin support. Perhaps something like 'batch', or 'bundle'. Something that
// has no mocha correlate. This may also eliminate the need for root/non-root
// suite distinctions.

export interface SharedState {}

/**
 * A Mocha suite (or suites) run within a child iframe, but reported as if they
 * are part of the current context.
 */
export default class ChildRunner {
  private url: string;
  parentScope: Window;
  private state: 'initializing'|'loading'|'complete';
  private iframe?: HTMLIFrameElement;
  private onRunComplete: (error?: any) => void;
  private timeoutId: undefined|number;
  private share: SharedState;

  constructor(url: string, parentScope: Window) {
    const urlBits = util.parseUrl(url);
    util.mergeParams(
        urlBits.params, util.getParams(parentScope.location.search));
    delete urlBits.params.cli_browser_id;

    this.url = urlBits.base + util.paramsToQuery(urlBits.params);
    this.parentScope = parentScope;

    this.state = 'initializing';
  }

  // ChildRunners get a pretty generous load timeout by default.
  static loadTimeout = 60000;

  // We can't maintain properties on iframe elements in Firefox/Safari/???, so
  // we track childRunners by URL.
  static _byUrl: {[url: string]: undefined|ChildRunner} = {};

  /**
   * @return {ChildRunner} The `ChildRunner` that was registered for this
   * window.
   */
  static current(): ChildRunner {
    return ChildRunner.get(window);
  }

  /**
   * @param {!Window} target A window to find the ChildRunner of.
   * @param {boolean} traversal Whether this is a traversal from a child window.
   * @return {ChildRunner} The `ChildRunner` that was registered for `target`.
   */
  static get(target: Window, traversal?: boolean): ChildRunner {
    const childRunner = ChildRunner._byUrl[target.location.href];
    if (childRunner) {
      return childRunner;
    }
    if (window.parent === window) {  // Top window.
      if (traversal) {
        console.warn(
            'Subsuite loaded but was never registered. This most likely is due to wonky history behavior. Reloading...');
        window.location.reload();
      }
      return null;
    }
    // Otherwise, traverse.
    return window.parent.WCT._ChildRunner.get(target, true);
  }

  /**
   * Loads and runs the subsuite.
   *
   * @param {function} done Node-style callback.
   */
  run(done: (error?: any) => void) {
    util.debug('ChildRunner#run', this.url);
    this.state = 'loading';
    this.onRunComplete = done;

    this.iframe = document.createElement('iframe');
    this.iframe.src = this.url;
    this.iframe.classList.add('subsuite');

    let container = document.getElementById('subsuites');
    if (!container) {
      container = document.createElement('div');
      container.id = 'subsuites';
      document.body.appendChild(container);
    }
    container.appendChild(this.iframe);

    // let the iframe expand the URL for us.
    this.url = this.iframe.src;
    ChildRunner._byUrl[this.url] = this;

    this.timeoutId = setTimeout(
        this.loaded.bind(this, new Error('Timed out loading ' + this.url)),
        ChildRunner.loadTimeout);

    this.iframe.addEventListener(
        'error',
        this.loaded.bind(
            this, new Error('Failed to load document ' + this.url)));

    this.iframe.contentWindow.addEventListener(
        'DOMContentLoaded', this.loaded.bind(this, null));
  }

  /**
   * Called when the sub suite's iframe has loaded (or errored during load).
   *
   * @param {*} error The error that occured, if any.
   */
  loaded(error: any) {
    util.debug('ChildRunner#loaded', this.url, error);

    if (this.iframe.contentWindow == null && error) {
      this.signalRunComplete(error);
      this.done();
      return;
    }

    // Not all targets have WCT loaded (compatiblity mode)
    if (this.iframe.contentWindow.WCT) {
      this.share = this.iframe.contentWindow.WCT.share;
    }

    if (error) {
      this.signalRunComplete(error);
      this.done();
    }
  }

  /**
   * Called in mocha/run.js when all dependencies have loaded, and the child is
   * ready to start running tests
   *
   * @param {*} error The error that occured, if any.
   */
  ready(error?: any) {
    util.debug('ChildRunner#ready', this.url, error);
    if (this.timeoutId) {
      clearTimeout(this.timeoutId);
    }
    if (error) {
      this.signalRunComplete(error);
      this.done();
    }
  }

  /**
   * Called when the sub suite's tests are complete, so that it can clean up.
   */
  done() {
    util.debug('ChildRunner#done', this.url, arguments);

    // make sure to clear that timeout
    this.ready();
    this.signalRunComplete();

    if (!this.iframe)
      return;
    // Be safe and avoid potential browser crashes when logic attempts to
    // interact with the removed iframe.
    setTimeout(function() {
      this.iframe.parentNode.removeChild(this.iframe);
      this.iframe = null;
      this.share = null;
    }.bind(this), 1);
  }

  signalRunComplete(error?: any) {
    if (!this.onRunComplete)
      return;
    this.state = 'complete';
    this.onRunComplete(error);
    this.onRunComplete = null;
  }
}


================================================
FILE: browser/clisocket.ts
================================================
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
 * Google as part of the polymer project is also subject to an additional IP
 * rights grant found at http://polymer.github.io/PATENTS.txt
 */

import ChildRunner from './childrunner.js';
import * as util from './util.js';

const SOCKETIO_ENDPOINT =
    window.location.protocol + '//' + window.location.host;
const SOCKETIO_LIBRARY = SOCKETIO_ENDPOINT + '/socket.io/socket.io.js';

/**
 * A socket for communication between the CLI and browser runners.
 *
 * @param {string} browserId An ID generated by the CLI runner.
 * @param {!io.Socket} socket The socket.io `Socket` to communicate over.
 */
export default class CLISocket {
  private readonly socket: SocketIO.Socket;
  private readonly browserId: string;
  constructor(browserId: string, socket: SocketIO.Socket) {
    this.browserId = browserId;
    this.socket = socket;
  }

  /**
   * @param {!Mocha.Runner} runner The Mocha `Runner` to observe, reporting
   *     interesting events back to the CLI runner.
   */
  observe(runner: Mocha.IRunner) {
    this.emitEvent('browser-start', {
      url: window.location.toString(),
    });

    // We only emit a subset of events that we care about, and follow a more
    // general event format that is hopefully applicable to test runners beyond
    // mocha.
    //
    // For all possible mocha events, see:
    // https://github.com/visionmedia/mocha/blob/master/lib/runner.js#L36
    runner.on('test', (test: Mocha.IRunnable) => {
      this.emitEvent('test-start', {test: getTitles(test)});
    });

    runner.on('test end', (test: Mocha.IRunnable) => {
      this.emitEvent('test-end', {
        state: getState(test),
        test: getTitles(test),
        duration: (test as any).duration,
        error: (test as any).err,
      });
    });

    runner.on('fail', (test, err) => {
      // fail the test run if we catch errors outside of a test function
      if (test.type !== 'test') {
        this.emitEvent(
            'browser-fail',
            'Error thrown outside of test function: ' + err.stack);
      }
    });

    runner.on('childRunner start', (childRunner) => {
      this.emitEvent('sub-suite-start', childRunner.share);
    });

    runner.on('childRunner end', (childRunner) => {
      this.emitEvent('sub-suite-end', childRunner.share);
    });

    runner.on('end', () => {
      this.emitEvent('browser-end');
    });
  }

  /**
   * @param {string} event The name of the event to fire.
   * @param {*} data Additional data to pass with the event.
   */
  emitEvent(event: string, data?: any) {
    this.socket.emit('client-event', {
      browserId: this.browserId,
      event: event,
      data: data,
    });
  }

  /**
   * Builds a `CLISocket` if we are within a CLI-run environment; short-circuits
   * otherwise.
   *
   * @param {function(*, CLISocket)} done Node-style callback.
   */
  static init(done: (error?: any, socket?: CLISocket) => void) {
    const browserId = util.getParam('cli_browser_id');
    if (!browserId)
      return done();
    // Only fire up the socket for root runners.
    if (ChildRunner.current())
      return done();

    util.loadScript(SOCKETIO_LIBRARY, function(error: any) {
      if (error)
        return done(error);

      const socket = io(SOCKETIO_ENDPOINT);
      socket.on('error', function(error?: any) {
        socket.off();
        done(error);
      });

      socket.on('connect', function() {
        socket.off();
        done(null, new CLISocket(browserId, socket as any));
      });
    });
  }
}

// Misc Utility

/**
 * @param {!Mocha.Runnable} runnable The test or suite to extract titles from.
 * @return {!Array.<string>} The titles of the runnable and its parents.
 */
function getTitles(runnable: Mocha.IRunnable) {
  const titles = [];
  while (runnable && !runnable.root && runnable.title) {
    titles.unshift(runnable.title);
    runnable = runnable.parent as any;
  }
  return titles;
}

/**
 * @param {!Mocha.Runnable} runnable
 * @return {string}
 */
function getState(runnable: Mocha.IRunnable) {
  if (runnable.state === 'passed') {
    return 'passing';
  } else if (runnable.state === 'failed') {
    return 'failing';
  } else if (runnable.pending) {
    return 'pending';
  } else {
    return 'unknown';
  }
}


================================================
FILE: browser/config.ts
================================================
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
 * Google as part of the polymer project is also subject to an additional IP
 * rights grant found at http://polymer.github.io/PATENTS.txt
 */
import ChildRunner from './childrunner.js';
import * as util from './util.js';

export interface Config {
  /**
   * `.js` scripts to be loaded (synchronously) before WCT starts in earnest.
   *
   * Paths are relative to `scriptPrefix`.
   */
  environmentScripts: string[];
  environmentImports: string[];
  /** Absolute root for client scripts. Detected in `setup()` if not set. */
  root: null|string;
  /** By default, we wait for any web component frameworks to load. */
  waitForFrameworks: boolean;
  /**
   * Alternate callback for waiting for tests.
   * `this` for the callback will be the window currently running tests.
   */
  waitFor: null|Function;
  /** How many `.html` suites that can be concurrently loaded & run. */
  numConcurrentSuites: number;
  /** Whether `console.error` should be treated as a test failure. */
  trackConsoleError: boolean;
  /** Configuration passed to mocha.setup. */
  mochaOptions: MochaSetupOptions;
  /** Whether WCT should emit (extremely verbose) debugging log messages. */
  verbose: boolean;
}

/**
 * The global configuration state for WCT's browser client.
 */
export let _config: Config = {
  environmentScripts: !!window.__wctUseNpm ?
      [
        'stacky/browser.js', 'async/lib/async.js', 'lodash/index.js',
        'mocha/mocha.js', 'chai/chai.js', '@polymer/sinonjs/sinon.js',
        'sinon-chai/lib/sinon-chai.js',
        'accessibility-developer-tools/dist/js/axs_testing.js',
        '@polymer/test-fixture/test-fixture.js'
      ] :
      [
        'stacky/browser.js', 'async/lib/async.js', 'lodash/lodash.js',
        'mocha/mocha.js', 'chai/chai.js', 'sinonjs/sinon.js',
        'sinon-chai/lib/sinon-chai.js',
        'accessibility-developer-tools/dist/js/axs_testing.js'
      ],

  environmentImports: !!window.__wctUseNpm ? [] :
                                             ['test-fixture/test-fixture.html'],
  root: null as null | string,
  waitForFrameworks: true,
  waitFor: null as null | Function,
  numConcurrentSuites: 1,
  trackConsoleError: true,
  mochaOptions: {timeout: 10 * 1000},
  verbose: false,
};

/**
 * Merges initial `options` into WCT's global configuration.
 *
 * @param {Object} options The options to merge. See `browser/config.js` for a
 *     reference.
 */
export function setup(options: Config) {
  const childRunner = ChildRunner.current();
  if (childRunner) {
    _deepMerge(_config, childRunner.parentScope.WCT._config);
    // But do not force the mocha UI
    delete _config.mochaOptions.ui;
  }

  if (options && typeof options === 'object') {
    _deepMerge(_config, options);
  }

  if (!_config.root) {
    // Sibling dependencies.
    const root = util.scriptPrefix('browser.js');
    _config.root = util.basePath(root.substr(0, root.length - 1));
    if (!_config.root) {
      throw new Error(
          'Unable to detect root URL for WCT sources. Please set WCT.root before including browser.js');
    }
  }
}

/**
 * Retrieves a configuration value.
 */
export function get<K extends keyof Config>(key: K): Config[K] {
  return _config[key];
}

// Internal
function _deepMerge(target: Partial<Config>, source: Config) {
  Object.keys(source).forEach(function(key) {
    if (target[key] !== null && typeof target[key] === 'object' &&
        !Array.isArray(target[key])) {
      _deepMerge(target[key], source[key]);
    } else {
      target[key] = source[key];
    }
  });
}


================================================
FILE: browser/declarations.ts
================================================
import * as ChaiStatic from 'chai';
import * as SinonStatic from 'sinon';
import * as SocketIOStatic from 'socket.io';
import * as StackyStatic from 'stacky';

import {default as ChildRunner, SharedState} from './childrunner.js';
import {Config} from './config.js';
import MultiReporter from './reporters/multi.js';
import * as suites from './suites.js';

type loadSuitesType = (typeof suites.loadSuites);

declare global {
  interface Window {
    __wctUseNpm?: boolean;
    WebComponents?: WebComponentsStatic;
    Platform?: PlatformStatic;
    Polymer?: PolymerStatic;
    WCT: {
      readonly _ChildRunner: typeof ChildRunner; //
      readonly share: SharedState; //
      readonly _config: Config; //
      readonly loadSuites: loadSuitesType;
      _reporter: MultiReporter;
    };
    mocha: typeof mocha;
    Mocha: typeof Mocha;
    __generatedByWct?: boolean;

    chai: typeof ChaiStatic;
    assert: typeof ChaiStatic.assert;
    expect: typeof ChaiStatic.expect;
  }
  interface WebComponentsStatic {
    ready?(): void;
    flush?(): void;
  }
  interface PlatformStatic {
    performMicrotaskCheckpoint(): void;
  }
  interface PolymerElement {
    _stampTemplate?(): void;
  }
  interface PolymerElementConstructor {
    prototype: PolymerElement;
  }
  interface PolymerStatic {
    flush(): void;
    dom: {flush(): void};
    Element: PolymerElementConstructor;
  }

  interface Element {
    isConnected?: boolean;
  }

  interface Mocha {
    suite: Mocha.ISuite;
  }

  var Stacky: typeof StackyStatic;
  var io: typeof SocketIOStatic;
  var Platform: PlatformStatic;
  var sinon: typeof SinonStatic;
}


================================================
FILE: browser/environment/compatability.ts
================================================
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
 * Google as part of the polymer project is also subject to an additional IP
 * rights grant found at http://polymer.github.io/PATENTS.txt
 */
import ChildRunner from '../childrunner.js';

// polymer-test-tools (and Polymer/tools) support HTML tests where each file is
// expected to call `done()`, which posts a message to the parent window.
window.addEventListener('message', function(event) {
  if (!event.data || (event.data !== 'ok' && !event.data.error)) {
    return;
  }
  const childRunner = ChildRunner.get(event.source);
  if (!childRunner) {
    return;
  }

  childRunner.ready();
  // The name of the suite as exposed to the user.
  const reporter = childRunner.parentScope.WCT._reporter;
  const title = reporter.suiteTitle(event.source.location);
  reporter.emitOutOfBandTest(
      'page-wide tests via global done()', event.data.error, title, true);

  childRunner.done();
});

// Attempt to ensure that we complete a test suite if it is interrupted by a
// document unload.
window.addEventListener('unload', function(_event: BeforeUnloadEvent) {
  // Mocha's hook queue is asynchronous; but we want synchronous behavior if
  // we've gotten to the point of unloading the document.
  Mocha.Runner.immediately = function(callback: () => void) {
    callback();
  };
});


================================================
FILE: browser/environment/errors.ts
================================================
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
 * Google as part of the polymer project is also subject to an additional IP
 * rights grant found at http://polymer.github.io/PATENTS.txt
 */
import * as config from '../config.js';

// We may encounter errors during initialization (for example, syntax errors in
// a test file). Hang onto those (and more) until we are ready to report them.
export let globalErrors: any[] = [];

/**
 * Hook the environment to pick up on global errors.
 */
export function listenForErrors() {
  window.addEventListener('error', function(event) {
    globalErrors.push(event.error);
  });

  // Also, we treat `console.error` as a test failure. Unless you prefer not.
  const origConsole = console;
  const origError = console.error;
  console.error = function wctShimmedError() {
    origError.apply(origConsole, arguments);
    if (config.get('trackConsoleError')) {
      throw 'console.error: ' + Array.prototype.join.call(arguments, ' ');
    }
  };
}


================================================
FILE: browser/environment/helpers.ts
================================================
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
 * Google as part of the polymer project is also subject to an additional IP
 * rights grant found at http://polymer.github.io/PATENTS.txt
 */

export {};

// Make sure that we use native timers, in case they're being stubbed out.
const nativeSetInterval = window.setInterval;
const nativeSetTimeout = window.setTimeout;
const nativeRequestAnimationFrame = window.requestAnimationFrame;



/**
 * Runs `stepFn`, catching any error and passing it to `callback` (Node-style).
 * Otherwise, calls `callback` with no arguments on success.
 *
 * @param {function()} callback
 * @param {function()} stepFn
 */
function safeStep(callback: (error?: any) => void, stepFn: () => void) {
  let err;
  try {
    stepFn();
  } catch (error) {
    err = error;
  }
  callback(err);
}

/**
 * Runs your test at declaration time (before Mocha has begun tests). Handy for
 * when you need to test document initialization.
 *
 * Be aware that any errors thrown asynchronously cannot be tied to your test.
 * You may want to catch them and pass them to the done event, instead. See
 * `safeStep`.
 *
 * @param {string} name The name of the test.
 * @param {function(?function())} testFn The test function. If an argument is
 *     accepted, the test will be treated as async, just like Mocha tests.
 */
function testImmediate(name: string, testFn: Function) {
  if (testFn.length > 0) {
    return testImmediateAsync(name, testFn);
  }

  let err: any;
  try {
    testFn();
  } catch (error) {
    err = error;
  }
  test(name, function(done) {
    done(err);
  });
}

/**
 * An async-only variant of `testImmediate`.
 *
 * @param {string} name
 * @param {function(?function())} testFn
 */
function testImmediateAsync(name: string, testFn: Function) {
  let testComplete = false;
  let err: any;

  test(name, function(done) {
    const intervalId = nativeSetInterval(function() {
      if (!testComplete)
        return;
      clearInterval(intervalId);
      done(err);
    }, 10);
  });

  try {
    testFn(function(error: any) {
      if (error)
        err = error;
      testComplete = true;
    });
  } catch (error) {
    err = error;
    testComplete = true;
  }
}

/**
 * Triggers a flush of any pending events, observations, etc and calls you back
 * after they have been processed.
 *
 * @param {function()} callback
 */
function flush(callback: () => void) {
  // Ideally, this function would be a call to Polymer.dom.flush, but that
  // doesn't support a callback yet
  // (https://github.com/Polymer/polymer-dev/issues/851),
  // ...and there's cross-browser flakiness to deal with.

  // Make sure that we're invoking the callback with no arguments so that the
  // caller can pass Mocha callbacks, etc.
  let done = function done() {
    callback();
  };

  // Because endOfMicrotask is flaky for IE, we perform microtask checkpoints
  // ourselves (https://github.com/Polymer/polymer-dev/issues/114):
  const isIE = navigator.appName === 'Microsoft Internet Explorer';
  if (isIE && window.Platform && window.Platform.performMicrotaskCheckpoint) {
    const reallyDone = done;
    done = function doneIE() {
      Platform.performMicrotaskCheckpoint();
      nativeSetTimeout(reallyDone, 0);
    };
  }

  // Everyone else gets a regular flush.
  let scope;
  if (window.Polymer && window.Polymer.dom && window.Polymer.dom.flush) {
    scope = window.Polymer.dom;
  } else if (window.Polymer && window.Polymer.flush) {
    scope = window.Polymer;
  } else if (window.WebComponents && window.WebComponents.flush) {
    scope = window.WebComponents;
  }
  if (scope) {
    scope.flush();
  }

  // Ensure that we are creating a new _task_ to allow all active microtasks to
  // finish (the code you're testing may be using endOfMicrotask, too).
  nativeSetTimeout(done, 0);
}

/**
 * Advances a single animation frame.
 *
 * Calls `flush`, `requestAnimationFrame`, `flush`, and `callback` sequentially
 * @param {function()} callback
 */
function animationFrameFlush(callback: () => void) {
  flush(function() {
    nativeRequestAnimationFrame(function() {
      flush(callback);
    });
  });
}

/**
 * DEPRECATED: Use `flush`.
 * @param {function} callback
 */
function asyncPlatformFlush(callback: () => void) {
  console.warn(
      'asyncPlatformFlush is deprecated in favor of the more terse flush()');
  return window.flush(callback);
}

export interface MutationEl {
  onMutation(mutationEl: this, cb: () => void): void;
}

/**
 *
 */
function waitFor(
    fn: () => void, next: () => void, intervalOrMutationEl: number|MutationEl,
    timeout: number, timeoutTime: number) {
  timeoutTime = timeoutTime || Date.now() + (timeout || 1000);
  intervalOrMutationEl = intervalOrMutationEl || 32;
  try {
    fn();
  } catch (e) {
    if (Date.now() > timeoutTime) {
      throw e;
    } else {
      if (typeof intervalOrMutationEl !== 'number') {
        intervalOrMutationEl.onMutation(intervalOrMutationEl, function() {
          waitFor(fn, next, intervalOrMutationEl, timeout, timeoutTime);
        });
      } else {
        nativeSetTimeout(function() {
          waitFor(fn, next, intervalOrMutationEl, timeout, timeoutTime);
        }, intervalOrMutationEl);
      }
      return;
    }
  }
  next();
}

declare global {
  interface Window {
    safeStep: typeof safeStep;
    testImmediate: typeof testImmediate;
    testImmediateAsync: typeof testImmediateAsync;
    flush: typeof flush;
    animationFrameFlush: typeof animationFrameFlush;
    asyncPlatformFlush: typeof asyncPlatformFlush;
    waitFor: typeof waitFor;
  }
}

window.safeStep = safeStep;
window.testImmediate = testImmediate;
window.testImmediateAsync = testImmediateAsync;
window.flush = flush;
window.animationFrameFlush = animationFrameFlush;
window.asyncPlatformFlush = asyncPlatformFlush;
window.waitFor = waitFor;


================================================
FILE: browser/environment.ts
================================================
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
 * Google as part of the polymer project is also subject to an additional IP
 * rights grant found at http://polymer.github.io/PATENTS.txt
 */
import * as config from './config.js';
import * as reporters from './reporters.js';
import * as util from './util.js';

/**
 * Loads all environment scripts ...synchronously ...after us.
 */
export function loadSync() {
  util.debug('Loading environment scripts:');
  const a11ySuiteScriptPath = 'web-component-tester/data/a11ySuite.js';
  const scripts = config.get('environmentScripts');
  const a11ySuiteWillBeLoaded =
      window.__generatedByWct || scripts.indexOf(a11ySuiteScriptPath) > -1;

  // We can't inject a11ySuite when running the npm version because it is a
  // module-based script that needs `<script type=module>` and compilation
  // for browsers without module support.
  if (!a11ySuiteWillBeLoaded && !window.__wctUseNpm) {
    // wct is running as a bower dependency, load a11ySuite from data/
    scripts.push(a11ySuiteScriptPath);
  }
  scripts.forEach(function(path) {
    const url = util.expandUrl(path, config.get('root'));
    util.debug('Loading environment script:', url);
    // Synchronous load.
    document.write(
        '<script src="' + encodeURI(url) +
        '"></script>');  // jshint ignore:line
  });
  util.debug('Environment scripts loaded');

  const imports = config.get('environmentImports');
  imports.forEach(function(path) {
    const url = util.expandUrl(path, config.get('root'));
    util.debug('Loading environment import:', url);
    // Synchronous load.
    document.write(
        '<link rel="import" href="' + encodeURI(url) +
        '">');  // jshint ignore:line
  });
  util.debug('Environment imports loaded');
}

/**
 * We have some hard dependencies on things that should be loaded via
 * `environmentScripts`, so we assert that they're present here; and do any
 * post-facto setup.
 */
export function ensureDependenciesPresent() {
  _ensureMocha();
  _checkChai();
}

function _ensureMocha() {
  const Mocha = window.Mocha;
  if (!Mocha) {
    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');
  }
  reporters.injectMocha(Mocha);
  // Magic loading of mocha's stylesheet
  const mochaPrefix = util.scriptPrefix('mocha.js');
  // only load mocha stylesheet for the test runner output
  // Not the end of the world, if it doesn't load.
  if (mochaPrefix && window.top === window.self) {
    util.loadStyle(mochaPrefix + 'mocha.css');
  }
}

function _checkChai() {
  if (!window.chai) {
    util.debug('Chai not present; not registering shorthands');
    return;
  }

  window.assert = window.chai.assert;
  window.expect = window.chai.expect;
}


================================================
FILE: browser/index.ts
================================================
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
 * Google as part of the polymer project is also subject to an additional IP
 * rights grant found at http://polymer.github.io/PATENTS.txt
 */
/**
 * This file is the entry point into `web-component-tester`'s browser client.
 */
// Registers a bunch of globals:
import './environment/helpers.js';

import ChildRunner from './childrunner.js';
import CLISocket from './clisocket.js';
import * as config from './config.js';
import * as environment from './environment.js';
import * as errors from './environment/errors.js';
import * as mocha from './mocha.js';
import * as reporters from './reporters.js';
import MultiReporter from './reporters/multi.js';
import * as suites from './suites.js';
import * as util from './util.js';

// You can configure WCT before it has loaded by assigning your custom
// configuration to the global `WCT`.
config.setup(window.WCT as any as config.Config);

// Maybe some day we'll expose WCT as a module to whatever module registry you
// are using (aka the UMD approach), or as an es6 module.
const WCT = window.WCT = {
  // A generic place to hang data about the current suite. This object is
  // reported
  // back via the `sub-suite-start` and `sub-suite-end` events.
  share: {},
  // Until then, we get to rely on it to expose parent runners to their
  // children.
  _ChildRunner: ChildRunner,
  _reporter: undefined as any,  // assigned below
  _config: config._config,

  // Public API

  /**
   * Loads suites of tests, supporting both `.js` and `.html` files.
   *
   * @param {!Array.<string>} files The files to load.
   */
  loadSuites: suites.loadSuites,
};

// Load Process

errors.listenForErrors();
mocha.stubInterfaces();
environment.loadSync();

// Give any scripts on the page a chance to declare tests and muck with things.
document.addEventListener('DOMContentLoaded', function() {
  util.debug('DOMContentLoaded');

  environment.ensureDependenciesPresent();

  // We need the socket built prior to building its reporter.
  CLISocket.init(function(error, socket) {
    if (error)
      throw error;

    // Are we a child of another run?
    const current = ChildRunner.current();
    const parent = current && current.parentScope.WCT._reporter;
    util.debug('parentReporter:', parent);

    const childSuites = suites.activeChildSuites();
    const reportersToUse = reporters.determineReporters(socket, parent);
    // +1 for any local tests.
    const reporter =
        new MultiReporter(childSuites.length + 1, reportersToUse, parent);
    WCT._reporter = reporter;  // For environment/compatibility.js

    // We need the reporter so that we can report errors during load.
    suites.loadJsSuites(reporter, function(error) {
      // Let our parent know that we're about to start the tests.
      if (current)
        current.ready(error);
      if (error)
        throw error;

      // Emit any errors we've encountered up til now
      errors.globalErrors.forEach(function onError(error) {
        reporter.emitOutOfBandTest('Test Suite Initialization', error);
      });

      suites.runSuites(reporter, childSuites, function(error) {
        // Make sure to let our parent know that we're done.
        if (current)
          current.done();
        if (error)
          throw error;
      });
    });
  });
});


================================================
FILE: browser/mocha/extend.ts
================================================

const interfaceExtensions: Array<() => void> = [];

/**
 * Registers an extension that extends the global `Mocha` implementation
 * with new helper methods. These helper methods will be added to the `window`
 * when tests run for both BDD and TDD interfaces.
 */
export function extendInterfaces(
    helperName: string,
    helperFactory: (
        context: any, teardown: (cb: () => void) => void,
        interfaceName: 'tdd'|'bdd') => void) {
  interfaceExtensions.push(function() {
    const Mocha = window.Mocha;
    // For all Mocha interfaces (probably just TDD and BDD):
    Object.keys((Mocha as any).interfaces)
        .forEach(function(interfaceName: 'tdd'|'bdd') {
          // This is the original callback that defines the interface (TDD or
          // BDD):
          const originalInterface = (Mocha as any).interfaces[interfaceName];
          // This is the name of the "teardown" or "afterEach" property for the
          // current interface:
          const teardownProperty =
              interfaceName === 'tdd' ? 'teardown' : 'afterEach';
          // The original callback is monkey patched with a new one that appends
          // to the global context however we want it to:
          (Mocha as any).interfaces[interfaceName] = function(suite: any) {
            // Call back to the original callback so that we get the base
            // interface:
            originalInterface.apply(this, arguments);
            // Register a listener so that we can further extend the base
            // interface:
            suite.on(
                'pre-require',
                function(context: any, _file: string, _mocha: any) {
                  // Capture a bound reference to the teardown function as a
                  // convenience:
                  const teardown = context[teardownProperty].bind(context);
                  // Add our new helper to the testing context. The helper is
                  // generated by a factory method that receives the context,
                  // the teardown function and the interface name and returns
                  // the new method to be added to that context:
                  context[helperName] =
                      helperFactory(context, teardown, interfaceName);
                });
          };
        });
  });
}

/**
 * Applies any registered interface extensions. The extensions will be applied
 * as many times as this function is called, so don't call it more than once.
 */
export function applyExtensions() {
  interfaceExtensions.forEach(function(applyExtension) {
    applyExtension();
  });
}


================================================
FILE: browser/mocha/fixture.ts
================================================
import {extendInterfaces} from './extend.js';

interface TestFixture extends HTMLElement {
  create(model: object): HTMLElement;
  restore(): void;
}

extendInterfaces('fixture', function(context, teardown) {

  // Return context.fixture if it is already a thing, for backwards
  // compatibility with `test-fixture-mocha.js`:
  return context.fixture || function fixture(fixtureId: string, model: object) {

    // Automatically register a teardown callback that will restore the
    // test-fixture:
    teardown(function() {
      (document.getElementById(fixtureId) as TestFixture).restore();
    });

    // Find the test-fixture with the provided ID and create it, returning
    // the results:
    return (document.getElementById(fixtureId) as TestFixture).create(model);
  };
});


================================================
FILE: browser/mocha/replace.ts
================================================
import {extendInterfaces} from './extend.js';

// replacement map stores what should be
let replacements = {};
let replaceTeardownAttached = false;

/**
 * replace
 *
 * The replace addon allows the tester to replace all usages of one element with
 * another element within all Polymer elements created within the time span of
 * the test. Usage example:
 *
 * beforeEach(function() {
 *   replace('x-foo').with('x-fake-foo');
 * });
 *
 * All annotations and attributes will be set on the placement element the way
 * they were set for the original element.
 */
extendInterfaces('replace', function(_context, teardown) {
  return function replace(oldTagName: string) {
    return {
      with: function(tagName: string) {
        // Standardizes our replacements map
        oldTagName = oldTagName.toLowerCase();
        tagName = tagName.toLowerCase();

        replacements[oldTagName] = tagName;

        // If the function is already a stub, restore it to original
        if ((document.importNode as any).isSinonProxy) {
          return;
        }

        if (!window.Polymer.Element) {
          window.Polymer.Element = function() {};
          window.Polymer.Element.prototype._stampTemplate = function() {};
        }

        // Keep a reference to the original `document.importNode`
        // implementation for later:
        const originalImportNode = document.importNode;

        // Use Sinon to stub `document.ImportNode`:
        sinon.stub(
            document, 'importNode', function(origContent: any, deep: boolean) {
              const templateClone = document.createElement('template');
              const content = templateClone.content;
              const inertDoc = content.ownerDocument;

              // imports node from inertDoc which holds inert nodes.
              templateClone.content.appendChild(
                  inertDoc.importNode(origContent, true));

              // optional arguments are not optional on IE.
              const nodeIterator = document.createNodeIterator(
                  content, NodeFilter.SHOW_ELEMENT, null, true);
              let node;

              // Traverses the tree. A recently-replaced node will be put next,
              // so if a node is replaced, it will be checked if it needs to be
              // replaced again.
              while (node = nodeIterator.nextNode() as Element) {
                let currentTagName = node.tagName.toLowerCase();

                if (replacements.hasOwnProperty(currentTagName)) {
                  currentTagName = replacements[currentTagName];

                  // find the final tag name.
                  while (replacements[currentTagName]) {
                    currentTagName = replacements[currentTagName];
                  }

                  // Create a replacement:
                  const replacement = document.createElement(currentTagName);

                  // For all attributes in the original node..
                  for (let index = 0; index < node.attributes.length; ++index) {
                    // Set that attribute on the replacement:
                    replacement.setAttribute(
                        node.attributes[index].name,
                        node.attributes[index].value);
                  }

                  // Replace the original node with the replacement node:
                  node.parentNode.replaceChild(replacement, node);
                }
              }

              return originalImportNode.call(this, content, deep);
            });

        if (!replaceTeardownAttached) {
          // After each test...
          teardown(function() {
            replaceTeardownAttached = true;
            // Restore the stubbed version of `document.importNode`:
            const documentImportNode = document.importNode as any;
            if (documentImportNode.isSinonProxy) {
              documentImportNode.restore();
            }

            // Empty the replacement map
            replacements = {};
          });
        }
      }
    };
  };
});


================================================
FILE: browser/mocha/stub.ts
================================================
import {extendInterfaces} from './extend.js';

/**
 * stub
 *
 * The stub addon allows the tester to partially replace the implementation of
 * an element with some custom implementation. Usage example:
 *
 * beforeEach(function() {
 *   stub('x-foo', {
 *     attached: function() {
 *       // Custom implementation of the `attached` method of element `x-foo`..
 *     },
 *     otherMethod: function() {
 *       // More custom implementation..
 *     },
 *     getterSetterProperty: {
 *       get: function() {
 *         // Custom getter implementation..
 *       },
 *       set: function() {
 *         // Custom setter implementation..
 *       }
 *     },
 *     // etc..
 *   });
 * });
 */
extendInterfaces('stub', function(_context, teardown) {

  return function stub(tagName: string, implementation: object) {
    // Find the prototype of the element being stubbed:
    const proto = document.createElement(tagName).constructor.prototype;

    // For all keys in the implementation to stub with..
    const stubs = Object.keys(implementation).map(function(key) {
      // Stub the method on the element prototype with Sinon:
      return sinon.stub(proto, key, implementation[key]);
    });

    // After all tests..
    teardown(function() {
      stubs.forEach(function(stub) {
        stub.restore();
      });
    });
  };
});


================================================
FILE: browser/mocha.ts
================================================
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
 * Google as part of the polymer project is also subject to an additional IP
 * rights grant found at http://polymer.github.io/PATENTS.txt
 */
import './mocha/fixture.js';
import './mocha/stub.js';
import './mocha/replace.js';

import * as config from './config.js';
import {applyExtensions} from './mocha/extend.js';

// Mocha global helpers, broken out by testing method.
//
// Keys are the method for a particular interface; values are their analog in
// the opposite interface.
const MOCHA_EXPORTS = {
  // https://github.com/visionmedia/mocha/blob/master/lib/interfaces/tdd.js
  tdd: {
    'setup': '"before"',
    'teardown': '"after"',
    'suiteSetup': '"beforeEach"',
    'suiteTeardown': '"afterEach"',
    'suite': '"describe" or "context"',
    'test': '"it" or "specify"',
  },
  // https://github.com/visionmedia/mocha/blob/master/lib/interfaces/bdd.js
  bdd: {
    'before': '"setup"',
    'after': '"teardown"',
    'beforeEach': '"suiteSetup"',
    'afterEach': '"suiteTeardown"',
    'describe': '"suite"',
    'context': '"suite"',
    'xdescribe': '"suite.skip"',
    'xcontext': '"suite.skip"',
    'it': '"test"',
    'xit': '"test.skip"',
    'specify': '"test"',
    'xspecify': '"test.skip"',
  },
};

/**
 * Exposes all Mocha methods up front, configuring and running mocha
 * automatically when you call them.
 *
 * The assumption is that it is a one-off (sub-)suite of tests being run.
 */
export function stubInterfaces() {
  const keys = Object.keys(MOCHA_EXPORTS) as Array<keyof typeof MOCHA_EXPORTS>;
  keys.forEach(function(ui) {
    Object.keys(MOCHA_EXPORTS[ui]).forEach(function(key) {
      window[key] = function wrappedMochaFunction() {
        _setupMocha(ui, key, MOCHA_EXPORTS[ui][key]);
        if (!window[key] || window[key] === wrappedMochaFunction) {
          throw new Error('Expected mocha.setup to define ' + key);
        }
        window[key].apply(window, arguments);
      };
    });
  });
}

// Whether we've called `mocha.setup`
const _mochaIsSetup = false;

/**
 * @param {string} ui Sets up mocha to run `ui`-style tests.
 * @param {string} key The method called that triggered this.
 * @param {string} alternate The matching method in the opposite interface.
 */
function _setupMocha(ui: 'tdd'|'bdd', key: string, alternate: 'string') {
  const mochaOptions = config.get('mochaOptions');
  if (mochaOptions.ui && mochaOptions.ui !== ui) {
    const message = 'Mixing ' + mochaOptions.ui + ' and ' + ui +
        ' Mocha styles is not supported. ' +
        'You called "' + key + '". Did you mean ' + alternate + '?';
    throw new Error(message);
  }
  if (_mochaIsSetup) {
    return;
  }

  applyExtensions();
  mochaOptions.ui = ui;
  mocha.setup(mochaOptions);  // Note that the reporter is configured in run.js.
}


================================================
FILE: browser/more-declarations.ts
================================================
declare namespace Mocha {
  interface UtilsStatic {
    highlightTags(somethingSomething: string): void;
  }
  let utils: UtilsStatic;
  interface IRunner extends NodeJS.EventEmitter {
    name?: string;
    total: number;
  }

  interface IRunnable {
    parent: ISuite;
    root: boolean;
    state: 'passed'|'failed'|undefined;
    pending: boolean;
  }

  interface ISuite {
    root: boolean;
  }

  let Runner: {prototype: IRunner; immediately(callback: () => void): void};
}

declare namespace SocketIO {
  interface Server {
    off(): void;
  }
}


================================================
FILE: browser/reporters/console.ts
================================================
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
 * Google as part of the polymer project is also subject to an additional IP
 * rights grant found at http://polymer.github.io/PATENTS.txt
 */
import * as util from '../util.js';

// We capture console events when running tests; so make sure we have a
// reference to the original one.
const console = window.console;

const FONT =
    ';font: normal 13px "Roboto", "Helvetica Neue", "Helvetica", sans-serif;';
const STYLES = {
  plain: FONT,
  suite: 'color: #5c6bc0' + FONT,
  test: FONT,
  passing: 'color: #259b24' + FONT,
  pending: 'color: #e65100' + FONT,
  failing: 'color: #c41411' + FONT,
  stack: 'color: #c41411',
  results: FONT + 'font-size: 16px',
};

// I don't think we can feature detect this one...
const userAgent = navigator.userAgent.toLowerCase();
const CAN_STYLE_LOG = userAgent.match('firefox') || userAgent.match('webkit');
const CAN_STYLE_GROUP = userAgent.match('webkit');
// Track the indent for faked `console.group`
let logIndent = '';

function log(text: string, style?: keyof typeof STYLES) {
  text = text.split('\n')
             .map(function(l) {
               return logIndent + l;
             })
             .join('\n');
  if (CAN_STYLE_LOG) {
    console.log('%c' + text, STYLES[style] || STYLES.plain);
  } else {
    console.log(text);
  }
}

function logGroup(text: string, style?: keyof typeof STYLES) {
  if (CAN_STYLE_GROUP) {
    console.group('%c' + text, STYLES[style] || STYLES.plain);
  } else if (console.group) {
    console.group(text);
  } else {
    logIndent = logIndent + '  ';
    log(text, style);
  }
}

function logGroupEnd() {
  if (console.groupEnd) {
    console.groupEnd();
  } else {
    logIndent = logIndent.substr(0, logIndent.length - 2);
  }
}

function logException(error: Error) {
  log(error.stack || error.message || (error + ''), 'stack');
}

/**
 * A Mocha reporter that logs results out to the web `console`.
 */
export default class Console {
  /**
   * @param runner The runner that is being reported on.
   */
  constructor(runner: Mocha.IRunner) {
    Mocha.reporters.Base.call(this, runner);

    runner.on('suite', function(suite: Mocha.ISuite) {
      if (suite.root) {
        return;
      }
      logGroup(suite.title, 'suite');
    }.bind(this));

    runner.on('suite end', function(suite: Mocha.ISuite) {
      if (suite.root) {
        return;
      }
      logGroupEnd();
    }.bind(this));

    runner.on('test', function(test: Mocha.ITest) {
      logGroup(test.title, 'test');
    }.bind(this));

    runner.on('pending', function(test: Mocha.ITest) {
      logGroup(test.title, 'pending');
    }.bind(this));

    runner.on('fail', function(_test: Mocha.ITest, error: any) {
      logException(error);
    }.bind(this));

    runner.on('test end', function(_test: Mocha.ITest) {
      logGroupEnd();
    }.bind(this));

    runner.on('end', this.logSummary.bind(this));
  }

  /** Prints out a final summary of test results. */
  logSummary() {
    logGroup('Test Results', 'results');

    if (this.stats.failures > 0) {
      log(util.pluralizedStat(this.stats.failures, 'failing'), 'failing');
    }
    if (this.stats.pending > 0) {
      log(util.pluralizedStat(this.stats.pending, 'pending'), 'pending');
    }
    log(util.pluralizedStat(this.stats.passes, 'passing'));

    if (!this.stats.failures) {
      log('test suite passed', 'passing');
    }
    log('Evaluated ' + this.stats.tests + ' tests in ' +
        (this.stats as any).duration + 'ms.');
    logGroupEnd();
  }
}

export default interface Console extends Mocha.reporters.Base {}


================================================
FILE: browser/reporters/html.ts
================================================
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
 * Google as part of the polymer project is also subject to an additional IP
 * rights grant found at http://polymer.github.io/PATENTS.txt
 */

/**
 * WCT-specific behavior on top of Mocha's default HTML reporter.
 *
 * @param {!Mocha.Runner} runner The runner that is being reported on.
 */
export default function HTML(runner: Mocha.IRunner) {
  const output = document.createElement('div');
  output.id = 'mocha';
  document.body.appendChild(output);

  runner.on('suite', function(_test: any) {
    this.total = runner.total;
  }.bind(this));

  Mocha.reporters.HTML.call(this, runner);
}

// Woo! What a hack. This just saves us from adding a bunch of complexity around
// style loading.
const style = document.createElement('style');
style.textContent = `
    html, body {
      position: relative;
      height: 100%;
      width:  100%;
      min-width: 900px;
    }
    #mocha, #subsuites {
      height: 100%;
      position: absolute;
      top: 0;
    }
    #mocha {
      box-sizing: border-box;
      margin: 0 !important;
      padding: 60px 20px;
      right: 0;
      left: 500px;
    }
    #subsuites {
      -ms-flex-direction: column;
      -webkit-flex-direction: column;
      display: -ms-flexbox;
      display: -webkit-flex;
      display: flex;
      flex-direction: column;
      left: 0;
      width: 500px;
    }
    #subsuites .subsuite {
      border: 0;
      width: 100%;
      height: 100%;
    }
    #mocha .test.pass .duration {
      color: #555 !important;
    }
`;
document.head.appendChild(style);


================================================
FILE: browser/reporters/multi.ts
================================================
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
 * Google as part of the polymer project is also subject to an additional IP
 * rights grant found at http://polymer.github.io/PATENTS.txt
 */
import * as util from '../util.js';

const STACKY_CONFIG = {
  indent: '  ',
  locationStrip: [
    /^https?:\/\/[^\/]+/,
    /\?.*$/,
  ],
  filter(line: {location: string}) {
    return !!line.location.match(/\/web-component-tester\/[^\/]+(\?.*)?$/);
  },
};

// https://github.com/visionmedia/mocha/blob/master/lib/runner.js#L36-46
const MOCHA_EVENTS = [
  'start', 'end', 'suite', 'suite end', 'test', 'test end', 'hook', 'hook end',
  'pass', 'fail', 'pending', 'childRunner end'
];

// Until a suite has loaded, we assume this many tests in it.
const ESTIMATED_TESTS_PER_SUITE = 3;

export interface Reporter {}

export interface ReporterFactory { new(parent: MultiReporter): Reporter; }

interface ExtendedTest extends Mocha.ITest {
  err: any;
}

/**
 * A Mocha-like reporter that combines the output of multiple Mocha suites.
 */
export default class MultiReporter implements Reporter {
  private readonly reporters: ReadonlyArray<Reporter>;
  private readonly parent: MultiReporter|undefined;
  private readonly basePath: string;
  total: number;
  private currentRunner: null|Mocha.IRunner;
  /** Arguments that would be called on emit(). */
  private pendingEvents: Array<any[]>;
  private complete: boolean|undefined;

  /**
   * @param numSuites The number of suites that will be run, in order to
   *     estimate the total number of tests that will be performed.
   * @param reporters The set of reporters that
   *     should receive the unified event stream.
   * @param parent The parent reporter, if present.
   */
  constructor(
      numSuites: number, reporters: ReporterFactory[],
      parent: MultiReporter|undefined) {
    this.reporters = reporters.map((reporter) => {
      return new reporter(this);
    });

    this.parent = parent;
    this.basePath = parent && parent.basePath || util.basePath(window.location);

    this.total = numSuites * ESTIMATED_TESTS_PER_SUITE;
    // Mocha reporters assume a stream of events, so we have to be careful to
    // only report on one runner at a time...
    this.currentRunner = null;
    // ...while we buffer events for any other active runners.
    this.pendingEvents = [];

    this.emit('start');
  }

  /**
   * @param location The location this reporter represents.
   * @return A reporter-like "class" for each child suite
   *     that should be passed to `mocha.run`.
   */
  childReporter(location: Location|string): ReporterFactory {
    const name = this.suiteTitle(location);
    // The reporter is used as a constructor, so we can't depend on `this` being
    // properly bound.
    const self = this;
    return class ChildReporter {
      constructor(runner: Mocha.IRunner) {
        runner.name = window.name;
        self.bindChildRunner(runner);
      }

      static title = window.name;
    };
  }

  /** Must be called once all runners have finished. */
  done() {
    this.complete = true;
    this.flushPendingEvents();
    this.emit('end');
  }

  /**
   * Emit a top level test that is not part of any suite managed by this
   * reporter.
   *
   * Helpful for reporting on global errors, loading issues, etc.
   *
   * @param title The title of the test.
   * @param error An error associated with this test. If falsy, test is
   *     considered to be passing.
   * @param suiteTitle Title for the suite that's wrapping the test.
   * @param estimated If this test was included in the original
   *     estimate of `numSuites`.
   */
  emitOutOfBandTest(
      title: string, error?: any, suiteTitle?: string, estimated?: boolean) {
    util.debug('MultiReporter#emitOutOfBandTest(', arguments, ')');
    const root: Mocha.ISuite = new (Mocha as any).Suite(suiteTitle || '');
    const test: ExtendedTest = new (Mocha as any).Test(title, function() {});
    test.parent = root;
    test.state = error ? 'failed' : 'passed';
    test.err = error;

    if (!estimated) {
      this.total = this.total + ESTIMATED_TESTS_PER_SUITE;
    }

    const runner = {total: 1} as Mocha.IRunner;
    this.proxyEvent('start', runner);
    this.proxyEvent('suite', runner, root);
    this.proxyEvent('test', runner, test);
    if (error) {
      this.proxyEvent('fail', runner, test, error);
    } else {
      this.proxyEvent('pass', runner, test);
    }
    this.proxyEvent('test end', runner, test);
    this.proxyEvent('suite end', runner, root);
    this.proxyEvent('end', runner);
  }

  /**
   * @param {!Location|string} location
   * @return {string}
   */
  suiteTitle(location: Location|string) {
    let path = util.relativeLocation(location, this.basePath);
    path = util.cleanLocation(path);
    return path;
  }

  // Internal Interface

  /** @param {!Mocha.runners.Base} runner The runner to listen to events for. */
  private bindChildRunner(runner: Mocha.IRunner) {
    MOCHA_EVENTS.forEach((eventName) => {
      runner.on(eventName, this.proxyEvent.bind(this, eventName, runner));
    });
  }

  /**
   * Evaluates an event fired by `runner`, proxying it forward or buffering it.
   *
   * @param {string} eventName
   * @param {!Mocha.runners.Base} runner The runner that emitted this event.
   * @param {...*} var_args Any additional data passed as part of the event.
   */
  private proxyEvent(
      eventName: string, runner: Mocha.IRunner, ..._args: any[]) {
    const extraArgs = Array.prototype.slice.call(arguments, 2);
    if (this.complete) {
      console.warn(
          'out of order Mocha event for ' + runner.name + ':', eventName,
          extraArgs);
      return;
    }

    if (this.currentRunner && runner !== this.currentRunner) {
      this.pendingEvents.push(Array.prototype.slice.call(arguments));
      return;
    }
    util.debug('MultiReporter#proxyEvent(', arguments, ')');

    // This appears to be a Mocha bug: Tests failed by passing an error to their
    // done function don't set `err` properly.
    //
    // TODO(nevir): Track down.
    if (eventName === 'fail' && !extraArgs[0].err) {
      extraArgs[0].err = extraArgs[1];
    }

    if (eventName === 'start') {
      this.onRunnerStart(runner);
    } else if (eventName === 'end') {
      this.onRunnerEnd(runner);
    } else {
      this.cleanEvent(eventName, runner, extraArgs);
      this.emit.apply(this, [eventName].concat(extraArgs));
    }
  }

  /**
   * Cleans or modifies an event if needed.
   *
   * @param eventName
   * @param runner The runner that emitted this event.
   * @param extraArgs
   */
  private cleanEvent(
      eventName: string, _runner: Mocha.IRunner, extraArgs: any[]) {
    // Suite hierarchy
    if (extraArgs[0]) {
      extraArgs[0] = this.showRootSuite(extraArgs[0]);
    }

    // Normalize errors
    if (eventName === 'fail') {
      extraArgs[1] = Stacky.normalize(extraArgs[1], STACKY_CONFIG);
    }
    if (extraArgs[0] && extraArgs[0].err) {
      extraArgs[0].err = Stacky.normalize(extraArgs[0].err, STACKY_CONFIG);
    }
  }

  /**
   * We like to show the root suite's title, which requires a little bit of
   * trickery in the suite hierarchy.
   *
   * @param {!Mocha.Runnable} node
   */
  private showRootSuite(node: Mocha.IRunnable) {
    const leaf = node = Object.create(node);
    while (node && node.parent) {
      const wrappedParent = Object.create(node.parent);
      node.parent = wrappedParent;
      node = wrappedParent;
    }
    node.root = false;

    return leaf;
  }

  /** @param {!Mocha.runners.Base} runner */
  private onRunnerStart(runner: Mocha.IRunner) {
    util.debug('MultiReporter#onRunnerStart:', runner.name);
    this.total = this.total - ESTIMATED_TESTS_PER_SUITE + runner.total;
    this.currentRunner = runner;
  }

  /** @param {!Mocha.runners.Base} runner */
  private onRunnerEnd(runner: Mocha.IRunner) {
    util.debug('MultiReporter#onRunnerEnd:', runner.name);
    this.currentRunner = null;
    this.flushPendingEvents();
  }

  /**
   * Flushes any buffered events and runs them through `proxyEvent`. This will
   * loop until all buffered runners are complete, or we have run out of
   * buffered events.
   */
  private flushPendingEvents() {
    const events = this.pendingEvents;
    this.pendingEvents = [];
    events.forEach((eventArgs) => {
      this.proxyEvent.apply(this, eventArgs);
    });
  }
}

export default interface MultiReporter extends Mocha.IRunner,
                                               NodeJS.EventEmitter {}


================================================
FILE: browser/reporters/title.ts
================================================
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
 * Google as part of the polymer project is also subject to an additional IP
 * rights grant found at http://polymer.github.io/PATENTS.txt
 */
import * as util from '../util.js';

const ARC_OFFSET = 0;  // start at the right.
const ARC_WIDTH = 6;

/**
 * A Mocha reporter that updates the document's title and favicon with
 * at-a-glance stats.
 *
 * @param {!Mocha.Runner} runner The runner that is being reported on.
 */
export default class Title {
  runner: Mocha.IRunner;
  constructor(runner: Mocha.IRunner) {
    Mocha.reporters.Base.call(this, runner);

    runner.on('test end', this.report.bind(this));
  }

  /** Reports current stats via the page title and favicon. */
  report() {
    this.updateTitle();
    this.updateFavicon();
  }

  /** Updates the document title with a summary of current stats. */
  updateTitle() {
    if (this.stats.failures > 0) {
      document.title = util.pluralizedStat(this.stats.failures, 'failing');
    } else {
      document.title = util.pluralizedStat(this.stats.passes, 'passing');
    }
  }

  /** Updates the document's favicon w/ a summary of current stats. */
  updateFavicon() {
    const canvas = document.createElement('canvas');
    canvas.height = canvas.width = 32;
    const context = canvas.getContext('2d');

    const passing = this.stats.passes;
    const pending = this.stats.pending;
    const failing = this.stats.failures;
    const total = Math.max(this.runner.total, passing + pending + failing);
    drawFaviconArc(context, total, 0, passing, '#0e9c57');
    drawFaviconArc(context, total, passing, pending, '#f3b300');
    drawFaviconArc(context, total, pending + passing, failing, '#ff5621');

    this.setFavicon(canvas.toDataURL());
  }

  /** Sets the current favicon by URL. */
  setFavicon(url: string) {
    const current = document.head.querySelector('link[rel="icon"]');
    if (current) {
      document.head.removeChild(current);
    }

    const link = document.createElement('link');
    link.rel = 'icon';
    link.type = 'image/x-icon';
    link.href = url;
    link.setAttribute('sizes', '32x32');
    document.head.appendChild(link);
  }
}

/**
 * Draws an arc for the favicon status, relative to the total number of tests.
 */
function drawFaviconArc(
    context: CanvasRenderingContext2D, total: number, start: number,
    length: number, color: string) {
  const arcStart = ARC_OFFSET + Math.PI * 2 * (start / total);
  const arcEnd = ARC_OFFSET + Math.PI * 2 * ((start + length) / total);

  context.beginPath();
  context.strokeStyle = color;
  context.lineWidth = ARC_WIDTH;
  context.arc(16, 16, 16 - ARC_WIDTH / 2, arcStart, arcEnd);
  context.stroke();
}

export default interface Title extends Mocha.reporters.Base {}


================================================
FILE: browser/reporters.ts
================================================
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
 * Google as part of the polymer project is also subject to an additional IP
 * rights grant found at http://polymer.github.io/PATENTS.txt
 */
import CLISocket from './clisocket.js';
import ConsoleReporter from './reporters/console.js';
import HTMLReporter from './reporters/html.js';
import MultiReporter, {ReporterFactory} from './reporters/multi.js';
import TitleReporter from './reporters/title.js';
import * as suites from './suites.js';

export let htmlSuites: Array<undefined> = [];
export let jsSuites: Array<undefined> = [];

/**
 * @param {CLISocket} socket The CLI socket, if present.
 * @param {MultiReporter} parent The parent reporter, if present.
 * @return {!Array.<!Mocha.reporters.Base} The reporters that should be used.
 */
export function determineReporters(
    socket: CLISocket, parent: MultiReporter): ReporterFactory[] {
  // Parents are greedy.
  if (parent) {
    return [parent.childReporter(window.location)];
  }

  // Otherwise, we get to run wild without any parental supervision!
  const reporters: Array<ReporterFactory> = [TitleReporter, ConsoleReporter];

  if (socket) {
    reporters.push(function(runner: MultiReporter) {
      socket.observe(runner);
    } as any);
  }

  if (suites.htmlSuites.length > 0 || suites.jsSuites.length > 0) {
    reporters.push(HTMLReporter as any);
  }

  return reporters;
}

export type MochaStatic = typeof Mocha;
/**
 * Yeah, hideous, but this allows us to be loaded before Mocha, which is handy.
 */
export function injectMocha(Mocha: MochaStatic) {
  _injectPrototype(ConsoleReporter, Mocha.reporters.Base.prototype);
  _injectPrototype(HTMLReporter, Mocha.reporters.HTML.prototype);
  // Mocha doesn't expose its `EventEmitter` shim directly, so:
  _injectPrototype(
      MultiReporter, Object.getPrototypeOf(Mocha.Runner.prototype));
}

function _injectPrototype(klass: any, prototype: any) {
  const newPrototype = Object.create(prototype);
  // Only support
  Object.keys(klass.prototype).forEach(function(key) {
    newPrototype[key] = klass.prototype[key];
  });

  klass.prototype = newPrototype;
}


================================================
FILE: browser/suites.ts
================================================
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
 * Google as part of the polymer project is also subject to an additional IP
 * rights grant found at http://polymer.github.io/PATENTS.txt
 */
import ChildRunner from './childrunner.js';
import * as config from './config.js';
import MultiReporter from './reporters/multi.js';
import * as util from './util.js';

export let htmlSuites: string[] = [];
export let jsSuites: string[] = [];

// We process grep ourselves to avoid loading suites that will be filtered.
let GREP = util.getParam('grep');
// work around mocha bug (https://github.com/mochajs/mocha/issues/2070)
if (GREP) {
  GREP = GREP.replace(/\\\./g, '.');
}

/**
 * Loads suites of tests, supporting both `.js` and `.html` files.
 *
 * @param files The files to load.
 */
export function loadSuites(files: string[]) {
  files.forEach(function(file) {
    if (/\.js(\?.*)?$/.test(file)) {
      jsSuites.push(file);
    } else if (/\.html(\?.*)?$/.test(file)) {
      htmlSuites.push(file);
    } else {
      throw new Error('Unknown resource type: ' + file);
    }
  });
}

/**
 * @return The child suites that should be loaded, ignoring
 *     those that would not match `GREP`.
 */
export function activeChildSuites(): string[] {
  let subsuites = htmlSuites;
  if (GREP) {
    const cleanSubsuites = [];
    for (let i = 0, subsuite; subsuite = subsuites[i]; i++) {
      if (GREP.indexOf(util.cleanLocation(subsuite)) !== -1) {
        cleanSubsuites.push(subsuite);
      }
    }
    subsuites = cleanSubsuites;
  }
  return subsuites;
}

/**
 * Loads all `.js` sources requested by the current suite.
 */
export function loadJsSuites(
    _reporter: MultiReporter, done: (error: Error) => void) {
  util.debug('loadJsSuites', jsSuites);

  const loaders = jsSuites.map(function(file) {
    // We only support `.js` dependencies for now.
    return util.loadScript.bind(util, file);
  });

  util.parallel(loaders, done);
}

export function runSuites(
    reporter: MultiReporter, childSuites: string[],
    done: (error?: any) => void) {
  util.debug('runSuites');

  const suiteRunners: Array<(next: () => void) => void> = [
    // Run the local tests (if any) first, not stopping on error;
    _runMocha.bind(null, reporter),
  ];

  // As well as any sub suites. Again, don't stop on error.
  childSuites.forEach(function(file) {
    suiteRunners.push(function(next) {
      const childRunner = new ChildRunner(file, window);
      reporter.emit('childRunner start', childRunner);
      childRunner.run(function(error) {
        reporter.emit('childRunner end', childRunner);
        if (error)
          reporter.emitOutOfBandTest(file, error);
        next();
      });
    });
  });

  util.parallel(
      suiteRunners, config.get('numConcurrentSuites'), function(error) {
        reporter.done();
        done(error);
      });
}

/**
 * Kicks off a mocha run, waiting for frameworks to load if necessary.
 *
 * @param {!MultiReporter} reporter Where to send Mocha's events.
 * @param {function} done A callback fired, _no error is passed_.
 */
function _runMocha(reporter: MultiReporter, done: () => void, waited: boolean) {
  if (config.get('waitForFrameworks') && !waited) {
    const waitFor =
        (config.get('waitFor') || util.whenFrameworksReady).bind(window);
    waitFor(_runMocha.bind(null, reporter, done, true));
    return;
  }
  util.debug('_runMocha');
  const mocha = window.mocha;
  const Mocha = window.Mocha;

  mocha.reporter(reporter.childReporter(window.location));
  mocha.suite.title = reporter.suiteTitle(window.location);
  mocha.grep(GREP);

  // We can't use `mocha.run` because it bashes over grep, invert, and friends.
  // See https://github.com/visionmedia/mocha/blob/master/support/tail.js#L137
  const runner = Mocha.prototype.run.call(mocha, function(_error: any) {
    if (document.getElementById('mocha')) {
      Mocha.utils.highlightTags('code');
    }
    done();  // We ignore the Mocha failure count.
  });

  // Mocha's default `onerror` handling strips the stack (to support really old
  // browsers). We upgrade this to get better stacks for async errors.
  //
  // TODO(nevir): Can we expand support to other browsers?
  if (navigator.userAgent.match(/chrome/i)) {
    window.onerror = null;
    window.addEventListener('error', function(event) {
      if (!event.error)
        return;
      if (event.error.ignore)
        return;
      runner.uncaught(event.error);
    });
  }
}


================================================
FILE: browser/tsconfig.json
================================================
{
  "compilerOptions": {
    "target": "es5",
    "module": "es6",
    "moduleResolution": "node",
    "isolatedModules": false,
    "noImplicitAny": true,
    "noUnusedLocals": false,
    "noUnusedParameters": true,
    "noImplicitThis": false,
    "strictNullChecks": false,
    "removeComments": false,
    "preserveConstEnums": true,
    "suppressImplicitAnyIndexErrors": true,
    "lib": [
      "es2017",
      "dom"
    ],
    "sourceMap": true,
    "pretty": true
  },
  "exclude": [
    "node_modules"
  ],
  "include": [
    "*.ts",
    "**/*.ts",
    "../custom_typings/*.d.ts"
  ]
}


================================================
FILE: browser/util.ts
================================================
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
 * Google as part of the polymer project is also subject to an additional IP
 * rights grant found at http://polymer.github.io/PATENTS.txt
 */

import * as config from './config.js';

/**
 * @param {function()} callback A function to call when the active web component
 *     frameworks have loaded.
 */
export function whenFrameworksReady(callback: () => void) {
  debug('whenFrameworksReady');
  const done = function() {
    debug('whenFrameworksReady done');
    callback();
  };

  // If webcomponents script is in the document, wait for WebComponentsReady.
  if (window.WebComponents && !window.WebComponents.ready) {
    debug('WebComponentsReady?');
    window.addEventListener('WebComponentsReady', function wcReady() {
      window.removeEventListener('WebComponentsReady', wcReady);
      debug('WebComponentsReady');
      done();
    });
  } else {
    done();
  }
}

/**
 * @return {string} '<count> <kind> tests' or '<count> <kind> test'.
 */
export function pluralizedStat(count: number, kind: string): string {
  if (count === 1) {
    return count + ' ' + kind + ' test';
  } else {
    return count + ' ' + kind + ' tests';
  }
}

/**
 * @param {string} path The URI of the script to load.
 * @param {function} done
 */
export function loadScript(path: string, done: (error?: any) => void) {
  const script = document.createElement('script');
  script.src = path;
  if (done) {
    script.onload = done.bind(null, null);
    script.onerror = done.bind(null, 'Failed to load script ' + script.src);
  }
  document.head.appendChild(script);
}

/**
 * @param {string} path The URI of the stylesheet to load.
 * @param {function} done
 */
export function loadStyle(path: string, done?: () => void) {
  const link = document.createElement('link');
  link.rel = 'stylesheet';
  link.href = path;
  if (done) {
    link.onload = done.bind(null, null);
    link.onerror = done.bind(null, 'Failed to load stylesheet ' + link.href);
  }
  document.head.appendChild(link);
}

/**
 * @param {...*} var_args Logs values to the console when the `debug`
 *     configuration option is true.
 */
export function debug(...var_args: any[]) {
  if (!config.get('verbose')) {
    return;
  }
  const args = [window.location.pathname, ...var_args];
  (console.debug || console.log).apply(console, args);
}

// URL Processing

/**
 * @param {string} url
 * @return {{base: string, params: string}}
 */
export function parseUrl(url: string) {
  const parts = url.match(/^(.*?)(?:\?(.*))?$/);
  return {
    base: parts[1],
    params: getParams(parts[2] || ''),
  };
}

/**
 * Expands a URL that may or may not be relative to `base`.
 *
 * @param {string} url
 * @param {string} base
 * @return {string}
 */
export function expandUrl(url: string, base: string) {
  if (!base)
    return url;
  if (url.match(/^(\/|https?:\/\/)/))
    return url;
  if (base.substr(base.length - 1) !== '/') {
    base = base + '/';
  }
  return base + url;
}

export interface Params { [param: string]: string[]; }

/**
 * @param {string=} opt_query A query string to parse.
 * @return {!Object<string, !Array<string>>} All params on the URL's query.
 */
export function getParams(query?: string): Params {
  query = typeof query === 'string' ? query : window.location.search;
  if (query.substring(0, 1) === '?') {
    query = query.substring(1);
  }
  // python's SimpleHTTPServer tacks a `/` on the end of query strings :(
  if (query.slice(-1) === '/') {
    query = query.substring(0, query.length - 1);
  }
  if (query === '')
    return {};

  const result: {[param: string]: string[]} = {};
  query.split('&').forEach(function(part) {
    const pair = part.split('=');
    if (pair.length !== 2) {
      console.warn('Invalid URL query part:', part);
      return;
    }
    const key = decodeURIComponent(pair[0]);
    const value = decodeURIComponent(pair[1]);

    if (!result[key]) {
      result[key] = [];
    }
    result[key].push(value);
  });

  return result;
}

/**
 * Merges params from `source` into `target` (mutating `target`).
 *
 * @param {!Object<string, !Array<string>>} target
 * @param {!Object<string, !Array<string>>} source
 */
export function mergeParams(target: Params, source: Params) {
  Object.keys(source).forEach(function(key) {
    if (!(key in target)) {
      target[key] = [];
    }
    target[key] = target[key].concat(source[key]);
  });
}

/**
 * @param {string} param The param to return a value for.
 * @return {?string} The first value for `param`, if found.
 */
export function getParam(param: string): string|null {
  const params = getParams();
  return params[param] ? params[param][0] : null;
}

/**
 * @param {!Object<string, !Array<string>>} params
 * @return {string} `params` encoded as a URI query.
 */
export function paramsToQuery(params: Params): string {
  const pairs: string[] = [];
  Object.keys(params).forEach(function(key) {
    params[key].forEach(function(value) {
      pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
    });
  });
  return (pairs.length > 0) ? ('?' + pairs.join('&')) : '';
}

function getPathName(location: Location|string): string {
  return typeof location === 'string' ? location : location.pathname;
}

export function basePath(location: Location|string) {
  return getPathName(location).match(/^.*\//)[0];
}

export function relativeLocation(location: Location|string, basePath: string) {
  let path = getPathName(location);
  if (path.indexOf(basePath) === 0) {
    path = path.substring(basePath.length);
  }
  return path;
}

export function cleanLocation(location: Location|string) {
  let path = getPathName(location);
  if (path.slice(-11) === '/index.html') {
    path = path.slice(0, path.length - 10);
  }
  return path;
}

export type Runner = (f: Function) => void;

/**
 * Like `async.parallelLimit`, but our own so that we don't force a dependency
 * on downstream code.
 *
 * @param runners Runners that call their given
 *     Node-style callback when done.
 * @param {number|function(*)} limit Maximum number of concurrent runners.
 *     (optional).
 * @param {?function(*)} done Callback that should be triggered once all runners
 *     have completed, or encountered an error.
 */
export function parallel(runners: Runner[], done: (error?: any) => void): void;
export function parallel(
    runners: Runner[], limit: number, done: (error?: any) => void): void;
export function parallel(
    runners: Runner[], maybeLimit: number|((error?: any) => void),
    done?: (error?: any) => void) {
  let limit: number;
  if (typeof maybeLimit !== 'number') {
    done = maybeLimit;
    limit = 0;
  } else {
    limit = maybeLimit;
  }
  if (!runners.length) {
    return done();
  }

  let called = false;
  const total = runners.length;
  let numActive = 0;
  let numDone = 0;

  function runnerDone(error: any) {
    if (called) {
      return;
    }
    numDone = numDone + 1;
    numActive = numActive - 1;

    if (error || numDone >= total) {
      called = true;
      done(error);
    } else {
      runOne();
    }
  }

  function runOne() {
    if (limit && numActive >= limit) {
      return;
    }
    if (!runners.length) {
      return;
    }
    numActive = numActive + 1;
    runners.shift()(runnerDone);
  }
  runners.forEach(runOne);
}

/**
 * Finds the directory that a loaded script is hosted on.
 *
 * @param {string} filename
 * @return {string?}
 */
export function scriptPrefix(filename: string): string|null {
  const scripts =
      document.querySelectorAll('script[src*="' + filename + '"]') as
      NodeListOf<HTMLScriptElement>;
  if (scripts.length !== 1) {
    return null;
  }
  const script = scripts[0].src;
  return script.substring(0, script.indexOf(filename));
}


================================================
FILE: browser-js-header.txt
================================================
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
 * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
 * Code distributed by Google as part of the polymer project is also
 * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
 */
 
/**
 * THIS FILE IS AUTOMATICALLY GENERATED! 
 * To make changes to browser.js, please edit the source files in the repo's `browser/` directory!
 */


================================================
FILE: browser.js
================================================
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
 * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
 * Code distributed by Google as part of the polymer project is also
 * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
 */
 
/**
 * THIS FILE IS AUTOMATICALLY GENERATED! 
 * To make changes to browser.js, please edit the source files in the repo's `browser/` directory!
 */

(function () {
'use strict';

window.__wctUseNpm = false;
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
 * Google as part of the polymer project is also subject to an additional IP
 * rights grant found at http://polymer.github.io/PATENTS.txt
 */
// Make sure that we use native timers, in case they're being stubbed out.
var nativeSetInterval = window.setInterval;
var nativeSetTimeout = window.setTimeout;
var nativeRequestAnimationFrame = window.requestAnimationFrame;
/**
 * Runs `stepFn`, catching any error and passing it to `callback` (Node-style).
 * Otherwise, calls `callback` with no arguments on success.
 *
 * @param {function()} callback
 * @param {function()} stepFn
 */
function safeStep(callback, stepFn) {
    var err;
    try {
        stepFn();
    }
    catch (error) {
        err = error;
    }
    callback(err);
}
/**
 * Runs your test at declaration time (before Mocha has begun tests). Handy for
 * when you need to test document initialization.
 *
 * Be aware that any errors thrown asynchronously cannot be tied to your test.
 * You may want to catch them and pass them to the done event, instead. See
 * `safeStep`.
 *
 * @param {string} name The name of the test.
 * @param {function(?function())} testFn The test function. If an argument is
 *     accepted, the test will be treated as async, just like Mocha tests.
 */
function testImmediate(name, testFn) {
    if (testFn.length > 0) {
        return testImmediateAsync(name, testFn);
    }
    var err;
    try {
        testFn();
    }
    catch (error) {
        err = error;
    }
    test(name, function (done) {
        done(err);
    });
}
/**
 * An async-only variant of `testImmediate`.
 *
 * @param {string} name
 * @param {function(?function())} testFn
 */
function testImmediateAsync(name, testFn) {
    var testComplete = false;
    var err;
    test(name, function (done) {
        var intervalId = nativeSetInterval(function () {
            if (!testComplete)
                return;
            clearInterval(intervalId);
            done(err);
        }, 10);
    });
    try {
        testFn(function (error) {
            if (error)
                err = error;
            testComplete = true;
        });
    }
    catch (error) {
        err = error;
        testComplete = true;
    }
}
/**
 * Triggers a flush of any pending events, observations, etc and calls you back
 * after they have been processed.
 *
 * @param {function()} callback
 */
function flush(callback) {
    // Ideally, this function would be a call to Polymer.dom.flush, but that
    // doesn't support a callback yet
    // (https://github.com/Polymer/polymer-dev/issues/851),
    // ...and there's cross-browser flakiness to deal with.
    // Make sure that we're invoking the callback with no arguments so that the
    // caller can pass Mocha callbacks, etc.
    var done = function done() {
        callback();
    };
    // Because endOfMicrotask is flaky for IE, we perform microtask checkpoints
    // ourselves (https://github.com/Polymer/polymer-dev/issues/114):
    var isIE = navigator.appName === 'Microsoft Internet Explorer';
    if (isIE && window.Platform && window.Platform.performMicrotaskCheckpoint) {
        var reallyDone_1 = done;
        done = function doneIE() {
            Platform.performMicrotaskCheckpoint();
            nativeSetTimeout(reallyDone_1, 0);
        };
    }
    // Everyone else gets a regular flush.
    var scope;
    if (window.Polymer && window.Polymer.dom && window.Polymer.dom.flush) {
        scope = window.Polymer.dom;
    }
    else if (window.Polymer && window.Polymer.flush) {
        scope = window.Polymer;
    }
    else if (window.WebComponents && window.WebComponents.flush) {
        scope = window.WebComponents;
    }
    if (scope) {
        scope.flush();
    }
    // Ensure that we are creating a new _task_ to allow all active microtasks to
    // finish (the code you're testing may be using endOfMicrotask, too).
    nativeSetTimeout(done, 0);
}
/**
 * Advances a single animation frame.
 *
 * Calls `flush`, `requestAnimationFrame`, `flush`, and `callback` sequentially
 * @param {function()} callback
 */
function animationFrameFlush(callback) {
    flush(function () {
        nativeRequestAnimationFrame(function () {
            flush(callback);
        });
    });
}
/**
 * DEPRECATED: Use `flush`.
 * @param {function} callback
 */
function asyncPlatformFlush(callback) {
    console.warn('asyncPlatformFlush is deprecated in favor of the more terse flush()');
    return window.flush(callback);
}
/**
 *
 */
function waitFor(fn, next, intervalOrMutationEl, timeout, timeoutTime) {
    timeoutTime = timeoutTime || Date.now() + (timeout || 1000);
    intervalOrMutationEl = intervalOrMutationEl || 32;
    try {
        fn();
    }
    catch (e) {
        if (Date.now() > timeoutTime) {
            throw e;
        }
        else {
            if (typeof intervalOrMutationEl !== 'number') {
                intervalOrMutationEl.onMutation(intervalOrMutationEl, function () {
                    waitFor(fn, next, intervalOrMutationEl, timeout, timeoutTime);
                });
            }
            else {
                nativeSetTimeout(function () {
                    waitFor(fn, next, intervalOrMutationEl, timeout, timeoutTime);
                }, intervalOrMutationEl);
            }
            return;
        }
    }
    next();
}
window.safeStep = safeStep;
window.testImmediate = testImmediate;
window.testImmediateAsync = testImmediateAsync;
window.flush = flush;
window.animationFrameFlush = animationFrameFlush;
window.asyncPlatformFlush = asyncPlatformFlush;
window.waitFor = waitFor;

/**
 * The global configuration state for WCT's browser client.
 */
var _config = {
    environmentScripts: !!window.__wctUseNpm ?
        [
            'stacky/browser.js', 'async/lib/async.js', 'lodash/index.js',
            'mocha/mocha.js', 'chai/chai.js', '@polymer/sinonjs/sinon.js',
            'sinon-chai/lib/sinon-chai.js',
            'accessibility-developer-tools/dist/js/axs_testing.js',
            '@polymer/test-fixture/test-fixture.js'
        ] :
        [
            'stacky/browser.js', 'async/lib/async.js', 'lodash/lodash.js',
            'mocha/mocha.js', 'chai/chai.js', 'sinonjs/sinon.js',
            'sinon-chai/lib/sinon-chai.js',
            'accessibility-developer-tools/dist/js/axs_testing.js'
        ],
    environmentImports: !!window.__wctUseNpm ? [] :
        ['test-fixture/test-fixture.html'],
    root: null,
    waitForFrameworks: true,
    waitFor: null,
    numConcurrentSuites: 1,
    trackConsoleError: true,
    mochaOptions: { timeout: 10 * 1000 },
    verbose: false,
};
/**
 * Merges initial `options` into WCT's global configuration.
 *
 * @param {Object} options The options to merge. See `browser/config.js` for a
 *     reference.
 */
function setup(options) {
    var childRunner = ChildRunner.current();
    if (childRunner) {
        _deepMerge(_config, childRunner.parentScope.WCT._config);
        // But do not force the mocha UI
        delete _config.mochaOptions.ui;
    }
    if (options && typeof options === 'object') {
        _deepMerge(_config, options);
    }
    if (!_config.root) {
        // Sibling dependencies.
        var root = scriptPrefix('browser.js');
        _config.root = basePath(root.substr(0, root.length - 1));
        if (!_config.root) {
            throw new Error('Unable to detect root URL for WCT sources. Please set WCT.root before including browser.js');
        }
    }
}
/**
 * Retrieves a configuration value.
 */
function get(key) {
    return _config[key];
}
// Internal
function _deepMerge(target, source) {
    Object.keys(source).forEach(function (key) {
        if (target[key] !== null && typeof target[key] === 'object' &&
            !Array.isArray(target[key])) {
            _deepMerge(target[key], source[key]);
        }
        else {
            target[key] = source[key];
        }
    });
}

/**
 * @param {function()} callback A function to call when the active web component
 *     frameworks have loaded.
 */
function whenFrameworksReady(callback) {
    debug('whenFrameworksReady');
    var done = function () {
        debug('whenFrameworksReady done');
        callback();
    };
    // If webcomponents script is in the document, wait for WebComponentsReady.
    if (window.WebComponents && !window.WebComponents.ready) {
        debug('WebComponentsReady?');
        window.addEventListener('WebComponentsReady', function wcReady() {
            window.removeEventListener('WebComponentsReady', wcReady);
            debug('WebComponentsReady');
            done();
        });
    }
    else {
        done();
    }
}
/**
 * @return {string} '<count> <kind> tests' or '<count> <kind> test'.
 */
function pluralizedStat(count, kind) {
    if (count === 1) {
        return count + ' ' + kind + ' test';
    }
    else {
        return count + ' ' + kind + ' tests';
    }
}
/**
 * @param {string} path The URI of the script to load.
 * @param {function} done
 */
function loadScript(path, done) {
    var script = document.createElement('script');
    script.src = path;
    if (done) {
        script.onload = done.bind(null, null);
        script.onerror = done.bind(null, 'Failed to load script ' + script.src);
    }
    document.head.appendChild(script);
}
/**
 * @param {string} path The URI of the stylesheet to load.
 * @param {function} done
 */
function loadStyle(path, done) {
    var link = document.createElement('link');
    link.rel = 'stylesheet';
    link.href = path;
    if (done) {
        link.onload = done.bind(null, null);
        link.onerror = done.bind(null, 'Failed to load stylesheet ' + link.href);
    }
    document.head.appendChild(link);
}
/**
 * @param {...*} var_args Logs values to the console when the `debug`
 *     configuration option is true.
 */
function debug() {
    var var_args = [];
    for (var _i = 0; _i < arguments.length; _i++) {
        var_args[_i] = arguments[_i];
    }
    if (!get('verbose')) {
        return;
    }
    var args = [window.location.pathname].concat(var_args);
    (console.debug || console.log).apply(console, args);
}
// URL Processing
/**
 * @param {string} url
 * @return {{base: string, params: string}}
 */
function parseUrl(url) {
    var parts = url.match(/^(.*?)(?:\?(.*))?$/);
    return {
        base: parts[1],
        params: getParams(parts[2] || ''),
    };
}
/**
 * Expands a URL that may or may not be relative to `base`.
 *
 * @param {string} url
 * @param {string} base
 * @return {string}
 */
function expandUrl(url, base) {
    if (!base)
        return url;
    if (url.match(/^(\/|https?:\/\/)/))
        return url;
    if (base.substr(base.length - 1) !== '/') {
        base = base + '/';
    }
    return base + url;
}
/**
 * @param {string=} opt_query A query string to parse.
 * @return {!Object<string, !Array<string>>} All params on the URL's query.
 */
function getParams(query) {
    query = typeof query === 'string' ? query : window.location.search;
    if (query.substring(0, 1) === '?') {
        query = query.substring(1);
    }
    // python's SimpleHTTPServer tacks a `/` on the end of query strings :(
    if (query.slice(-1) === '/') {
        query = query.substring(0, query.length - 1);
    }
    if (query === '')
        return {};
    var result = {};
    query.split('&').forEach(function (part) {
        var pair = part.split('=');
        if (pair.length !== 2) {
            console.warn('Invalid URL query part:', part);
            return;
        }
        var key = decodeURIComponent(pair[0]);
        var value = decodeURIComponent(pair[1]);
        if (!result[key]) {
            result[key] = [];
        }
        result[key].push(value);
    });
    return result;
}
/**
 * Merges params from `source` into `target` (mutating `target`).
 *
 * @param {!Object<string, !Array<string>>} target
 * @param {!Object<string, !Array<string>>} source
 */
function mergeParams(target, source) {
    Object.keys(source).forEach(function (key) {
        if (!(key in target)) {
            target[key] = [];
        }
        target[key] = target[key].concat(source[key]);
    });
}
/**
 * @param {string} param The param to return a value for.
 * @return {?string} The first value for `param`, if found.
 */
function getParam(param) {
    var params = getParams();
    return params[param] ? params[param][0] : null;
}
/**
 * @param {!Object<string, !Array<string>>} params
 * @return {string} `params` encoded as a URI query.
 */
function paramsToQuery(params) {
    var pairs = [];
    Object.keys(params).forEach(function (key) {
        params[key].forEach(function (value) {
            pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
        });
    });
    return (pairs.length > 0) ? ('?' + pairs.join('&')) : '';
}
function getPathName(location) {
    return typeof location === 'string' ? location : location.pathname;
}
function basePath(location) {
    return getPathName(location).match(/^.*\//)[0];
}
function relativeLocation(location, basePath) {
    var path = getPathName(location);
    if (path.indexOf(basePath) === 0) {
        path = path.substring(basePath.length);
    }
    return path;
}
function cleanLocation(location) {
    var path = getPathName(location);
    if (path.slice(-11) === '/index.html') {
        path = path.slice(0, path.length - 10);
    }
    return path;
}
function parallel(runners, maybeLimit, done) {
    var limit;
    if (typeof maybeLimit !== 'number') {
        done = maybeLimit;
        limit = 0;
    }
    else {
        limit = maybeLimit;
    }
    if (!runners.length) {
        return done();
    }
    var called = false;
    var total = runners.length;
    var numActive = 0;
    var numDone = 0;
    function runnerDone(error) {
        if (called) {
            return;
        }
        numDone = numDone + 1;
        numActive = numActive - 1;
        if (error || numDone >= total) {
            called = true;
            done(error);
        }
        else {
            runOne();
        }
    }
    function runOne() {
        if (limit && numActive >= limit) {
            return;
        }
        if (!runners.length) {
            return;
        }
        numActive = numActive + 1;
        runners.shift()(runnerDone);
    }
    runners.forEach(runOne);
}
/**
 * Finds the directory that a loaded script is hosted on.
 *
 * @param {string} filename
 * @return {string?}
 */
function scriptPrefix(filename) {
    var scripts = document.querySelectorAll('script[src*="' + filename + '"]');
    if (scripts.length !== 1) {
        return null;
    }
    var script = scripts[0].src;
    return script.substring(0, script.indexOf(filename));
}


var util = Object.freeze({
    whenFrameworksReady: whenFrameworksReady,
    pluralizedStat: pluralizedStat,
    loadScript: loadScript,
    loadStyle: loadStyle,
    debug: debug,
    parseUrl: parseUrl,
    expandUrl: expandUrl,
    getParams: getParams,
    mergeParams: mergeParams,
    getParam: getParam,
    paramsToQuery: paramsToQuery,
    basePath: basePath,
    relativeLocation: relativeLocation,
    cleanLocation: cleanLocation,
    parallel: parallel,
    scriptPrefix: scriptPrefix
});

/**
 * A Mocha suite (or suites) run within a child iframe, but reported as if they
 * are part of the current context.
 */
var ChildRunner = /** @class */ (function () {
    function ChildRunner(url, parentScope) {
        var urlBits = parseUrl(url);
        mergeParams(urlBits.params, getParams(parentScope.location.search));
        delete urlBits.params.cli_browser_id;
        this.url = urlBits.base + paramsToQuery(urlBits.params);
        this.parentScope = parentScope;
        this.state = 'initializing';
    }
    /**
     * @return {ChildRunner} The `ChildRunner` that was registered for this
     * window.
     */
    ChildRunner.current = function () {
        return ChildRunner.get(window);
    };
    /**
     * @param {!Window} target A window to find the ChildRunner of.
     * @param {boolean} traversal Whether this is a traversal from a child window.
     * @return {ChildRunner} The `ChildRunner` that was registered for `target`.
     */
    ChildRunner.get = function (target, traversal) {
        var childRunner = ChildRunner._byUrl[target.location.href];
        if (childRunner) {
            return childRunner;
        }
        if (window.parent === window) { // Top window.
            if (traversal) {
                console.warn('Subsuite loaded but was never registered. This most likely is due to wonky history behavior. Reloading...');
                window.location.reload();
            }
            return null;
        }
        // Otherwise, traverse.
        return window.parent.WCT._ChildRunner.get(target, true);
    };
    /**
     * Loads and runs the subsuite.
     *
     * @param {function} done Node-style callback.
     */
    ChildRunner.prototype.run = function (done) {
        debug('ChildRunner#run', this.url);
        this.state = 'loading';
        this.onRunComplete = done;
        this.iframe = document.createElement('iframe');
        this.iframe.src = this.url;
        this.iframe.classList.add('subsuite');
        var container = document.getElementById('subsuites');
        if (!container) {
            container = document.createElement('div');
            container.id = 'subsuites';
            document.body.appendChild(container);
        }
        container.appendChild(this.iframe);
        // let the iframe expand the URL for us.
        this.url = this.iframe.src;
        ChildRunner._byUrl[this.url] = this;
        this.timeoutId = setTimeout(this.loaded.bind(this, new Error('Timed out loading ' + this.url)), ChildRunner.loadTimeout);
        this.iframe.addEventListener('error', this.loaded.bind(this, new Error('Failed to load document ' + this.url)));
        this.iframe.contentWindow.addEventListener('DOMContentLoaded', this.loaded.bind(this, null));
    };
    /**
     * Called when the sub suite's iframe has loaded (or errored during load).
     *
     * @param {*} error The error that occured, if any.
     */
    ChildRunner.prototype.loaded = function (error) {
        debug('ChildRunner#loaded', this.url, error);
        if (this.iframe.contentWindow == null && error) {
            this.signalRunComplete(error);
            this.done();
            return;
        }
        // Not all targets have WCT loaded (compatiblity mode)
        if (this.iframe.contentWindow.WCT) {
            this.share = this.iframe.contentWindow.WCT.share;
        }
        if (error) {
            this.signalRunComplete(error);
            this.done();
        }
    };
    /**
     * Called in mocha/run.js when all dependencies have loaded, and the child is
     * ready to start running tests
     *
     * @param {*} error The error that occured, if any.
     */
    ChildRunner.prototype.ready = function (error) {
        debug('ChildRunner#ready', this.url, error);
        if (this.timeoutId) {
            clearTimeout(this.timeoutId);
        }
        if (error) {
            this.signalRunComplete(error);
            this.done();
        }
    };
    /**
     * Called when the sub suite's tests are complete, so that it can clean up.
     */
    ChildRunner.prototype.done = function () {
        debug('ChildRunner#done', this.url, arguments);
        // make sure to clear that timeout
        this.ready();
        this.signalRunComplete();
        if (!this.iframe)
            return;
        // Be safe and avoid potential browser crashes when logic attempts to
        // interact with the removed iframe.
        setTimeout(function () {
            this.iframe.parentNode.removeChild(this.iframe);
            this.iframe = null;
            this.share = null;
        }.bind(this), 1);
    };
    ChildRunner.prototype.signalRunComplete = function (error) {
        if (!this.onRunComplete)
            return;
        this.state = 'complete';
        this.onRunComplete(error);
        this.onRunComplete = null;
    };
    // ChildRunners get a pretty generous load timeout by default.
    ChildRunner.loadTimeout = 60000;
    // We can't maintain properties on iframe elements in Firefox/Safari/???, so
    // we track childRunners by URL.
    ChildRunner._byUrl = {};
    return ChildRunner;
}());

var SOCKETIO_ENDPOINT = window.location.protocol + '//' + window.location.host;
var SOCKETIO_LIBRARY = SOCKETIO_ENDPOINT + '/socket.io/socket.io.js';
/**
 * A socket for communication between the CLI and browser runners.
 *
 * @param {string} browserId An ID generated by the CLI runner.
 * @param {!io.Socket} socket The socket.io `Socket` to communicate over.
 */
var CLISocket = /** @class */ (function () {
    function CLISocket(browserId, socket) {
        this.browserId = browserId;
        this.socket = socket;
    }
    /**
     * @param {!Mocha.Runner} runner The Mocha `Runner` to observe, reporting
     *     interesting events back to the CLI runner.
     */
    CLISocket.prototype.observe = function (runner) {
        var _this = this;
        this.emitEvent('browser-start', {
            url: window.location.toString(),
        });
        // We only emit a subset of events that we care about, and follow a more
        // general event format that is hopefully applicable to test runners beyond
        // mocha.
        //
        // For all possible mocha events, see:
        // https://github.com/visionmedia/mocha/blob/master/lib/runner.js#L36
        runner.on('test', function (test) {
            _this.emitEvent('test-start', { test: getTitles(test) });
        });
        runner.on('test end', function (test) {
            _this.emitEvent('test-end', {
                state: getState(test),
                test: getTitles(test),
                duration: test.duration,
                error: test.err,
            });
        });
        runner.on('fail', function (test, err) {
            // fail the test run if we catch errors outside of a test function
            if (test.type !== 'test') {
                _this.emitEvent('browser-fail', 'Error thrown outside of test function: ' + err.stack);
            }
        });
        runner.on('childRunner start', function (childRunner) {
            _this.emitEvent('sub-suite-start', childRunner.share);
        });
        runner.on('childRunner end', function (childRunner) {
            _this.emitEvent('sub-suite-end', childRunner.share);
        });
        runner.on('end', function () {
            _this.emitEvent('browser-end');
        });
    };
    /**
     * @param {string} event The name of the event to fire.
     * @param {*} data Additional data to pass with the event.
     */
    CLISocket.prototype.emitEvent = function (event, data) {
        this.socket.emit('client-event', {
            browserId: this.browserId,
            event: event,
            data: data,
        });
    };
    /**
     * Builds a `CLISocket` if we are within a CLI-run environment; short-circuits
     * otherwise.
     *
     * @param {function(*, CLISocket)} done Node-style callback.
     */
    CLISocket.init = function (done) {
        var browserId = getParam('cli_browser_id');
        if (!browserId)
            return done();
        // Only fire up the socket for root runners.
        if (ChildRunner.current())
            return done();
        loadScript(SOCKETIO_LIBRARY, function (error) {
            if (error)
                return done(error);
            var socket = io(SOCKETIO_ENDPOINT);
            socket.on('error', function (error) {
                socket.off();
                done(error);
            });
            socket.on('connect', function () {
                socket.off();
                done(null, new CLISocket(browserId, socket));
            });
        });
    };
    return CLISocket;
}());
// Misc Utility
/**
 * @param {!Mocha.Runnable} runnable The test or suite to extract titles from.
 * @return {!Array.<string>} The titles of the runnable and its parents.
 */
function getTitles(runnable) {
    var titles = [];
    while (runnable && !runnable.root && runnable.title) {
        titles.unshift(runnable.title);
        runnable = runnable.parent;
    }
    return titles;
}
/**
 * @param {!Mocha.Runnable} runnable
 * @return {string}
 */
function getState(runnable) {
    if (runnable.state === 'passed') {
        return 'passing';
    }
    else if (runnable.state === 'failed') {
        return 'failing';
    }
    else if (runnable.pending) {
        return 'pending';
    }
    else {
        return 'unknown';
    }
}

// We capture console events when running tests; so make sure we have a
// reference to the original one.
var console$1 = window.console;
var FONT = ';font: normal 13px "Roboto", "Helvetica Neue", "Helvetica", sans-serif;';
var STYLES = {
    plain: FONT,
    suite: 'color: #5c6bc0' + FONT,
    test: FONT,
    passing: 'color: #259b24' + FONT,
    pending: 'color: #e65100' + FONT,
    failing: 'color: #c41411' + FONT,
    stack: 'color: #c41411',
    results: FONT + 'font-size: 16px',
};
// I don't think we can feature detect this one...
var userAgent = navigator.userAgent.toLowerCase();
var CAN_STYLE_LOG = userAgent.match('firefox') || userAgent.match('webkit');
var CAN_STYLE_GROUP = userAgent.match('webkit');
// Track the indent for faked `console.group`
var logIndent = '';
function log(text, style) {
    text = text.split('\n')
        .map(function (l) {
        return logIndent + l;
    })
        .join('\n');
    if (CAN_STYLE_LOG) {
        console$1.log('%c' + text, STYLES[style] || STYLES.plain);
    }
    else {
        console$1.log(text);
    }
}
function logGroup(text, style) {
    if (CAN_STYLE_GROUP) {
        console$1.group('%c' + text, STYLES[style] || STYLES.plain);
    }
    else if (console$1.group) {
        console$1.group(text);
    }
    else {
        logIndent = logIndent + '  ';
        log(text, style);
    }
}
function logGroupEnd() {
    if (console$1.groupEnd) {
        console$1.groupEnd();
    }
    else {
        logIndent = logIndent.substr(0, logIndent.length - 2);
    }
}
function logException(error) {
    log(error.stack || error.message || (error + ''), 'stack');
}
/**
 * A Mocha reporter that logs results out to the web `console`.
 */
var Console = /** @class */ (function () {
    /**
     * @param runner The runner that is being reported on.
     */
    function Console(runner) {
        Mocha.reporters.Base.call(this, runner);
        runner.on('suite', function (suite) {
            if (suite.root) {
                return;
            }
            logGroup(suite.title, 'suite');
        }.bind(this));
        runner.on('suite end', function (suite) {
            if (suite.root) {
                return;
            }
            logGroupEnd();
        }.bind(this));
        runner.on('test', function (test) {
            logGroup(test.title, 'test');
        }.bind(this));
        runner.on('pending', function (test) {
            logGroup(test.title, 'pending');
        }.bind(this));
        runner.on('fail', function (_test, error) {
            logException(error);
        }.bind(this));
        runner.on('test end', function (_test) {
            logGroupEnd();
        }.bind(this));
        runner.on('end', this.logSummary.bind(this));
    }
    /** Prints out a final summary of test results. */
    Console.prototype.logSummary = function () {
        logGroup('Test Results', 'results');
        if (this.stats.failures > 0) {
            log(pluralizedStat(this.stats.failures, 'failing'), 'failing');
        }
        if (this.stats.pending > 0) {
            log(pluralizedStat(this.stats.pending, 'pending'), 'pending');
        }
        log(pluralizedStat(this.stats.passes, 'passing'));
        if (!this.stats.failures) {
            log('test suite passed', 'passing');
        }
        log('Evaluated ' + this.stats.tests + ' tests in ' +
            this.stats.duration + 'ms.');
        logGroupEnd();
    };
    return Console;
}());

/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
 * Google as part of the polymer project is also subject to an additional IP
 * rights grant found at http://polymer.github.io/PATENTS.txt
 */
/**
 * WCT-specific behavior on top of Mocha's default HTML reporter.
 *
 * @param {!Mocha.Runner} runner The runner that is being reported on.
 */
function HTML(runner) {
    var output = document.createElement('div');
    output.id = 'mocha';
    document.body.appendChild(output);
    runner.on('suite', function (_test) {
        this.total = runner.total;
    }.bind(this));
    Mocha.reporters.HTML.call(this, runner);
}
// Woo! What a hack. This just saves us from adding a bunch of complexity around
// style loading.
var style = document.createElement('style');
style.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";
document.head.appendChild(style);

var STACKY_CONFIG = {
    indent: '  ',
    locationStrip: [
        /^https?:\/\/[^\/]+/,
        /\?.*$/,
    ],
    filter: function (line) {
        return !!line.location.match(/\/web-component-tester\/[^\/]+(\?.*)?$/);
    },
};
// https://github.com/visionmedia/mocha/blob/master/lib/runner.js#L36-46
var MOCHA_EVENTS = [
    'start', 'end', 'suite', 'suite end', 'test', 'test end', 'hook', 'hook end',
    'pass', 'fail', 'pending', 'childRunner end'
];
// Until a suite has loaded, we assume this many tests in it.
var ESTIMATED_TESTS_PER_SUITE = 3;
/**
 * A Mocha-like reporter that combines the output of multiple Mocha suites.
 */
var MultiReporter = /** @class */ (function () {
    /**
     * @param numSuites The number of suites that will be run, in order to
     *     estimate the total number of tests that will be performed.
     * @param reporters The set of reporters that
     *     should receive the unified event stream.
     * @param parent The parent reporter, if present.
     */
    function MultiReporter(numSuites, reporters, parent) {
        var _this = this;
        this.reporters = reporters.map(function (reporter) {
            return new reporter(_this);
        });
        this.parent = parent;
        this.basePath = parent && parent.basePath || basePath(window.location);
        this.total = numSuites * ESTIMATED_TESTS_PER_SUITE;
        // Mocha reporters assume a stream of events, so we have to be careful to
        // only report on one runner at a time...
        this.currentRunner = null;
        // ...while we buffer events for any other active runners.
        this.pendingEvents = [];
        this.emit('start');
    }
    /**
     * @param location The location this reporter represents.
     * @return A reporter-like "class" for each child suite
     *     that should be passed to `mocha.run`.
     */
    MultiReporter.prototype.childReporter = function (location) {
        var name = this.suiteTitle(location);
        // The reporter is used as a constructor, so we can't depend on `this` being
        // properly bound.
        var self = this;
        return _a = /** @class */ (function () {
                function ChildReporter(runner) {
                    runner.name = window.name;
                    self.bindChildRunner(runner);
                }
                return ChildReporter;
            }()),
            _a.title = window.name,
            _a;
        var _a;
    };
    /** Must be called once all runners have finished. */
    MultiReporter.prototype.done = function () {
        this.complete = true;
        this.flushPendingEvents();
        this.emit('end');
    };
    /**
     * Emit a top level test that is not part of any suite managed by this
     * reporter.
     *
     * Helpful for reporting on global errors, loading issues, etc.
     *
     * @param title The title of the test.
     * @param error An error associated with this test. If falsy, test is
     *     considered to be passing.
     * @param suiteTitle Title for the suite that's wrapping the test.
     * @param estimated If this test was included in the original
     *     estimate of `numSuites`.
     */
    MultiReporter.prototype.emitOutOfBandTest = function (title, error, suiteTitle, estimated) {
        debug('MultiReporter#emitOutOfBandTest(', arguments, ')');
        var root = new Mocha.Suite(suiteTitle || '');
        var test = new Mocha.Test(title, function () { });
        test.parent = root;
        test.state = error ? 'failed' : 'passed';
        test.err = error;
        if (!estimated) {
            this.total = this.total + ESTIMATED_TESTS_PER_SUITE;
        }
        var runner = { total: 1 };
        this.proxyEvent('start', runner);
        this.proxyEvent('suite', runner, root);
        this.proxyEvent('test', runner, test);
        if (error) {
            this.proxyEvent('fail', runner, test, error);
        }
        else {
            this.proxyEvent('pass', runner, test);
        }
        this.proxyEvent('test end', runner, test);
        this.proxyEvent('suite end', runner, root);
        this.proxyEvent('end', runner);
    };
    /**
     * @param {!Location|string} location
     * @return {string}
     */
    MultiReporter.prototype.suiteTitle = function (location) {
        var path = relativeLocation(location, this.basePath);
        path = cleanLocation(path);
        return path;
    };
    // Internal Interface
    /** @param {!Mocha.runners.Base} runner The runner to listen to events for. */
    MultiReporter.prototype.bindChildRunner = function (runner) {
        var _this = this;
        MOCHA_EVENTS.forEach(function (eventName) {
            runner.on(eventName, _this.proxyEvent.bind(_this, eventName, runner));
        });
    };
    /**
     * Evaluates an event fired by `runner`, proxying it forward or buffering it.
     *
     * @param {string} eventName
     * @param {!Mocha.runners.Base} runner The runner that emitted this event.
     * @param {...*} var_args Any additional data passed as part of the event.
     */
    MultiReporter.prototype.proxyEvent = function (eventName, runner) {
        var _args = [];
        for (var _i = 2; _i < arguments.length; _i++) {
            _args[_i - 2] = arguments[_i];
        }
        var extraArgs = Array.prototype.slice.call(arguments, 2);
        if (this.complete) {
            console.warn('out of order Mocha event for ' + runner.name + ':', eventName, extraArgs);
            return;
        }
        if (this.currentRunner && runner !== this.currentRunner) {
            this.pendingEvents.push(Array.prototype.slice.call(arguments));
            return;
        }
        debug('MultiReporter#proxyEvent(', arguments, ')');
        // This appears to be a Mocha bug: Tests failed by passing an error to their
        // done function don't set `err` properly.
        //
        // TODO(nevir): Track down.
        if (eventName === 'fail' && !extraArgs[0].err) {
            extraArgs[0].err = extraArgs[1];
        }
        if (eventName === 'start') {
            this.onRunnerStart(runner);
        }
        else if (eventName === 'end') {
            this.onRunnerEnd(runner);
        }
        else {
            this.cleanEvent(eventName, runner, extraArgs);
            this.emit.apply(this, [eventName].concat(extraArgs));
        }
    };
    /**
     * Cleans or modifies an event if needed.
     *
     * @param eventName
     * @param runner The runner that emitted this event.
     * @param extraArgs
     */
    MultiReporter.prototype.cleanEvent = function (eventName, _runner, extraArgs) {
        // Suite hierarchy
        if (extraArgs[0]) {
            extraArgs[0] = this.showRootSuite(extraArgs[0]);
        }
        // Normalize errors
        if (eventName === 'fail') {
            extraArgs[1] = Stacky.normalize(extraArgs[1], STACKY_CONFIG);
        }
        if (extraArgs[0] && extraArgs[0].err) {
            extraArgs[0].err = Stacky.normalize(extraArgs[0].err, STACKY_CONFIG);
        }
    };
    /**
     * We like to show the root suite's title, which requires a little bit of
     * trickery in the suite hierarchy.
     *
     * @param {!Mocha.Runnable} node
     */
    MultiReporter.prototype.showRootSuite = function (node) {
        var leaf = node = Object.create(node);
        while (node && node.parent) {
            var wrappedParent = Object.create(node.parent);
            node.parent = wrappedParent;
            node = wrappedParent;
        }
        node.root = false;
        return leaf;
    };
    /** @param {!Mocha.runners.Base} runner */
    MultiReporter.prototype.onRunnerStart = function (runner) {
        debug('MultiReporter#onRunnerStart:', runner.name);
        this.total = this.total - ESTIMATED_TESTS_PER_SUITE + runner.total;
        this.currentRunner = runner;
    };
    /** @param {!Mocha.runners.Base} runner */
    MultiReporter.prototype.onRunnerEnd = function (runner) {
        debug('MultiReporter#onRunnerEnd:', runner.name);
        this.currentRunner = null;
        this.flushPendingEvents();
    };
    /**
     * Flushes any buffered events and runs them through `proxyEvent`. This will
     * loop until all buffered runners are complete, or we have run out of
     * buffered events.
     */
    MultiReporter.prototype.flushPendingEvents = function () {
        var _this = this;
        var events = this.pendingEvents;
        this.pendingEvents = [];
        events.forEach(function (eventArgs) {
            _this.proxyEvent.apply(_this, eventArgs);
        });
    };
    return MultiReporter;
}());

var ARC_OFFSET = 0; // start at the right.
var ARC_WIDTH = 6;
/**
 * A Mocha reporter that updates the document's title and favicon with
 * at-a-glance stats.
 *
 * @param {!Mocha.Runner} runner The runner that is being reported on.
 */
var Title = /** @class */ (function () {
    function Title(runner) {
        Mocha.reporters.Base.call(this, runner);
        runner.on('test end', this.report.bind(this));
    }
    /** Reports current stats via the page title and favicon. */
    Title.prototype.report = function () {
        this.updateTitle();
        this.updateFavicon();
    };
    /** Updates the document title with a summary of current stats. */
    Title.prototype.updateTitle = function () {
        if (this.stats.failures > 0) {
            document.title = pluralizedStat(this.stats.failures, 'failing');
        }
        else {
            document.title = pluralizedStat(this.stats.passes, 'passing');
        }
    };
    /** Updates the document's favicon w/ a summary of current stats. */
    Title.prototype.updateFavicon = function () {
        var canvas = document.createElement('canvas');
        canvas.height = canvas.width = 32;
        var context = canvas.getContext('2d');
        var passing = this.stats.passes;
        var pending = this.stats.pending;
        var failing = this.stats.failures;
        var total = Math.max(this.runner.total, passing + pending + failing);
        drawFaviconArc(context, total, 0, passing, '#0e9c57');
        drawFaviconArc(context, total, passing, pending, '#f3b300');
        drawFaviconArc(context, total, pending + passing, failing, '#ff5621');
        this.setFavicon(canvas.toDataURL());
    };
    /** Sets the current favicon by URL. */
    Title.prototype.setFavicon = function (url) {
        var current = document.head.querySelector('link[rel="icon"]');
        if (current) {
            document.head.removeChild(current);
        }
        var link = document.createElement('link');
        link.rel = 'icon';
        link.type = 'image/x-icon';
        link.href = url;
        link.setAttribute('sizes', '32x32');
        document.head.appendChild(link);
    };
    return Title;
}());
/**
 * Draws an arc for the favicon status, relative to the total number of tests.
 */
function drawFaviconArc(context, total, start, length, color) {
    var arcStart = ARC_OFFSET + Math.PI * 2 * (start / total);
    var arcEnd = ARC_OFFSET + Math.PI * 2 * ((start + length) / total);
    context.beginPath();
    context.strokeStyle = color;
    context.lineWidth = ARC_WIDTH;
    context.arc(16, 16, 16 - ARC_WIDTH / 2, arcStart, arcEnd);
    context.stroke();
}

var htmlSuites$1 = [];
var jsSuites$1 = [];
// We process grep ourselves to avoid loading suites that will be filtered.
var GREP = getParam('grep');
// work around mocha bug (https://github.com/mochajs/mocha/issues/2070)
if (GREP) {
    GREP = GREP.replace(/\\\./g, '.');
}
/**
 * Loads suites of tests, supporting both `.js` and `.html` files.
 *
 * @param files The files to load.
 */
function loadSuites(files) {
    files.forEach(function (file) {
        if (/\.js(\?.*)?$/.test(file)) {
            jsSuites$1.push(file);
        }
        else if (/\.html(\?.*)?$/.test(file)) {
            htmlSuites$1.push(file);
        }
        else {
            throw new Error('Unknown resource type: ' + file);
        }
    });
}
/**
 * @return The child suites that should be loaded, ignoring
 *     those that would not match `GREP`.
 */
function activeChildSuites() {
    var subsuites = htmlSuites$1;
    if (GREP) {
        var cleanSubsuites = [];
        for (var i = 0, subsuite = void 0; subsuite = subsuites[i]; i++) {
            if (GREP.indexOf(cleanLocation(subsuite)) !== -1) {
                cleanSubsuites.push(subsuite);
            }
        }
        subsuites = cleanSubsuites;
    }
    return subsuites;
}
/**
 * Loads all `.js` sources requested by the current suite.
 */
function loadJsSuites(_reporter, done) {
    debug('loadJsSuites', jsSuites$1);
    var loaders = jsSuites$1.map(function (file) {
        // We only support `.js` dependencies for now.
        return loadScript.bind(util, file);
    });
    parallel(loaders, done);
}
function runSuites(reporter, childSuites, done) {
    debug('runSuites');
    var suiteRunners = [
        // Run the local tests (if any) first, not stopping on error;
        _runMocha.bind(null, reporter),
    ];
    // As well as any sub suites. Again, don't stop on error.
    childSuites.forEach(function (file) {
        suiteRunners.push(function (next) {
            var childRunner = new ChildRunner(file, window);
            reporter.emit('childRunner start', childRunner);
            childRunner.run(function (error) {
                reporter.emit('childRunner end', childRunner);
                if (error)
                    reporter.emitOutOfBandTest(file, error);
                next();
            });
        });
    });
    parallel(suiteRunners, get('numConcurrentSuites'), function (error) {
        reporter.done();
        done(error);
    });
}
/**
 * Kicks off a mocha run, waiting for frameworks to load if necessary.
 *
 * @param {!MultiReporter} reporter Where to send Mocha's events.
 * @param {function} done A callback fired, _no error is passed_.
 */
function _runMocha(reporter, done, waited) {
    if (get('waitForFrameworks') && !waited) {
        var waitFor = (get('waitFor') || whenFrameworksReady).bind(window);
        waitFor(_runMocha.bind(null, reporter, done, true));
        return;
    }
    debug('_runMocha');
    var mocha = window.mocha;
    var Mocha = window.Mocha;
    mocha.reporter(reporter.childReporter(window.location));
    mocha.suite.title = reporter.suiteTitle(window.location);
    mocha.grep(GREP);
    // We can't use `mocha.run` because it bashes over grep, invert, and friends.
    // See https://github.com/visionmedia/mocha/blob/master/support/tail.js#L137
    var runner = Mocha.prototype.run.call(mocha, function (_error) {
        if (document.getElementById('mocha')) {
            Mocha.utils.highlightTags('code');
        }
        done(); // We ignore the Mocha failure count.
    });
    // Mocha's default `onerror` handling strips the stack (to support really old
    // browsers). We upgrade this to get better stacks for async errors.
    //
    // TODO(nevir): Can we expand support to other browsers?
    if (navigator.userAgent.match(/chrome/i)) {
        window.onerror = null;
        window.addEventListener('error', function (event) {
            if (!event.error)
                return;
            if (event.error.ignore)
                return;
            runner.uncaught(event.error);
        });
    }
}

/**
 * @param {CLISocket} socket The CLI socket, if present.
 * @param {MultiReporter} parent The parent reporter, if present.
 * @return {!Array.<!Mocha.reporters.Base} The reporters that should be used.
 */
function determineReporters(socket, parent) {
    // Parents are greedy.
    if (parent) {
        return [parent.childReporter(window.location)];
    }
    // Otherwise, we get to run wild without any parental supervision!
    var reporters = [Title, Console];
    if (socket) {
        reporters.push(function (runner) {
            socket.observe(runner);
        });
    }
    if (htmlSuites$1.length > 0 || jsSuites$1.length > 0) {
        reporters.push(HTML);
    }
    return reporters;
}
/**
 * Yeah, hideous, but this allows us to be loaded before Mocha, which is handy.
 */
function injectMocha(Mocha) {
    _injectPrototype(Console, Mocha.reporters.Base.prototype);
    _injectPrototype(HTML, Mocha.reporters.HTML.prototype);
    // Mocha doesn't expose its `EventEmitter` shim directly, so:
    _injectPrototype(MultiReporter, Object.getPrototypeOf(Mocha.Runner.prototype));
}
function _injectPrototype(klass, prototype) {
    var newPrototype = Object.create(prototype);
    // Only support
    Object.keys(klass.prototype).forEach(function (key) {
        newPrototype[key] = klass.prototype[key];
    });
    klass.prototype = newPrototype;
}

/**
 * Loads all environment scripts ...synchronously ...after us.
 */
function loadSync() {
    debug('Loading environment scripts:');
    var a11ySuiteScriptPath = 'web-component-tester/data/a11ySuite.js';
    var scripts = get('environmentScripts');
    var a11ySuiteWillBeLoaded = window.__generatedByWct || scripts.indexOf(a11ySuiteScriptPath) > -1;
    // We can't inject a11ySuite when running the npm version because it is a
    // module-based script that needs `<script type=module>` and compilation
    // for browsers without module support.
    if (!a11ySuiteWillBeLoaded && !window.__wctUseNpm) {
        // wct is running as a bower dependency, load a11ySuite from data/
        scripts.push(a11ySuiteScriptPath);
    }
    scripts.forEach(function (path) {
        var url = expandUrl(path, get('root'));
        debug('Loading environment script:', url);
        // Synchronous load.
        document.write('<script src="' + encodeURI(url) +
            '"></script>'); // jshint ignore:line
    });
    debug('Environment scripts loaded');
    var imports = get('environmentImports');
    imports.forEach(function (path) {
        var url = expandUrl(path, get('root'));
        debug('Loading environment import:', url);
        // Synchronous load.
        document.write('<link rel="import" href="' + encodeURI(url) +
            '">'); // jshint ignore:line
    });
    debug('Environment imports loaded');
}
/**
 * We have some hard dependencies on things that should be loaded via
 * `environmentScripts`, so we assert that they're present here; and do any
 * post-facto setup.
 */
function ensureDependenciesPresent() {
    _ensureMocha();
    _checkChai();
}
function _ensureMocha() {
    var Mocha = window.Mocha;
    if (!Mocha) {
        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');
    }
    injectMocha(Mocha);
    // Magic loading of mocha's stylesheet
    var mochaPrefix = scriptPrefix('mocha.js');
    // only load mocha stylesheet for the test runner output
    // Not the end of the world, if it doesn't load.
    if (mochaPrefix && window.top === window.self) {
        loadStyle(mochaPrefix + 'mocha.css');
    }
}
function _checkChai() {
    if (!window.chai) {
        debug('Chai not present; not registering shorthands');
        return;
    }
    window.assert = window.chai.assert;
    window.expect = window.chai.expect;
}

// We may encounter errors during initialization (for example, syntax errors in
// a test file). Hang onto those (and more) until we are ready to report them.
var globalErrors = [];
/**
 * Hook the environment to pick up on global errors.
 */
function listenForErrors() {
    window.addEventListener('error', function (event) {
        globalErrors.push(event.error);
    });
    // Also, we treat `console.error` as a test failure. Unless you prefer not.
    var origConsole = console;
    var origError = console.error;
    console.error = function wctShimmedError() {
        origError.apply(origConsole, arguments);
        if (get('trackConsoleError')) {
            throw 'console.error: ' + Array.prototype.join.call(arguments, ' ');
        }
    };
}

var interfaceExtensions = [];
/**
 * Registers an extension that extends the global `Mocha` implementation
 * with new helper methods. These helper methods will be added to the `window`
 * when tests run for both BDD and TDD interfaces.
 */
function extendInterfaces(helperName, helperFactory) {
    interfaceExtensions.push(function () {
        var Mocha = window.Mocha;
        // For all Mocha interfaces (probably just TDD and BDD):
        Object.keys(Mocha.interfaces)
            .forEach(function (interfaceName) {
            // This is the original callback that defines the interface (TDD or
            // BDD):
            var originalInterface = Mocha.interfaces[interfaceName];
            // This is the name of the "teardown" or "afterEach" property for the
            // current interface:
            var teardownProperty = interfaceName === 'tdd' ? 'teardown' : 'afterEach';
            // The original callback is monkey patched with a new one that appends
            // to the global context however we want it to:
            Mocha.interfaces[interfaceName] = function (suite) {
                // Call back to the original callback so that we get the base
                // interface:
                originalInterface.apply(this, arguments);
                // Register a listener so that we can further extend the base
                // interface:
                suite.on('pre-require', function (context, _file, _mocha) {
                    // Capture a bound reference to the teardown function as a
                    // convenience:
                    var teardown = context[teardownProperty].bind(context);
                    // Add our new helper to the testing context. The helper is
                    // generated by a factory method that receives the context,
                    // the teardown function and the interface name and returns
                    // the new method to be added to that context:
                    context[helperName] =
                        helperFactory(context, teardown, interfaceName);
                });
            };
        });
    });
}
/**
 * Applies any registered interface extensions. The extensions will be applied
 * as many times as this function is called, so don't call it more than once.
 */
function applyExtensions() {
    interfaceExtensions.forEach(function (applyExtension) {
        applyExtension();
    });
}

extendInterfaces('fixture', function (context, teardown) {
    // Return context.fixture if it is already a thing, for backwards
    // compatibility with `test-fixture-mocha.js`:
    return context.fixture || function fixture(fixtureId, model) {
        // Automatically register a teardown callback that will restore the
        // test-fixture:
        teardown(function () {
            document.getElementById(fixtureId).restore();
        });
        // Find the test-fixture with the provided ID and create it, returning
        // the results:
        return document.getElementById(fixtureId).create(model);
    };
});

/**
 * stub
 *
 * The stub addon allows the tester to partially replace the implementation of
 * an element with some custom implementation. Usage example:
 *
 * beforeEach(function() {
 *   stub('x-foo', {
 *     attached: function() {
 *       // Custom implementation of the `attached` method of element `x-foo`..
 *     },
 *     otherMethod: function() {
 *       // More custom implementation..
 *     },
 *     getterSetterProperty: {
 *       get: function() {
 *         // Custom getter implementation..
 *       },
 *       set: function() {
 *         // Custom setter implementation..
 *       }
 *     },
 *     // etc..
 *   });
 * });
 */
extendInterfaces('stub', function (_context, teardown) {
    return function stub(tagName, implementation) {
        // Find the prototype of the element being stubbed:
        var proto = document.createElement(tagName).constructor.prototype;
        // For all keys in the implementation to stub with..
        var stubs = Object.keys(implementation).map(function (key) {
            // Stub the method on the element prototype with Sinon:
            return sinon.stub(proto, key, implementation[key]);
        });
        // After all tests..
        teardown(function () {
            stubs.forEach(function (stub) {
                stub.restore();
            });
        });
    };
});

// replacement map stores what should be
var replacements = {};
var replaceTeardownAttached = false;
/**
 * replace
 *
 * The replace addon allows the tester to replace all usages of one element with
 * another element within all Polymer elements created within the time span of
 * the test. Usage example:
 *
 * beforeEach(function() {
 *   replace('x-foo').with('x-fake-foo');
 * });
 *
 * All annotations and attributes will be set on the placement element the way
 * they were set for the original element.
 */
extendInterfaces('replace', function (_context, teardown) {
    return function replace(oldTagName) {
        return {
            with: function (tagName) {
                // Standardizes our replacements map
                oldTagName = oldTagName.toLowerCase();
                tagName = tagName.toLowerCase();
                replacements[oldTagName] = tagName;
                // If the function is already a stub, restore it to original
                if (document.importNode.isSinonProxy) {
                    return;
                }
                if (!window.Polymer.Element) {
                    window.Polymer.Element = function () { };
                    window.Polymer.Element.prototype._stampTemplate = function () { };
                }
                // Keep a reference to the original `document.importNode`
                // implementation for later:
                var originalImportNode = document.importNode;
                // Use Sinon to stub `document.ImportNode`:
                sinon.stub(document, 'importNode', function (origContent, deep) {
                    var templateClone = document.createElement('template');
                    var content = templateClone.content;
                    var inertDoc = content.ownerDocument;
                    // imports node from inertDoc which holds inert nodes.
                    templateClone.content.appendChild(inertDoc.importNode(origContent, true));
                    // optional arguments are not optional on IE.
                    var nodeIterator = document.createNodeIterator(content, NodeFilter.SHOW_ELEMENT, null, true);
                    var node;
                    // Traverses the tree. A recently-replaced node will be put next,
                    // so if a node is replaced, it will be checked if it needs to be
                    // replaced again.
                    while (node = nodeIterator.nextNode()) {
                        var currentTagName = node.tagName.toLowerCase();
                        if (replacements.hasOwnProperty(currentTagName)) {
                            currentTagName = replacements[currentTagName];
                            // find the final tag name.
                            while (replacements[currentTagName]) {
                                currentTagName = replacements[currentTagName];
                            }
                            // Create a replacement:
                            var replacement = document.createElement(currentTagName);
                            // For all attributes in the original node..
                            for (var index = 0; index < node.attributes.length; ++index) {
                                // Set that attribute on the replacement:
                                replacement.setAttribute(node.attributes[index].name, node.attributes[index].value);
                            }
                            // Replace the original node with the replacement node:
                            node.parentNode.replaceChild(replacement, node);
                        }
                    }
                    return originalImportNode.call(this, content, deep);
                });
                if (!replaceTeardownAttached) {
                    // After each test...
                    teardown(function () {
                        replaceTeardownAttached = true;
                        // Restore the stubbed version of `document.importNode`:
                        var documentImportNode = document.importNode;
                        if (documentImportNode.isSinonProxy) {
                            documentImportNode.restore();
                        }
                        // Empty the replacement map
                        replacements = {};
                    });
                }
            }
        };
    };
});

// Mocha global helpers, broken out by testing method.
//
// Keys are the method for a particular interface; values are their analog in
// the opposite interface.
var MOCHA_EXPORTS = {
    // https://github.com/visionmedia/mocha/blob/master/lib/interfaces/tdd.js
    tdd: {
        'setup': '"before"',
        'teardown': '"after"',
        'suiteSetup': '"beforeEach"',
        'suiteTeardown': '"afterEach"',
        'suite': '"describe" or "context"',
        'test': '"it" or "specify"',
    },
    // https://github.com/visionmedia/mocha/blob/master/lib/interfaces/bdd.js
    bdd: {
        'before': '"setup"',
        'after': '"teardown"',
        'beforeEach': '"suiteSetup"',
        'afterEach': '"suiteTeardown"',
        'describe': '"suite"',
        'context': '"suite"',
        'xdescribe': '"suite.skip"',
        'xcontext': '"suite.skip"',
        'it': '"test"',
        'xit': '"test.skip"',
        'specify': '"test"',
        'xspecify': '"test.skip"',
    },
};
/**
 * Exposes all Mocha methods up front, configuring and running mocha
 * automatically when you call them.
 *
 * The assumption is that it is a one-off (sub-)suite of tests being run.
 */
function stubInterfaces() {
    var keys = Object.keys(MOCHA_EXPORTS);
    keys.forEach(function (ui) {
        Object.keys(MOCHA_EXPORTS[ui]).forEach(function (key) {
            window[key] = function wrappedMochaFunction() {
                _setupMocha(ui, key, MOCHA_EXPORTS[ui][key]);
                if (!window[key] || window[key] === wrappedMochaFunction) {
                    throw new Error('Expected mocha.setup to define ' + key);
                }
                window[key].apply(window, arguments);
            };
        });
    });
}
// Whether we've called `mocha.setup`
var _mochaIsSetup = false;
/**
 * @param {string} ui Sets up mocha to run `ui`-style tests.
 * @param {string} key The method called that triggered this.
 * @param {string} alternate The matching method in the opposite interface.
 */
function _setupMocha(ui, key, alternate) {
    var mochaOptions = get('mochaOptions');
    if (mochaOptions.ui && mochaOptions.ui !== ui) {
        var message = 'Mixing ' + mochaOptions.ui + ' and ' + ui +
            ' Mocha styles is not supported. ' +
            'You called "' + key + '". Did you mean ' + alternate + '?';
        throw new Error(message);
    }
    if (_mochaIsSetup) {
        return;
    }
    applyExtensions();
    mochaOptions.ui = ui;
    mocha.setup(mochaOptions); // Note that the reporter is configured in run.js.
}

// You can configure WCT before it has loaded by assigning your custom
// configuration to the global `WCT`.
setup(window.WCT);
// Maybe some day we'll expose WCT as a module to whatever module registry you
// are using (aka the UMD approach), or as an es6 module.
var WCT = window.WCT = {
    // A generic place to hang data about the current suite. This object is
    // reported
    // back via the `sub-suite-start` and `sub-suite-end` events.
    share: {},
    // Until then, we get to rely on it to expose parent runners to their
    // children.
    _ChildRunner: ChildRunner,
    _reporter: undefined,
    _config: _config,
    // Public API
    /**
     * Loads suites of tests, supporting both `.js` and `.html` files.
     *
     * @param {!Array.<string>} files The files to load.
     */
    loadSuites: loadSuites,
};
// Load Process
listenForErrors();
stubInterfaces();
loadSync();
// Give any scripts on the page a chance to declare tests and muck with things.
document.addEventListener('DOMContentLoaded', function () {
    debug('DOMContentLoaded');
    ensureDependenciesPresent();
    // We need the socket built prior to building its reporter.
    CLISocket.init(function (error, socket) {
        if (error)
            throw error;
        // Are we a child of another run?
        var current = ChildRunner.current();
        var parent = current && current.parentScope.WCT._reporter;
        debug('parentReporter:', parent);
        var childSuites = activeChildSuites();
        var reportersToUse = determineReporters(socket, parent);
        // +1 for any local tests.
        var reporter = new MultiReporter(childSuites.length + 1, reportersToUse, parent);
        WCT._reporter = reporter; // For environment/compatibility.js
        // We need the reporter so that we can report errors during load.
        loadJsSuites(reporter, function (error) {
            // Let our parent know that we're about to start the tests.
            if (current)
                current.ready(error);
            if (error)
                throw error;
            // Emit any errors we've encountered up til now
            globalErrors.forEach(function onError(error) {
                reporter.emitOutOfBandTest('Test Suite Initialization', error);
            });
            runSuites(reporter, childSuites, function (error) {
                // Make sure to let our parent know that we're done.
                if (current)
                    current.done();
                if (error)
                    throw error;
            });
        });
    });
});

}());
//# sourceMappingURL=browser.js.map

================================================
FILE: custom_typings/bower-config.d.ts
================================================
declare module 'bower-config' {
  interface Config {
    directory: string;
  }
  function read(cwd: string, overrides?: boolean): Config;
}


================================================
FILE: custom_typings/findup-sync.d.ts
================================================
declare module 'findup-sync' {
  import * as minimatch from 'minimatch';

  interface IOptions extends minimatch.IOptions {
    cwd?: string;
  }

  function mod(pattern: string[]|string, opts?: IOptions): string;
  namespace mod {}
  export = mod;
}


================================================
FILE: custom_typings/promisify-node.d.ts
================================================
declare module 'promisify-node' {
  interface NodeCallback<T> {
    (err: any, value: T): void;
  }
  function promisify<T>(f: (cb: NodeCallback<T>) => void): () => Promise<T>;
  function promisify<A1, T>(f: (a: A1, cb: NodeCallback<T>) => void): (a: A1) =>
      Promise<T>;
  function promisify<A1, A2, T>(
      f: (a: A1, a2: A2, cb: NodeCallback<T>) => void): (a: A1, a2: A2) =>
      Promise<T>;
  function promisify<A1, A2, A3, T>(
      f: (a: A1, a2: A2, a3: A3, cb: NodeCallback<T>) =>
          void): (a: A1, a2: A2, a3: A3) => Promise<T>;
  namespace promisify {}
  export = promisify;
}


================================================
FILE: custom_typings/send.d.ts
================================================
declare module 'send' {
  import * as http from 'http';
  import * as events from 'events';


  function send(req: http.IncomingMessage, path: string, options?: send.Options): send.SendStream;
  namespace send {
    export interface SendStream extends events.EventEmitter {
      pipe(res: http.ServerResponse): void;
    }

    export interface Options {
      dotfiles?: 'allow' | 'deny' | 'ignore';
      end?: number;
      etag?: boolean;
      extensions?: string[];
      index?: boolean|string|string[];
      lastModified?: boolean;
      maxAge?: number;
      root?: string;
      start?: number;
    }
  }
  export = send;
}


================================================
FILE: custom_typings/server-destroy.d.ts
================================================
declare module 'server-destroy' {
  import * as http from 'http';

  /**
   * Monkey-patches the destroy() method onto the given server.
   *
   * It only accepts DestroyableServers as parameters to remind the user
   * to update their type annotations elsewhere, as we can't express the
   * mutation in the type system directly.
   */
  function enableDestroy(server: enableDestroy.DestroyableServer): void;
  namespace enableDestroy {
    interface DestroyableServer extends http.Server {
      destroy(): void;
    }
  }
  export = enableDestroy;
}


================================================
FILE: custom_typings/stacky.d.ts
================================================
declare module 'stacky' {
  interface ParsedStackFrame {
    method: string;
    location: string;
    line: number;
    column: number;
  }
  type StyleFunction = (part: string) => string;
  interface Options {
    maxMethodPadding?: number;
    indent?: string;
    methodPlaceholder?: string;
    locationStrip?: (string|RegExp)[];
    unimportantLocation?: (string|RegExp)[];
    filter?: (line: ParsedStackFrame) => boolean;
    styles?: {
      method?: StyleFunction;
      location?: StyleFunction;
      line?: StyleFunction;
      column?: StyleFunction;
      unimportant?: StyleFunction;
    };
  }
  export function clean(lines: ParsedStackFrame[], options: Options): void;
  export function pretty(
      errorStack: string|ParsedStackFrame[], options: Options): string;

  export function normalize(error: Error, config: Options): Error;
}


================================================
FILE: custom_typings/wd.d.ts
================================================


declare module 'wd' {
  interface NodeCB<T> {
    (err: any, value: T): void;
  }
  export interface Browser {
    configureHttp(options: {
      retries: number
    }): void;
    attach(sessionId: string, callback: NodeCB<Capabilities>): void;
    init(capabilities: Capabilities, callback: NodeCB<string>): void;

    get(url: string, callback: NodeCB<void>): void;
    quit(callback: NodeCB<void>): void;

    on(eventName: string, handler: Function): void;
  }
  export interface Capabilities {
    /** The name of the browser being used */
    browserName: 'android'|'chrome'|'firefox'|'htmlunit'|'internet explorer'|'iPhone'|'iPad'|'opera'|'safari';
    /** The browser version, or the empty string if unknown. */
    version: string;
    /** A key specifying which platform the browser should be running on. */
    platform: 'WINDOWS'|'XP'|'VISTA'|'MAC'|'LINUX'|'UNIX'|'ANDROID'|'ANY';

    /** Whether the session can interact with modal popups,
     *  such as window.alert and window.confirm. */
    handlesAlerts: boolean;
    /** Whether the session supports CSS selectors when searching for elements. */
    cssSelectorsEnabled: boolean;

    webdriver: {
      remote: {
        quietExceptions: boolean;
      }
    };

    selenium: {
      server: {
        url: string;
      }
    };
  }

  export function remote(
      hostnameOrUrl: string, port?: number,
      username?: string, password?: string): Browser;
  export function remote(
      options: {hostname: string, port?: number,
                auth?: string, path?: string, }
      ): Browser;
  export function remote(
      options: {host: string, port?: number,
                username?: string, accesskey?: string, path?: string, }
      ): Browser;
}


================================================
FILE: data/a11ySuite-npm-header.txt
================================================
import * as polymerDom from '../@polymer/polymer/lib/legacy/polymer.dom.js';
const Polymer = { dom: polymerDom };
export {a11ySuiteExport as a11ySuite};

// wct-browser-legacy/a11ySuite.js is a generated file.  Source is in web-component-tester/data/a11ySuite.js


================================================
FILE: data/a11ySuite.js
================================================
/**
 * @license
 * Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
 * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
 * Code distributed by Google as part of the polymer project is also
 * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
 */

var a11ySuiteExport;

(function(Mocha, chai, axs) {

  Object.keys(Mocha.interfaces).forEach(function(iface) {
    var orig = Mocha.interfaces[iface];

    Mocha.interfaces[iface] = function(suite) {
      orig.apply(this, arguments);

      var Suite = Mocha.Suite;
      var Test = Mocha.Test;

      suite.on('pre-require', function(context, file, mocha) {

        /**
         * Runs the Chrome Accessibility Developer Tools Audit against a test-fixture
         *
         * @param {String} fixtureId ID of the fixture element in the document to use
         * @param {Array?} ignoredRules Array of rules to ignore for this suite
         * @param {Function?} beforeEach Function to be called before each test to ensure proper setup
         */
        a11ySuiteExport = context.a11ySuite = function(fixtureId, ignoredRules, beforeEach) {
          // capture a reference to the fixture element early
          var fixtureElement = document.getElementById(fixtureId);
          if (!fixtureElement) {
            return;
          }

          // build an audit config to disable certain ignorable tests
          var axsConfig = new axs.AuditConfiguration();
          axsConfig.scope = document.body;
          axsConfig.showUnsupportedRulesWarning = false;
          axsConfig.auditRulesToIgnore = ignoredRules;

          // build mocha suite
          var a11ySuite = Suite.create(suite, 'A11y Audit - Fixture: ' + fixtureId);

          // override the `eachTest` function to hackily create the tests
          //
          // eachTest is called right before test runs to calculate the total
          // number of tests
          a11ySuite.eachTest = function() {
            // instantiate fixture
            fixtureElement.create();

            // Make sure lazy-loaded dom is ready (eg <template is='dom-repeat'>)
            Polymer.dom.flush();

            // If we have a beforeEach function, call it
            if (beforeEach) {
              beforeEach();
            }

            // run audit
            var auditResults = axs.Audit.run(axsConfig);

            // create tests for audit results
            auditResults.forEach(function(result, index) {
              // only show applicable tests
              if (result.result !== 'NA') {
                var title = result.rule.heading;
                // fail test if audit result is FAIL
                var error = result.result === 'FAIL' ? axs.Audit.accessibilityErrorMessage(result) : null;
                var test = new Test(title, function() {
                  if (error) {
                    throw new Error(error);
                  }
                });
                test.file = file;
                a11ySuite.addTest(test);
              }
            });

            // teardown fixture
            fixtureElement.restore();

            suite.eachTest.apply(a11ySuite, arguments);
            this.eachTest = suite.eachTest;
          };

          return a11ySuite;
        };
      });
    };
  });

  chai.use(function(chai, util) {
    var Assertion = chai.Assertion;

    // assert
    chai.assert.a11yLabel = function(node, exp, msg){
      new Assertion(node).to.have.a11yLabel(exp, msg);
    };

    // expect / should
    Assertion.addMethod('a11yLabel', function(str, msg) {
      if (msg) {
        util.flag(this, 'message', msg);
      }

      var node = this._obj;

      // obj must be a Node
      new Assertion(node).to.be.instanceOf(Node);

      // vind the text alternative with the help of accessibility dev tools
      var textAlternative = axs.properties.findTextAlternatives(node, {});

      this.assert(
        textAlternative === str,
        'expected #{this} to have text alternative #{exp} but got #{act}',
        'expected #{this} to not have text alternative #{act}',
        str,
        textAlternative,
        true
      );
    });
  });
})(window.Mocha, window.chai, window.axs);


================================================
FILE: data/index.html
================================================
<!doctype html>
<html>

<head>
  <meta charset="utf-8">
  <script>
    WCT = <%= JSON.stringify(clientOptions) %>;
  </script>
  <script>
    window.__generatedByWct = true;
  </script>
  <script src="../<%= browserScript %>"></script>
  <% extraScripts.forEach(function(script) { %>
    <script src="<%- script %>"></script>
  <% }); %>
  <% if (typeof npm === 'undefined' || !npm) { %>
    <script src="../<%= a11ySuiteScript %>"></script>
  <% } %>
</head>

<body>
  <script>
    WCT.loadSuites(<%= JSON.stringify(suites) %>);
  </script>
</body>

</html>


================================================
FILE: gulpfile.js
================================================
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
 * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
 * Code distributed by Google as part of the polymer project is also
 * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
 */
'use strict';

const concat = require('gulp-concat');
const depcheck = require('depcheck');
const fs = require('fs');
const glob = require('glob');
const gulp = require('gulp');
const bower = require('gulp-bower');
const mocha = require('gulp-spawn-mocha');
const tslint = require('gulp-tslint');
const ts = require('gulp-typescript');
const lazypipe = require('lazypipe');
const path = require('path');
const rollup = require('rollup');
const runSequence = require('run-sequence');
const typescript = require('typescript');

const mochaConfig = { reporter: 'spec' };
if (process.env.MOCHA_TIMEOUT) {
  mochaConfig.timeout = parseInt(process.env.MOCHA_TIMEOUT, 10);
}

// const commonTools = require('tools-common/gulpfile');
const commonTools = {
  depcheck: commonDepCheck
};

gulp.task('lint', ['tslint', 'depcheck']);

// Meta tasks

gulp.task('default', ['test']);

function removeFile(path) {
  try {
    fs.unlinkSync(path);
    return;
  } catch (e) {
    try {
      fs.statSync(path);
    } catch (e) {
      return;
    }
    throw new Error('Unable to remove file: ' + path);
  }
}

gulp.task('clean', (done) => {
  removeFile('browser.js');
  removeFile('browser.js.map');
  const patterns = ['runner/*.js', 'browser/**/*.js', 'browser/**/*.js.map'];
  for (const pattern of patterns) {
    glob(pattern, (err, files) => {
      if (err) {
        return done(err);
      }
      try {
        for (const file of files) {
          removeFile(file);
        }
      } catch (e) {
        return done(e);
      }
    });
  }
  done();
});

gulp.task('test', function (done) {
  runSequence(
    'build:typescript-server',
    'lint',
    'test:unit',
    'test:integration',
    done);
});

gulp.task('build-all', (done) => {
  runSequence('clean', 'lint', 'build', done);
});

gulp.task('build',
  ['build:typescript-server', 'build:browser', 'build:wct-browser-legacy']);

const tsProject = ts.createProject('tsconfig.json', { typescript });
gulp.task('build:typescript-server', function () {
  // Ignore typescript errors, because gulp-typescript, like most things
  // gulp, can't be trusted.
  return tsProject.src().pipe(tsProject(ts.reporter.nullReporter())).js.pipe(gulp.dest('./'));
});

const browserTsProject = ts.createProject('browser/tsconfig.json', {
  typescript
});
gulp.task('build:typescript-browser', function () {
  return browserTsProject.src().pipe(
    browserTsProject(ts.reporter.nullReporter())).js.pipe(gulp.dest('./browser/'));
});

// Specific tasks

gulp.task('build:browser', ['build:typescript-browser'], function (done) {
  rollup.rollup({
    entry: 'browser/index.js',
  }).then(function (bundle) {
    bundle.write({
      indent: false,
      format: 'iife',
      banner: fs.readFileSync('browser-js-header.txt', 'utf-8'),
      intro: 'window.__wctUseNpm = false;',
      dest: 'browser.js',
      sourceMap: true,
      sourceMapFile: path.resolve('browser.js.map')
    }).then(function () {
      done();
    });
  }).catch(done);
});

gulp.task('build:wct-browser-legacy:a11ySuite', function () {
  return gulp.src(['data/a11ySuite-npm-header.txt', 'data/a11ySuite.js'])
    .pipe(concat('a11ySuite.js'))
    .pipe(gulp.dest('wct-browser-legacy/'));
});

gulp.task('build:wct-browser-legacy:browser', ['build:typescript-browser'], function (done) {
  rollup.rollup({
    entry: 'browser/index.js',
  }).then(function (bundle) {
    bundle.write({
      indent: false,
      format: 'iife',
      banner: fs.readFileSync('browser-js-header.txt', 'utf-8'),
      intro: 'window.__wctUseNpm = true;',
      dest: 'wct-browser-legacy/browser.js',
      sourceMap: true,
      sourceMapFile: path.resolve('browser.js.map')
    }).then(function () {
      done();
    });
  }).catch(done);
});

gulp.task('build:wct-browser-legacy', [
  'build:wct-browser-legacy:a11ySuite',
  'build:wct-browser-legacy:browser',
]);


gulp.task('test:unit', function () {
  return gulp.src('test/unit/*.js', { read: false })
    .pipe(mocha(mochaConfig));
});

gulp.task('bower', function () {
  return bower();
});

gulp.task('test:integration', ['bower'], function () {
  return gulp.src('test/integration/*.js', { read: false })
    .pipe(mocha(mochaConfig));
});

gulp.task('tslint', () =>
  gulp.src([
    'runner/**/*.ts', '!runner/**/*.d.ts',
    'test/**/*.ts', '!test/**/*.d.ts',
    'custom_typings/*.d.ts', 'browser/**/*.ts', '!browser/**/*.ts'
  ])
    .pipe(tslint())
    .pipe(tslint.report({ formatter: 'verbose' })));

// Flows

commonTools.depcheck({
  stickyDeps: new Set([
    // Used in browser.js
    'accessibility-developer-tools',
    'mocha',
    'test-fixture',
    '@polymer/sinonjs',
    '@polymer/test-fixture',
    '@webcomponents/webcomponentsjs',
    'async',
    'findup-sync',

    // Only included to satisfy peer dependency and suppress error on install
    'sinon',

    // Used in the wct binary
    'resolve'
  ])
});

function commonDepCheck(options) {
  const defaultOptions = { stickyDeps: new Set() };
  options = Object.assign({}, defaultOptions, options);

  gulp.task('depcheck', () => {
    return new Promise((resolve, reject) => {
      depcheck(
        __dirname, { ignoreDirs: [], ignoreMatches: ['@types/*'] }, resolve);
    }).then((result) => {
      const invalidFiles = Object.keys(result.invalidFiles) || [];
      const invalidJsFiles = invalidFiles.filter((f) => f.endsWith('.js'));

      if (invalidJsFiles.length > 0) {
        console.log('Invalid files:', result.invalidFiles);
        throw new Error('Invalid files');
      }

      const unused = new Set(result.dependencies);
      for (const falseUnused of options.stickyDeps) {
        unused.delete(falseUnused);
      }
      if (unused.size > 0) {
        console.log('Unused dependencies:', unused);
        throw new Error('Unused dependencies');
      }
    });
  });
}

gulp.task('prepublish', function (done) {
  // We can't run the integration tests here because on travis we may not
  // be running with an x instance when we do `npm install`. We can change
  // this to just `test` from `test:unit` once all supported npm versions
  // no longer run `prepublish` on install.
  runSequence('build-all', 'test:unit', done);
});


================================================
FILE: package.json
================================================
{
  "name": "web-component-tester",
  "version": "6.6.0-pre.5",
  "--private-wct--": {
    "client-side-version-range": "4 - 6 || ^6.6.0-pre.1",
    "wct-browser-legacy-version-range": "0.0.1-pre.1 || ^1.0.0"
  },
  "description": "web-component-tester makes testing your web components a breeze!",
  "keywords": [
    "browser",
    "grunt",
    "gruntplugin",
    "gulp",
    "polymer",
    "test",
    "testing",
    "web component",
    "web"
  ],
  "homepage": "https://github.com/Polymer/web-component-tester",
  "bugs": "https://github.com/Polymer/web-component-tester/issues",
  "license": "BSD-3-Clause",
  "repository": {
    "type": "git",
    "url": "https://github.com/Polymer/web-component-tester.git"
  },
  "main": "runner.js",
  "bin": {
    "wct": "./bin/wct",
    "wct-st": "./bin/wct-st"
  },
  "files": [
    "bin/",
    "data/",
    "runner/",
    "scripts/",
    "tasks/",
    ".bowerrc",
    "bower.json",
    "browser.js",
    "browser.js.map",
    "package.json",
    "LICENSE",
    "README.md",
    "runner.js"
  ],
  "scripts": {
    "lint": "gulp lint",
    "build": "tsc && gulp build",
    "test": "tsc && gulp test",
    "prepublishOnly": "gulp prepublish",
    "test:watch": "watch 'gulp test:unit' runner/ browser/ bin/ test/ tasks/",
    "format": "find runner test | grep '\\.js$\\|\\.ts$' | xargs ./node_modules/.bin/clang-format --style=file -i"
  },
  "dependencies": {
    "@polymer/sinonjs": "^1.14.1",
    "@polymer/test-fixture": "^0.0.3",
    "@webcomponents/webcomponentsjs": "^1.0.7",
    "accessibility-developer-tools": "^2.12.0",
    "async": "^2.4.1",
    "body-parser": "^1.17.2",
    "bower-config": "^1.4.0",
    "chai": "^4.0.2",
    "chalk": "^1.1.3",
    "cleankill": "^2.0.0",
    "express": "^4.15.3",
    "findup-sync": "^2.0.0",
    "glob": "^7.1.2",
    "lodash": "^3.10.1",
    "mocha": "^3.4.2",
    "multer": "^1.3.0",
    "nomnom": "^1.8.1",
    "polyserve": "^0.27.2",
    "promisify-node": "^0.4.0",
    "resolve": "^1.5.0",
    "semver": "^5.3.0",
    "send": "^0.11.1",
    "server-destroy": "^1.0.1",
    "sinon": "^2.3.5",
    "sinon-chai": "^2.10.0",
    "socket.io": "^2.0.3",
    "stacky": "^1.3.1",
    "wd": "^1.2.0"
  },
  "optionalDependencies": {
    "update-notifier": "^2.2.0",
    "wct-local": "^2.1.0",
    "wct-sauce": "^2.0.0"
  },
  "devDependencies": {
    "@types/body-parser": "0.0.33",
    "@types/chai": "^3.4.34",
    "@types/chalk": "^0.4.31",
    "@types/express": "^4.0.33",
    "@types/express-serve-static-core": "^4.0.39",
    "@types/glob": "^5.0.30",
    "@types/grunt": "^0.4.20",
    "@types/gulp": "^3.8.8",
    "@types/lodash": "^4.14.38",
    "@types/mime": "0.0.29",
    "@types/minimatch": "^2.0.29",
    "@types/mocha": "^2.2.32",
    "@types/multer": "0.0.32",
    "@types/node": "^8.0.0",
    "@types/nomnom": "0.0.28",
    "@types/rimraf": "0.0.28",
    "@types/semver": "^5.3.30",
    "@types/sinon": "^1.16.31",
    "@types/sinon-chai": "^2.7.27",
    "@types/socket.io": "^1.4.27",
    "bower": "^1.7.9",
    "clang-format": "^1.0.43",
    "depcheck": "^0.6.3",
    "grunt": "^0.4.5",
    "gulp": "^3.8.8",
    "gulp-bower": "0.0.13",
    "gulp-concat": "^2.6.1",
    "gulp-spawn-mocha": "^3.1.0",
    "gulp-tslint": "^8.1.2",
    "gulp-typescript": "^3.1.2",
    "lazypipe": "^1.0.1",
    "rimraf": "^2.5.4",
    "rollup": "^0.25.1",
    "run-sequence": "^1.0.1",
    "tslint": "^5.7.0",
    "typescript": "^2.1.4",
    "watch": "^0.18.0"
  },
  "engines": {
    "node": ">= 6.0"
  }
}


================================================
FILE: runner/browserrunner.ts
================================================
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt
 * The complete set of authors may be found at
 * http://polymer.github.io/AUTHORS.txt
 * The complete set of contributors may be found at
 * http://polymer.github.io/CONTRIBUTORS.txt
 * Code distributed by Google as part of the polymer project is also
 * subject to an additional IP rights grant found at
 * http://polymer.github.io/PATENTS.txt
 */

import * as chalk from 'chalk';
import * as cleankill from 'cleankill';
import * as _ from 'lodash';
import * as wd from 'wd';
import {Config} from './config';

export interface Stats {
  status: string;
  passing?: number;
  pending?: number;
  failing?: number;
}

export interface BrowserDef extends wd.Capabilities {
  id: number;
  url: string;
  sessionId: string;
  deviceName?: string;
  variant?: string;
}

// Browser abstraction, responsible for spinning up a browser instance via wd.js
// and executing runner.html test files passed in options.files
export class BrowserRunner {
  timeout: number;
  browser: wd.Browser;
  stats: Stats;
  sessionId: string;
  timeoutId: NodeJS.Timer;
  emitter: NodeJS.EventEmitter;
  def: BrowserDef;
  options: Config;
  donePromise: Promise<void>;

  /**
   * The url of the initial page to load in the browser when starting tests.
   */
  url: string;

  private _resolve: () => void;
  private _reject: (err: any) => void;

  /**
   * @param emitter The emitter to send updates about test progress to.
   * @param def A BrowserDef describing and defining the browser to be run.
   *     Includes both metadata and a method for connecting/launching the
   *     browser.
   * @param options WCT options.
   * @param url The url of the generated index.html file that the browser should
   *     point at.
   * @param waitFor Optional. If given, we won't try to start/connect to the
   *     browser until this promise resolves. Used for serializing access to
   *     Safari webdriver, which can only have one instance running at once.
   */
  constructor(
      emitter: NodeJS.EventEmitter, def: BrowserDef, options: Config,
      url: string, waitFor?: Promise<void>) {
    this.emitter = emitter;
    this.def = def;
    this.options = options;
    this.timeout = options.testTimeout;
    this.emitter = emitter;
    this.url = url;

    this.stats = {status: 'initializing'};

    this.donePromise = new Promise<void>((resolve, reject) => {
      this._resolve = resolve;
      this._reject = reject;
    });

    waitFor = waitFor || Promise.resolve();
    waitFor.then(() => {
      this.browser = wd.remote(this.def.url);

      // never retry selenium commands
      this.browser.configureHttp({retries: -1});


      cleankill.onInterrupt(() => {
        return new Promise((resolve) => {
          if (!this.browser) {
            return resolve();
          }

          this.donePromise.then(() => resolve(), () => resolve());
          this.done('Interrupting');
        });
      });

      this.browser.on('command', (method: any, context: any) => {
        emitter.emit('log:debug', this.def, chalk.cyan(method), context);
      });

      this.browser.on('http', (method: any, path: any, data: any) => {
        if (data) {
          emitter.emit(
              'log:debug', this.def, chalk.magenta(method), chalk.cyan(path),
              data);
        } else {
          emitter.emit(
              'log:debug', this.def, chalk.magenta(method), chalk.cyan(path));
        }
      });

      this.browser.on('connection', (code: any, message: any, error: any) => {
        emitter.emit(
            'log:warn', this.def, 'Error code ' + code + ':', message, error);
      });

      this.emitter.emit('browser-init', this.def, this.stats);

      // Make sure that we are passing a pristine capabilities object to
      // webdriver. None of our screwy custom properties!
      const webdriverCapabilities = _.clone(this.def);
      delete webdriverCapabilities.id;
      delete webdriverCapabilities.url;
      delete webdriverCapabilities.sessionId;

      // Reusing a session?
      if (this.def.sessionId) {
        this.browser.attach(this.def.sessionId, (error) => {
          this._init(error, this.def.sessionId);
        });
      } else {
        this.browser.init(webdriverCapabilities, this._init.bind(this));
      }
    });
  }

  _init(error: any, sessionId: string) {
    if (!this.browser) {
      return;  // When interrupted.
    }
    if (error) {
      // TODO(nevir): BEGIN TEMPORARY CHECK.
      // https://github.com/Polymer/web-component-tester/issues/51
      if (this.def.browserName === 'safari' && error.data) {
        // debugger;
        try {
          const data = JSON.parse(error.data);
          if (data.value && data.value.message &&
              /Failed to connect to SafariDriver/i.test(data.value.message)) {
            error = 'Until Selenium\'s SafariDriver supports ' +
                'Safari 6.2+, 7.1+, & 8.0+, you must\n' +
                'manually install it. Follow the steps at:\n' +
                'https://github.com/SeleniumHQ/selenium/' +
                'wiki/SafariDriver#getting-started';
          }
        } catch (error) {
          // Show the original error.
        }
      }
      // END TEMPORARY CHECK
      this.done(error.data || error);
    } else {
      this.sessionId = sessionId;
      this.startTest();
      this.extendTimeout();
    }
  }

  startTest() {
    const paramDelim = (this.url.indexOf('?') === -1 ? '?' : '&');
    const extra = `${paramDelim}cli_browser_id=${this.def.id}`;
    this.browser.get(this.url + extra, (error) => {
      if (error) {
        this.done(error.data || error);
      } else {
        this.extendTimeout();
      }
    });
  }

  onEvent(event: string, data: any) {
    this.extendTimeout();
    if (event === 'browser-start') {
      // Always assign, to handle re-runs (no browser-init).
      this.stats = {
        status: 'running',
        passing: 0,
        pending: 0,
        failing: 0,
      };
    } else if (event === 'test-end') {
      this.stats[data.state] = this.stats[data.state] + 1;
    }

    if (event === 'browser-end' || event === 'browser-fail') {
      this.done(data);
    } else {
      this.emitter.emit(event, this.def, data, this.stats, this.browser);
    }
  }

  done(error: any) {
    // No quitting for you!
    if (this.options.persistent) {
      return;
    }

    if (this.timeoutId) {
      clearTimeout(this.timeoutId);
    }
    // Don't double-quit.
    if (!this.browser) {
      return;
    }
    const browser = this.browser;
    this.browser = null;

    this.stats.status = error ? 'error' : 'complete';
    if (!error && this.stats.failing > 0) {
      error = this.stats.failing + ' failed tests';
    }

    this.emitter.emit(
        'browser-end', this.def, error, this.stats, this.sessionId, browser);

    // Nothing to quit.
    if (!this.sessionId) {
      error ? this._reject(error) : this._resolve();
    }

    browser.quit((quitError) => {
      if (quitError) {
        this.emitter.emit(
            'log:warn', this.def,
            'Failed to quit:', quitError.data || quitError);
      }
      if (error) {
        this._reject(error);
      } else {
        this._resolve();
      }
    });
  }

  extendTimeout() {
    if (this.options.persistent) {
      return;
    }
    if (this.timeoutId) {
      clearTimeout(this.timeoutId);
    }
    this.timeoutId = setTimeout(() => {
      this.done('Timed out');
    }, this.timeout);
  }

  quit() {
    this.done('quit was called');
  }

  // HACK
  static BrowserRunner = BrowserRunner;
}

module.exports = BrowserRunner;


================================================
FILE: runner/cli.ts
================================================
/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt
 * The complete set of authors may be found at
 * http://polymer.github.io/AUTHORS.txt
 * The complete set of contributors may be found at
 * http://polymer.github.io/CONTRIBUTORS.txt
 * Code distributed by Google as part of the polymer project is also
 * subject to an additional IP rights grant found at
 * http://polymer.github.io/PATENTS.txt
 */

import * as chalk from 'chalk';
import * as events from 'events';
import * as _ from 'lodash';

import {CliReporter} from './clireporter';
import * as config from './config';
import {Context} from './context';
import {Plugin} from './plugin';
import {test} from './test';

const PACKAGE_INFO = require('../package.json');
const noopNotifier = {
  notify: () => {}
}
Download .txt
gitextract_nxebno92/

├── .clang-format
├── .github/
│   └── PULL_REQUEST_TEMPLATE
├── .gitignore
├── .npmignore
├── .travis.yml
├── .vscode/
│   └── settings.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── bin/
│   ├── wct
│   └── wct-st
├── bower.json
├── browser/
│   ├── childrunner.ts
│   ├── clisocket.ts
│   ├── config.ts
│   ├── declarations.ts
│   ├── environment/
│   │   ├── compatability.ts
│   │   ├── errors.ts
│   │   └── helpers.ts
│   ├── environment.ts
│   ├── index.ts
│   ├── mocha/
│   │   ├── extend.ts
│   │   ├── fixture.ts
│   │   ├── replace.ts
│   │   └── stub.ts
│   ├── mocha.ts
│   ├── more-declarations.ts
│   ├── reporters/
│   │   ├── console.ts
│   │   ├── html.ts
│   │   ├── multi.ts
│   │   └── title.ts
│   ├── reporters.ts
│   ├── suites.ts
│   ├── tsconfig.json
│   └── util.ts
├── browser-js-header.txt
├── browser.js
├── custom_typings/
│   ├── bower-config.d.ts
│   ├── findup-sync.d.ts
│   ├── promisify-node.d.ts
│   ├── send.d.ts
│   ├── server-destroy.d.ts
│   ├── stacky.d.ts
│   └── wd.d.ts
├── data/
│   ├── a11ySuite-npm-header.txt
│   ├── a11ySuite.js
│   └── index.html
├── gulpfile.js
├── package.json
├── runner/
│   ├── browserrunner.ts
│   ├── cli.ts
│   ├── clireporter.ts
│   ├── config.ts
│   ├── context.ts
│   ├── gulp.ts
│   ├── httpbin.ts
│   ├── paths.ts
│   ├── plugin.ts
│   ├── port-scanner.ts
│   ├── steps.ts
│   ├── test.ts
│   └── webserver.ts
├── runner.js
├── tasks/
│   └── test.js
├── test/
│   ├── fixtures/
│   │   ├── cli/
│   │   │   ├── conf/
│   │   │   │   ├── branch/
│   │   │   │   │   └── leaf/
│   │   │   │   │       └── thing.js
│   │   │   │   ├── json/
│   │   │   │   │   ├── wct.conf.js
│   │   │   │   │   └── wct.conf.json
│   │   │   │   ├── rooted/
│   │   │   │   │   └── wct.conf.js
│   │   │   │   ├── test/
│   │   │   │   │   └── foo.js
│   │   │   │   └── wct.conf.js
│   │   │   └── standard/
│   │   │       ├── test/
│   │   │       │   ├── a.html
│   │   │       │   └── b.js
│   │   │       └── x-foo.html
│   │   ├── early-failure/
│   │   │   ├── bower_components/
│   │   │   │   └── web-component-tester/
│   │   │   │       └── package.json
│   │   │   └── test/
│   │   │       └── index.html
│   │   ├── fake-packages/
│   │   │   ├── duplicated-dep/
│   │   │   │   └── package.json
│   │   │   └── singleton-dep/
│   │   │       └── package.json
│   │   ├── integration/
│   │   │   ├── compilation/
│   │   │   │   ├── golden.json
│   │   │   │   └── test/
│   │   │   │       └── index.html
│   │   │   ├── components_dir/
│   │   │   │   ├── bower_components/
│   │   │   │   │   └── foo-element/
│   │   │   │   │       └── foo-element.js
│   │   │   │   ├── golden.json
│   │   │   │   ├── test/
│   │   │   │   │   └── index.html
│   │   │   │   └── wct.conf.json
│   │   │   ├── custom-components_dir/
│   │   │   │   ├── .bowerrc
│   │   │   │   ├── golden.json
│   │   │   │   ├── my_components/
│   │   │   │   │   └── bar-element/
│   │   │   │   │       └── bar-element.js
│   │   │   │   └── test/
│   │   │   │       └── index.html
│   │   │   ├── custom-multiple-component_dirs/
│   │   │   │   ├── .bowerrc
│   │   │   │   ├── golden.json
│   │   │   │   ├── my_components/
│   │   │   │   │   └── package/
│   │   │   │   │       └── index.js
│   │   │   │   ├── my_components-bar/
│   │   │   │   │   └── package/
│   │   │   │   │       └── index.js
│   │   │   │   ├── my_components-foo/
│   │   │   │   │   └── package/
│   │   │   │   │       └── index.js
│   │   │   │   └── test/
│   │   │   │       └── index.html
│   │   │   ├── define-webserver-hook/
│   │   │   │   ├── golden.json
│   │   │   │   └── test/
│   │   │   │       ├── index.html
│   │   │   │       └── tests.html
│   │   │   ├── failing/
│   │   │   │   ├── golden.json
│   │   │   │   └── test/
│   │   │   │       ├── index.html
│   │   │   │       ├── tests.html
│   │   │   │       └── tests.js
│   │   │   ├── missing/
│   │   │   │   ├── golden.json
│   │   │   │   └── test/
│   │   │   │       └── missing.html
│   │   │   ├── mixed-suites/
│   │   │   │   ├── golden.json
│   │   │   │   └── test/
│   │   │   │       ├── index.html
│   │   │   │       ├── one.html
│   │   │   │       ├── one.js
│   │   │   │       ├── two.html
│   │   │   │       └── two.js
│   │   │   ├── multiple-component_dirs/
│   │   │   │   ├── bower_components/
│   │   │   │   │   └── package/
│   │   │   │   │       └── index.js
│   │   │   │   ├── bower_components-bar/
│   │   │   │   │   └── package/
│   │   │   │   │       └── index.js
│   │   │   │   ├── bower_components-foo/
│   │   │   │   │   └── package/
│   │   │   │   │       └── index.js
│   │   │   │   ├── golden.json
│   │   │   │   └── test/
│   │   │   │       └── index.html
│   │   │   ├── multiple-replace/
│   │   │   │   ├── dom-if-element.html
│   │   │   │   ├── dom-repeat-fixture.html
│   │   │   │   ├── exception-element.html
│   │   │   │   ├── exception-fixture.html
│   │   │   │   ├── golden.json
│   │   │   │   ├── normal-element.html
│   │   │   │   ├── projection-element-2.html
│   │   │   │   ├── projection-element-3.html
│   │   │   │   ├── projection-element-4.html
│   │   │   │   ├── projection-element.html
│   │   │   │   └── test/
│   │   │   │       ├── index.html
│   │   │   │       └── tests.html
│   │   │   ├── nested/
│   │   │   │   ├── golden.json
│   │   │   │   └── test/
│   │   │   │       ├── index.html
│   │   │   │       ├── leaf.html
│   │   │   │       ├── leaf.js
│   │   │   │       ├── one/
│   │   │   │       │   ├── index.html
│   │   │   │       │   └── tests.html
│   │   │   │       └── two/
│   │   │   │           └── index.html
│   │   │   ├── no-tests/
│   │   │   │   ├── golden.json
│   │   │   │   └── test/
│   │   │   │       └── index.html
│   │   │   └── query-string/
│   │   │       ├── golden.json
│   │   │       └── test/
│   │   │           ├── index.html
│   │   │           ├── tests.html
│   │   │           └── tests.js
│   │   └── paths/
│   │       ├── bar/
│   │       │   ├── a.js
│   │       │   ├── index.html
│   │       │   └── index.js
│   │       ├── baz/
│   │       │   ├── a/
│   │       │   │   └── fizz.html
│   │       │   ├── a.html
│   │       │   ├── b/
│   │       │   │   ├── deep/
│   │       │   │   │   ├── index.html
│   │       │   │   │   ├── stuff.html
│   │       │   │   │   └── stuff.js
│   │       │   │   ├── index.html
│   │       │   │   └── one.js
│   │       │   └── b.js
│   │       ├── foo/
│   │       │   ├── one.js
│   │       │   ├── three.css
│   │       │   └── two.html
│   │       ├── foo.html
│   │       └── foo.js
│   ├── integration/
│   │   ├── browser.ts
│   │   └── setup_test_dir.ts
│   └── unit/
│       ├── cli.ts
│       ├── config.ts
│       ├── context.ts
│       ├── grunt.ts
│       ├── gulp.ts
│       └── paths.ts
├── tsconfig.json
├── tslint.json
└── wct-browser-legacy/
    ├── a11ySuite.js
    ├── browser.js
    └── package.json
Download .txt
SYMBOL INDEX (403 symbols across 48 files)

FILE: browser.js
  function safeStep (line 41) | function safeStep(callback, stepFn) {
  function testImmediate (line 63) | function testImmediate(name, testFn) {
  function testImmediateAsync (line 84) | function testImmediateAsync(name, testFn) {
  function flush (line 113) | function flush(callback) {
  function animationFrameFlush (line 157) | function animationFrameFlush(callback) {
  function asyncPlatformFlush (line 168) | function asyncPlatformFlush(callback) {
  function waitFor (line 175) | function waitFor(fn, next, intervalOrMutationEl, timeout, timeoutTime) {
  function setup (line 243) | function setup(options) {
  function get (line 265) | function get(key) {
  function _deepMerge (line 269) | function _deepMerge(target, source) {
  function whenFrameworksReady (line 285) | function whenFrameworksReady(callback) {
  function pluralizedStat (line 307) | function pluralizedStat(count, kind) {
  function loadScript (line 319) | function loadScript(path, done) {
  function loadStyle (line 332) | function loadStyle(path, done) {
  function debug (line 346) | function debug() {
  function parseUrl (line 362) | function parseUrl(url) {
  function expandUrl (line 376) | function expandUrl(url, base) {
  function getParams (line 390) | function getParams(query) {
  function mergeParams (line 423) | function mergeParams(target, source) {
  function getParam (line 435) | function getParam(param) {
  function paramsToQuery (line 443) | function paramsToQuery(params) {
  function getPathName (line 452) | function getPathName(location) {
  function basePath (line 455) | function basePath(location) {
  function relativeLocation (line 458) | function relativeLocation(location, basePath) {
  function cleanLocation (line 465) | function cleanLocation(location) {
  function parallel (line 472) | function parallel(runners, maybeLimit, done) {
  function scriptPrefix (line 520) | function scriptPrefix(filename) {
  function ChildRunner (line 554) | function ChildRunner(url, parentScope) {
  function CLISocket (line 694) | function CLISocket(browserId, socket) {
  function getTitles (line 785) | function getTitles(runnable) {
  function getState (line 797) | function getState(runnable) {
  function log (line 832) | function log(text, style) {
  function logGroup (line 845) | function logGroup(text, style) {
  function logGroupEnd (line 857) | function logGroupEnd() {
  function logException (line 865) | function logException(error) {
  function Console (line 875) | function Console(runner) {
  function HTML (line 938) | function HTML(runner) {
  function MultiReporter (line 981) | function MultiReporter(numSuites, reporters, parent) {
  function ChildReporter (line 1007) | function ChildReporter(runner) {
  function Title (line 1190) | function Title(runner) {
  function drawFaviconArc (line 1240) | function drawFaviconArc(context, total, start, length, color) {
  function loadSuites (line 1263) | function loadSuites(files) {
  function activeChildSuites (line 1280) | function activeChildSuites() {
  function loadJsSuites (line 1296) | function loadJsSuites(_reporter, done) {
  function runSuites (line 1304) | function runSuites(reporter, childSuites, done) {
  function _runMocha (line 1334) | function _runMocha(reporter, done, waited) {
  function determineReporters (line 1375) | function determineReporters(socket, parent) {
  function injectMocha (line 1395) | function injectMocha(Mocha) {
  function _injectPrototype (line 1401) | function _injectPrototype(klass, prototype) {
  function loadSync (line 1413) | function loadSync() {
  function ensureDependenciesPresent (line 1448) | function ensureDependenciesPresent() {
  function _ensureMocha (line 1452) | function _ensureMocha() {
  function _checkChai (line 1466) | function _checkChai() {
  function listenForErrors (line 1481) | function listenForErrors() {
  function extendInterfaces (line 1502) | function extendInterfaces(helperName, helperFactory) {
  function applyExtensions (line 1541) | function applyExtensions() {
  function stubInterfaces (line 1730) | function stubInterfaces() {
  function _setupMocha (line 1751) | function _setupMocha(ui, key, alternate) {

FILE: browser/childrunner.ts
  type SharedState (line 19) | interface SharedState {}
  class ChildRunner (line 25) | class ChildRunner {
    method constructor (line 34) | constructor(url: string, parentScope: Window) {
    method current (line 57) | static current(): ChildRunner {
    method get (line 66) | static get(target: Window, traversal?: boolean): ChildRunner {
    method run (line 88) | run(done: (error?: any) => void) {
    method loaded (line 127) | loaded(error: any) {
    method ready (line 153) | ready(error?: any) {
    method done (line 167) | done() {
    method signalRunComplete (line 185) | signalRunComplete(error?: any) {

FILE: browser/clisocket.ts
  constant SOCKETIO_ENDPOINT (line 15) | const SOCKETIO_ENDPOINT =
  constant SOCKETIO_LIBRARY (line 17) | const SOCKETIO_LIBRARY = SOCKETIO_ENDPOINT + '/socket.io/socket.io.js';
  class CLISocket (line 25) | class CLISocket {
    method constructor (line 28) | constructor(browserId: string, socket: SocketIO.Socket) {
    method observe (line 37) | observe(runner: Mocha.IRunner) {
    method emitEvent (line 87) | emitEvent(event: string, data?: any) {
    method init (line 101) | static init(done: (error?: any, socket?: CLISocket) => void) {
  function getTitles (line 133) | function getTitles(runnable: Mocha.IRunnable) {
  function getState (line 146) | function getState(runnable: Mocha.IRunnable) {

FILE: browser/config.ts
  type Config (line 14) | interface Config {
  function setup (line 77) | function setup(options: Config) {
  function get (line 103) | function get<K extends keyof Config>(key: K): Config[K] {
  function _deepMerge (line 108) | function _deepMerge(target: Partial<Config>, source: Config) {

FILE: browser/declarations.ts
  type loadSuitesType (line 11) | type loadSuitesType = (typeof suites.loadSuites);
  type Window (line 14) | interface Window {
  type WebComponentsStatic (line 34) | interface WebComponentsStatic {
  type PlatformStatic (line 38) | interface PlatformStatic {
  type PolymerElement (line 41) | interface PolymerElement {
  type PolymerElementConstructor (line 44) | interface PolymerElementConstructor {
  type PolymerStatic (line 47) | interface PolymerStatic {
  type Element (line 53) | interface Element {
  type Mocha (line 57) | interface Mocha {

FILE: browser/environment.ts
  function loadSync (line 18) | function loadSync() {
  function ensureDependenciesPresent (line 59) | function ensureDependenciesPresent() {
  function _ensureMocha (line 64) | function _ensureMocha() {
  function _checkChai (line 80) | function _checkChai() {

FILE: browser/environment/errors.ts
  function listenForErrors (line 20) | function listenForErrors() {

FILE: browser/environment/helpers.ts
  function safeStep (line 28) | function safeStep(callback: (error?: any) => void, stepFn: () => void) {
  function testImmediate (line 50) | function testImmediate(name: string, testFn: Function) {
  function testImmediateAsync (line 72) | function testImmediateAsync(name: string, testFn: Function) {
  function flush (line 103) | function flush(callback: () => void) {
  function animationFrameFlush (line 150) | function animationFrameFlush(callback: () => void) {
  function asyncPlatformFlush (line 162) | function asyncPlatformFlush(callback: () => void) {
  type MutationEl (line 168) | interface MutationEl {
  function waitFor (line 175) | function waitFor(
  type Window (line 202) | interface Window {

FILE: browser/index.ts
  constant WCT (line 34) | const WCT = window.WCT = {

FILE: browser/mocha.ts
  constant MOCHA_EXPORTS (line 22) | const MOCHA_EXPORTS = {
  function stubInterfaces (line 55) | function stubInterfaces() {
  function _setupMocha (line 78) | function _setupMocha(ui: 'tdd'|'bdd', key: string, alternate: 'string') {

FILE: browser/mocha/extend.ts
  function extendInterfaces (line 9) | function extendInterfaces(
  function applyExtensions (line 56) | function applyExtensions() {

FILE: browser/mocha/fixture.ts
  type TestFixture (line 3) | interface TestFixture extends HTMLElement {

FILE: browser/more-declarations.ts
  type UtilsStatic (line 2) | interface UtilsStatic {
  type IRunner (line 6) | interface IRunner extends NodeJS.EventEmitter {
  type IRunnable (line 11) | interface IRunnable {
  type ISuite (line 18) | interface ISuite {
  type Server (line 26) | interface Server {

FILE: browser/reporters.ts
  function determineReporters (line 26) | function determineReporters(
  type MochaStatic (line 49) | type MochaStatic = typeof Mocha;
  function injectMocha (line 53) | function injectMocha(Mocha: MochaStatic) {
  function _injectPrototype (line 61) | function _injectPrototype(klass: any, prototype: any) {

FILE: browser/reporters/console.ts
  constant FONT (line 17) | const FONT =
  constant STYLES (line 19) | const STYLES = {
  constant CAN_STYLE_LOG (line 32) | const CAN_STYLE_LOG = userAgent.match('firefox') || userAgent.match('web...
  constant CAN_STYLE_GROUP (line 33) | const CAN_STYLE_GROUP = userAgent.match('webkit');
  function log (line 37) | function log(text: string, style?: keyof typeof STYLES) {
  function logGroup (line 50) | function logGroup(text: string, style?: keyof typeof STYLES) {
  function logGroupEnd (line 61) | function logGroupEnd() {
  function logException (line 69) | function logException(error: Error) {
  class Console (line 76) | class Console {
    method constructor (line 80) | constructor(runner: Mocha.IRunner) {
    method logSummary (line 117) | logSummary() {
  type Console (line 137) | interface Console extends Mocha.reporters.Base {}
    method constructor (line 80) | constructor(runner: Mocha.IRunner) {
    method logSummary (line 117) | logSummary() {

FILE: browser/reporters/html.ts
  function HTML (line 17) | function HTML(runner: Mocha.IRunner) {

FILE: browser/reporters/multi.ts
  constant STACKY_CONFIG (line 13) | const STACKY_CONFIG = {
  method filter (line 19) | filter(line: {location: string}) {
  constant MOCHA_EVENTS (line 25) | const MOCHA_EVENTS = [
  constant ESTIMATED_TESTS_PER_SUITE (line 31) | const ESTIMATED_TESTS_PER_SUITE = 3;
  type Reporter (line 33) | interface Reporter {}
  type ReporterFactory (line 35) | interface ReporterFactory { new(parent: MultiReporter): Reporter; }
  type ExtendedTest (line 37) | interface ExtendedTest extends Mocha.ITest {
  class MultiReporter (line 44) | class MultiReporter implements Reporter {
    method constructor (line 61) | constructor(
    method childReporter (line 86) | childReporter(location: Location|string): ReporterFactory {
    method done (line 102) | done() {
    method emitOutOfBandTest (line 121) | emitOutOfBandTest(
    method suiteTitle (line 152) | suiteTitle(location: Location|string) {
    method bindChildRunner (line 161) | private bindChildRunner(runner: Mocha.IRunner) {
    method proxyEvent (line 174) | private proxyEvent(
    method cleanEvent (line 215) | private cleanEvent(
    method showRootSuite (line 237) | private showRootSuite(node: Mocha.IRunnable) {
    method onRunnerStart (line 250) | private onRunnerStart(runner: Mocha.IRunner) {
    method onRunnerEnd (line 257) | private onRunnerEnd(runner: Mocha.IRunner) {
    method flushPendingEvents (line 268) | private flushPendingEvents() {
  type MultiReporter (line 277) | interface MultiReporter extends Mocha.IRunner,
    method constructor (line 61) | constructor(
    method childReporter (line 86) | childReporter(location: Location|string): ReporterFactory {
    method done (line 102) | done() {
    method emitOutOfBandTest (line 121) | emitOutOfBandTest(
    method suiteTitle (line 152) | suiteTitle(location: Location|string) {
    method bindChildRunner (line 161) | private bindChildRunner(runner: Mocha.IRunner) {
    method proxyEvent (line 174) | private proxyEvent(
    method cleanEvent (line 215) | private cleanEvent(
    method showRootSuite (line 237) | private showRootSuite(node: Mocha.IRunnable) {
    method onRunnerStart (line 250) | private onRunnerStart(runner: Mocha.IRunner) {
    method onRunnerEnd (line 257) | private onRunnerEnd(runner: Mocha.IRunner) {
    method flushPendingEvents (line 268) | private flushPendingEvents() {

FILE: browser/reporters/title.ts
  constant ARC_OFFSET (line 13) | const ARC_OFFSET = 0;
  constant ARC_WIDTH (line 14) | const ARC_WIDTH = 6;
  class Title (line 22) | class Title {
    method constructor (line 24) | constructor(runner: Mocha.IRunner) {
    method report (line 31) | report() {
    method updateTitle (line 37) | updateTitle() {
    method updateFavicon (line 46) | updateFavicon() {
    method setFavicon (line 63) | setFavicon(url: string) {
  function drawFaviconArc (line 81) | function drawFaviconArc(
  type Title (line 94) | interface Title extends Mocha.reporters.Base {}
    method constructor (line 24) | constructor(runner: Mocha.IRunner) {
    method report (line 31) | report() {
    method updateTitle (line 37) | updateTitle() {
    method updateFavicon (line 46) | updateFavicon() {
    method setFavicon (line 63) | setFavicon(url: string) {

FILE: browser/suites.ts
  constant GREP (line 20) | let GREP = util.getParam('grep');
  function loadSuites (line 31) | function loadSuites(files: string[]) {
  function activeChildSuites (line 47) | function activeChildSuites(): string[] {
  function loadJsSuites (line 64) | function loadJsSuites(
  function runSuites (line 76) | function runSuites(
  function _runMocha (line 113) | function _runMocha(reporter: MultiReporter, done: () => void, waited: bo...

FILE: browser/util.ts
  function whenFrameworksReady (line 18) | function whenFrameworksReady(callback: () => void) {
  function pluralizedStat (line 41) | function pluralizedStat(count: number, kind: string): string {
  function loadScript (line 53) | function loadScript(path: string, done: (error?: any) => void) {
  function loadStyle (line 67) | function loadStyle(path: string, done?: () => void) {
  function debug (line 82) | function debug(...var_args: any[]) {
  function parseUrl (line 96) | function parseUrl(url: string) {
  function expandUrl (line 111) | function expandUrl(url: string, base: string) {
  type Params (line 122) | interface Params { [param: string]: string[]; }
  function getParams (line 128) | function getParams(query?: string): Params {
  function mergeParams (line 165) | function mergeParams(target: Params, source: Params) {
  function getParam (line 178) | function getParam(param: string): string|null {
  function paramsToQuery (line 187) | function paramsToQuery(params: Params): string {
  function getPathName (line 197) | function getPathName(location: Location|string): string {
  function basePath (line 201) | function basePath(location: Location|string) {
  function relativeLocation (line 205) | function relativeLocation(location: Location|string, basePath: string) {
  function cleanLocation (line 213) | function cleanLocation(location: Location|string) {
  type Runner (line 221) | type Runner = (f: Function) => void;
  function parallel (line 237) | function parallel(
  function scriptPrefix (line 290) | function scriptPrefix(filename: string): string|null {

FILE: custom_typings/bower-config.d.ts
  type Config (line 2) | interface Config {

FILE: custom_typings/findup-sync.d.ts
  type IOptions (line 4) | interface IOptions extends minimatch.IOptions {

FILE: custom_typings/promisify-node.d.ts
  type NodeCallback (line 2) | interface NodeCallback<T> {

FILE: custom_typings/send.d.ts
  type SendStream (line 8) | interface SendStream extends events.EventEmitter {
  type Options (line 12) | interface Options {

FILE: custom_typings/server-destroy.d.ts
  type DestroyableServer (line 13) | interface DestroyableServer extends http.Server {

FILE: custom_typings/stacky.d.ts
  type ParsedStackFrame (line 2) | interface ParsedStackFrame {
  type StyleFunction (line 8) | type StyleFunction = (part: string) => string;
  type Options (line 9) | interface Options {

FILE: custom_typings/wd.d.ts
  type NodeCB (line 4) | interface NodeCB<T> {
  type Browser (line 7) | interface Browser {
  type Capabilities (line 19) | interface Capabilities {

FILE: gulpfile.js
  function removeFile (line 43) | function removeFile(path) {
  function commonDepCheck (line 204) | function commonDepCheck(options) {

FILE: runner/browserrunner.ts
  type Stats (line 21) | interface Stats {
  type BrowserDef (line 28) | interface BrowserDef extends wd.Capabilities {
  class BrowserRunner (line 38) | class BrowserRunner {
    method constructor (line 69) | constructor(
    method _init (line 145) | _init(error: any, sessionId: string) {
    method startTest (line 177) | startTest() {
    method onEvent (line 189) | onEvent(event: string, data: any) {
    method done (line 210) | done(error: any) {
    method extendTimeout (line 253) | extendTimeout() {
    method quit (line 265) | quit() {

FILE: runner/cli.ts
  constant PACKAGE_INFO (line 25) | const PACKAGE_INFO = require('../package.json');
  function run (line 39) | async function run(
  function _run (line 44) | async function _run(args: string[], output: NodeJS.WritableStream) {
  function runSauceTunnel (line 74) | async function runSauceTunnel(
  function _runSauceTunnel (line 79) | async function _runSauceTunnel(args: string[], output: NodeJS.WritableSt...
  function wrapResult (line 115) | async function wrapResult(

FILE: runner/clireporter.ts
  constant STACKY_CONFIG (line 26) | const STACKY_CONFIG = {
  type State (line 37) | type State = 'passing'|'pending'|'failing'|'unknown'|'error';
  type CompletedState (line 38) | type CompletedState = 'passing'|'failing'|'pending'|'unknown';
  type Formatter (line 39) | type Formatter = (value: string) => string;
  constant STATE_ICONS (line 41) | const STATE_ICONS = {
  constant STATE_COLORS (line 48) | const STATE_COLORS: {[state: string]: Formatter} = {
  constant SHORT (line 57) | const SHORT = {
  constant BROWSER_PAD (line 61) | const BROWSER_PAD = 24;
  constant STATUS_PAD (line 62) | const STATUS_PAD = 38;
  type TestEndData (line 65) | interface TestEndData {
  class CliReporter (line 75) | class CliReporter {
    method constructor (line 87) | constructor(
    method updateStatus (line 161) | updateStatus(force?: boolean) {
    method writeTestError (line 199) | writeTestError(browser: BrowserDef, data: TestEndData) {
    method stateIcon (line 224) | stateIcon(state: State) {
    method prettyTest (line 229) | prettyTest(data: TestEndData) {
    method prettyBrowser (line 234) | prettyBrowser(browser: BrowserDef) {
    method log (line 258) | log() {
    method writeWrapped (line 280) | writeWrapped(blocks: string[], separator: string) {
    method write (line 306) | write(line: string) {
    method writeLines (line 311) | writeLines(lines: string[]) {
    method flush (line 324) | flush() {
  function padRight (line 338) | function padRight(str: string, length: number) {

FILE: runner/config.ts
  constant HOME_DIR (line 29) | const HOME_DIR = path.resolve(
  constant JSON_MATCHER (line 31) | const JSON_MATCHER = 'wct.conf.json';
  constant CONFIG_MATCHER (line 32) | const CONFIG_MATCHER = 'wct.conf.*';
  type Browser (line 34) | type Browser = string|{browserName: string, platform: string};
  type Config (line 36) | interface Config {
  type NPMPackage (line 90) | interface NPMPackage {
  function readJsonSync (line 109) | function readJsonSync(filename: string, dir?: string): any|null {
  function getPackageName (line 132) | function getPackageName(options: Config): string|undefined {
  function truncatePathToDir (line 154) | function truncatePathToDir(directory: string, pathName: string): string|
  function resolveWctNpmEntrypointNames (line 176) | function resolveWctNpmEntrypointNames(
  function defaults (line 221) | function defaults(): Config {
  constant ARG_CONFIG (line 319) | const ARG_CONFIG = {
  constant PREPARSE_ARGS (line 424) | const PREPARSE_ARGS =
  type PreparsedArgs (line 427) | interface PreparsedArgs {
  function fromDisk (line 442) | function fromDisk(matcher: string, root?: string): Config {
  function loadProjectFile (line 462) | function loadProjectFile(file: string) {
  function preparseArgs (line 488) | function preparseArgs(args: string[]): PreparsedArgs {
  function parseArgs (line 507) | async function parseArgs(
  function _configurePluginOptions (line 523) | function _configurePluginOptions(
  function _expandOptionPaths (line 545) | function _expandOptionPaths(options: {[key: string]: any}): any {
  function merge (line 564) | function merge(): Config {
  function normalize (line 582) | function normalize(config: Config): Config {
  function expand (line 608) | async function expand(context: Context): Promise<void> {
  function expandDeprecated (line 625) | function expandDeprecated(context: Context) {
  function validate (line 688) | async function validate(options: Config): Promise<void> {

FILE: runner/context.ts
  constant JSON_MATCHER (line 27) | const JSON_MATCHER = 'wct.conf.json';
  constant CONFIG_MATCHER (line 28) | const CONFIG_MATCHER = 'wct.conf.*';
  type Handler (line 30) | type Handler =
  class Context (line 47) | class Context extends events.EventEmitter {
    method constructor (line 54) | constructor(options?: config.Config) {
    method hook (line 86) | hook(name: string, handler: Handler) {
    method hookLate (line 97) | hookLate(name: string, handler: Handler) {
    method emitHook (line 132) | async emitHook(name: string, ...args: any[]): Promise<void> {
    method plugins (line 194) | async plugins(): Promise<Plugin[]> {
    method enabledPlugins (line 205) | enabledPlugins(): string[] {
    method pluginOptions (line 217) | pluginOptions(name: string) {

FILE: runner/gulp.ts
  function init (line 22) | function init(gulp: Gulp, dependencies?: string[]): void {
  function cleanError (line 45) | function cleanError(error: any) {

FILE: runner/httpbin.ts
  function capWords (line 30) | function capWords(s: string) {
  function formatRequest (line 36) | function formatRequest(req: express.Request) {
  function main (line 85) | async function main() {

FILE: runner/paths.ts
  function expand (line 29) | async function expand(
  function unglob (line 37) | async function unglob(baseDir: string, patterns: string[]): Promise<stri...
  function expandDirectories (line 56) | async function expandDirectories(baseDir: string, paths: string[]) {
  function expandDirectory (line 66) | async function expandDirectory(

FILE: runner/plugin.ts
  constant PREFIXES (line 22) | const PREFIXES = [
  type Metadata (line 27) | interface Metadata {}
  class Plugin (line 33) | class Plugin {
    method constructor (line 38) | constructor(packageName: string, metadata: Metadata) {
    method execute (line 50) | async execute(context: Context): Promise<void> {
    method get (line 64) | static async get(name: string): Promise<Plugin> {
    method shortName (line 87) | static shortName(name: string) {
  function _tryLoadPluginPackage (line 110) | function _tryLoadPluginPackage(packageName: string) {

FILE: runner/port-scanner.ts
  function checkPort (line 17) | function checkPort(port: number): Promise<boolean> {
  type PromiseGetter (line 43) | interface PromiseGetter<T> {
  function detectSeries (line 47) | async function detectSeries<T>(
  function findPort (line 57) | async function findPort(ports: number[]): Promise<number> {

FILE: runner/steps.ts
  type ClientMessage (line 24) | interface ClientMessage<T> {
  function setupOverrides (line 32) | async function setupOverrides(context: Context): Promise<void> {
  function loadPlugins (line 38) | async function loadPlugins(context: Context): Promise<Plugin[]> {
  function configure (line 51) | async function configure(context: Context): Promise<void> {
  function prepare (line 77) | async function prepare(context: Context): Promise<void> {
  function runTests (line 81) | async function runTests(context: Context): Promise<void> {
  function cancelTests (line 108) | function cancelTests(context: Context): void {
  function runBrowsers (line 119) | function runBrowsers(context: Context) {

FILE: runner/test.ts
  function test (line 67) | async function test(options: Config|Context): Promise<void> {

FILE: runner/webserver.ts
  constant INDEX_TEMPLATE (line 30) | const INDEX_TEMPLATE = _.template(fs.readFileSync(
  constant DEFAULT_HEADERS (line 33) | const DEFAULT_HEADERS = {
  constant ENVIRONMENT_SCRIPTS (line 40) | const ENVIRONMENT_SCRIPTS: NPMPackage[] = [
  function webserver (line 62) | function webserver(wct: Context): void {
  function exists (line 300) | function exists(path: string): boolean {

FILE: test/integration/browser.ts
  function parseList (line 28) | function parseList(stringList?: string): string[] {
  function loadOptionsFile (line 35) | function loadOptionsFile(dir: string): config.Config {
  type TestErrorExpectation (line 59) | interface TestErrorExpectation {
  type Golden (line 66) | type Golden = VariantsGolden|VariantResultGolden;
  function isVariantsGolden (line 68) | function isVariantsGolden(golden: Golden): golden is VariantsGolden {
  type VariantsGolden (line 72) | interface VariantsGolden {
  type VariantResultGolden (line 77) | interface VariantResultGolden {
  type TestNode (line 85) | type TestNode = {
  class TestResults (line 89) | class TestResults {
    method getVariantResults (line 94) | getVariantResults(variantName: string): VariantResults {
  class VariantResults (line 101) | class VariantResults {
  function runsAllIntegrationSuites (line 111) | function runsAllIntegrationSuites(options: config.Config = {}) {
  function runIntegrationSuiteForDir (line 127) | function runIntegrationSuiteForDir(
  function runsIntegrationSuite (line 164) | function runsIntegrationSuite(
  function assertPassed (line 308) | function assertPassed(context: TestResults) {
  function assertFailed (line 323) | function assertFailed(context: VariantResults, expectedError: string) {
  function assertStats (line 330) | function assertStats(
  function assertTests (line 338) | function assertTests(context: VariantResults, expected: TestNode) {
  function assertTestErrors (line 344) | function assertTestErrors(
  function assertVariantResultsConformToGolden (line 386) | function assertVariantResultsConformToGolden(
  function getBrowserName (line 421) | function getBrowserName(browser: BrowserDef) {
  function repeatBrowsers (line 440) | function repeatBrowsers<T>(

FILE: test/integration/setup_test_dir.ts
  function makeProperTestDir (line 22) | async function makeProperTestDir(dirname: string) {
  function copyDir (line 80) | async function copyDir(from: string, to: string) {
  function exists (line 95) | async function exists(fn: string) {

FILE: test/unit/cli.ts
  constant FIXTURES (line 28) | const FIXTURES = path.resolve(__dirname, '../fixtures/cli');
  constant LOCAL_BROWSERS (line 30) | const LOCAL_BROWSERS = {

FILE: test/unit/grunt.ts
  constant LOCAL_BROWSERS (line 28) | const LOCAL_BROWSERS = {
  function runTask (line 65) | async function runTask(task: string) {

FILE: test/unit/gulp.ts
  constant FIXTURES (line 28) | const FIXTURES = path.resolve(__dirname, '../fixtures/cli');
  function runGulpTask (line 59) | async function runGulpTask(name: string) {

FILE: test/unit/paths.ts
  function expectExpands (line 24) | async function expectExpands(patterns: string[], expected: string[]) {

FILE: wct-browser-legacy/browser.js
  function safeStep (line 41) | function safeStep(callback, stepFn) {
  function testImmediate (line 63) | function testImmediate(name, testFn) {
  function testImmediateAsync (line 84) | function testImmediateAsync(name, testFn) {
  function flush (line 113) | function flush(callback) {
  function animationFrameFlush (line 157) | function animationFrameFlush(callback) {
  function asyncPlatformFlush (line 168) | function asyncPlatformFlush(callback) {
  function waitFor (line 175) | function waitFor(fn, next, intervalOrMutationEl, timeout, timeoutTime) {
  function setup (line 243) | function setup(options) {
  function get (line 265) | function get(key) {
  function _deepMerge (line 269) | function _deepMerge(target, source) {
  function whenFrameworksReady (line 285) | function whenFrameworksReady(callback) {
  function pluralizedStat (line 307) | function pluralizedStat(count, kind) {
  function loadScript (line 319) | function loadScript(path, done) {
  function loadStyle (line 332) | function loadStyle(path, done) {
  function debug (line 346) | function debug() {
  function parseUrl (line 362) | function parseUrl(url) {
  function expandUrl (line 376) | function expandUrl(url, base) {
  function getParams (line 390) | function getParams(query) {
  function mergeParams (line 423) | function mergeParams(target, source) {
  function getParam (line 435) | function getParam(param) {
  function paramsToQuery (line 443) | function paramsToQuery(params) {
  function getPathName (line 452) | function getPathName(location) {
  function basePath (line 455) | function basePath(location) {
  function relativeLocation (line 458) | function relativeLocation(location, basePath) {
  function cleanLocation (line 465) | function cleanLocation(location) {
  function parallel (line 472) | function parallel(runners, maybeLimit, done) {
  function scriptPrefix (line 520) | function scriptPrefix(filename) {
  function ChildRunner (line 554) | function ChildRunner(url, parentScope) {
  function CLISocket (line 694) | function CLISocket(browserId, socket) {
  function getTitles (line 785) | function getTitles(runnable) {
  function getState (line 797) | function getState(runnable) {
  function log (line 832) | function log(text, style) {
  function logGroup (line 845) | function logGroup(text, style) {
  function logGroupEnd (line 857) | function logGroupEnd() {
  function logException (line 865) | function logException(error) {
  function Console (line 875) | function Console(runner) {
  function HTML (line 938) | function HTML(runner) {
  function MultiReporter (line 981) | function MultiReporter(numSuites, reporters, parent) {
  function ChildReporter (line 1007) | function ChildReporter(runner) {
  function Title (line 1190) | function Title(runner) {
  function drawFaviconArc (line 1240) | function drawFaviconArc(context, total, start, length, color) {
  function loadSuites (line 1263) | function loadSuites(files) {
  function activeChildSuites (line 1280) | function activeChildSuites() {
  function loadJsSuites (line 1296) | function loadJsSuites(_reporter, done) {
  function runSuites (line 1304) | function runSuites(reporter, childSuites, done) {
  function _runMocha (line 1334) | function _runMocha(reporter, done, waited) {
  function determineReporters (line 1375) | function determineReporters(socket, parent) {
  function injectMocha (line 1395) | function injectMocha(Mocha) {
  function _injectPrototype (line 1401) | function _injectPrototype(klass, prototype) {
  function loadSync (line 1413) | function loadSync() {
  function ensureDependenciesPresent (line 1448) | function ensureDependenciesPresent() {
  function _ensureMocha (line 1452) | function _ensureMocha() {
  function _checkChai (line 1466) | function _checkChai() {
  function listenForErrors (line 1481) | function listenForErrors() {
  function extendInterfaces (line 1502) | function extendInterfaces(helperName, helperFactory) {
  function applyExtensions (line 1541) | function applyExtensions() {
  function stubInterfaces (line 1730) | function stubInterfaces() {
  function _setupMocha (line 1751) | function _setupMocha(ui, key, alternate) {
Condensed preview — 167 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (446K chars).
[
  {
    "path": ".clang-format",
    "chars": 217,
    "preview": "BasedOnStyle: Google\nAllowShortBlocksOnASingleLine: false\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortFunctionsOn"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE",
    "chars": 418,
    "preview": "<!--\n  Thanks for the PR!\n\n  If this change has a user visible change (including\n  bug fixes, new features, etc) please "
  },
  {
    "path": ".gitignore",
    "chars": 428,
    "preview": "# Update .npmignore whenever you update this file!\n\n.todo\n/bower_components\n/node_modules\npackage-lock.json\nnpm-debug.lo"
  },
  {
    "path": ".npmignore",
    "chars": 139,
    "preview": "# Update .gitignore whenever you update this file!\n\n.todo\nbower_components\nnode_modules\nnpm-debug.log\ntypings/\n\n# Don't "
  },
  {
    "path": ".travis.yml",
    "chars": 634,
    "preview": "sudo: required\ndist: trusty\naddons:\n  firefox: latest\n  apt:\n    sources:\n    - google-chrome\n    packages:\n    - google"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 648,
    "preview": "// Place your settings in this file to overwrite default and user settings.\n{\n  \"clang-format.style\": \"file\",\n  \"editor."
  },
  {
    "path": "CHANGELOG.md",
    "chars": 12990,
    "preview": "# Change Log\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Chang"
  },
  {
    "path": "LICENSE",
    "chars": 1484,
    "preview": "Copyright (c) 2015 The Polymer Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or"
  },
  {
    "path": "README.md",
    "chars": 530,
    "preview": "## 🚨 Moved to [`Polymer/tools/packages/web-component-tester`][1] 🚨\n\nThe [`Polymer/web-component-tester`][2] repo has bee"
  },
  {
    "path": "bin/wct",
    "chars": 971,
    "preview": "#!/usr/bin/env node\n/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code"
  },
  {
    "path": "bin/wct-st",
    "chars": 985,
    "preview": "#!/usr/bin/env node\n/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code"
  },
  {
    "path": "bower.json",
    "chars": 917,
    "preview": "{\n  \"name\": \"web-component-tester\",\n  \"description\": \"web-component-tester makes testing your web components a breeze!\","
  },
  {
    "path": "browser/childrunner.ts",
    "chars": 5870,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "browser/clisocket.ts",
    "chars": 4628,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "browser/config.ts",
    "chars": 3936,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "browser/declarations.ts",
    "chars": 1628,
    "preview": "import * as ChaiStatic from 'chai';\nimport * as SinonStatic from 'sinon';\nimport * as SocketIOStatic from 'socket.io';\ni"
  },
  {
    "path": "browser/environment/compatability.ts",
    "chars": 1680,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "browser/environment/errors.ts",
    "chars": 1334,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "browser/environment/helpers.ts",
    "chars": 6195,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "browser/environment.ts",
    "chars": 3176,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "browser/index.ts",
    "chars": 3674,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "browser/mocha/extend.ts",
    "chars": 2598,
    "preview": "\nconst interfaceExtensions: Array<() => void> = [];\n\n/**\n * Registers an extension that extends the global `Mocha` imple"
  },
  {
    "path": "browser/mocha/fixture.ts",
    "chars": 788,
    "preview": "import {extendInterfaces} from './extend.js';\n\ninterface TestFixture extends HTMLElement {\n  create(model: object): HTML"
  },
  {
    "path": "browser/mocha/replace.ts",
    "chars": 4042,
    "preview": "import {extendInterfaces} from './extend.js';\n\n// replacement map stores what should be\nlet replacements = {};\nlet repla"
  },
  {
    "path": "browser/mocha/stub.ts",
    "chars": 1346,
    "preview": "import {extendInterfaces} from './extend.js';\n\n/**\n * stub\n *\n * The stub addon allows the tester to partially replace t"
  },
  {
    "path": "browser/mocha.ts",
    "chars": 3173,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "browser/more-declarations.ts",
    "chars": 556,
    "preview": "declare namespace Mocha {\n  interface UtilsStatic {\n    highlightTags(somethingSomething: string): void;\n  }\n  let utils"
  },
  {
    "path": "browser/reporters/console.ts",
    "chars": 3948,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "browser/reporters/html.ts",
    "chars": 1931,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "browser/reporters/multi.ts",
    "chars": 8877,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "browser/reporters/title.ts",
    "chars": 3114,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "browser/reporters.ts",
    "chars": 2478,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "browser/suites.ts",
    "chars": 4803,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "browser/tsconfig.json",
    "chars": 595,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"module\": \"es6\",\n    \"moduleResolution\": \"node\",\n    \"isolatedModules\""
  },
  {
    "path": "browser/util.ts",
    "chars": 8106,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "browser-js-header.txt",
    "chars": 684,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "browser.js",
    "chars": 65022,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "custom_typings/bower-config.d.ts",
    "chars": 141,
    "preview": "declare module 'bower-config' {\n  interface Config {\n    directory: string;\n  }\n  function read(cwd: string, overrides?:"
  },
  {
    "path": "custom_typings/findup-sync.d.ts",
    "chars": 251,
    "preview": "declare module 'findup-sync' {\n  import * as minimatch from 'minimatch';\n\n  interface IOptions extends minimatch.IOption"
  },
  {
    "path": "custom_typings/promisify-node.d.ts",
    "chars": 601,
    "preview": "declare module 'promisify-node' {\n  interface NodeCallback<T> {\n    (err: any, value: T): void;\n  }\n  function promisify"
  },
  {
    "path": "custom_typings/send.d.ts",
    "chars": 637,
    "preview": "declare module 'send' {\n  import * as http from 'http';\n  import * as events from 'events';\n\n\n  function send(req: http."
  },
  {
    "path": "custom_typings/server-destroy.d.ts",
    "chars": 553,
    "preview": "declare module 'server-destroy' {\n  import * as http from 'http';\n\n  /**\n   * Monkey-patches the destroy() method onto t"
  },
  {
    "path": "custom_typings/stacky.d.ts",
    "chars": 855,
    "preview": "declare module 'stacky' {\n  interface ParsedStackFrame {\n    method: string;\n    location: string;\n    line: number;\n   "
  },
  {
    "path": "custom_typings/wd.d.ts",
    "chars": 1738,
    "preview": "\n\ndeclare module 'wd' {\n  interface NodeCB<T> {\n    (err: any, value: T): void;\n  }\n  export interface Browser {\n    con"
  },
  {
    "path": "data/a11ySuite-npm-header.txt",
    "chars": 263,
    "preview": "import * as polymerDom from '../@polymer/polymer/lib/legacy/polymer.dom.js';\nconst Polymer = { dom: polymerDom };\nexport"
  },
  {
    "path": "data/a11ySuite.js",
    "chars": 4460,
    "preview": "/**\n * @license\n * Copyright (c) 2015 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "data/index.html",
    "chars": 559,
    "preview": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script>\n    WCT = <%= JSON.stringify(clientOptions) %>;\n  </s"
  },
  {
    "path": "gulpfile.js",
    "chars": 6724,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "package.json",
    "chars": 3501,
    "preview": "{\n  \"name\": \"web-component-tester\",\n  \"version\": \"6.6.0-pre.5\",\n  \"--private-wct--\": {\n    \"client-side-version-range\": "
  },
  {
    "path": "runner/browserrunner.ts",
    "chars": 7776,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "runner/cli.ts",
    "chars": 4301,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "runner/clireporter.ts",
    "chars": 9402,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "runner/config.ts",
    "chars": 22323,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "runner/context.ts",
    "chars": 7471,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "runner/gulp.ts",
    "chars": 1435,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "runner/httpbin.ts",
    "chars": 3095,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "runner/paths.ts",
    "chars": 2768,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "runner/plugin.ts",
    "chars": 3637,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "runner/port-scanner.ts",
    "chars": 1793,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "runner/steps.ts",
    "chars": 5861,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "runner/test.ts",
    "chars": 2651,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "runner/webserver.ts",
    "chars": 11411,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "runner.js",
    "chars": 737,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "tasks/test.js",
    "chars": 963,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "test/fixtures/cli/conf/branch/leaf/thing.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/fixtures/cli/conf/json/wct.conf.js",
    "chars": 154,
    "preview": "var path = require('path');\n\nmodule.exports = {\n  root: path.resolve(__dirname, '..'),\n  plugins: {\n    sauce: {\n      u"
  },
  {
    "path": "test/fixtures/cli/conf/json/wct.conf.json",
    "chars": 89,
    "preview": "{\n  \"root\": \"..\",\n  \"plugins\": {\n    \"sauce\": {\n      \"username\": \"jsonconf\"\n    }\n  }\n}\n"
  },
  {
    "path": "test/fixtures/cli/conf/rooted/wct.conf.js",
    "chars": 125,
    "preview": "var path = require('path');\n\nmodule.exports = {\n  root: path.resolve(__dirname, '../../..'),\n  suites: ['cli/conf/test']"
  },
  {
    "path": "test/fixtures/cli/conf/test/foo.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/fixtures/cli/conf/wct.conf.js",
    "chars": 86,
    "preview": "module.exports = {\n  plugins: {\n    sauce: {\n      username: 'abc123',\n    },\n  },\n};\n"
  },
  {
    "path": "test/fixtures/cli/standard/test/a.html",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/fixtures/cli/standard/test/b.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/fixtures/cli/standard/x-foo.html",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/fixtures/early-failure/bower_components/web-component-tester/package.json",
    "chars": 25,
    "preview": "{\n  \"version\": \"0.0.1\"\n}\n"
  },
  {
    "path": "test/fixtures/early-failure/test/index.html",
    "chars": 269,
    "preview": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></sc"
  },
  {
    "path": "test/fixtures/fake-packages/duplicated-dep/package.json",
    "chars": 52,
    "preview": "{\n  \"name\": \"duplicated-dep\",\n  \"version\": \"1.0.0\"\n}"
  },
  {
    "path": "test/fixtures/fake-packages/singleton-dep/package.json",
    "chars": 51,
    "preview": "{\n  \"name\": \"singleton-dep\",\n  \"version\": \"1.0.0\"\n}"
  },
  {
    "path": "test/fixtures/integration/compilation/golden.json",
    "chars": 170,
    "preview": "{\n  \"passing\": 1,\n  \"pending\": 0,\n  \"failing\": 0,\n  \"status\": \"complete\",\n  \"tests\": {\n    \"test/\": {\n      \"ES6 works\":"
  },
  {
    "path": "test/fixtures/integration/compilation/test/index.html",
    "chars": 492,
    "preview": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></sc"
  },
  {
    "path": "test/fixtures/integration/components_dir/bower_components/foo-element/foo-element.js",
    "chars": 32,
    "preview": "window.fooElementLoaded = 'yes';"
  },
  {
    "path": "test/fixtures/integration/components_dir/golden.json",
    "chars": 163,
    "preview": "{\n  \"passing\": 1,\n  \"pending\": 0,\n  \"failing\": 0,\n  \"status\": \"complete\",\n  \"tests\": {\n    \"test/\": {\n      \"inline pass"
  },
  {
    "path": "test/fixtures/integration/components_dir/test/index.html",
    "chars": 346,
    "preview": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></sc"
  },
  {
    "path": "test/fixtures/integration/components_dir/wct.conf.json",
    "chars": 124,
    "preview": "{\n  \"plugins\": {\n    \"local\": {\n      \"browsers\": [\n        \"chrome\"\n      ],\n      \"skipSeleniumInstall\": true\n    }\n  "
  },
  {
    "path": "test/fixtures/integration/custom-components_dir/.bowerrc",
    "chars": 35,
    "preview": "{\n  \"directory\": \"my_components/\"\n}"
  },
  {
    "path": "test/fixtures/integration/custom-components_dir/golden.json",
    "chars": 163,
    "preview": "{\n  \"passing\": 1,\n  \"pending\": 0,\n  \"failing\": 0,\n  \"status\": \"complete\",\n  \"tests\": {\n    \"test/\": {\n      \"inline pass"
  },
  {
    "path": "test/fixtures/integration/custom-components_dir/my_components/bar-element/bar-element.js",
    "chars": 32,
    "preview": "window.barElementLoaded = 'yes';"
  },
  {
    "path": "test/fixtures/integration/custom-components_dir/test/index.html",
    "chars": 367,
    "preview": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <script src=\"/components/web-component-tester/browser.js\""
  },
  {
    "path": "test/fixtures/integration/custom-multiple-component_dirs/.bowerrc",
    "chars": 34,
    "preview": "{\n  \"directory\": \"my_components\"\n}"
  },
  {
    "path": "test/fixtures/integration/custom-multiple-component_dirs/golden.json",
    "chars": 1127,
    "preview": "{\n  \"variants\": {\n    \"\": {\n      \"passing\": 1,\n      \"pending\": 0,\n      \"failing\": 0,\n      \"status\": \"complete\",\n    "
  },
  {
    "path": "test/fixtures/integration/custom-multiple-component_dirs/my_components/package/index.js",
    "chars": 32,
    "preview": "window.nameOfThing = 'mainline';"
  },
  {
    "path": "test/fixtures/integration/custom-multiple-component_dirs/my_components-bar/package/index.js",
    "chars": 27,
    "preview": "window.nameOfThing = 'bar';"
  },
  {
    "path": "test/fixtures/integration/custom-multiple-component_dirs/my_components-foo/package/index.js",
    "chars": 27,
    "preview": "window.nameOfThing = 'foo';"
  },
  {
    "path": "test/fixtures/integration/custom-multiple-component_dirs/test/index.html",
    "chars": 380,
    "preview": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <script src=\"/components/web-component-tester/browser.js\""
  },
  {
    "path": "test/fixtures/integration/define-webserver-hook/golden.json",
    "chars": 265,
    "preview": "{\n  \"passing\": 2,\n  \"pending\": 0,\n  \"failing\": 0,\n  \"status\": \"complete\",\n  \"tests\": {\n    \"test/tests.html\": {\n      \"s"
  },
  {
    "path": "test/fixtures/integration/define-webserver-hook/test/index.html",
    "chars": 218,
    "preview": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></sc"
  },
  {
    "path": "test/fixtures/integration/define-webserver-hook/test/tests.html",
    "chars": 302,
    "preview": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></sc"
  },
  {
    "path": "test/fixtures/integration/failing/golden.json",
    "chars": 913,
    "preview": "{\n  \"passing\": 3,\n  \"pending\": 0,\n  \"failing\": 3,\n  \"status\": \"complete\",\n  \"tests\": {\n    \"test/\": {\n      \"failing tes"
  },
  {
    "path": "test/fixtures/integration/failing/test/index.html",
    "chars": 362,
    "preview": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></sc"
  },
  {
    "path": "test/fixtures/integration/failing/test/tests.html",
    "chars": 299,
    "preview": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></sc"
  },
  {
    "path": "test/fixtures/integration/failing/test/tests.js",
    "chars": 99,
    "preview": "test('passing test', function() {});\ntest('failing test', function() {\n  assert.isTrue(false);\n});\n"
  },
  {
    "path": "test/fixtures/integration/missing/golden.json",
    "chars": 71,
    "preview": "{\n  \"passing\": 0,\n  \"pending\": 0,\n  \"failing\": 0,\n  \"status\": \"error\"\n}"
  },
  {
    "path": "test/fixtures/integration/missing/test/missing.html",
    "chars": 229,
    "preview": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></sc"
  },
  {
    "path": "test/fixtures/integration/mixed-suites/golden.json",
    "chars": 927,
    "preview": "{\n  \"passing\": 10,\n  \"pending\": 0,\n  \"failing\": 0,\n  \"status\": \"complete\",\n  \"tests\": {\n    \"test/\": {\n      \"inline sui"
  },
  {
    "path": "test/fixtures/integration/mixed-suites/test/index.html",
    "chars": 390,
    "preview": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></sc"
  },
  {
    "path": "test/fixtures/integration/mixed-suites/test/one.html",
    "chars": 307,
    "preview": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></sc"
  },
  {
    "path": "test/fixtures/integration/mixed-suites/test/one.js",
    "chars": 105,
    "preview": "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",
    "chars": 307,
    "preview": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></sc"
  },
  {
    "path": "test/fixtures/integration/mixed-suites/test/two.js",
    "chars": 105,
    "preview": "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",
    "chars": 32,
    "preview": "window.nameOfThing = 'mainline';"
  },
  {
    "path": "test/fixtures/integration/multiple-component_dirs/bower_components-bar/package/index.js",
    "chars": 27,
    "preview": "window.nameOfThing = 'bar';"
  },
  {
    "path": "test/fixtures/integration/multiple-component_dirs/bower_components-foo/package/index.js",
    "chars": 27,
    "preview": "window.nameOfThing = 'foo';"
  },
  {
    "path": "test/fixtures/integration/multiple-component_dirs/golden.json",
    "chars": 1201,
    "preview": "{\n  \"variants\": {\n    \"\": {\n      \"passing\": 1,\n      \"pending\": 0,\n      \"failing\": 0,\n      \"status\": \"complete\",\n    "
  },
  {
    "path": "test/fixtures/integration/multiple-component_dirs/test/index.html",
    "chars": 359,
    "preview": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></sc"
  },
  {
    "path": "test/fixtures/integration/multiple-replace/dom-if-element.html",
    "chars": 607,
    "preview": "<link rel=\"import\" href=\"../polymer/polymer.html\">\n<link rel=\"import\" href=\"./normal-element.html\">\n<link rel=\"import\" h"
  },
  {
    "path": "test/fixtures/integration/multiple-replace/dom-repeat-fixture.html",
    "chars": 794,
    "preview": "<link rel=\"import\" href=\"../polymer/polymer.html\">\n<link rel=\"import\" href=\"./exception-element.html\">\n\n<dom-module id=\""
  },
  {
    "path": "test/fixtures/integration/multiple-replace/exception-element.html",
    "chars": 295,
    "preview": "<link rel=\"import\" href=\"../polymer/polymer.html\">\n<dom-module id=\"exception-element\">\n  <template>\n  </template>\n  <scr"
  },
  {
    "path": "test/fixtures/integration/multiple-replace/exception-fixture.html",
    "chars": 415,
    "preview": "<link rel=\"import\" href=\"../polymer/polymer.html\">\n<link rel=\"import\" href=\"./normal-element.html\">\n<link rel=\"import\" h"
  },
  {
    "path": "test/fixtures/integration/multiple-replace/golden.json",
    "chars": 680,
    "preview": "{\n  \"passing\": 10,\n  \"pending\": 0,\n  \"failing\": 0,\n  \"status\": \"complete\",\n  \"tests\": {\n    \"test/tests.html\": {\n      \""
  },
  {
    "path": "test/fixtures/integration/multiple-replace/normal-element.html",
    "chars": 344,
    "preview": "<link rel=\"import\" href=\"../polymer/polymer.html\">\n\n<dom-module id=\"normal-element\">\n  <template>\n    <div>\n      Hello "
  },
  {
    "path": "test/fixtures/integration/multiple-replace/projection-element-2.html",
    "chars": 242,
    "preview": "<link rel=\"import\" href=\"../polymer/polymer.html\">\n\n<dom-module id=\"projection-element-2\">\n  <template>\n    <div id=\"con"
  },
  {
    "path": "test/fixtures/integration/multiple-replace/projection-element-3.html",
    "chars": 244,
    "preview": "<link rel=\"import\" href=\"../polymer/polymer.html\">\n\n<dom-module id=\"projection-element-3\">\n  <template>\n    <div id=\"con"
  },
  {
    "path": "test/fixtures/integration/multiple-replace/projection-element-4.html",
    "chars": 253,
    "preview": "<link rel=\"import\" href=\"../polymer/polymer.html\">\n\n<dom-module id=\"projection-element-4\">\n  <template>\n    <content sel"
  },
  {
    "path": "test/fixtures/integration/multiple-replace/projection-element.html",
    "chars": 557,
    "preview": "<link rel=\"import\" href=\"../polymer/polymer.html\">\n<link rel=\"import\" href=\"./projection-element-2.html\">\n<link rel=\"imp"
  },
  {
    "path": "test/fixtures/integration/multiple-replace/test/index.html",
    "chars": 353,
    "preview": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"../../webcomponentsjs/webcomponents-lite.js\"></sc"
  },
  {
    "path": "test/fixtures/integration/multiple-replace/test/tests.html",
    "chars": 6468,
    "preview": "<html>\n\n<head>\n  <link rel=\"import\" href=\"../../test-fixture/test-fixture.html\">\n  <script src=\"../../webcomponentsjs/we"
  },
  {
    "path": "test/fixtures/integration/nested/golden.json",
    "chars": 309,
    "preview": "{\n  \"passing\": 4,\n  \"pending\": 0,\n  \"failing\": 0,\n  \"status\": \"complete\",\n  \"tests\": {\n    \"test/\": {\"js test\": {\"state\""
  },
  {
    "path": "test/fixtures/integration/nested/test/index.html",
    "chars": 263,
    "preview": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></sc"
  },
  {
    "path": "test/fixtures/integration/nested/test/leaf.html",
    "chars": 216,
    "preview": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></sc"
  },
  {
    "path": "test/fixtures/integration/nested/test/leaf.js",
    "chars": 32,
    "preview": "test('js test', function() {});\n"
  },
  {
    "path": "test/fixtures/integration/nested/test/one/index.html",
    "chars": 217,
    "preview": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></sc"
  },
  {
    "path": "test/fixtures/integration/nested/test/one/tests.html",
    "chars": 216,
    "preview": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></sc"
  },
  {
    "path": "test/fixtures/integration/nested/test/two/index.html",
    "chars": 223,
    "preview": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></sc"
  },
  {
    "path": "test/fixtures/integration/no-tests/golden.json",
    "chars": 74,
    "preview": "{\n  \"passing\": 0,\n  \"pending\": 0,\n  \"failing\": 0,\n  \"status\": \"complete\"\n}"
  },
  {
    "path": "test/fixtures/integration/no-tests/test/index.html",
    "chars": 205,
    "preview": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></sc"
  },
  {
    "path": "test/fixtures/integration/query-string/golden.json",
    "chars": 390,
    "preview": "{\n  \"passing\": 3,\n  \"pending\": 0,\n  \"failing\": 0,\n  \"status\": \"complete\",\n  \"tests\": {\n    \"test/tests.html\": {\n      \"p"
  },
  {
    "path": "test/fixtures/integration/query-string/test/index.html",
    "chars": 448,
    "preview": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></sc"
  },
  {
    "path": "test/fixtures/integration/query-string/test/tests.html",
    "chars": 295,
    "preview": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <script src=\"/components/web-component-tester/browser.js\"></sc"
  },
  {
    "path": "test/fixtures/integration/query-string/test/tests.js",
    "chars": 620,
    "preview": "// TODO(usergenic): Figure out a reasonable solution to get URL() and\n// document.currentScript to work in IE11 and then"
  },
  {
    "path": "test/fixtures/paths/bar/a.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/fixtures/paths/bar/index.html",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/fixtures/paths/bar/index.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/fixtures/paths/baz/a/fizz.html",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/fixtures/paths/baz/a.html",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/fixtures/paths/baz/b/deep/index.html",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/fixtures/paths/baz/b/deep/stuff.html",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/fixtures/paths/baz/b/deep/stuff.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/fixtures/paths/baz/b/index.html",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/fixtures/paths/baz/b/one.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/fixtures/paths/baz/b.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/fixtures/paths/foo/one.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/fixtures/paths/foo/three.css",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/fixtures/paths/foo/two.html",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/fixtures/paths/foo.html",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/fixtures/paths/foo.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/integration/browser.ts",
    "chars": 18541,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "test/integration/setup_test_dir.ts",
    "chars": 3516,
    "preview": "import * as fs from 'fs';\nimport * as path from 'path';\nimport * as rimraf from 'rimraf';\nimport * as bowerConfig from '"
  },
  {
    "path": "test/unit/cli.ts",
    "chars": 9082,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "test/unit/config.ts",
    "chars": 4400,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "test/unit/context.ts",
    "chars": 4930,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "test/unit/grunt.ts",
    "chars": 3665,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "test/unit/gulp.ts",
    "chars": 2905,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "test/unit/paths.ts",
    "chars": 3505,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "tsconfig.json",
    "chars": 749,
    "preview": "{\n    \"compilerOptions\": {\n        \"target\": \"es6\",\n        \"module\": \"commonjs\",\n        \"moduleResolution\": \"node\",\n  "
  },
  {
    "path": "tslint.json",
    "chars": 1407,
    "preview": "{\n    \"rules\": {\n        \"arrow-parens\": true,\n        \"class-name\": true,\n        \"indent\": [\n            true,\n       "
  },
  {
    "path": "wct-browser-legacy/a11ySuite.js",
    "chars": 4724,
    "preview": "import * as polymerDom from '../@polymer/polymer/lib/legacy/polymer.dom.js';\nconst Polymer = { dom: polymerDom };\nexport"
  },
  {
    "path": "wct-browser-legacy/browser.js",
    "chars": 65021,
    "preview": "/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used un"
  },
  {
    "path": "wct-browser-legacy/package.json",
    "chars": 1113,
    "preview": "{\n  \"name\": \"wct-browser-legacy\",\n  \"version\": \"0.0.1-pre.12\",\n  \"description\": \"Client-side dependencies for web-compon"
  }
]

About this extraction

This page contains the full source code of the Polymer/web-component-tester GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 167 files (409.9 KB), approximately 106.9k tokens, and a symbol index with 403 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!