Merge "Convert gr-label-scores_test to typescript"
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 9c3c8b6..724436f 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -239,6 +239,12 @@
current user.
--
+[[submit-requirements]]
+--
+* `SUBMIT_REQUIREMENTS`: detailed result of the evaluated submit requirements
+ for this change.
+--
+
[[current-revision]]
--
* `CURRENT_REVISION`: describe the current revision (patch set)
@@ -6510,7 +6516,11 @@
entities.
|`requirements` |optional|
List of the link:rest-api-changes.html#requirement[requirements] to be met before this change
-can be submitted.
+can be submitted. This field is deprecated in favour of `submit_requirements`.
+|`submit_requirements` |optional|
+List of the link:#submit-requirement-result-info[SubmitRequirementResultInfo]
+containing the evaluated submit requirements for the change.
+Only set if link:#submit-requirements[`SUBMIT_REQUIREMENTS`] is requested.
|`labels` |optional|
The labels of the change as a map that maps the label names to
link:#label-info[LabelInfo] entries. +
@@ -8137,6 +8147,56 @@
the failure of the rule predicate.
|===========================
+[[submit-requirement-expression-info]]
+=== SubmitRequirementExpressionInfo
+The `SubmitRequirementExpressionInfo` describes the result of evaluating a
+single submit requirement expression, for example `label:code-review=+2`.
+
+[options="header",cols="1,6"]
+|===========================
+|Field Name |Description
+|`expression`|
+The submit requirement expression as a string, for example
+`branch:refs/heads/foo and label:verified=+1`.
+|`fulfilled`|
+True if the submit requirement is fulfilled for the change.
+|`passing_atoms`|
+A list of passing atoms as strings. For the above expression,
+`passing_atoms` can contain ["branch:refs/heads/foo"] if the branch predicate is
+fulfilled for the change.
+|`failing_atoms`|
+A list of failing atoms. This is similar to `passing_atoms` except that it
+contains the list of predicates that are not fulfilled for the change.
+|===========================
+
+[[submit-requirement-result-info]]
+=== SubmitRequirementResultInfo
+The `SubmitRequirementResultInfo` describes the result of evaluating a
+submit requirement on a change.
+
+[options="header",cols="1,^1,5"]
+|===========================
+|Field Name ||Description
+|`name`||
+The submit requirement name.
+|`description`|optional|
+Description of the submit requirement.
+|`status`||
+Status describing the result of evaluating the submit requirement. The status
+is one of (`SATISFIED`, `UNSATISFED`, `OVERRIDDEN`, `NOT_APPLICABLE`).
+|`applicability_expression_result`|optional|
+A link:#submit-requirement-expression-info[SubmitRequirementExpressionInfo]
+containing the result of evaluating the applicability expression. Not set if the
+submit requirement did not define an applicability expression.
+|`submittability_expression_result`||
+A link:#submit-requirement-expression-info[SubmitRequirementExpressionInfo]
+containing the result of evaluating the submittability expression.
+|`override_expression_result`|optional|
+A link:#submit-requirement-expression-info[SubmitRequirementExpressionInfo]
+containing the result of evaluating the override expression. Not set if the
+submit requirement did not define an override expression.
+|===========================
+
[[submitted-together-info]]
=== SubmittedTogetherInfo
The `SubmittedTogetherInfo` entity contains information about a
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index fd78bd8..7ddf2ba 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -71,6 +71,7 @@
import com.google.gerrit.entities.PermissionRule.Action;
import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.entities.SubmitRequirement;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.ReviewInput;
@@ -1595,6 +1596,14 @@
ObjectId.fromString(get(changeId, ListChangesOption.CURRENT_REVISION).currentRevision));
}
+ protected void configSubmitRequirement(
+ Project.NameKey project, SubmitRequirement submitRequirement) throws Exception {
+ try (ProjectConfigUpdate u = updateProject(project)) {
+ u.getConfig().upsertSubmitRequirement(submitRequirement);
+ u.save();
+ }
+ }
+
protected void configLabel(String label, LabelFunction func) throws Exception {
configLabel(label, func, ImmutableList.of());
}
diff --git a/java/com/google/gerrit/entities/SubmitRequirementExpressionResult.java b/java/com/google/gerrit/entities/SubmitRequirementExpressionResult.java
index 94c0e91..f7a883e 100644
--- a/java/com/google/gerrit/entities/SubmitRequirementExpressionResult.java
+++ b/java/com/google/gerrit/entities/SubmitRequirementExpressionResult.java
@@ -98,7 +98,7 @@
public abstract static class PredicateResult {
abstract ImmutableList<PredicateResult> childPredicateResults();
- abstract String predicateString();
+ public abstract String predicateString();
/** true if the predicate is passing for a given change. */
abstract boolean status();
diff --git a/java/com/google/gerrit/extensions/client/ListChangesOption.java b/java/com/google/gerrit/extensions/client/ListChangesOption.java
index 6071cc7..f1f7831 100644
--- a/java/com/google/gerrit/extensions/client/ListChangesOption.java
+++ b/java/com/google/gerrit/extensions/client/ListChangesOption.java
@@ -85,7 +85,10 @@
* Skip diffstat computation that compute the insertions field (number of lines inserted) and
* deletions field (number of lines deleted)
*/
- SKIP_DIFFSTAT(23);
+ SKIP_DIFFSTAT(23),
+
+ /** Include the evaluated submit requirements for the caller. */
+ SUBMIT_REQUIREMENTS(24);
private final int value;
diff --git a/java/com/google/gerrit/extensions/common/ChangeInfo.java b/java/com/google/gerrit/extensions/common/ChangeInfo.java
index 9e915f5..6afe8ac 100644
--- a/java/com/google/gerrit/extensions/common/ChangeInfo.java
+++ b/java/com/google/gerrit/extensions/common/ChangeInfo.java
@@ -112,6 +112,7 @@
public List<PluginDefinedInfo> plugins;
public Collection<TrackingIdInfo> trackingIds;
public Collection<LegacySubmitRequirementInfo> requirements;
+ public Collection<SubmitRequirementResultInfo> submitRequirements;
public ChangeInfo() {}
diff --git a/java/com/google/gerrit/extensions/common/SubmitRequirementExpressionInfo.java b/java/com/google/gerrit/extensions/common/SubmitRequirementExpressionInfo.java
new file mode 100644
index 0000000..4d1fce2
--- /dev/null
+++ b/java/com/google/gerrit/extensions/common/SubmitRequirementExpressionInfo.java
@@ -0,0 +1,39 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.extensions.common;
+
+import java.util.List;
+
+/** Result of evaluating a single submit requirement expression. */
+public class SubmitRequirementExpressionInfo {
+
+ /** Submit requirement expression as a String. */
+ public String expression;
+
+ /** A boolean indicating if the expression is fulfilled on a change. */
+ public boolean fulfilled;
+
+ /**
+ * A list of all atoms that are passing, for example query "branch:refs/heads/foo and project:bar"
+ * has two atoms: ["branch:refs/heads/foo", "project:bar"].
+ */
+ public List<String> passingAtoms;
+
+ /**
+ * A list of all atoms that are failing, for example query "branch:refs/heads/foo and project:bar"
+ * has two atoms: ["branch:refs/heads/foo", "project:bar"].
+ */
+ public List<String> failingAtoms;
+}
diff --git a/java/com/google/gerrit/extensions/common/SubmitRequirementResultInfo.java b/java/com/google/gerrit/extensions/common/SubmitRequirementResultInfo.java
new file mode 100644
index 0000000..685e81a
--- /dev/null
+++ b/java/com/google/gerrit/extensions/common/SubmitRequirementResultInfo.java
@@ -0,0 +1,58 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.extensions.common;
+
+/** Result of evaluating a submit requirement on a change. */
+public class SubmitRequirementResultInfo {
+ public enum Status {
+ /** Submit requirement is fulfilled. */
+ SATISFIED,
+
+ /**
+ * Submit requirement is not satisfied. Happens when {@code submittabilityExpressionResult} is
+ * not fulfilled.
+ */
+ UNSATISFIED,
+
+ /**
+ * Submit requirement is overridden. Happens when {@code overrideExpressionResult} is fulfilled.
+ */
+ OVERRIDDEN,
+
+ /**
+ * Submit requirement is not applicable for the change. Happens when {@code
+ * applicabilityExpressionResult} is not fulfilled.
+ */
+ NOT_APPLICABLE
+ }
+
+ /** Submit requirement name. */
+ public String name;
+
+ /** Submit requirement description. */
+ public String description;
+
+ /** Overall result (status) of evaluating this submit requirement. */
+ public Status status;
+
+ /** Result of evaluating the applicability expression. */
+ public SubmitRequirementExpressionInfo applicabilityExpressionResult;
+
+ /** Result of evaluating the submittability expression. */
+ public SubmitRequirementExpressionInfo submittabilityExpressionResult;
+
+ /** Result of evaluating the override expression. */
+ public SubmitRequirementExpressionInfo overrideExpressionResult;
+}
diff --git a/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index 30de2f5..4db657d 100644
--- a/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -83,6 +83,8 @@
import com.google.gerrit.server.query.approval.ApprovalModule;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeIsVisibleToPredicate;
+import com.google.gerrit.server.query.change.ChangeQueryBuilder;
+import com.google.gerrit.server.query.change.ConflictsCacheImpl;
import com.google.gerrit.server.restapi.group.GroupModule;
import com.google.gerrit.server.rules.DefaultSubmitRule;
import com.google.gerrit.server.rules.IgnoreSelfApprovalRule;
@@ -174,6 +176,7 @@
modules.add(new NoteDbModule());
modules.add(AccountCacheImpl.module());
modules.add(ApprovalCacheImpl.module());
+ modules.add(ConflictsCacheImpl.module());
modules.add(DefaultPreferencesCacheImpl.module());
modules.add(GroupCacheImpl.module());
modules.add(GroupIncludeCacheImpl.module());
@@ -190,6 +193,10 @@
factory(ChangeIsVisibleToPredicate.Factory.class);
factory(ProjectState.Factory.class);
+ DynamicMap.mapOf(binder(), ChangeQueryBuilder.ChangeOperatorFactory.class);
+ DynamicMap.mapOf(binder(), ChangeQueryBuilder.ChangeHasOperandFactory.class);
+ DynamicMap.mapOf(binder(), ChangeQueryBuilder.ChangeIsOperandFactory.class);
+
// Submit rules
DynamicSet.setOf(binder(), SubmitRule.class);
factory(SubmitRuleEvaluator.Factory.class);
diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java
index 029f231..e9c9946 100644
--- a/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/java/com/google/gerrit/server/change/ChangeJson.java
@@ -31,6 +31,7 @@
import static com.google.gerrit.extensions.client.ListChangesOption.REVIEWER_UPDATES;
import static com.google.gerrit.extensions.client.ListChangesOption.SKIP_DIFFSTAT;
import static com.google.gerrit.extensions.client.ListChangesOption.SUBMITTABLE;
+import static com.google.gerrit.extensions.client.ListChangesOption.SUBMIT_REQUIREMENTS;
import static com.google.gerrit.extensions.client.ListChangesOption.TRACKING_IDS;
import static com.google.gerrit.server.ChangeMessagesUtil.createChangeMessageInfo;
import static com.google.gerrit.server.util.AttentionSetUtil.additionsOnly;
@@ -59,6 +60,11 @@
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.entities.SubmitRecord;
import com.google.gerrit.entities.SubmitRecord.Status;
+import com.google.gerrit.entities.SubmitRequirement;
+import com.google.gerrit.entities.SubmitRequirementExpression;
+import com.google.gerrit.entities.SubmitRequirementExpressionResult;
+import com.google.gerrit.entities.SubmitRequirementExpressionResult.PredicateResult;
+import com.google.gerrit.entities.SubmitRequirementResult;
import com.google.gerrit.entities.SubmitTypeRecord;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.FixInput;
@@ -75,6 +81,8 @@
import com.google.gerrit.extensions.common.ProblemInfo;
import com.google.gerrit.extensions.common.ReviewerUpdateInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
+import com.google.gerrit.extensions.common.SubmitRequirementExpressionInfo;
+import com.google.gerrit.extensions.common.SubmitRequirementResultInfo;
import com.google.gerrit.extensions.common.TrackingIdInfo;
import com.google.gerrit.extensions.restapi.Url;
import com.google.gerrit.index.RefState;
@@ -362,11 +370,54 @@
return reqInfos;
}
+ private static Collection<SubmitRequirementResultInfo> submitRequirementsFor(ChangeData cd) {
+ Collection<SubmitRequirementResultInfo> reqInfos = new ArrayList<>();
+ Map<SubmitRequirement, SubmitRequirementResult> requirements = cd.submitRequirements();
+ for (Map.Entry<SubmitRequirement, SubmitRequirementResult> entry : requirements.entrySet()) {
+ reqInfos.add(submitRequirementToInfo(entry.getKey(), entry.getValue()));
+ }
+ return reqInfos;
+ }
+
private static LegacySubmitRequirementInfo requirementToInfo(
LegacySubmitRequirement req, Status status) {
return new LegacySubmitRequirementInfo(status.name(), req.fallbackText(), req.type());
}
+ private static SubmitRequirementResultInfo submitRequirementToInfo(
+ SubmitRequirement req, SubmitRequirementResult result) {
+ SubmitRequirementResultInfo info = new SubmitRequirementResultInfo();
+ info.name = req.name();
+ info.description = req.description().orElse(null);
+ if (req.applicabilityExpression().isPresent()) {
+ info.applicabilityExpressionResult =
+ submitRequirementExpressionToInfo(
+ req.applicabilityExpression().get(), result.applicabilityExpressionResult().get());
+ }
+ if (req.overrideExpression().isPresent()) {
+ info.overrideExpressionResult =
+ submitRequirementExpressionToInfo(
+ req.overrideExpression().get(), result.overrideExpressionResult().get());
+ }
+ info.submittabilityExpressionResult =
+ submitRequirementExpressionToInfo(
+ req.submittabilityExpression(), result.submittabilityExpressionResult());
+ info.status = SubmitRequirementResultInfo.Status.valueOf(result.status().toString());
+ return info;
+ }
+
+ private static SubmitRequirementExpressionInfo submitRequirementExpressionToInfo(
+ SubmitRequirementExpression expression, SubmitRequirementExpressionResult result) {
+ SubmitRequirementExpressionInfo info = new SubmitRequirementExpressionInfo();
+ info.expression = expression.expressionString();
+ info.fulfilled = result.status().equals(SubmitRequirementExpressionResult.Status.PASS);
+ info.passingAtoms =
+ result.getPassingAtoms().stream().map(PredicateResult::predicateString).collect(toList());
+ info.failingAtoms =
+ result.getFailingAtoms().stream().map(PredicateResult::predicateString).collect(toList());
+ return info;
+ }
+
private static void finish(ChangeInfo info) {
info.id =
Joiner.on('~')
@@ -612,6 +663,9 @@
out.labels = labelsJson.labelsFor(accountLoader, cd, has(LABELS), has(DETAILED_LABELS));
out.requirements = requirementsFor(cd);
+ if (has(SUBMIT_REQUIREMENTS)) {
+ out.submitRequirements = submitRequirementsFor(cd);
+ }
if (out.labels != null && has(DETAILED_LABELS)) {
// If limited to specific patch sets but not the current patch set, don't
diff --git a/java/com/google/gerrit/server/project/ProjectConfig.java b/java/com/google/gerrit/server/project/ProjectConfig.java
index 9f898d9..3253282 100644
--- a/java/com/google/gerrit/server/project/ProjectConfig.java
+++ b/java/com/google/gerrit/server/project/ProjectConfig.java
@@ -529,6 +529,10 @@
return submitRequirementSections;
}
+ public void upsertSubmitRequirement(SubmitRequirement requirement) {
+ submitRequirementSections.put(requirement.name(), requirement);
+ }
+
/** Adds or replaces the given {@link LabelType} in this config. */
public void upsertLabelType(LabelType labelType) {
labelSections.put(labelType.getName(), labelType);
diff --git a/java/com/google/gerrit/server/project/ProjectState.java b/java/com/google/gerrit/server/project/ProjectState.java
index 03d38b3..4569027 100644
--- a/java/com/google/gerrit/server/project/ProjectState.java
+++ b/java/com/google/gerrit/server/project/ProjectState.java
@@ -22,6 +22,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.entities.AccessSection;
@@ -37,6 +38,7 @@
import com.google.gerrit.entities.PermissionRule;
import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.StoredCommentLinkInfo;
+import com.google.gerrit.entities.SubmitRequirement;
import com.google.gerrit.entities.SubscribeSection;
import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
import com.google.gerrit.extensions.client.SubmitType;
@@ -392,6 +394,21 @@
return false;
}
+ /** Get all submit requirements for a project, including those from parent projects. */
+ public Map<String, SubmitRequirement> getSubmitRequirements() {
+ Map<String, SubmitRequirement> requirements = new LinkedHashMap<>();
+ for (ProjectState s : treeInOrder()) {
+ for (SubmitRequirement requirement : s.getConfig().getSubmitRequirementSections().values()) {
+ String lowerName = requirement.name().toLowerCase();
+ SubmitRequirement old = requirements.get(lowerName);
+ if (old == null || old.allowOverrideInChildProjects()) {
+ requirements.put(lowerName, requirement);
+ }
+ }
+ }
+ return ImmutableMap.copyOf(requirements);
+ }
+
/** All available label types. */
public LabelTypes getLabelTypes() {
Map<String, LabelType> types = new LinkedHashMap<>();
diff --git a/java/com/google/gerrit/server/query/change/ChangeData.java b/java/com/google/gerrit/server/query/change/ChangeData.java
index 6f8b097..ac28342 100644
--- a/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -53,6 +53,8 @@
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.entities.SubmitRecord;
+import com.google.gerrit.entities.SubmitRequirement;
+import com.google.gerrit.entities.SubmitRequirementResult;
import com.google.gerrit.entities.SubmitTypeRecord;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -86,6 +88,7 @@
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.project.SubmitRequirementsEvaluator;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
import com.google.gerrit.server.project.SubmitRuleOptions;
import com.google.gerrit.server.util.time.TimeUtil;
@@ -265,7 +268,7 @@
ChangeData cd =
new ChangeData(
null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, project, id, null, null);
+ null, null, project, id, null, null);
cd.currentPatchSet =
PatchSet.builder()
.id(PatchSet.id(id, currentPatchSetId))
@@ -291,6 +294,7 @@
private final ProjectCache projectCache;
private final TrackingFooters trackingFooters;
private final PureRevert pureRevert;
+ private final SubmitRequirementsEvaluator submitRequirementsEvaluator;
private final SubmitRuleEvaluator.Factory submitRuleEvaluatorFactory;
// Required assisted injected fields.
@@ -302,6 +306,8 @@
private final Map<SubmitRuleOptions, List<SubmitRecord>> submitRecords =
Maps.newLinkedHashMapWithExpectedSize(1);
+ private Map<SubmitRequirement, SubmitRequirementResult> submitRequirements;
+
private StorageConstraint storageConstraint = StorageConstraint.NOTEDB_ONLY;
private Change change;
private ChangeNotes notes;
@@ -365,6 +371,7 @@
ProjectCache projectCache,
TrackingFooters trackingFooters,
PureRevert pureRevert,
+ SubmitRequirementsEvaluator submitRequirementsEvaluator,
SubmitRuleEvaluator.Factory submitRuleEvaluatorFactory,
@Assisted Project.NameKey project,
@Assisted Change.Id id,
@@ -384,6 +391,7 @@
this.starredChangesUtil = starredChangesUtil;
this.trackingFooters = trackingFooters;
this.pureRevert = pureRevert;
+ this.submitRequirementsEvaluator = submitRequirementsEvaluator;
this.submitRuleEvaluatorFactory = submitRuleEvaluatorFactory;
this.project = project;
@@ -920,6 +928,21 @@
return messages;
}
+ /** Get all submit requirements for this change, including those from parent projects. */
+ public Map<SubmitRequirement, SubmitRequirementResult> submitRequirements() {
+ if (submitRequirements == null) {
+ ProjectState state = projectCache.get(project()).orElseThrow(illegalState(project()));
+ Map<String, SubmitRequirement> requirements = state.getSubmitRequirements();
+ ImmutableMap.Builder<SubmitRequirement, SubmitRequirementResult> result =
+ ImmutableMap.builderWithExpectedSize(requirements.size());
+ for (SubmitRequirement requirement : requirements.values()) {
+ result.put(requirement, submitRequirementsEvaluator.evaluate(requirement, this));
+ }
+ submitRequirements = result.build();
+ }
+ return submitRequirements;
+ }
+
public List<SubmitRecord> submitRecords(SubmitRuleOptions options) {
// If the change is not submitted yet, 'strict' and 'lenient' both have the same result. If the
// change is submitted, SubmitRecord requested with 'strict' will contain just a single entry
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 529ce73..22a727a 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -100,6 +100,8 @@
import com.google.gerrit.entities.Permission;
import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.entities.SubmitRequirement;
+import com.google.gerrit.entities.SubmitRequirementExpression;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.annotations.Exports;
import com.google.gerrit.extensions.api.accounts.DeleteDraftCommentsInput;
@@ -142,6 +144,8 @@
import com.google.gerrit.extensions.common.GitPerson;
import com.google.gerrit.extensions.common.LabelInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
+import com.google.gerrit.extensions.common.SubmitRequirementResultInfo;
+import com.google.gerrit.extensions.common.SubmitRequirementResultInfo.Status;
import com.google.gerrit.extensions.common.TrackingIdInfo;
import com.google.gerrit.extensions.events.WorkInProgressStateChangedListener;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -3990,6 +3994,192 @@
}
@Test
+ public void submitRequirementIsSatisfied_whenSubmittabilityExpressionIsFulfilled()
+ throws Exception {
+ configSubmitRequirement(
+ project,
+ SubmitRequirement.builder()
+ .setName("code-review")
+ .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+2"))
+ .setAllowOverrideInChildProjects(false)
+ .build());
+ configSubmitRequirement(
+ project,
+ SubmitRequirement.builder()
+ .setName("verified")
+ .setSubmittabilityExpression(SubmitRequirementExpression.create("label:verified=+1"))
+ .setAllowOverrideInChildProjects(false)
+ .build());
+
+ PushOneCommit.Result r = createChange();
+ String changeId = r.getChangeId();
+
+ ChangeInfo change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(2);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.UNSATISFIED);
+ assertSubmitRequirementStatus(change.submitRequirements, "verified", Status.UNSATISFIED);
+
+ voteLabel(changeId, "code-review", 2);
+
+ change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(2);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.SATISFIED);
+ assertSubmitRequirementStatus(change.submitRequirements, "verified", Status.UNSATISFIED);
+ }
+
+ @Test
+ public void submitRequirementIsNotApplicable_whenApplicabilityExpressionIsNotFulfilled()
+ throws Exception {
+ configSubmitRequirement(
+ project,
+ SubmitRequirement.builder()
+ .setName("code-review")
+ .setApplicabilityExpression(SubmitRequirementExpression.of("project:foo"))
+ .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+2"))
+ .setAllowOverrideInChildProjects(false)
+ .build());
+
+ PushOneCommit.Result r = createChange();
+ String changeId = r.getChangeId();
+
+ ChangeInfo change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(1);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.NOT_APPLICABLE);
+ }
+
+ @Test
+ public void submitRequirementIsOverridden_whenOverrideExpressionIsFulfilled() throws Exception {
+ configLabel("build-cop-override", LabelFunction.MAX_WITH_BLOCK);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(
+ allowLabel("build-cop-override")
+ .ref("refs/heads/master")
+ .group(REGISTERED_USERS)
+ .range(-1, 1))
+ .update();
+
+ configSubmitRequirement(
+ project,
+ SubmitRequirement.builder()
+ .setName("code-review")
+ .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+2"))
+ .setOverrideExpression(SubmitRequirementExpression.of("label:build-cop-override=+1"))
+ .setAllowOverrideInChildProjects(false)
+ .build());
+
+ PushOneCommit.Result r = createChange();
+ String changeId = r.getChangeId();
+ ChangeInfo change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(1);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.UNSATISFIED);
+
+ voteLabel(changeId, "build-cop-override", 1);
+
+ change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(1);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.OVERRIDDEN);
+ }
+
+ @Test
+ public void submitRequirement_overriddenInChildProject() throws Exception {
+ configSubmitRequirement(
+ allProjects,
+ SubmitRequirement.builder()
+ .setName("code-review")
+ .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+1"))
+ .setOverrideExpression(SubmitRequirementExpression.of("label:build-cop-override=+1"))
+ .setAllowOverrideInChildProjects(true)
+ .build());
+
+ // Override submit requirement in child project (requires code-review=+2 instead of +1)
+ configSubmitRequirement(
+ project,
+ SubmitRequirement.builder()
+ .setName("code-review")
+ .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+2"))
+ .setOverrideExpression(SubmitRequirementExpression.of("label:build-cop-override=+1"))
+ .setAllowOverrideInChildProjects(false)
+ .build());
+
+ PushOneCommit.Result r = createChange();
+ String changeId = r.getChangeId();
+ ChangeInfo change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(1);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.UNSATISFIED);
+
+ voteLabel(changeId, "code-review", 1);
+ change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(1);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.UNSATISFIED);
+
+ voteLabel(changeId, "code-review", 2);
+ change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(1);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.SATISFIED);
+ }
+
+ @Test
+ public void submitRequirement_inheritedFromParentProject() throws Exception {
+ configSubmitRequirement(
+ allProjects,
+ SubmitRequirement.builder()
+ .setName("code-review")
+ .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+1"))
+ .setOverrideExpression(SubmitRequirementExpression.of("label:build-cop-override=+1"))
+ .setAllowOverrideInChildProjects(false)
+ .build());
+
+ PushOneCommit.Result r = createChange();
+ String changeId = r.getChangeId();
+ ChangeInfo change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(1);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.UNSATISFIED);
+
+ voteLabel(changeId, "code-review", 1);
+ change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(1);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.SATISFIED);
+ }
+
+ @Test
+ public void submitRequirement_ignoredInChildProject_ifParentDoesNotAllowOverride()
+ throws Exception {
+ configSubmitRequirement(
+ allProjects,
+ SubmitRequirement.builder()
+ .setName("code-review")
+ .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+1"))
+ .setOverrideExpression(SubmitRequirementExpression.of("label:build-cop-override=+1"))
+ .setAllowOverrideInChildProjects(false)
+ .build());
+
+ // Override submit requirement in child project (requires code-review=+2 instead of +1).
+ // Will have no effect since parent does not allow override.
+ configSubmitRequirement(
+ project,
+ SubmitRequirement.builder()
+ .setName("code-review")
+ .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+2"))
+ .setOverrideExpression(SubmitRequirementExpression.of("label:build-cop-override=+1"))
+ .setAllowOverrideInChildProjects(false)
+ .build());
+
+ PushOneCommit.Result r = createChange();
+ String changeId = r.getChangeId();
+ ChangeInfo change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(1);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.UNSATISFIED);
+
+ voteLabel(changeId, "code-review", 1);
+ change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(1);
+ // +1 was enough to fulfill the requirement: override in child project was ignored
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.SATISFIED);
+ }
+
+ @Test
public void fourByteEmoji() throws Exception {
// U+1F601 GRINNING FACE WITH SMILING EYES
String smile = new String(Character.toChars(0x1f601));
@@ -4630,4 +4820,21 @@
event.getChange().workInProgress != null ? event.getChange().workInProgress : false;
}
}
+
+ private void voteLabel(String changeId, String labelName, int score) throws RestApiException {
+ gApi.changes().id(changeId).current().review(new ReviewInput().label(labelName, score));
+ }
+
+ private void assertSubmitRequirementStatus(
+ Collection<SubmitRequirementResultInfo> results,
+ String requirementName,
+ SubmitRequirementResultInfo.Status status) {
+ for (SubmitRequirementResultInfo result : results) {
+ if (result.name.equals(requirementName) && result.status == status) {
+ return;
+ }
+ }
+ throw new AssertionError(
+ "Could not find submit requirement " + requirementName + " with status " + status);
+ }
}
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_test.ts b/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_test.ts
index 42aed07..195ccb7 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_test.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_test.ts
@@ -15,9 +15,9 @@
* limitations under the License.
*/
-import '../../../test/common-test-setup-karma.js';
-import './gr-create-commands-dialog.js';
-import {GrCreateCommandsDialog} from './gr-create-commands-dialog.js';
+import '../../../test/common-test-setup-karma';
+import './gr-create-commands-dialog';
+import {GrCreateCommandsDialog} from './gr-create-commands-dialog';
const basicFixture = fixtureFromElement('gr-create-commands-dialog');
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
index 58cf489..8b37cf0 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
@@ -171,7 +171,7 @@
import {takeUntil} from 'rxjs/operators';
import {aPluginHasRegistered$} from '../../../services/checks/checks-model';
import {Subject} from 'rxjs';
-import {debounce, DelayedTask} from '../../../utils/async-util';
+import {debounce, DelayedTask, throttleWrap} from '../../../utils/async-util';
import {Interaction, Timing} from '../../../constants/reporting';
import {ChangeStates} from '../../shared/gr-change-status/gr-change-status';
import {getRevertCreatedChangeIds} from '../../../utils/message-util';
@@ -603,7 +603,7 @@
/** @override */
connectedCallback() {
super.connectedCallback();
- this._throttledToggleChangeStar = this._throttleWrap(e =>
+ this._throttledToggleChangeStar = throttleWrap(e =>
this._handleToggleChangeStar(e as CustomKeyboardEvent)
);
this._getServerConfig().then(config => {
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.ts b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.ts
index 8b9b80c..be3a0be 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.ts
@@ -167,6 +167,30 @@
<span class="separator"></span>
</span>
</template>
+ <div class="fileViewActions">
+ <span class="fileViewActionsLabel">Diff view:</span>
+ <gr-diff-mode-selector
+ id="modeSelect"
+ mode="{{diffViewMode}}"
+ save-on-change="[[!diffPrefsDisabled]]"
+ ></gr-diff-mode-selector>
+ <span
+ id="diffPrefsContainer"
+ class="hideOnEdit"
+ hidden$="[[_computePrefsButtonHidden(diffPrefs, diffPrefsDisabled)]]"
+ hidden=""
+ >
+ <gr-button
+ link=""
+ has-tooltip=""
+ title="Diff preferences"
+ class="prefsButton desktop"
+ on-click="_handlePrefsTap"
+ ><iron-icon icon="gr-icons:settings"></iron-icon
+ ></gr-button>
+ </span>
+ </div>
+ <span class="separator"></span>
<span class="downloadContainer desktop">
<gr-button
link=""
@@ -209,30 +233,6 @@
Bulk actions disabled because there are too many files.
</div>
</template>
- <div class="fileViewActions">
- <span class="separator"></span>
- <span class="fileViewActionsLabel">Diff view:</span>
- <gr-diff-mode-selector
- id="modeSelect"
- mode="{{diffViewMode}}"
- save-on-change="[[!diffPrefsDisabled]]"
- ></gr-diff-mode-selector>
- <span
- id="diffPrefsContainer"
- class="hideOnEdit"
- hidden$="[[_computePrefsButtonHidden(diffPrefs, diffPrefsDisabled)]]"
- hidden=""
- >
- <gr-button
- link=""
- has-tooltip=""
- title="Diff preferences"
- class="prefsButton desktop"
- on-click="_handlePrefsTap"
- ><iron-icon icon="gr-icons:settings"></iron-icon
- ></gr-button>
- </span>
- </div>
</div>
</div>
`;
diff --git a/polygerrit-ui/app/elements/checks/gr-hovercard-run_test.ts b/polygerrit-ui/app/elements/checks/gr-hovercard-run_test.ts
index 80d8e5e..67781f5 100644
--- a/polygerrit-ui/app/elements/checks/gr-hovercard-run_test.ts
+++ b/polygerrit-ui/app/elements/checks/gr-hovercard-run_test.ts
@@ -15,10 +15,10 @@
* limitations under the License.
*/
-import '../../test/common-test-setup-karma.js';
-import './gr-hovercard-run.js';
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
-import {GrHovercardRun} from './gr-hovercard-run.js';
+import '../../test/common-test-setup-karma';
+import './gr-hovercard-run';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
+import {GrHovercardRun} from './gr-hovercard-run';
const basicFixture = fixtureFromTemplate(html`
<gr-hovercard-run class="hovered"></gr-hovercard-run>
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.ts b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.ts
index 8f9dfe2..8352319 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.ts
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.ts
@@ -75,6 +75,37 @@
};
}
+export function constructServerErrorMsg({
+ errorText,
+ status,
+ statusText,
+ url,
+ trace,
+ tip,
+}: ErrorMsg) {
+ let err = '';
+ if (tip) {
+ err += `${tip}\n\n`;
+ }
+ err += `Error ${status}`;
+ if (statusText) {
+ err += ` (${statusText})`;
+ }
+ if (errorText || url) {
+ err += ': ';
+ }
+ if (errorText) {
+ err += errorText;
+ }
+ if (url) {
+ err += `\nEndpoint: ${url}`;
+ }
+ if (trace) {
+ err += `\nTrace Id: ${trace}`;
+ }
+ return err;
+}
+
@customElement('gr-error-manager')
export class GrErrorManager extends PolymerElement {
static get template() {
@@ -221,7 +252,7 @@
this._showQuotaExceeded({status, statusText});
} else {
this._showErrorDialog(
- this._constructServerErrorMsg({
+ constructServerErrorMsg({
status,
statusText,
errorText,
@@ -247,7 +278,7 @@
? 'You might have not enough privileges.'
: 'You might have not enough privileges. Sign in and try again.';
this._showErrorDialog(
- this._constructServerErrorMsg({
+ constructServerErrorMsg({
status,
statusText,
errorText,
@@ -266,7 +297,7 @@
const tip = 'Try again later';
const errorText = 'Too many requests from this client';
this._showErrorDialog(
- this._constructServerErrorMsg({
+ constructServerErrorMsg({
status,
statusText,
errorText,
@@ -275,37 +306,6 @@
);
}
- _constructServerErrorMsg({
- errorText,
- status,
- statusText,
- url,
- trace,
- tip,
- }: ErrorMsg) {
- let err = '';
- if (tip) {
- err += `${tip}\n\n`;
- }
- err += `Error ${status}`;
- if (statusText) {
- err += ` (${statusText})`;
- }
- if (errorText || url) {
- err += ': ';
- }
- if (errorText) {
- err += errorText;
- }
- if (url) {
- err += `\nEndpoint: ${url}`;
- }
- if (trace) {
- err += `\nTrace Id: ${trace}`;
- }
- return err;
- }
-
private readonly handleShowAlert = (e: CustomEvent<ShowAlertEventDetail>) => {
this._showAlert(
e.detail.message,
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.js b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.js
index fe4d9da..4cf15b4 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.js
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.js
@@ -18,7 +18,7 @@
import '../../../test/common-test-setup-karma.js';
import './gr-error-manager.js';
import {_testOnly_initGerritPluginApi} from '../../shared/gr-js-api-interface/gr-gerrit.js';
-import {__testOnly_ErrorType} from './gr-error-manager.js';
+import {constructServerErrorMsg, __testOnly_ErrorType} from './gr-error-manager.js';
import {stubRestApi} from '../../../test/test-utils.js';
import {appContext} from '../../../services/app-context.js';
import {createPreferences} from '../../../test/test-data-generators.js';
@@ -132,27 +132,26 @@
});
});
- test('_constructServerErrorMsg', () => {
+ test('constructServerErrorMsg', () => {
const errorText = 'change conflicts';
const status = 409;
const statusText = 'Conflict';
const url = '/my/test/url';
- assert.equal(element._constructServerErrorMsg({status}),
+ assert.equal(constructServerErrorMsg({status}),
'Error 409');
- assert.equal(element._constructServerErrorMsg({status, url}),
+ assert.equal(constructServerErrorMsg({status, url}),
'Error 409: \nEndpoint: /my/test/url');
- assert.equal(element.
- _constructServerErrorMsg({status, statusText, url}),
- 'Error 409 (Conflict): \nEndpoint: /my/test/url');
- assert.equal(element._constructServerErrorMsg({
+ assert.equal(constructServerErrorMsg({status, statusText, url}),
+ 'Error 409 (Conflict): \nEndpoint: /my/test/url');
+ assert.equal(constructServerErrorMsg({
status,
statusText,
errorText,
url,
}), 'Error 409 (Conflict): change conflicts' +
'\nEndpoint: /my/test/url');
- assert.equal(element._constructServerErrorMsg({
+ assert.equal(constructServerErrorMsg({
status,
statusText,
errorText,
diff --git a/polygerrit-ui/app/elements/diff/gr-context-controls/gr-context-controls.ts b/polygerrit-ui/app/elements/diff/gr-context-controls/gr-context-controls.ts
index c502346..86a60ce 100644
--- a/polygerrit-ui/app/elements/diff/gr-context-controls/gr-context-controls.ts
+++ b/polygerrit-ui/app/elements/diff/gr-context-controls/gr-context-controls.ts
@@ -22,7 +22,7 @@
import '@polymer/paper-icon-button/paper-icon-button';
import '@polymer/paper-item/paper-item';
import '@polymer/paper-listbox/paper-listbox';
-import '@polymer/paper-tooltip/paper-tooltip.js';
+import '@polymer/paper-tooltip/paper-tooltip';
import {of, EMPTY, Subject} from 'rxjs';
import {switchMap, delay, takeUntil} from 'rxjs/operators';
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
index dacd8e7..9004517 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
@@ -106,6 +106,7 @@
import {assertIsDefined} from '../../../utils/common-util';
import {toggleClass, getKeyboardEvent} from '../../../utils/dom-util';
import {CursorMoveResult} from '../../../api/core';
+import {throttleWrap} from '../../../utils/async-util';
const ERR_REVIEW_STATUS = 'Couldn’t change file review status.';
const MSG_LOADING_BLAME = 'Loading blame...';
@@ -342,7 +343,7 @@
/** @override */
connectedCallback() {
super.connectedCallback();
- this._throttledToggleFileReviewed = this._throttleWrap(e =>
+ this._throttledToggleFileReviewed = throttleWrap(e =>
this._handleToggleFileReviewed(e as CustomKeyboardEvent)
);
this._getLoggedIn().then(loggedIn => {
diff --git a/polygerrit-ui/app/elements/gr-app-element.ts b/polygerrit-ui/app/elements/gr-app-element.ts
index be9c7c5..c9f0506 100644
--- a/polygerrit-ui/app/elements/gr-app-element.ts
+++ b/polygerrit-ui/app/elements/gr-app-element.ts
@@ -55,7 +55,10 @@
ElementPropertyDeepChange,
ServerInfo,
} from '../types/common';
-import {GrErrorManager} from './core/gr-error-manager/gr-error-manager';
+import {
+ constructServerErrorMsg,
+ GrErrorManager,
+} from './core/gr-error-manager/gr-error-manager';
import {GrOverlay} from './shared/gr-overlay/gr-overlay';
import {GrRegistrationDialog} from './settings/gr-registration-dialog/gr-registration-dialog';
import {
@@ -565,7 +568,15 @@
err.emoji = 'o_O';
if (response) {
response.text().then(text => {
- err.moreInfo = text;
+ const trace =
+ response.headers && response.headers.get('X-Gerrit-Trace');
+ const {status, statusText} = response;
+ err.moreInfo = constructServerErrorMsg({
+ status,
+ statusText,
+ errorText: text,
+ trace,
+ });
this._lastError = err;
});
}
diff --git a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_test.ts b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_test.ts
index cf0c34a..3478a9a 100644
--- a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_test.ts
@@ -15,9 +15,9 @@
* limitations under the License.
*/
-import '../../../test/common-test-setup-karma.js';
-import './gr-alert.js';
-import {GrAlert} from './gr-alert.js';
+import '../../../test/common-test-setup-karma';
+import './gr-alert';
+import {GrAlert} from './gr-alert';
import * as MockInteractions from '@polymer/iron-test-helpers/mock-interactions';
suite('gr-alert tests', () => {
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.ts b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.ts
index 2eeb12d..d068cea 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.ts
@@ -44,7 +44,7 @@
tap,
pressAndReleaseKeyOn,
} from '@polymer/iron-test-helpers/mock-interactions';
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
import {stubRestApi, stubStorage} from '../../../test/test-utils';
const basicFixture = fixtureFromElement('gr-comment-thread');
diff --git a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_test.ts b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_test.ts
index 1a7d2cd..a17b171 100644
--- a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_test.ts
@@ -15,9 +15,9 @@
* limitations under the License.
*/
-import '../../../test/common-test-setup-karma.js';
-import './gr-shell-command.js';
-import {GrShellCommand} from './gr-shell-command.js';
+import '../../../test/common-test-setup-karma';
+import './gr-shell-command';
+import {GrShellCommand} from './gr-shell-command';
const basicFixture = fixtureFromElement('gr-shell-command');
diff --git a/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.ts b/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.ts
index 555a256..3d5a208 100644
--- a/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.ts
+++ b/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.ts
@@ -120,8 +120,6 @@
const V_KEY_TIMEOUT_MS = 1000;
-const THROTTLE_INTERVAL_MS = 500;
-
/**
* Enum for all shortcut sections, where that shortcut should be applied to.
*/
@@ -675,8 +673,6 @@
if (!bindings) {
return null;
}
- // TODO(TS): should check base on length to differentiate two
- // cases
if (bindings[0] === SPECIAL_SHORTCUT.GO_KEY) {
return bindings
.slice(1)
@@ -851,13 +847,6 @@
return getKeyboardEvent(e);
}
- // TODO(TS): maybe remove, no reference in the code base
- getRootTarget(e: CustomKeyboardEvent) {
- // TODO(TS): worth checking if we can limit this to EventApi only
- // dom currently returns DomNativeApi|EventApi
- return (dom(getKeyboardEvent(e)) as EventApi).rootTarget;
- }
-
bindShortcut(shortcut: Shortcut, ...bindings: string[]) {
shortcutManager.bindShortcut(shortcut, ...bindings);
}
@@ -868,20 +857,6 @@
return desc && shortcut ? `${desc} (shortcut: ${shortcut})` : '';
}
- _throttleWrap(fn: (e: Event) => void) {
- let lastCall: number | undefined;
- return (e: Event) => {
- if (
- lastCall !== undefined &&
- Date.now() - lastCall < THROTTLE_INTERVAL_MS
- ) {
- return;
- }
- lastCall = Date.now();
- fn(e);
- };
- }
-
_addOwnKeyBindings(shortcut: Shortcut, handler: string) {
const bindings = shortcutManager.getBindingsForShortcut(shortcut);
if (!bindings) {
@@ -1128,8 +1103,6 @@
modifierPressed(event: CustomKeyboardEvent): boolean;
addKeyboardShortcutDirectoryListener(listener: ShortcutListener): void;
removeKeyboardShortcutDirectoryListener(listener: ShortcutListener): void;
- // TODO(TS): Remove underscore. Apparently not a private method.
- _throttleWrap(eventListener: EventListener): EventListener;
}
export function _testOnly_getShortcutManagerInstance() {
diff --git a/polygerrit-ui/app/utils/account-util_test.js b/polygerrit-ui/app/utils/account-util_test.ts
similarity index 75%
rename from polygerrit-ui/app/utils/account-util_test.js
rename to polygerrit-ui/app/utils/account-util_test.ts
index 0628f2d..835cd6d 100644
--- a/polygerrit-ui/app/utils/account-util_test.js
+++ b/polygerrit-ui/app/utils/account-util_test.ts
@@ -15,13 +15,12 @@
* limitations under the License.
*/
-import '../test/common-test-setup-karma.js';
-import {isServiceUser, removeServiceUsers} from './account-util.js';
-import {AccountTag} from '../constants/constants.js';
+import '../test/common-test-setup-karma';
+import {isServiceUser, removeServiceUsers} from './account-util';
+import {AccountTag} from '../constants/constants';
const EMPTY = {};
const ERNIE = {name: 'Ernie'};
-const KERMIT = {name: 'Kermit', tags: ['FROG']};
const SERVY = {name: 'Servy', tags: [AccountTag.SERVICE_USER]};
const BOTTY = {name: 'Botty', tags: [AccountTag.SERVICE_USER]};
@@ -30,17 +29,17 @@
assert.isFalse(isServiceUser());
assert.isFalse(isServiceUser(EMPTY));
assert.isFalse(isServiceUser(ERNIE));
- assert.isFalse(isServiceUser(KERMIT));
assert.isTrue(isServiceUser(SERVY));
assert.isTrue(isServiceUser(BOTTY));
});
test('removeServiceUsers', () => {
assert.sameMembers(removeServiceUsers([]), []);
- assert.sameMembers(removeServiceUsers([EMPTY, ERNIE, KERMIT]),
- [EMPTY, ERNIE, KERMIT]);
+ assert.sameMembers(removeServiceUsers([EMPTY, ERNIE]), [EMPTY, ERNIE]);
assert.sameMembers(removeServiceUsers([SERVY, BOTTY]), []);
- assert.sameMembers(removeServiceUsers([EMPTY, SERVY, ERNIE, BOTTY, KERMIT]),
- [EMPTY, ERNIE, KERMIT]);
+ assert.sameMembers(removeServiceUsers([EMPTY, SERVY, ERNIE, BOTTY]), [
+ EMPTY,
+ ERNIE,
+ ]);
});
});
diff --git a/polygerrit-ui/app/utils/async-util.ts b/polygerrit-ui/app/utils/async-util.ts
index 2b36fee..c82f5e4 100644
--- a/polygerrit-ui/app/utils/async-util.ts
+++ b/polygerrit-ui/app/utils/async-util.ts
@@ -110,3 +110,23 @@
existingTask?.cancel();
return new DelayedTask(callback, waitMs);
}
+
+const THROTTLE_INTERVAL_MS = 500;
+
+/**
+ * Ensure only one call is made within THROTTLE_INTERVAL_MS and any call within
+ * this interval is ignored
+ */
+export function throttleWrap(fn: (e: Event) => void) {
+ let lastCall: number | undefined;
+ return (e: Event) => {
+ if (
+ lastCall !== undefined &&
+ Date.now() - lastCall < THROTTLE_INTERVAL_MS
+ ) {
+ return;
+ }
+ lastCall = Date.now();
+ fn(e);
+ };
+}
diff --git a/polygerrit-ui/app/utils/safe-types-util_test.js b/polygerrit-ui/app/utils/safe-types-util_test.ts
similarity index 83%
rename from polygerrit-ui/app/utils/safe-types-util_test.js
rename to polygerrit-ui/app/utils/safe-types-util_test.ts
index e3968d0..03253e0 100644
--- a/polygerrit-ui/app/utils/safe-types-util_test.js
+++ b/polygerrit-ui/app/utils/safe-types-util_test.ts
@@ -15,12 +15,12 @@
* limitations under the License.
*/
-import '../test/common-test-setup-karma.js';
-import {safeTypesBridge, _testOnly_SafeUrl} from './safe-types-util.js';
+import '../test/common-test-setup-karma';
+import {safeTypesBridge, _testOnly_SafeUrl} from './safe-types-util';
suite('safe-types-util tests', () => {
test('SafeUrl accepts valid urls', () => {
- function accepts(url) {
+ function accepts(url: string) {
const safeUrl = new _testOnly_SafeUrl(url);
assert.isOk(safeUrl);
assert.equal(url, safeUrl.toString());
@@ -35,8 +35,10 @@
});
test('SafeUrl rejects invalid urls', () => {
- function rejects(url) {
- assert.throws(() => { new _testOnly_SafeUrl(url); });
+ function rejects(url: string) {
+ assert.throws(() => {
+ new _testOnly_SafeUrl(url);
+ });
}
rejects('javascript://alert("evil");');
rejects('ftp:example.com');
@@ -44,13 +46,14 @@
});
suite('safeTypesBridge', () => {
- function acceptsString(value, type) {
- assert.equal(safeTypesBridge(value, type),
- value);
+ function acceptsString(value: string, type: string) {
+ assert.equal(safeTypesBridge(value, type), value);
}
- function rejects(value, type) {
- assert.throws(() => { safeTypesBridge(value, type); });
+ function rejects(value: unknown, type: string) {
+ assert.throws(() => {
+ safeTypesBridge(value, type);
+ });
}
test('accepts valid URL strings', () => {