Improve error message for grayed out submit buttons, 2nd edition
When the submit button is grayed out because the changes would not merge cleanly, let the user know in the tool tip. To do this properly we need to first check for the current change and then for all other changes in the same topic. There is a fine distinction between BLOCKED_TOPIC_TOOLTIP ("Other changes in this topic are not ready") and CLICK_FAILURE_OTHER_TOOLTIP ("Clicking the button would fail for other changes in the topic."). Former indicates that a label is missing whereas the latter indicates a dry run of a submission has failed (all labels ok, but the submission is likely to fail) This subtle behavioral difference is of course tested in ActionsIT, where you'll have two approved changes, which are colliding with upstream though. Change-Id: I7a8389e98d9e20794b06245dab007d93fed7ccf3
This commit is contained in:
@@ -18,9 +18,13 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||
import com.google.gerrit.acceptance.PushOneCommit;
|
||||
import com.google.gerrit.extensions.api.changes.ReviewInput;
|
||||
import com.google.gerrit.extensions.client.SubmitType;
|
||||
import com.google.gerrit.extensions.common.ActionInfo;
|
||||
import com.google.gerrit.testutil.ConfigSuite;
|
||||
|
||||
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
|
||||
import org.eclipse.jgit.junit.TestRepository;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -34,7 +38,7 @@ public class ActionsIT extends AbstractDaemonTest {
|
||||
|
||||
@Test
|
||||
public void revisionActionsOneChangePerTopicUnapproved() throws Exception {
|
||||
String changeId = createChangeWithTopic(name("foo1")).getChangeId();
|
||||
String changeId = createChangeWithTopic().getChangeId();
|
||||
Map<String, ActionInfo> actions = getActions(changeId);
|
||||
assertThat(actions).containsKey("cherrypick");
|
||||
assertThat(actions).containsKey("rebase");
|
||||
@@ -43,7 +47,7 @@ public class ActionsIT extends AbstractDaemonTest {
|
||||
|
||||
@Test
|
||||
public void revisionActionsOneChangePerTopic() throws Exception {
|
||||
String changeId = createChangeWithTopic(name("foo1")).getChangeId();
|
||||
String changeId = createChangeWithTopic().getChangeId();
|
||||
approve(changeId);
|
||||
Map<String, ActionInfo> actions = getActions(changeId);
|
||||
commonActionsAssertions(actions);
|
||||
@@ -54,10 +58,10 @@ public class ActionsIT extends AbstractDaemonTest {
|
||||
|
||||
@Test
|
||||
public void revisionActionsTwoChangeChangesInTopic() throws Exception {
|
||||
String changeId = createChangeWithTopic(name("foo2")).getChangeId();
|
||||
String changeId = createChangeWithTopic().getChangeId();
|
||||
approve(changeId);
|
||||
// create another change with the same topic
|
||||
createChangeWithTopic(name("foo2")).getChangeId();
|
||||
createChangeWithTopic().getChangeId();
|
||||
Map<String, ActionInfo> actions = getActions(changeId);
|
||||
commonActionsAssertions(actions);
|
||||
if (isSubmitWholeTopicEnabled()) {
|
||||
@@ -71,12 +75,43 @@ public class ActionsIT extends AbstractDaemonTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void revisionActionsTwoChangeChangesInTopic_conflicting() throws Exception {
|
||||
String changeId = createChangeWithTopic().getChangeId();
|
||||
approve(changeId);
|
||||
|
||||
// create another change with the same topic
|
||||
String changeId2 = createChangeWithTopic(testRepo, "foo2", "touching b",
|
||||
"b.txt", "real content").getChangeId();
|
||||
approve(changeId2);
|
||||
|
||||
// collide with the other change in the same topic
|
||||
testRepo.reset("HEAD~2");
|
||||
String collidingChange = createChangeWithTopic(testRepo, "off_topic",
|
||||
"rewriting file b", "b.txt", "garbage\ngarbage\ngarbage").getChangeId();
|
||||
gApi.changes().id(collidingChange).current().review(ReviewInput.approve());
|
||||
gApi.changes().id(collidingChange).current().submit();
|
||||
|
||||
Map<String, ActionInfo> actions = getActions(changeId);
|
||||
commonActionsAssertions(actions);
|
||||
if (isSubmitWholeTopicEnabled()) {
|
||||
ActionInfo info = actions.get("submit");
|
||||
assertThat(info.enabled).isNull();
|
||||
assertThat(info.label).isEqualTo("Submit whole topic");
|
||||
assertThat(info.method).isEqualTo("POST");
|
||||
assertThat(info.title).isEqualTo(
|
||||
"Clicking the button would fail for other changes in the topic.");
|
||||
} else {
|
||||
noSubmitWholeTopicAssertions(actions);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void revisionActionsTwoChangeChangesInTopicReady() throws Exception {
|
||||
String changeId = createChangeWithTopic(name("foo2")).getChangeId();
|
||||
String changeId = createChangeWithTopic().getChangeId();
|
||||
approve(changeId);
|
||||
// create another change with the same topic
|
||||
String changeId2 = createChangeWithTopic(name("foo2")).getChangeId();
|
||||
String changeId2 = createChangeWithTopic().getChangeId();
|
||||
approve(changeId2);
|
||||
Map<String, ActionInfo> actions = getActions(changeId);
|
||||
commonActionsAssertions(actions);
|
||||
@@ -106,10 +141,18 @@ public class ActionsIT extends AbstractDaemonTest {
|
||||
assertThat(actions).containsKey("rebase");
|
||||
}
|
||||
|
||||
private PushOneCommit.Result createChangeWithTopic(String topic)
|
||||
throws Exception {
|
||||
PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
|
||||
private PushOneCommit.Result createChangeWithTopic(
|
||||
TestRepository<InMemoryRepository> repo, String topic,
|
||||
String commitMsg, String fileName, String content) throws Exception {
|
||||
PushOneCommit push = pushFactory.create(db, admin.getIdent(),
|
||||
repo, commitMsg, fileName, content);
|
||||
assertThat(topic).isNotEmpty();
|
||||
return push.to("refs/for/master/" + topic);
|
||||
return push.to("refs/for/master/" + name(topic));
|
||||
}
|
||||
|
||||
private PushOneCommit.Result createChangeWithTopic()
|
||||
throws Exception {
|
||||
return createChangeWithTopic(testRepo, "foo2",
|
||||
"a message", "a.txt", "content\n");
|
||||
}
|
||||
}
|
||||
|
@@ -35,7 +35,6 @@ import com.google.gerrit.extensions.api.changes.SubmitInput;
|
||||
import com.google.gerrit.extensions.common.ChangeInfo;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.extensions.restapi.RestModifyView;
|
||||
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
|
||||
import com.google.gerrit.extensions.webui.UiAction;
|
||||
@@ -100,6 +99,8 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
||||
"Other changes in this topic are not ready";
|
||||
private static final String BLOCKED_HIDDEN_TOPIC_TOOLTIP =
|
||||
"Other hidden changes in this topic are not ready";
|
||||
private static final String CLICK_FAILURE_OTHER_TOOLTIP =
|
||||
"Clicking the button would fail for other changes in the topic";
|
||||
|
||||
public enum Status {
|
||||
SUBMITTED, MERGED
|
||||
@@ -134,7 +135,6 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
||||
private final ParameterizedString submitTopicTooltip;
|
||||
private final boolean submitWholeTopic;
|
||||
private final Provider<InternalChangeQuery> queryProvider;
|
||||
private final Provider<Mergeable> mergeableProvider;
|
||||
|
||||
@Inject
|
||||
Submit(@GerritPersonIdent PersonIdent serverIdent,
|
||||
@@ -151,8 +151,7 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
||||
ChangeIndexer indexer,
|
||||
LabelNormalizer labelNormalizer,
|
||||
@GerritServerConfig Config cfg,
|
||||
Provider<InternalChangeQuery> queryProvider,
|
||||
Provider<Mergeable> mergeableProvider) {
|
||||
Provider<InternalChangeQuery> queryProvider) {
|
||||
this.serverIdent = serverIdent;
|
||||
this.dbProvider = dbProvider;
|
||||
this.repoManager = repoManager;
|
||||
@@ -180,7 +179,6 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
||||
cfg.getString("change", null, "submitTopicTooltip"),
|
||||
DEFAULT_TOPIC_TOOLTIP));
|
||||
this.queryProvider = queryProvider;
|
||||
this.mergeableProvider = mergeableProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -245,14 +243,15 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
||||
}
|
||||
|
||||
/**
|
||||
* @param changes list of changes to be submitted at once
|
||||
* @param changeList list of changes to be submitted at once
|
||||
* @param identifiedUser the user who is checking to submit
|
||||
* @return a reason why any of the changes is not submittable or null
|
||||
*/
|
||||
private String problemsForSubmittingChanges(List<ChangeData> changes,
|
||||
private String problemsForSubmittingChanges(
|
||||
List<ChangeData> changeList,
|
||||
IdentifiedUser identifiedUser) {
|
||||
for (ChangeData c : changes) {
|
||||
try {
|
||||
try {
|
||||
for (ChangeData c : changeList) {
|
||||
ChangeControl changeControl = c.changeControl().forUser(
|
||||
identifiedUser);
|
||||
if (!changeControl.isVisible(dbProvider.get())) {
|
||||
@@ -261,13 +260,16 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
||||
if (!changeControl.canSubmit()) {
|
||||
return BLOCKED_TOPIC_TOOLTIP;
|
||||
}
|
||||
if (!c.isMergeable()) {
|
||||
return CLICK_FAILURE_OTHER_TOOLTIP;
|
||||
}
|
||||
checkSubmitRule(c, c.currentPatchSet(), false);
|
||||
} catch (OrmException e) {
|
||||
log.error("Error checking if change is submittable", e);
|
||||
throw new OrmRuntimeException(e);
|
||||
} catch (ResourceConflictException e) {
|
||||
return BLOCKED_TOPIC_TOOLTIP;
|
||||
}
|
||||
} catch (ResourceConflictException e) {
|
||||
return BLOCKED_TOPIC_TOOLTIP;
|
||||
} catch (OrmException e) {
|
||||
log.error("Error checking if change is submittable", e);
|
||||
throw new OrmRuntimeException("Could not determine problems for the change", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -280,12 +282,16 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
||||
&& resource.getChange().getStatus().isOpen()
|
||||
&& resource.getPatchSet().getId().equals(current)
|
||||
&& resource.getControl().canSubmit();
|
||||
|
||||
ReviewDb db = dbProvider.get();
|
||||
ChangeData cd = changeDataFactory.create(db, resource.getControl());
|
||||
if (problemsForSubmittingChanges(Arrays.asList(cd), resource.getUser())
|
||||
!= null) {
|
||||
|
||||
try {
|
||||
checkSubmitRule(cd, cd.currentPatchSet(), false);
|
||||
} catch (ResourceConflictException e) {
|
||||
visible = false;
|
||||
} catch (OrmException e) {
|
||||
log.error("Error checking if change is submittable", e);
|
||||
throw new OrmRuntimeException("Could not determine problems for the change", e);
|
||||
}
|
||||
|
||||
if (!visible) {
|
||||
@@ -297,8 +303,8 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
||||
|
||||
boolean enabled;
|
||||
try {
|
||||
enabled = mergeableProvider.get().apply(resource).mergeable;
|
||||
} catch (RestApiException | OrmException | IOException e) {
|
||||
enabled = cd.isMergeable();
|
||||
} catch (OrmException e) {
|
||||
throw new OrmRuntimeException("Could not determine mergeability", e);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user