Migrate from karma to web-test-runner

We want to get rid of the final remnants of karma from gerrit core.
The recommended way to run tests is with web-test-runner.

Depends-On: I4e9134e04475d7375d7a09d618892edbe0b555bc
Change-Id: Ie33cd05a21788b5ac6fa4bef3f807a62f6565d86
diff --git a/web/BUILD b/web/BUILD
index 8a33086..f8b0e82 100644
--- a/web/BUILD
+++ b/web/BUILD
@@ -1,6 +1,6 @@
 load("//tools/bzl:plugin.bzl", "gerrit_plugin")
 load("//tools/js:eslint.bzl", "plugin_eslint")
-load("//tools/bzl:js.bzl", "gerrit_js_bundle", "karma_test")
+load("//tools/bzl:js.bzl", "gerrit_js_bundle", "web_test_runner")
 load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project")
 
 package_group(
@@ -57,8 +57,12 @@
 # Creates lint_test and lint_bin rules.
 plugin_eslint()
 
-karma_test(
-    name = "karma_test",
-    srcs = ["karma_test.sh"],
-    data = [":checks-ts-tests"],
+web_test_runner(
+    name = "web_test_runner",
+    srcs = ["web_test_runner.sh"],
+    data = [
+        ":tsconfig",
+        ":checks-ts-tests",
+        "@plugins_npm//:node_modules",
+    ],
 )
diff --git a/web/fetcher_test.ts b/web/fetcher_test.ts
index 25a66da..a5e66f4 100644
--- a/web/fetcher_test.ts
+++ b/web/fetcher_test.ts
@@ -26,6 +26,8 @@
 } from '@gerritcodereview/typescript-api/checks';
 import {Check} from './types';
 import {ChangeInfo} from '@gerritcodereview/typescript-api/rest-api';
+import {assert} from '@open-wc/testing';
+import sinon from 'sinon';
 
 const check1: Check = {
   state: 'SUCCESSFUL',
diff --git a/web/gr-checkers-list.ts b/web/gr-checkers-list.ts
index 5fc912b..9b752cc 100644
--- a/web/gr-checkers-list.ts
+++ b/web/gr-checkers-list.ts
@@ -16,7 +16,7 @@
  */
 import './gr-create-checkers-dialog';
 import {css, CSSResult, html, LitElement} from 'lit';
-import {customElement, property, query, state} from 'lit/decorators';
+import {customElement, property, query, state} from 'lit/decorators.js';
 import {Checker} from './types';
 import {CancelEvent, GrCreateCheckersDialog} from './gr-create-checkers-dialog';
 import {value} from './util';
@@ -82,8 +82,8 @@
   }
 
   static override styles = [
-    window.Gerrit.styles.table as CSSResult,
-    window.Gerrit.styles.modal as CSSResult,
+    window.Gerrit?.styles.table as CSSResult,
+    window.Gerrit?.styles.modal as CSSResult,
     css`
       #container {
         width: 80vw;
diff --git a/web/gr-checkers-list_test.ts b/web/gr-checkers-list_test.ts
index b0a6a9b..36efa9a 100644
--- a/web/gr-checkers-list_test.ts
+++ b/web/gr-checkers-list_test.ts
@@ -20,6 +20,7 @@
 import {Checker} from './types';
 import {queryAll, queryAndAssert} from './test/test-util';
 import {PluginApi} from '@gerritcodereview/typescript-api/plugin';
+import {assert} from '@open-wc/testing';
 
 const CHECKERS = [
   {
diff --git a/web/gr-create-checkers-dialog.ts b/web/gr-create-checkers-dialog.ts
index 6fd6f14..6061288 100644
--- a/web/gr-create-checkers-dialog.ts
+++ b/web/gr-create-checkers-dialog.ts
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 import './gr-repo-chip';
-import {customElement, property, state} from 'lit/decorators';
+import {customElement, property, state} from 'lit/decorators.js';
 import {css, CSSResult, html, LitElement, PropertyValues} from 'lit';
 import {HttpMethod, RestPluginApi} from '@gerritcodereview/typescript-api/rest';
 import {Checker} from './types';
@@ -108,7 +108,7 @@
   }
 
   static override styles = [
-    window.Gerrit.styles.form as CSSResult,
+    window.Gerrit?.styles.form as CSSResult,
     css`
       :host {
         display: inline-block;
diff --git a/web/gr-create-checkers-dialog_test.ts b/web/gr-create-checkers-dialog_test.ts
index 875dbe3..d5561eb 100644
--- a/web/gr-create-checkers-dialog_test.ts
+++ b/web/gr-create-checkers-dialog_test.ts
@@ -15,23 +15,19 @@
  * limitations under the License.
  */
 import './test/test-setup';
-import {queryAll, queryAndAssert} from './test/test-util';
 import './gr-create-checkers-dialog';
+import {queryAll, queryAndAssert} from './test/test-util';
 import {GrCreateCheckersDialog} from './gr-create-checkers-dialog';
+import {fixture, html, assert} from '@open-wc/testing';
 
 suite('gr-create-checkers-dialog tests', () => {
   let element: GrCreateCheckersDialog;
 
   setup(async () => {
-    element = document.createElement('gr-create-checkers-dialog');
-    document.body.appendChild(element);
+    element = await fixture(html`<gr-create-checkers-dialog></gr-create-checkers-dialog>`);
     await element.updateComplete;
   });
 
-  teardown(() => {
-    document.body.removeChild(element);
-  });
-
   test('all sections are rendered', () => {
     const div = queryAndAssert<HTMLElement>(element, 'div.gr-form-styles');
     const sections = queryAll<HTMLElement>(div, 'section');
diff --git a/web/gr-repo-chip.ts b/web/gr-repo-chip.ts
index 20df8ab..cb22f95 100644
--- a/web/gr-repo-chip.ts
+++ b/web/gr-repo-chip.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {customElement, property} from 'lit/decorators';
+import {customElement, property} from 'lit/decorators.js';
 import {css, html, LitElement} from 'lit';
 import {fire} from './util';
 
diff --git a/web/gr-repo-chip_test.ts b/web/gr-repo-chip_test.ts
index c5ffdd7..2e1f406 100644
--- a/web/gr-repo-chip_test.ts
+++ b/web/gr-repo-chip_test.ts
@@ -15,23 +15,19 @@
  * limitations under the License.
  */
 import './test/test-setup';
-import {queryAndAssert} from './test/test-util';
 import './gr-repo-chip';
+import {queryAndAssert} from './test/test-util';
 import {GrRepoChip} from './gr-repo-chip';
+import {fixture, html, assert} from '@open-wc/testing';
 
 suite('gr-repo-chip tests', () => {
   let element: GrRepoChip;
 
   setup(async () => {
-    element = document.createElement('gr-repo-chip');
-    document.body.appendChild(element);
+    element = await fixture(html`<gr-repo-chip></gr-repo-chip>`);
     await element.updateComplete;
   });
 
-  teardown(() => {
-    document.body.removeChild(element);
-  });
-
   test('a button is rendered', () => {
     queryAndAssert<HTMLElement>(element, 'gr-button');
   });
diff --git a/web/karma_test.sh b/web/karma_test.sh
deleted file mode 100755
index bb45218..0000000
--- a/web/karma_test.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/bash
-
-set -euo pipefail
-./$1 start $2 --single-run \
-  --root 'plugins/checks/web/_bazel_ts_out_tests/' \
-  --test-files '*_test.js'
diff --git a/web/plugin.ts b/web/plugin.ts
index 2c56734..d9b5b70 100644
--- a/web/plugin.ts
+++ b/web/plugin.ts
@@ -17,7 +17,7 @@
 import '@gerritcodereview/typescript-api/gerrit';
 import {ChecksFetcher} from './fetcher';
 
-window.Gerrit.install(plugin => {
+window.Gerrit?.install(plugin => {
   const checksApi = plugin.checks();
   const fetcher = new ChecksFetcher(plugin);
   checksApi.register({
diff --git a/web/test/test-setup.ts b/web/test/test-setup.ts
index ddeaf89..4f113a1 100644
--- a/web/test/test-setup.ts
+++ b/web/test/test-setup.ts
@@ -14,30 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import 'chai/chai';
 import '@gerritcodereview/typescript-api/gerrit';
 import {css} from 'lit';
+import sinon from 'sinon';
 
 declare global {
   interface Window {
-    assert: typeof chai.assert;
-    expect: typeof chai.expect;
     sinon: typeof sinon;
   }
-  let assert: typeof chai.assert;
-  let expect: typeof chai.expect;
   let sinon: typeof sinon;
 }
-window.assert = chai.assert;
-window.expect = chai.expect;
 window.sinon = sinon;
 
 window.Gerrit = {
   install: () => {},
   styles: {
+    font: css``,
     form: css``,
+    icon: css``,
     menuPage: css``,
+    spinner: css``,
     subPage: css``,
     table: css``,
+    modal: css``,
   },
 };
diff --git a/web/tsconfig.json b/web/tsconfig.json
index 9b1c0a1..2e224c5 100644
--- a/web/tsconfig.json
+++ b/web/tsconfig.json
@@ -2,7 +2,7 @@
   "extends": "../../tsconfig-plugins-base.json",
   "compilerOptions": {
     /* outDir for IDE (overridden by Bazel rule arg) */
-    "outDir": "../../../.ts-out/plugins/checks/web",
+    "outDir": "../../../.ts-out/plugins/checks/web"
   },
   "include": [
     "**/*"
diff --git a/web/util_test.ts b/web/util_test.ts
index 94baa03..6090995 100644
--- a/web/util_test.ts
+++ b/web/util_test.ts
@@ -16,6 +16,7 @@
  */
 import './test/test-setup';
 import {pluralize, generateDurationString} from './util';
+import {assert} from '@open-wc/testing';
 
 suite('util tests', () => {
   test('pluralize', () => {
diff --git a/web/web_test_runner.sh b/web/web_test_runner.sh
new file mode 100755
index 0000000..09cdb9c
--- /dev/null
+++ b/web/web_test_runner.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -euo pipefail
+./$1 --config $2 \
+  --dir 'plugins/checks/web/_bazel_ts_out_tests' \
+  --test-files 'plugins/checks/web/_bazel_ts_out_tests/*_test.js' \
+  --ts-config="plugins/checks/web/tsconfig.json"