Block revert submission if message is not edited
Stop users from reverting a submission without editing the message.
The check is also valid for reverting a single submission.
The check is only added to frontend as a first step. The final check
if needed must be done at the backend.
Change-Id: Ida3f9e07efef1c5ef08a3b5285ff6f8ad0ef40b2
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
index 9c1a527..6495adb 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
@@ -827,7 +827,7 @@
.querySelector('gr-button[data-action-key="revert"]');
MockInteractions.tap(revertButton);
flush(() => {
- assert.equal(element.$.confirmRevertDialog.message, newRevertMsg);
+ assert.equal(element.$.confirmRevertDialog._message, newRevertMsg);
done();
});
});
@@ -866,7 +866,7 @@
'1234567890:random' + '\n' +
'23456:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...' +
'\n';
- assert.equal(confirmRevertDialog.message, expectedMsg);
+ assert.equal(confirmRevertDialog._message, expectedMsg);
const radioInputs = confirmRevertDialog.shadowRoot
.querySelectorAll('input[name="revertOptions"]');
MockInteractions.tap(radioInputs[0]);
@@ -874,7 +874,26 @@
expectedMsg = 'Revert "random commit message"\n\nThis reverts '
+ 'commit 2000.\n\nReason'
+ ' for revert: <INSERT REASONING HERE>\n';
- assert.equal(confirmRevertDialog.message, expectedMsg);
+ assert.equal(confirmRevertDialog._message, expectedMsg);
+ done();
+ });
+ });
+ });
+
+ test('submit fails if message is not edited', done => {
+ const revertButton = element.shadowRoot
+ .querySelector('gr-button[data-action-key="revert"]');
+ const confirmRevertDialog = element.$.confirmRevertDialog;
+ MockInteractions.tap(revertButton);
+ const fireStub = sandbox.stub(confirmRevertDialog, 'fire');
+ flush(() => {
+ const confirmButton = element.$.confirmRevertDialog.shadowRoot
+ .querySelector('gr-dialog')
+ .shadowRoot.querySelector('#confirm');
+ MockInteractions.tap(confirmButton);
+ flush(() => {
+ assert.isTrue(confirmRevertDialog._showErrorMessage);
+ assert.isFalse(fireStub.called);
done();
});
});
@@ -898,20 +917,20 @@
'Revert "random commit message"\n\nThis reverts '
+ 'commit 2000.\n\nReason'
+ ' for revert: <INSERT REASONING HERE>\n';
- assert.equal(confirmRevertDialog.message, revertSubmissionMsg);
+ assert.equal(confirmRevertDialog._message, revertSubmissionMsg);
const newRevertMsg = revertSubmissionMsg + 'random';
const newSingleChangeMsg = singleChangeMsg + 'random';
- confirmRevertDialog.message = newRevertMsg;
+ confirmRevertDialog._message = newRevertMsg;
MockInteractions.tap(radioInputs[0]);
flush(() => {
- assert.equal(confirmRevertDialog.message, singleChangeMsg);
- confirmRevertDialog.message = newSingleChangeMsg;
+ assert.equal(confirmRevertDialog._message, singleChangeMsg);
+ confirmRevertDialog._message = newSingleChangeMsg;
MockInteractions.tap(radioInputs[1]);
flush(() => {
- assert.equal(confirmRevertDialog.message, newRevertMsg);
+ assert.equal(confirmRevertDialog._message, newRevertMsg);
MockInteractions.tap(radioInputs[0]);
flush(() => {
- assert.equal(confirmRevertDialog.message, newSingleChangeMsg);
+ assert.equal(confirmRevertDialog._message, newSingleChangeMsg);
done();
});
});
@@ -932,6 +951,25 @@
]));
});
+ test('submit fails if message is not edited', done => {
+ const revertButton = element.shadowRoot
+ .querySelector('gr-button[data-action-key="revert"]');
+ const confirmRevertDialog = element.$.confirmRevertDialog;
+ MockInteractions.tap(revertButton);
+ const fireStub = sandbox.stub(confirmRevertDialog, 'fire');
+ flush(() => {
+ const confirmButton = element.$.confirmRevertDialog.shadowRoot
+ .querySelector('gr-dialog')
+ .shadowRoot.querySelector('#confirm');
+ MockInteractions.tap(confirmButton);
+ flush(() => {
+ assert.isTrue(confirmRevertDialog._showErrorMessage);
+ assert.isFalse(fireStub.called);
+ done();
+ });
+ });
+ });
+
test('confirm revert dialog shows one radio button', done => {
const revertButton = element.shadowRoot
.querySelector('gr-button[data-action-key="revert"]');
@@ -944,7 +982,9 @@
const msg = 'Revert "random commit message"\n\n'
+ 'This reverts commit 2000.\n\nReason '
+ 'for revert: <INSERT REASONING HERE>\n';
- assert.equal(confirmRevertDialog.message, msg);
+ assert.equal(confirmRevertDialog._message, msg);
+ const editedMsg = msg + 'hello';
+ confirmRevertDialog._message += 'hello';
const confirmButton = element.$.confirmRevertDialog.shadowRoot
.querySelector('gr-dialog')
.shadowRoot.querySelector('#confirm');
@@ -952,7 +992,8 @@
flush(() => {
assert.equal(fireActionStub.getCall(0).args[0], '/revert');
assert.equal(fireActionStub.getCall(0).args[1].__key, 'revert');
- assert.equal(fireActionStub.getCall(0).args[3].message, msg);
+ assert.equal(fireActionStub.getCall(0).args[3].message,
+ editedMsg);
done();
});
});
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.html b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.html
index 7bffe8a..0ad3ed3 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.html
@@ -51,13 +51,22 @@
line-height: var(--line-height-mono);
width: 73ch; /* Add a char to account for the border. */
}
+ .error {
+ color: var(--error-text-color);
+ margin-bottom: var(--spacing-m);
+ }
</style>
<gr-dialog
confirm-label="Revert"
on-confirm="_handleConfirmTap"
on-cancel="_handleCancelTap">
- <div class="header" slot="header">Revert Merged Change</div>
+ <div class="header" slot="header">
+ Revert Merged Change
+ </div>
<div class="main" slot="main">
+ <div class="error" hidden$="[[!_showErrorMessage]]">
+ <span> A reason is required </span>
+ </div>
<div class="revertSubmissionLayout">
<input
name="revertOptions"
@@ -70,11 +79,12 @@
</label>
</div>
<template is="dom-if" if="[[_showRevertSubmission]]">
- <div on-click="_handleRevertSubmissionClicked" class="revertSubmissionLayout">
+ <div class="revertSubmissionLayout">
<input
name="revertOptions"
type="radio"
id="revertSubmission"
+ on-change="_handleRevertSubmissionClicked"
checked="[[_computeIfRevertSubmission(_revertType)]]">
<label for="revertSubmission" class="label revertSubmission">
Revert entire submission ([[changes.length]] Changes)
@@ -93,7 +103,7 @@
class="message"
autocomplete="on"
max-rows="15"
- bind-value="{{message}}"></iron-autogrow-textarea>
+ bind-value="{{_message}}"></iron-autogrow-textarea>
</template>
</gr-endpoint-decorator>
<template is="dom-if" if="[[_computeIfRevertSubmission(_revertType)]]">
@@ -105,7 +115,7 @@
class="message"
autocomplete="on"
max-rows="15"
- bind-value="{{message}}"></iron-autogrow-textarea>
+ bind-value="{{_message}}"></iron-autogrow-textarea>
</template>
</div>
</gr-dialog>
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.js
index 438440e..b07738a 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.js
@@ -51,7 +51,9 @@
static get properties() {
return {
- message: String,
+ /* The revert message updated by the user
+ The default value is set by the dialog */
+ _message: String,
_revertType: {
type: Number,
value: REVERT_TYPES.REVERT_SINGLE_CHANGE,
@@ -60,12 +62,31 @@
type: Boolean,
value: false,
},
+ /* List of changes with the same topic
+ value is empty if only a single change is being reverted */
changes: {
type: Array,
value() { return []; },
},
change: Object,
+ /* commit message is _latestCommitMessage coming from gr-change-view
+ read only and is not meant to be modified */
commitMessage: String,
+ _showErrorMessage: {
+ type: Boolean,
+ value: false,
+ },
+ /* store the default revert messages per revert type so that we can
+ check if user has edited the revert message or not */
+ _originalRevertMessages: {
+ type: Array,
+ value() { return []; },
+ },
+ // Store the actual messages that the user has edited
+ _revertMessages: {
+ type: Array,
+ value() { return []; },
+ },
};
}
@@ -90,12 +111,10 @@
onInputUpdate(change, commitMessage, changes) {
if (!change || !changes) return;
+ // The option to revert a single change is always available
this._populateRevertSingleChangeMessage(
change, commitMessage, change.current_revision);
- if (changes.length > 1) {
- this._populateRevertSubmissionMessage(
- change, changes);
- }
+ this._populateRevertSubmissionMessage(change, changes);
}
_populateRevertSingleChangeMessage(change, commitMessage, commitHash) {
@@ -108,14 +127,15 @@
}
const revertCommitText = `This reverts commit ${commitHash}.`;
- this.revertSingleChangeMessage =
- `${revertTitle}\n\n${revertCommitText}\n\n` +
+ this._message = `${revertTitle}\n\n${revertCommitText}\n\n` +
`Reason for revert: <INSERT REASONING HERE>\n`;
// This is to give plugins a chance to update message
- this.revertSingleChangeMessage =
- this._modifyRevertMsg(change, commitMessage,
- this.revertSingleChangeMessage);
- this.message = this.revertSingleChangeMessage;
+ this._message = this._modifyRevertMsg(change, commitMessage,
+ this._message);
+ this._revertType = REVERT_TYPES.REVERT_SINGLE_CHANGE;
+ this._showRevertSubmission = false;
+ this._revertMessages[this._revertType] = this._message;
+ this._originalRevertMessages[this._revertType] = this._message;
}
_getTrimmedChangeSubject(subject) {
@@ -124,9 +144,9 @@
return subject.substring(0, CHANGE_SUBJECT_LIMIT) + '...';
}
- _modifyRevertSubmissionMsg(change) {
- return this.$.jsAPI.modifyRevertSubmissionMsg(change,
- this.revertSubmissionMessage, this.commitMessage);
+ _modifyRevertSubmissionMsg(change, msg) {
+ return this.$.jsAPI.modifyRevertSubmissionMsg(change, msg,
+ this.commitMessage);
}
_populateRevertSubmissionMessage(change, changes) {
@@ -139,39 +159,43 @@
if (!changes || changes.length <= 1) return;
const submissionId = change.submission_id;
const revertTitle = 'Revert submission ' + submissionId;
- this.changes = changes;
- this.revertSubmissionMessage = revertTitle + '\n\n' +
- 'Reason for revert: <INSERT REASONING HERE>\n';
- this.revertSubmissionMessage += 'Reverted Changes:\n';
+ this._message = revertTitle + '\n\n' + 'Reason for revert: <INSERT ' +
+ 'REASONING HERE>\n';
+ this._message += 'Reverted Changes:\n';
changes.forEach(change => {
- this.revertSubmissionMessage += change.change_id.substring(0, 10) + ':'
+ this._message += change.change_id.substring(0, 10) + ':'
+ this._getTrimmedChangeSubject(change.subject) + '\n';
});
- this.revertSubmissionMessage = this._modifyRevertSubmissionMsg(change);
- this.message = this.revertSubmissionMessage;
+ this._message = this._modifyRevertSubmissionMsg(change, this._message);
this._revertType = REVERT_TYPES.REVERT_SUBMISSION;
+ this._revertMessages[this._revertType] = this._message;
+ this._originalRevertMessages[this._revertType] = this._message;
this._showRevertSubmission = true;
}
_handleRevertSingleChangeClicked() {
- if (this._revertType === REVERT_TYPES.REVERT_SINGLE_CHANGE) return;
- this.revertSubmissionMessage = this.message;
- this.message = this.revertSingleChangeMessage;
+ this._showErrorMessage = false;
+ this._revertMessages[REVERT_TYPES.REVERT_SUBMISSION] = this._message;
+ this._message = this._revertMessages[REVERT_TYPES.REVERT_SINGLE_CHANGE];
this._revertType = REVERT_TYPES.REVERT_SINGLE_CHANGE;
}
_handleRevertSubmissionClicked() {
- if (this._revertType === REVERT_TYPES.REVERT_SUBMISSION) return;
+ this._showErrorMessage = false;
this._revertType = REVERT_TYPES.REVERT_SUBMISSION;
- this.revertSingleChangeMessage = this.message;
- this.message = this.revertSubmissionMessage;
+ this._revertMessages[REVERT_TYPES.REVERT_SINGLE_CHANGE] = this._message;
+ this._message = this._revertMessages[REVERT_TYPES.REVERT_SUBMISSION];
}
_handleConfirmTap(e) {
e.preventDefault();
e.stopPropagation();
+ if (this._message === this._originalRevertMessages[this._revertType]) {
+ this._showErrorMessage = true;
+ return;
+ }
this.fire('confirm', {revertType: this._revertType,
- message: this.message}, {bubbles: false});
+ message: this._message}, {bubbles: false});
}
_handleCancelTap(e) {
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.html
index 1d28d32..76f5ecd 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.html
@@ -47,7 +47,7 @@
teardown(() => sandbox.restore());
test('no match', () => {
- assert.isNotOk(element.message);
+ assert.isNotOk(element._message);
const alertStub = sandbox.stub();
element.addEventListener('show-alert', alertStub);
element._populateRevertSingleChangeMessage({},
@@ -56,47 +56,47 @@
});
test('single line', () => {
- assert.isNotOk(element.message);
+ assert.isNotOk(element._message);
element._populateRevertSingleChangeMessage({},
'one line commit\n\nChange-Id: abcdefg\n',
'abcd123');
const expected = 'Revert "one line commit"\n\n' +
'This reverts commit abcd123.\n\n' +
'Reason for revert: <INSERT REASONING HERE>\n';
- assert.equal(element.message, expected);
+ assert.equal(element._message, expected);
});
test('multi line', () => {
- assert.isNotOk(element.message);
+ assert.isNotOk(element._message);
element._populateRevertSingleChangeMessage({},
'many lines\ncommit\n\nmessage\n\nChange-Id: abcdefg\n',
'abcd123');
const expected = 'Revert "many lines"\n\n' +
'This reverts commit abcd123.\n\n' +
'Reason for revert: <INSERT REASONING HERE>\n';
- assert.equal(element.message, expected);
+ assert.equal(element._message, expected);
});
test('issue above change id', () => {
- assert.isNotOk(element.message);
+ assert.isNotOk(element._message);
element._populateRevertSingleChangeMessage({},
'much lines\nvery\n\ncommit\n\nBug: Issue 42\nChange-Id: abcdefg\n',
'abcd123');
const expected = 'Revert "much lines"\n\n' +
'This reverts commit abcd123.\n\n' +
'Reason for revert: <INSERT REASONING HERE>\n';
- assert.equal(element.message, expected);
+ assert.equal(element._message, expected);
});
test('revert a revert', () => {
- assert.isNotOk(element.message);
+ assert.isNotOk(element._message);
element._populateRevertSingleChangeMessage({},
'Revert "one line commit"\n\nChange-Id: abcdefg\n',
'abcd123');
const expected = 'Revert "Revert "one line commit""\n\n' +
'This reverts commit abcd123.\n\n' +
'Reason for revert: <INSERT REASONING HERE>\n';
- assert.equal(element.message, expected);
+ assert.equal(element._message, expected);
});
});
</script>