Merge branch 'stable-3.11'

* stable-3.11:
  Fix NPE in SyncReviewerManager

Change-Id: Iae674221a0efac3d8e2e0604e0009b425df59991
diff --git a/README.md b/README.md
index 4ff322d..65b95b4 100644
--- a/README.md
+++ b/README.md
@@ -89,16 +89,16 @@
 Example:
 
 ```
-   $ git clone https://gerrit.googlesource.com/plugins/owners
-   $ git clone https://gerrit.googlesource.com/gerrit
-   $ cd gerrit/plugins
-   $ ln -s ../../owners/owners .
-   $ ln -s ../../owners/owners-autoassign .
-   $ ln -s ../../owners/owners-api .
-   $ ln -sf ../../owners/external_plugin_deps.bzl .
-   $ cd ..
-   $ ln -s ../owners/owners-common .
-   $ bazel build plugins/owners plugins/owners-autoassign
+  git clone https://gerrit.googlesource.com/plugins/owners
+  git clone --recurse-submodules https://gerrit.googlesource.com/gerrit
+  cd gerrit/plugins
+  ln -s ../../owners/owners .
+  ln -s ../../owners/owners-autoassign .
+  ln -s ../../owners/owners-api .
+  ln -sf ../../owners/external_plugin_deps.bzl .
+  cd ..
+  ln -s ../owners/owners-common .
+  bazel build plugins/owners plugins/owners-autoassign
 ```
 
 NOTE: the owners-common folder is producing shared artifacts for the two plugins
diff --git a/owners/src/test/java/com/googlesource/gerrit/owners/OwnersSubmitRequirementITAbstract.java b/owners/src/test/java/com/googlesource/gerrit/owners/OwnersSubmitRequirementITAbstract.java
index ba35cdb..e7baba6 100644
--- a/owners/src/test/java/com/googlesource/gerrit/owners/OwnersSubmitRequirementITAbstract.java
+++ b/owners/src/test/java/com/googlesource/gerrit/owners/OwnersSubmitRequirementITAbstract.java
@@ -273,40 +273,12 @@
     ChangeInfo ownersVoteNotSufficient = forChange(r).get();
     assertThat(ownersVoteNotSufficient.submittable).isFalse();
     verifyChangeReady(ownersVoteNotSufficient);
-    verifyHasSubmitRecord(
-        ownersVoteNotSufficient.submitRecords,
-        LabelId.CODE_REVIEW,
-        SubmitRecordInfo.Label.Status.NEED);
 
     requestScopeOperations.setApiUser(admin.id());
     forChange(r).current().review(ReviewInput.approve());
     ChangeInfo changeReadyWithMaxScore = forChange(r).get();
     assertThat(changeReadyWithMaxScore.submittable).isTrue();
     verifyChangeReady(changeReadyWithMaxScore);
-    verifyHasSubmitRecord(
-        changeReadyWithMaxScore.submitRecords,
-        LabelId.CODE_REVIEW,
-        SubmitRecordInfo.Label.Status.OK);
-  }
-
-  @Test
-  public void shouldConfiguredLabelScoreByCodeOwnersOverwriteSubmitRequirement() throws Exception {
-    installLabel(TestLabels.codeReview().toBuilder().setFunction(LabelFunction.NO_OP).build());
-
-    TestAccount admin2 = accountCreator.admin2();
-    addOwnerFileToRoot(true, LabelDefinition.parse("Code-Review,1").get(), admin2);
-
-    PushOneCommit.Result r = createChange("Add a file", "foo", "bar");
-    ChangeApi changeApi = forChange(r);
-    ChangeInfo changeNotReady = changeApi.get();
-    assertThat(changeNotReady.submittable).isFalse();
-    verifyChangeNotReady(changeNotReady);
-
-    requestScopeOperations.setApiUser(admin2.id());
-    forChange(r).current().review(ReviewInput.recommend());
-    ChangeInfo ownersVoteSufficient = forChange(r).get();
-    assertThat(ownersVoteSufficient.submittable).isTrue();
-    verifyChangeReady(ownersVoteSufficient);
   }
 
   @Test
diff --git a/owners/web/BUILD b/owners/web/BUILD
index 56ab697..6d9430a 100644
--- a/owners/web/BUILD
+++ b/owners/web/BUILD
@@ -1,5 +1,5 @@
 load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project")
-load("//tools/bzl:js.bzl", "gerrit_js_bundle", "karma_test")
+load("//tools/bzl:js.bzl", "gerrit_js_bundle", "web_test_runner")
 load("//tools/js:eslint.bzl", "plugin_eslint")
 
 package_group(
@@ -59,10 +59,15 @@
     entry_point = "_bazel_ts_out/plugin.js",
 )
 
-karma_test(
-    name = "karma_test",
-    srcs = ["karma_test.sh"],
-    data = [":owners-ts-tests"],
+web_test_runner(
+    name = "web_test_runner",
+    srcs = ["web_test_runner.sh"],
+    data = [
+        ":tsconfig",
+        ":owners-ts-tests",
+        "@plugins_npm//:node_modules",
+        "@ui_dev_npm//:node_modules",
+    ],
 )
 
 plugin_eslint()
diff --git a/owners/web/gr-files.ts b/owners/web/gr-files.ts
index d0fe189..dbacad8 100644
--- a/owners/web/gr-files.ts
+++ b/owners/web/gr-files.ts
@@ -16,7 +16,7 @@
  */
 
 import {css, html, LitElement, nothing, PropertyValues, CSSResult} from 'lit';
-import {customElement, property} from 'lit/decorators';
+import {customElement, property} from 'lit/decorators.js';
 import {
   AccountInfo,
   ApprovalInfo,
diff --git a/owners/web/gr-owned-files.ts b/owners/web/gr-owned-files.ts
index 29f8b7a..1e64398 100644
--- a/owners/web/gr-owned-files.ts
+++ b/owners/web/gr-owned-files.ts
@@ -26,7 +26,7 @@
 } from 'lit';
 import {ifDefined} from 'lit/directives/if-defined.js';
 import {OwnersMixin} from './owners-mixin';
-import {customElement, property} from 'lit/decorators';
+import {customElement, property} from 'lit/decorators.js';
 import {
   AccountInfo,
   ChangeInfo,
diff --git a/owners/web/karma_test.sh b/owners/web/karma_test.sh
deleted file mode 100755
index 7cecdae..0000000
--- a/owners/web/karma_test.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/bash
-set -euo pipefail
-./$1 start $2 --single-run \
-  --root 'plugins/owners/web/_bazel_ts_out_tests/' \
-  --test-files '*_test.js' \
-  --browsers ${3:-ChromeHeadless}
diff --git a/owners/web/owners-mixin.ts b/owners/web/owners-mixin.ts
index cd9217a..14f0c47 100644
--- a/owners/web/owners-mixin.ts
+++ b/owners/web/owners-mixin.ts
@@ -17,7 +17,7 @@
 
 import {Subscription} from 'rxjs';
 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 {FilesOwners, OwnersService} from './owners-service';
 import {RestPluginApi} from '@gerritcodereview/typescript-api/rest';
diff --git a/owners/web/owners-service_test.ts b/owners/web/owners-service_test.ts
index fbb9dc7..a58f935 100644
--- a/owners/web/owners-service_test.ts
+++ b/owners/web/owners-service_test.ts
@@ -33,6 +33,7 @@
 import {assert} from '@open-wc/testing';
 import {UserRole} from './owners-model';
 import {deepEqual} from './utils';
+import sinon from 'sinon';
 
 suite('owners service tests', () => {
   const fakeRestApi = {} as unknown as RestPluginApi;
diff --git a/owners/web/plugin.ts b/owners/web/plugin.ts
index 596c8ab..8ea2dc4 100644
--- a/owners/web/plugin.ts
+++ b/owners/web/plugin.ts
@@ -28,7 +28,7 @@
   OwnedFilesTabHeader,
 } from './gr-owned-files';
 
-window.Gerrit.install(plugin => {
+window.Gerrit?.install(plugin => {
   const restApi = plugin.restApi();
 
   plugin
diff --git a/owners/web/tsconfig.json b/owners/web/tsconfig.json
index 0a15ca9..4a70a7d 100644
--- a/owners/web/tsconfig.json
+++ b/owners/web/tsconfig.json
@@ -7,7 +7,7 @@
       "dom.iterable",
       "es2021",
       "webworker"
-    ],
+    ]
   },
   "include": [
     "**/*.ts"
diff --git a/owners/web/web_test_runner.sh b/owners/web/web_test_runner.sh
new file mode 100755
index 0000000..cfb56f6
--- /dev/null
+++ b/owners/web/web_test_runner.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -euo pipefail
+./$1 --config $2 \
+  --dir 'plugins/owners/web/_bazel_ts_out_tests' \
+  --test-files 'plugins/owners/web/_bazel_ts_out_tests/*_test.js' \
+  --ts-config="plugins/owners/web/tsconfig.json"