Add docs for karma rules

Closes #417

PiperOrigin-RevId: 233688614
diff --git a/DEVELOPING.md b/DEVELOPING.md
index f45639b..81bc6e1 100644
--- a/DEVELOPING.md
+++ b/DEVELOPING.md
@@ -56,14 +56,14 @@
 (This may not sound like semver - but since our major version is a zero, the
 rule is that minors are breaking changes and patches are new features).
 
-1. Re-generate the API docs: `yarn skydoc`
+1. Re-generate the API docs: `yarn skydoc && (cd internal/karma; yarn skydoc)`
 1. May be necessary if Go code has changed though probably it was already necessary to run this to keep CI green: `bazel run :gazelle`
 1. If we depend on a newer rules_nodejs, update the `check_rules_nodejs_version` in `ts_repositories.bzl`
 1. `git commit -a -m 'Update docs for release'`
 1. `npm config set tag-version-prefix ''`
 1. `npm version minor -m 'rel: %s'` (replace `minor` with `patch` if no breaking changes)
-1. Build npm packages and publish them: `TMP=$(mktemp -d -t bazel-release.XXXXXXX); bazel --output_base=$TMP run //:npm_package.publish && cd internal/karma && bazel --output_base=$TMP run //:npm_package.publish`
-1. `git push && git push --tags`
+1. Build npm packages and publish them: `TMP=$(mktemp -d -t bazel-release.XXXXXXX); bazel --output_base=$TMP run //:npm_package.publish && ( cd internal/karma && bazel --output_base=$TMP run //:npm_package.publish )`
+1. `git push upstream && git push upstream --tags` (assumes you named the bazelbuild fork as "upstream")
 1. (Temporary): submit a google3 CL to update the versions in package.bzl and package.json
 
 [releases]: https://github.com/bazelbuild/rules_typescript/releases
diff --git a/README.md b/README.md
index 736956d..b404de8 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,9 @@
 
 The TypeScript rules integrate the TypeScript compiler with Bazel.
 
+This repo used to contain Karma rules `ts_web_test` and `karma_web_test`.
+These are now documented in the README at http://npmjs.com/package/@bazel/karma
+
 ## API Docs
 
 Generated documentation for using each rule is at:
@@ -18,15 +21,12 @@
 First, install a current Bazel distribution.
 
 Add the `@bazel/typescript` npm package to your `package.json` `devDependencies`.
-Optionally add the `@bazel/karma` npm package if you would like to use the
-`ts_web_test`, `ts_web_test_suite`, `karma_web_test` or `karma_web_test_suite` rules.
 
 ```
 {
   ...
   "devDependencies": {
     "@bazel/typescript": "0.25.0",
-    "@bazel/karma": "0.25.0",
     ...
   },
   ...
@@ -59,7 +59,7 @@
 node_repositories()
 
 # Setup Bazel managed npm dependencies with the `yarn_install` rule.
-# The name of this rule should be set to `npm` so that `ts_library` and `ts_web_test_suite`
+# The name of this rule should be set to `npm` so that `ts_library`
 # can find your npm dependencies by default in the `@npm` workspace. You may
 # also use the `npm_install` rule with a `package-lock.json` file if you prefer.
 # See https://github.com/bazelbuild/rules_nodejs#dependencies for more info.
@@ -73,23 +73,9 @@
 load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies")
 install_bazel_dependencies()
 
-# Fetch transitive Bazel dependencies of npm_bazel_karma
-# ONLY REQUIRED if you are using the @bazel/karma npm package
-load("@npm_bazel_karma//:package.bzl", "rules_karma_dependencies")
-rules_karma_dependencies()
-
 # Setup TypeScript toolchain
 load("@npm_bazel_typescript//:defs.bzl", "ts_setup_workspace")
 ts_setup_workspace()
-
-# Setup web testing, choose browsers we can test on
-# ONLY REQUIRED if you are using the @bazel/karma npm package
-load("@io_bazel_rules_webtesting//web:repositories.bzl", "browser_repositories", "web_test_repositories")
-
-web_test_repositories()
-browser_repositories(
-    chromium = True,
-)
 ```
 
 # Self-managed npm dependencies
@@ -143,15 +129,6 @@
     # Point bazel to your node_modules to find the entry point
     node_modules = ["//:node_modules"],
 )
-
-# Create a karma rule to use in ts_web_test_suite karma
-# attribute when using self-managed dependencies
-nodejs_binary(
-    name = "karma/karma",
-    entry_point = "karma/bin/karma",
-    # Point bazel to your node_modules to find the entry point
-    node_modules = ["//:node_modules"],
-)
 ```
 
 See https://github.com/bazelbuild/rules_nodejs#dependencies for more information on
@@ -325,7 +302,4 @@
 At some point, we plan to release a tool similar to [gazelle] to generate the
 BUILD files from your source code.
 
-In the meantime, we suggest associating the `.bazel` extension with Python in
-your editor, so that you get useful syntax highlighting.
-
 [gazelle]: https://github.com/bazelbuild/rules_go/tree/master/go/tools/gazelle
diff --git a/docs/BUILD.bazel b/docs/BUILD.bazel
index edd7143..019f3dc 100644
--- a/docs/BUILD.bazel
+++ b/docs/BUILD.bazel
@@ -8,9 +8,6 @@
         "//internal:ts_repositories.bzl",
         "//internal/devserver:ts_devserver.bzl",
         "//internal/protobufjs:ts_proto_library.bzl",
-        # TODO(gregmagolan): fix docs for npm_bazel_karma
-        # "@npm_bazel_karma//:karma_web_test.bzl",
-        # "@npm_bazel_karma//:ts_web_test.bzl",
     ],
     format = "html",
     # The site is served at http://tsetse.info so the URL doesn't include a
diff --git a/internal/karma/.gitignore b/internal/karma/.gitignore
new file mode 100644
index 0000000..bae2b58
--- /dev/null
+++ b/internal/karma/.gitignore
@@ -0,0 +1,2 @@
+# This file is generated during the build
+README.md
diff --git a/internal/karma/WORKSPACE b/internal/karma/WORKSPACE
index a2a3401..f584c27 100644
--- a/internal/karma/WORKSPACE
+++ b/internal/karma/WORKSPACE
@@ -43,12 +43,27 @@
     yarn_lock = "//:yarn.lock",
 )
 
-# Setup gazelle toolchain
-load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies")
-
-gazelle_dependencies()
-
 # Setup typescript toolchain
 load("@npm_bazel_typescript//:defs.bzl", "ts_setup_workspace")
 
 ts_setup_workspace()
+
+# Dependencies for generating documentation
+load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories")
+
+sass_repositories()
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+http_archive(
+    name = "com_google_protobuf",
+    strip_prefix = "protobuf-3.6.1.3",
+    sha256 = "9510dd2afc29e7245e9e884336f848c8a6600a14ae726adb6befdb4f786f0be2",
+    # v3.6.1.3 as of 2019-01-15
+    urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.6.1.3.zip"],
+    type = "zip",
+)
+
+load("@io_bazel_skydoc//skylark:skylark.bzl", "skydoc_repositories")
+
+skydoc_repositories()
diff --git a/internal/karma/docs/BUILD.bazel b/internal/karma/docs/BUILD.bazel
new file mode 100644
index 0000000..dc0a3bc
--- /dev/null
+++ b/internal/karma/docs/BUILD.bazel
@@ -0,0 +1,10 @@
+load("@io_bazel_skydoc//skylark:skylark.bzl", "skylark_doc")
+
+skylark_doc(
+    name = "docs",
+    srcs = [
+        "//:karma_web_test.bzl",
+        "//:ts_web_test.bzl",
+    ],
+    format = "markdown",
+)
diff --git a/internal/karma/docs/index.md b/internal/karma/docs/index.md
new file mode 100644
index 0000000..697f82f
--- /dev/null
+++ b/internal/karma/docs/index.md
@@ -0,0 +1,89 @@
+
+# Overview
+
+
+<nav class="toc">
+  <h2>Rule sets</h2>
+  <ul>
+    <li><a href="#karma_web_test">Unit testing with Karma</a></li>
+    <li><a href="#ts_web_test">Unit testing in a browser</a></li>
+  </ul>
+</nav>
+
+<h2><a href="/karma_web_test.html" id="karma_web_test">Unit testing with Karma</a></h2>
+
+<h3>Macros</h3>
+<table class="overview-table">
+  <colgroup>
+    <col class="col-name" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr>
+      <td>
+        <a href="/karma_web_test.html#run_karma_web_test">
+          <code>run_karma_web_test</code>
+        </a>
+      </td>
+      <td>
+        <p>Creates an action that can run karma.</p>
+
+      </td>
+    </tr>
+    <tr>
+      <td>
+        <a href="/karma_web_test.html#karma_web_test">
+          <code>karma_web_test</code>
+        </a>
+      </td>
+      <td>
+        <p>Runs unit tests in a browser with Karma.</p>
+
+      </td>
+    </tr>
+    <tr>
+      <td>
+        <a href="/karma_web_test.html#karma_web_test_suite">
+          <code>karma_web_test_suite</code>
+        </a>
+      </td>
+      <td>
+        <p>Defines a test_suite of web_test targets that wrap a karma_web_test target.</p>
+
+      </td>
+    </tr>
+  </tbody>
+</table>
+<h2><a href="/ts_web_test.html" id="ts_web_test">Unit testing in a browser</a></h2>
+
+<h3>Macros</h3>
+<table class="overview-table">
+  <colgroup>
+    <col class="col-name" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr>
+      <td>
+        <a href="/ts_web_test.html#ts_web_test">
+          <code>ts_web_test</code>
+        </a>
+      </td>
+      <td>
+        <p>Runs unit tests in a browser.</p>
+
+      </td>
+    </tr>
+    <tr>
+      <td>
+        <a href="/ts_web_test.html#ts_web_test_suite">
+          <code>ts_web_test_suite</code>
+        </a>
+      </td>
+      <td>
+        <p>Defines a test_suite of web_test targets that wrap a ts_web_test target.</p>
+
+      </td>
+    </tr>
+  </tbody>
+</table>
diff --git a/internal/karma/docs/install.md b/internal/karma/docs/install.md
new file mode 100644
index 0000000..e04b558
--- /dev/null
+++ b/internal/karma/docs/install.md
@@ -0,0 +1,80 @@
+# Karma rules for Bazel
+
+**WARNING: this is beta-quality software. Breaking changes are likely. Not recommended for production use without expert support.**
+
+The Karma rules run karma tests with Bazel.
+
+## Installation
+
+Add the `@bazel/karma` npm package to your `devDependencies` in `package.json`.
+
+Your `WORKSPACE` should declare a `yarn_install` or `npm_install` rule named `npm`.
+It should then install the rules found in the npm packages.
+
+This is copied from the README for rules_nodejs:
+
+```python
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+# Fetch rules_nodejs
+# (you can check https://github.com/bazelbuild/rules_nodejs/releases for a newer release than this)
+http_archive(
+    name = "build_bazel_rules_nodejs",
+    urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.18.5/rules_nodejs-0.18.5.tar.gz"],
+    sha256 = "c8cd6a77433f7d3bb1f4ac87f15822aa102989f8e9eb1907ca0cad718573985b",
+)
+
+# Setup the NodeJS toolchain
+load("@build_bazel_rules_nodejs//:defs.bzl", "node_repositories", "yarn_install")
+node_repositories()
+
+yarn_install(
+  name = "npm",
+  package_json = "//:package.json",
+  yarn_lock = "//:yarn.lock",
+)
+
+# Install all Bazel dependencies needed for npm packages that supply Bazel rules
+load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies")
+install_bazel_dependencies()
+```
+
+This causes the `@bazel/karma` package to be installed as a Bazel workspace named `npm_bazel_karma`.
+
+Now add this to your `WORKSPACE` to install the Karma dependencies:
+
+```python
+# Fetch transitive Bazel dependencies of npm_bazel_karma
+load("@npm_bazel_karma//:package.bzl", "rules_karma_dependencies")
+rules_karma_dependencies()
+```
+
+This installs the `io_bazel_rules_webtesting` repository, if you haven't installed it earlier.
+
+Finally, configure the rules_webtesting:
+
+```python
+# Setup web testing, choose browsers we can test on
+load("@io_bazel_rules_webtesting//web:repositories.bzl", "browser_repositories", "web_test_repositories")
+
+web_test_repositories()
+browser_repositories(
+    chromium = True,
+)
+```
+
+## Installing with self-managed dependencies
+
+If you didn't use the `yarn_install` or `npm_install` rule to create an `npm` workspace, you'll have to declare a rule in your root `BUILD.bazel` file to execute karma:
+
+```python
+# Create a karma rule to use in ts_web_test_suite karma
+# attribute when using self-managed dependencies
+nodejs_binary(
+    name = "karma/karma",
+    entry_point = "karma/bin/karma",
+    # Point bazel to your node_modules to find the entry point
+    node_modules = ["//:node_modules"],
+)
+```
+
diff --git a/internal/karma/docs/karma_web_test.md b/internal/karma/docs/karma_web_test.md
new file mode 100644
index 0000000..39c6543
--- /dev/null
+++ b/internal/karma/docs/karma_web_test.md
@@ -0,0 +1,315 @@
+
+<!---
+Documentation generated by Skydoc
+-->
+<h1>Unit testing with Karma</h1>
+
+
+<nav class="toc">
+  <h2>Macros</h2>
+  <ul>
+    <li><a href="#run_karma_web_test">run_karma_web_test</a></li>
+    <li><a href="#karma_web_test">karma_web_test</a></li>
+    <li><a href="#karma_web_test_suite">karma_web_test_suite</a></li>
+  </ul>
+</nav>
+<a name="run_karma_web_test"></a>
+## run_karma_web_test
+
+<pre>
+run_karma_web_test(<a href="#run_karma_web_test.ctx">ctx</a>)
+</pre>
+
+Creates an action that can run karma.
+
+This is also used by ts_web_test_rule.
+
+Returns:
+  The runfiles for the generated action.
+
+
+<a name="run_karma_web_test_args"></a>
+### Attributes
+
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="run_karma_web_test.ctx">
+      <td><code>ctx</code></td>
+      <td>
+        <p><code>Unknown; Required</code></p>
+        <p>Bazel rule execution context</p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+<a name="karma_web_test"></a>
+## karma_web_test
+
+<pre>
+karma_web_test(<a href="#karma_web_test.srcs">srcs</a>, <a href="#karma_web_test.deps">deps</a>, <a href="#karma_web_test.data">data</a>, <a href="#karma_web_test.configuration_env_vars">configuration_env_vars</a>, <a href="#karma_web_test.bootstrap">bootstrap</a>, <a href="#karma_web_test.runtime_deps">runtime_deps</a>, <a href="#karma_web_test.static_files">static_files</a>, <a href="#karma_web_test.config_file">config_file</a>, <a href="#karma_web_test.tags">tags</a>, <a href="#karma_web_test.**kwargs">**kwargs</a>)
+</pre>
+
+Runs unit tests in a browser with Karma.
+
+When executed under `bazel test`, this uses a headless browser for speed.
+This is also because `bazel test` allows multiple targets to be tested together,
+and we don't want to open a Chrome window on your machine for each one. Also,
+under `bazel test` the test will execute and immediately terminate.
+
+Running under `ibazel test` gives you a "watch mode" for your tests. The rule is
+optimized for this case - the test runner server will stay running and just
+re-serve the up-to-date JavaScript source bundle.
+
+To debug a single test target, run it with `bazel run` instead. This will open a
+browser window on your computer. Also you can use any other browser by opening
+the URL printed when the test starts up. The test will remain running until you
+cancel the `bazel run` command.
+
+This rule will use your system Chrome by default. In the default case, your
+environment must specify CHROME_BIN so that the rule will know which Chrome binary to run.
+Other `browsers` and `customLaunchers` may be set using the a base Karma configuration
+specified in the `config_file` attribute.
+
+
+<a name="karma_web_test_args"></a>
+### Attributes
+
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="karma_web_test.srcs">
+      <td><code>srcs</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>A list of JavaScript test files</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test.deps">
+      <td><code>deps</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>Other targets which produce JavaScript such as <code>ts_library</code></p>
+      </td>
+    </tr>
+    <tr id="karma_web_test.data">
+      <td><code>data</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>Runtime dependencies</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test.configuration_env_vars">
+      <td><code>configuration_env_vars</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>Pass these configuration environment variables to the resulting binary.
+Chooses a subset of the configuration environment variables (taken from ctx.var), which also
+includes anything specified via the --define flag.
+Note, this can lead to different outputs produced by this rule.</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test.bootstrap">
+      <td><code>bootstrap</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>JavaScript files to include <em>before</em> the module loader (require.js).
+For example, you can include Reflect,js for TypeScript decorator metadata reflection,
+or UMD bundles for third-party libraries.</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test.runtime_deps">
+      <td><code>runtime_deps</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>Dependencies which should be loaded after the module loader but before the srcs and deps.
+These should be a list of targets which produce JavaScript such as <code>ts_library</code>.
+The files will be loaded in the same order they are declared by that rule.</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test.static_files">
+      <td><code>static_files</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>Arbitrary files which are available to be served on request.
+Files are served at:
+<code>/base/&amp;lt;WORKSPACE_NAME&amp;gt;/&amp;lt;path-to-file&amp;gt;</code>, e.g.
+<code>/base/npm_bazel_typescript/examples/testing/static_script.js</code></p>
+      </td>
+    </tr>
+    <tr id="karma_web_test.config_file">
+      <td><code>config_file</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>User supplied Karma configuration file. Bazel will override
+certain attributes of this configuration file. Attributes that are
+overridden will be outputted to the test log.</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test.tags">
+      <td><code>tags</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>Standard Bazel tags, this macro adds tags for ibazel support</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test.**kwargs">
+      <td><code>**kwargs</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>Passed through to <code>karma_web_test</code></p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+<a name="karma_web_test_suite"></a>
+## karma_web_test_suite
+
+<pre>
+karma_web_test_suite(<a href="#karma_web_test_suite.name">name</a>, <a href="#karma_web_test_suite.browsers">browsers</a>, <a href="#karma_web_test_suite.args">args</a>, <a href="#karma_web_test_suite.browser_overrides">browser_overrides</a>, <a href="#karma_web_test_suite.config">config</a>, <a href="#karma_web_test_suite.flaky">flaky</a>, <a href="#karma_web_test_suite.local">local</a>, <a href="#karma_web_test_suite.shard_count">shard_count</a>, <a href="#karma_web_test_suite.size">size</a>, <a href="#karma_web_test_suite.tags">tags</a>, <a href="#karma_web_test_suite.test_suite_tags">test_suite_tags</a>, <a href="#karma_web_test_suite.timeout">timeout</a>, <a href="#karma_web_test_suite.visibility">visibility</a>, <a href="#karma_web_test_suite.web_test_data">web_test_data</a>, <a href="#karma_web_test_suite.wrapped_test_tags">wrapped_test_tags</a>, <a href="#karma_web_test_suite.**remaining_keyword_args">**remaining_keyword_args</a>)
+</pre>
+
+Defines a test_suite of web_test targets that wrap a karma_web_test target.
+
+This macro also accepts all parameters in karma_web_test. See karma_web_test docs
+for details.
+
+
+<a name="karma_web_test_suite_args"></a>
+### Attributes
+
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="karma_web_test_suite.name">
+      <td><code>name</code></td>
+      <td>
+        <p><code><a href="https://bazel.build/docs/build-ref.html#name">Name</a>; Required</code></p>
+        <p>The base name of the test</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test_suite.browsers">
+      <td><code>browsers</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>A sequence of labels specifying the browsers to use.</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test_suite.args">
+      <td><code>args</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>Args for web_test targets generated by this extension.</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test_suite.browser_overrides">
+      <td><code>browser_overrides</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>Dictionary; optional; default is an empty dictionary. A
+dictionary mapping from browser names to browser-specific web_test
+attributes, such as shard_count, flakiness, timeout, etc. For example:
+{'//browsers:chrome-native': {'shard_count': 3, 'flaky': 1}
+'//browsers:firefox-native': {'shard_count': 1, 'timeout': 100}}.</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test_suite.config">
+      <td><code>config</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>Label; optional; Configuration of web test features.</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test_suite.flaky">
+      <td><code>flaky</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>A boolean specifying that the test is flaky. If set, the test will
+be retried up to 3 times (default: 0)</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test_suite.local">
+      <td><code>local</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>boolean; optional.</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test_suite.shard_count">
+      <td><code>shard_count</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>The number of test shards to use per browser. (default: 1)</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test_suite.size">
+      <td><code>size</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>A string specifying the test size. (default: 'large')</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test_suite.tags">
+      <td><code>tags</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>A list of test tag strings to apply to each generated web_test target.
+This macro adds a couple for ibazel.</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test_suite.test_suite_tags">
+      <td><code>test_suite_tags</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>A list of tag strings for the generated test_suite.</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test_suite.timeout">
+      <td><code>timeout</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>A string specifying the test timeout (default: computed from size)</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test_suite.visibility">
+      <td><code>visibility</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>List of labels; optional.</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test_suite.web_test_data">
+      <td><code>web_test_data</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>Data dependencies for the web_test.</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test_suite.wrapped_test_tags">
+      <td><code>wrapped_test_tags</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>A list of test tag strings to use for the wrapped test</p>
+      </td>
+    </tr>
+    <tr id="karma_web_test_suite.**remaining_keyword_args">
+      <td><code>**remaining_keyword_args</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>Arguments for the wrapped test target.</p>
+      </td>
+    </tr>
+  </tbody>
+</table>
diff --git a/internal/karma/docs/ts_web_test.md b/internal/karma/docs/ts_web_test.md
new file mode 100644
index 0000000..eca8535
--- /dev/null
+++ b/internal/karma/docs/ts_web_test.md
@@ -0,0 +1,272 @@
+
+<!---
+Documentation generated by Skydoc
+-->
+<h1>Unit testing in a browser</h1>
+
+
+<nav class="toc">
+  <h2>Macros</h2>
+  <ul>
+    <li><a href="#ts_web_test">ts_web_test</a></li>
+    <li><a href="#ts_web_test_suite">ts_web_test_suite</a></li>
+  </ul>
+</nav>
+<a name="ts_web_test"></a>
+## ts_web_test
+
+<pre>
+ts_web_test(<a href="#ts_web_test.srcs">srcs</a>, <a href="#ts_web_test.deps">deps</a>, <a href="#ts_web_test.data">data</a>, <a href="#ts_web_test.configuration_env_vars">configuration_env_vars</a>, <a href="#ts_web_test.bootstrap">bootstrap</a>, <a href="#ts_web_test.runtime_deps">runtime_deps</a>, <a href="#ts_web_test.static_files">static_files</a>, <a href="#ts_web_test.tags">tags</a>, <a href="#ts_web_test.**kwargs">**kwargs</a>)
+</pre>
+
+Runs unit tests in a browser.
+
+When executed under `bazel test`, this uses a headless browser for speed.
+This is also because `bazel test` allows multiple targets to be tested together,
+and we don't want to open a Chrome window on your machine for each one. Also,
+under `bazel test` the test will execute and immediately terminate.
+
+Running under `ibazel test` gives you a "watch mode" for your tests. The rule is
+optimized for this case - the test runner server will stay running and just
+re-serve the up-to-date JavaScript source bundle.
+
+To debug a single test target, run it with `bazel run` instead. This will open a
+browser window on your computer. Also you can use any other browser by opening
+the URL printed when the test starts up. The test will remain running until you
+cancel the `bazel run` command.
+
+This rule will use your system Chrome. Your environment must specify CHROME_BIN
+so that the rule will know which Chrome binary to run.
+
+Currently this rule uses Karma as the test runner under the hood, but this is
+an implementation detail. We might switch to another runner like Jest in the future.
+
+
+<a name="ts_web_test_args"></a>
+### Attributes
+
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="ts_web_test.srcs">
+      <td><code>srcs</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>A list of JavaScript test files</p>
+      </td>
+    </tr>
+    <tr id="ts_web_test.deps">
+      <td><code>deps</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>Other targets which produce JavaScript such as <code>ts_library</code></p>
+      </td>
+    </tr>
+    <tr id="ts_web_test.data">
+      <td><code>data</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>Runtime dependencies</p>
+      </td>
+    </tr>
+    <tr id="ts_web_test.configuration_env_vars">
+      <td><code>configuration_env_vars</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>Pass these configuration environment variables to the resulting binary.
+Chooses a subset of the configuration environment variables (taken from ctx.var), which also
+includes anything specified via the --define flag.
+Note, this can lead to different outputs produced by this rule.</p>
+      </td>
+    </tr>
+    <tr id="ts_web_test.bootstrap">
+      <td><code>bootstrap</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>JavaScript files to include <em>before</em> the module loader (require.js).
+For example, you can include Reflect,js for TypeScript decorator metadata reflection,
+or UMD bundles for third-party libraries.</p>
+      </td>
+    </tr>
+    <tr id="ts_web_test.runtime_deps">
+      <td><code>runtime_deps</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>Dependencies which should be loaded after the module loader but before the srcs and deps.
+These should be a list of targets which produce JavaScript such as <code>ts_library</code>.
+The files will be loaded in the same order they are declared by that rule.</p>
+      </td>
+    </tr>
+    <tr id="ts_web_test.static_files">
+      <td><code>static_files</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>Arbitrary files which are available to be served on request.
+Files are served at:
+<code>/base/&amp;lt;WORKSPACE_NAME&amp;gt;/&amp;lt;path-to-file&amp;gt;</code>, e.g.
+<code>/base/npm_bazel_typescript/examples/testing/static_script.js</code></p>
+      </td>
+    </tr>
+    <tr id="ts_web_test.tags">
+      <td><code>tags</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>Standard Bazel tags, this macro adds tags for ibazel support as well as</p>
+      </td>
+    </tr>
+    <tr id="ts_web_test.**kwargs">
+      <td><code>**kwargs</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>Passed through to <code>ts_web_test</code></p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+<a name="ts_web_test_suite"></a>
+## ts_web_test_suite
+
+<pre>
+ts_web_test_suite(<a href="#ts_web_test_suite.name">name</a>, <a href="#ts_web_test_suite.browsers">browsers</a>, <a href="#ts_web_test_suite.args">args</a>, <a href="#ts_web_test_suite.browser_overrides">browser_overrides</a>, <a href="#ts_web_test_suite.config">config</a>, <a href="#ts_web_test_suite.flaky">flaky</a>, <a href="#ts_web_test_suite.local">local</a>, <a href="#ts_web_test_suite.shard_count">shard_count</a>, <a href="#ts_web_test_suite.size">size</a>, <a href="#ts_web_test_suite.tags">tags</a>, <a href="#ts_web_test_suite.test_suite_tags">test_suite_tags</a>, <a href="#ts_web_test_suite.timeout">timeout</a>, <a href="#ts_web_test_suite.visibility">visibility</a>, <a href="#ts_web_test_suite.web_test_data">web_test_data</a>, <a href="#ts_web_test_suite.wrapped_test_tags">wrapped_test_tags</a>, <a href="#ts_web_test_suite.**remaining_keyword_args">**remaining_keyword_args</a>)
+</pre>
+
+Defines a test_suite of web_test targets that wrap a ts_web_test target.
+
+This macro also accepts all parameters in ts_web_test. See ts_web_test docs for
+details.
+
+
+<a name="ts_web_test_suite_args"></a>
+### Attributes
+
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="ts_web_test_suite.name">
+      <td><code>name</code></td>
+      <td>
+        <p><code><a href="https://bazel.build/docs/build-ref.html#name">Name</a>; Required</code></p>
+        <p>The base name of the test.</p>
+      </td>
+    </tr>
+    <tr id="ts_web_test_suite.browsers">
+      <td><code>browsers</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>A sequence of labels specifying the browsers to use.</p>
+      </td>
+    </tr>
+    <tr id="ts_web_test_suite.args">
+      <td><code>args</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>Args for web_test targets generated by this extension.</p>
+      </td>
+    </tr>
+    <tr id="ts_web_test_suite.browser_overrides">
+      <td><code>browser_overrides</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>Dictionary; optional; default is an empty dictionary. A
+dictionary mapping from browser names to browser-specific web_test
+attributes, such as shard_count, flakiness, timeout, etc. For example:
+{'//browsers:chrome-native': {'shard_count': 3, 'flaky': 1}
+'//browsers:firefox-native': {'shard_count': 1, 'timeout': 100}}.</p>
+      </td>
+    </tr>
+    <tr id="ts_web_test_suite.config">
+      <td><code>config</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>Label; optional; Configuration of web test features.</p>
+      </td>
+    </tr>
+    <tr id="ts_web_test_suite.flaky">
+      <td><code>flaky</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>A boolean specifying that the test is flaky. If set, the test will
+be retried up to 3 times (default: 0)</p>
+      </td>
+    </tr>
+    <tr id="ts_web_test_suite.local">
+      <td><code>local</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>boolean; optional.</p>
+      </td>
+    </tr>
+    <tr id="ts_web_test_suite.shard_count">
+      <td><code>shard_count</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>The number of test shards to use per browser. (default: 1)</p>
+      </td>
+    </tr>
+    <tr id="ts_web_test_suite.size">
+      <td><code>size</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>A string specifying the test size. (default: 'large')</p>
+      </td>
+    </tr>
+    <tr id="ts_web_test_suite.tags">
+      <td><code>tags</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>A list of test tag strings to apply to each generated web_test_suite target.
+This macro adds a couple for ibazel.</p>
+      </td>
+    </tr>
+    <tr id="ts_web_test_suite.test_suite_tags">
+      <td><code>test_suite_tags</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>A list of tag strings for the generated test_suite.</p>
+      </td>
+    </tr>
+    <tr id="ts_web_test_suite.timeout">
+      <td><code>timeout</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>A string specifying the test timeout (default: computed from size)</p>
+      </td>
+    </tr>
+    <tr id="ts_web_test_suite.visibility">
+      <td><code>visibility</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>List of labels; optional.</p>
+      </td>
+    </tr>
+    <tr id="ts_web_test_suite.web_test_data">
+      <td><code>web_test_data</code></td>
+      <td>
+        <p><code>List of strings; Optional</code></p>
+        <p>Data dependencies for the web_test_suite.</p>
+      </td>
+    </tr>
+    <tr id="ts_web_test_suite.wrapped_test_tags">
+      <td><code>wrapped_test_tags</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>A list of test tag strings to use for the wrapped test</p>
+      </td>
+    </tr>
+    <tr id="ts_web_test_suite.**remaining_keyword_args">
+      <td><code>**remaining_keyword_args</code></td>
+      <td>
+        <p><code>Unknown; Optional</code></p>
+        <p>Arguments for the wrapped test target.</p>
+      </td>
+    </tr>
+  </tbody>
+</table>
diff --git a/internal/karma/package.json b/internal/karma/package.json
index 5f71978..e714d26 100644
--- a/internal/karma/package.json
+++ b/internal/karma/package.json
@@ -43,5 +43,8 @@
       "compatVersion": "0.0.0-COMPAT_VERSION",
       "rootPath": "."
     }
+  },
+  "scripts": {
+    "skydoc": "bazel build --symlink_prefix=bazel- //docs && unzip -o -d docs bazel-bin/docs/docs-skydoc.zip && cat docs/install.md docs/*_web_test.md | sed 's/^##/\\\n##/' > README.md"
   }
 }