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: Ic708f4b0d53970c3f1ed5fb1e6a0cad97a66d284
diff --git a/web/BUILD b/web/BUILD
index 893105f..ab06e56 100644
--- a/web/BUILD
+++ b/web/BUILD
@@ -1,5 +1,5 @@
 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(
@@ -53,10 +53,19 @@
     entry_point = "_bazel_ts_out/plugin.js",
 )
 
-karma_test(
-    name = "karma_test",
-    srcs = ["karma_test.sh"],
-    data = [":code-owners-ts-tests"],
+filegroup(
+    name = "tsconfig-sources",
+    srcs = glob(["**/tsconfig.json"])
+)
+
+web_test_runner(
+    name = "web_test_runner",
+    srcs = ["web_test_runner.sh"],
+    data = [
+        ":tsconfig-sources",
+        ":code-owners-ts-tests",
+        "@plugins_npm//:node_modules",
+    ],
 )
 
 plugin_eslint()
diff --git a/web/code-owners-model-mixin.ts b/web/code-owners-model-mixin.ts
index 23bb9fe..770631a 100644
--- a/web/code-owners-model-mixin.ts
+++ b/web/code-owners-model-mixin.ts
@@ -27,7 +27,7 @@
   UserRole,
 } from './code-owners-model';
 import {LitElement, PropertyValues} from 'lit';
-import {property, state} from 'lit/decorators';
+import {property, state} from 'lit/decorators.js';
 import {ChangeInfo} from '@gerritcodereview/typescript-api/rest-api';
 import {ReportingPluginApi} from '@gerritcodereview/typescript-api/reporting';
 import {RestPluginApi} from '@gerritcodereview/typescript-api/rest';
diff --git a/web/code-owners-service_test.ts b/web/code-owners-service_test.ts
index f547a8e..279415a 100644
--- a/web/code-owners-service_test.ts
+++ b/web/code-owners-service_test.ts
@@ -9,6 +9,7 @@
 } from '@gerritcodereview/typescript-api/rest-api.js';
 import {SuggestionsType} from './code-owners-model.js';
 import {assert} from '@open-wc/testing';
+import sinon from 'sinon';
 
 function flush() {
   return new Promise((resolve, _reject) => {
diff --git a/web/gr-check-code-owner.ts b/web/gr-check-code-owner.ts
index e6de8a7..9215ba9 100644
--- a/web/gr-check-code-owner.ts
+++ b/web/gr-check-code-owner.ts
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-import {customElement, query, property, state} from 'lit/decorators';
+import {customElement, query, property, state} from 'lit/decorators.js';
 import {css, CSSResult, html, LitElement} from 'lit';
 import {classMap} from 'lit/directives/class-map.js';
 import {PluginApi} from '@gerritcodereview/typescript-api/plugin';
@@ -69,8 +69,8 @@
 
   static override get styles() {
     return [
-      window.Gerrit.styles.font as CSSResult,
-      window.Gerrit.styles.form as CSSResult,
+      window.Gerrit?.styles.font as CSSResult,
+      window.Gerrit?.styles.form as CSSResult,
       css`
         main {
           margin: 2em auto;
diff --git a/web/karma_test.sh b/web/karma_test.sh
deleted file mode 100755
index fd686da..0000000
--- a/web/karma_test.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/bash
-set -euo pipefail
-./$1 start $2 --single-run \
-  --root 'plugins/code-owners/web/_bazel_ts_out_tests/' \
-  --test-files '*_test.js' \
-  --browsers ${3:-ChromeHeadless}
diff --git a/web/owner-requirement.ts b/web/owner-requirement.ts
index a1cc7a8..a17e347 100644
--- a/web/owner-requirement.ts
+++ b/web/owner-requirement.ts
@@ -19,8 +19,8 @@
 import {showPluginFailedMessage} from './code-owners-banner';
 import {isPluginErrorState, UserRole} from './code-owners-model';
 import {css, html, LitElement} from 'lit';
-import {customElement} from 'lit/decorators';
-import {when} from 'lit/directives/when';
+import {customElement} from 'lit/decorators.js';
+import {when} from 'lit/directives/when.js';
 import {
   ApprovalInfo,
   DetailedLabelInfo,
diff --git a/web/owner-status-column.ts b/web/owner-status-column.ts
index 9b0cb86..1fa3d70 100644
--- a/web/owner-status-column.ts
+++ b/web/owner-status-column.ts
@@ -21,7 +21,7 @@
   RevisionPatchSetNum,
 } from '@gerritcodereview/typescript-api/rest-api';
 import {css, html, LitElement, nothing, PropertyValues} from 'lit';
-import {customElement, property} from 'lit/decorators';
+import {customElement, property} from 'lit/decorators.js';
 import {OwnerStatus} from './code-owners-api';
 import {FileStatus, PluginState} from './code-owners-model';
 import {CodeOwnersModelMixin} from './code-owners-model-mixin';
diff --git a/web/plugin.ts b/web/plugin.ts
index 2e26843..2dcac79 100644
--- a/web/plugin.ts
+++ b/web/plugin.ts
@@ -30,7 +30,7 @@
 import {CodeOwnersModelMixinInterface} from './code-owners-model-mixin';
 import './gr-check-code-owner';
 
-window.Gerrit.install(plugin => {
+window.Gerrit?.install(plugin => {
   const restApi = plugin.restApi();
   const reporting = plugin.reporting();
 
diff --git a/web/suggest-owners-trigger.ts b/web/suggest-owners-trigger.ts
index fb2ca98..089ca8b 100644
--- a/web/suggest-owners-trigger.ts
+++ b/web/suggest-owners-trigger.ts
@@ -16,7 +16,7 @@
  */
 import {CodeOwnersModelMixin} from './code-owners-model-mixin';
 import {css, html, LitElement, nothing} from 'lit';
-import {customElement} from 'lit/decorators';
+import {customElement} from 'lit/decorators.js';
 
 const base = CodeOwnersModelMixin(LitElement);
 
diff --git a/web/suggest-owners.ts b/web/suggest-owners.ts
index 7f07906..17bc893 100644
--- a/web/suggest-owners.ts
+++ b/web/suggest-owners.ts
@@ -17,10 +17,10 @@
 import {CodeOwnersModelMixin} from './code-owners-model-mixin';
 import {SuggestionsState, SuggestionsType} from './code-owners-model';
 import {getDisplayOwnersGroups, GroupedFiles} from './suggest-owners-util';
-import {customElement, property, state} from 'lit/decorators';
+import {customElement, property, state} from 'lit/decorators.js';
 import {css, html, LitElement, nothing, PropertyValues} from 'lit';
-import {when} from 'lit/directives/when';
-import {ifDefined} from 'lit/directives/if-defined';
+import {when} from 'lit/directives/when.js';
+import {ifDefined} from 'lit/directives/if-defined.js';
 import {CodeOwnerInfo, CodeOwnersInfo, FetchedFile} from './code-owners-api';
 import {
   AccountId,
diff --git a/web/tsconfig.json b/web/tsconfig.json
index 00f6c7e..ab4cca2 100644
--- a/web/tsconfig.json
+++ b/web/tsconfig.json
@@ -3,9 +3,9 @@
   "compilerOptions": {
     "experimentalDecorators": true,
     /* outDir for IDE (overridden by Bazel rule arg) */
-    "outDir": "../../../.ts-out/plugins/code-owners/ui",
+    "outDir": "../../../.ts-out/plugins/code-owners/ui"
   },
   "include": [
-    "**/*",
-  ],
+    "**/*"
+  ]
 }
diff --git a/web/web_test_runner.sh b/web/web_test_runner.sh
new file mode 100755
index 0000000..b37e0d8
--- /dev/null
+++ b/web/web_test_runner.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -euo pipefail
+./$1 --config $2 \
+  --dir 'plugins/code-owners/web/_bazel_ts_out_tests' \
+  --test-files 'plugins/code-owners/web/_bazel_ts_out_tests/*_test.js' \
+  --type-config="plugins/code-owners/web/tsconfig.json"