Update rebase dialog to give parent autocomplete suggestions
- The rest API is called once, with a query for recent changes. - Input text is filtered against those changes for substring matches (includes the change number and change title). - The current change does not show up in its own suggestion list. Bug: Issue 4651 Change-Id: I4e87a5f6439999bce71c1a0ab0de42eae886fed2
This commit is contained in:
@@ -132,6 +132,7 @@ limitations under the License.
|
||||
<gr-overlay id="overlay" with-backdrop>
|
||||
<gr-confirm-rebase-dialog id="confirmRebase"
|
||||
class="confirmDialog"
|
||||
change-number="[[change._number]]"
|
||||
on-confirm="_handleRebaseConfirm"
|
||||
on-cancel="_handleConfirmDialogCancel"
|
||||
branch="[[change.branch]]"
|
||||
|
||||
@@ -173,7 +173,7 @@
|
||||
*/
|
||||
|
||||
properties: {
|
||||
/** @type {{ branch: string, project: string }} */
|
||||
/** @type {{ _number: number, branch: string, project: string }} */
|
||||
change: Object,
|
||||
actions: {
|
||||
type: Object,
|
||||
@@ -802,6 +802,7 @@
|
||||
switch (key) {
|
||||
case RevisionActions.REBASE:
|
||||
this._showActionDialog(this.$.confirmRebase);
|
||||
this.$.confirmRebase.fetchRecentChanges();
|
||||
break;
|
||||
case RevisionActions.CHERRYPICK:
|
||||
this._handleCherrypickTap();
|
||||
|
||||
@@ -306,6 +306,9 @@ limitations under the License.
|
||||
|
||||
test('rebase change', done => {
|
||||
const fireActionStub = sandbox.stub(element, '_fireAction');
|
||||
const fetchChangesStub = sandbox.stub(element.$.confirmRebase,
|
||||
'fetchRecentChanges').returns(Promise.resolve([]));
|
||||
element._hasKnownChainState = true;
|
||||
flush(() => {
|
||||
const rebaseButton = element.$$('gr-button[data-action-key="rebase"]');
|
||||
MockInteractions.tap(rebaseButton);
|
||||
@@ -318,6 +321,7 @@ limitations under the License.
|
||||
method: 'POST',
|
||||
title: 'Rebase onto tip of branch or parent change',
|
||||
};
|
||||
assert.isTrue(fetchChangesStub.called);
|
||||
// rebase on other
|
||||
element.$.confirmRebase.base = '1234';
|
||||
element._handleRebaseConfirm();
|
||||
@@ -340,6 +344,22 @@ limitations under the License.
|
||||
});
|
||||
});
|
||||
|
||||
test(`rebase dialog gets recent changes each time it's opened`, done => {
|
||||
const fetchChangesStub = sandbox.stub(element.$.confirmRebase,
|
||||
'fetchRecentChanges').returns(Promise.resolve([]));
|
||||
element._hasKnownChainState = true;
|
||||
const rebaseButton = element.$$('gr-button[data-action-key="rebase"]');
|
||||
MockInteractions.tap(rebaseButton);
|
||||
assert.isTrue(fetchChangesStub.calledOnce);
|
||||
|
||||
flush(() => {
|
||||
element.$.confirmRebase.fire('cancel');
|
||||
MockInteractions.tap(rebaseButton);
|
||||
assert.isTrue(fetchChangesStub.calledTwice);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('two dialogs are not shown at the same time', done => {
|
||||
element._hasKnownChainState = true;
|
||||
flush(() => {
|
||||
|
||||
@@ -15,7 +15,9 @@ limitations under the License.
|
||||
-->
|
||||
|
||||
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
||||
<link rel="import" href="../../shared/gr-autocomplete/gr-autocomplete.html">
|
||||
<link rel="import" href="../../shared/gr-confirm-dialog/gr-confirm-dialog.html">
|
||||
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||
<link rel="import" href="../../../styles/shared-styles.html">
|
||||
|
||||
<dom-module id="gr-confirm-rebase-dialog">
|
||||
@@ -49,6 +51,7 @@ limitations under the License.
|
||||
}
|
||||
</style>
|
||||
<gr-confirm-dialog
|
||||
id="confirmDialog"
|
||||
confirm-label="Rebase"
|
||||
on-confirm="_handleConfirmTap"
|
||||
on-cancel="_handleCancelTap">
|
||||
@@ -98,15 +101,18 @@ limitations under the License.
|
||||
</label>
|
||||
</div>
|
||||
<div class="parentRevisionContainer">
|
||||
<input is="iron-input"
|
||||
type="text"
|
||||
<gr-autocomplete
|
||||
id="parentInput"
|
||||
bind-value="{{base}}"
|
||||
query="[[_query]]"
|
||||
text="{{_inputText}}"
|
||||
on-tap="_handleEnterChangeNumberTap"
|
||||
on-commit="_handleBaseSelected"
|
||||
placeholder="Change number">
|
||||
</gr-autocomplete>
|
||||
</div>
|
||||
</div>
|
||||
</gr-confirm-dialog>
|
||||
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
|
||||
</template>
|
||||
<script src="gr-confirm-rebase-dialog.js"></script>
|
||||
</dom-module>
|
||||
|
||||
@@ -36,14 +36,62 @@
|
||||
* @type {?string} */
|
||||
base: String,
|
||||
branch: String,
|
||||
changeNumber: Number,
|
||||
hasParent: Boolean,
|
||||
rebaseOnCurrent: Boolean,
|
||||
_inputText: String,
|
||||
_query: {
|
||||
type: Function,
|
||||
value() {
|
||||
return this._getChangeSuggestions.bind(this);
|
||||
},
|
||||
},
|
||||
_recentChanges: Array,
|
||||
},
|
||||
|
||||
observers: [
|
||||
'_updateSelectedOption(rebaseOnCurrent, hasParent)',
|
||||
],
|
||||
|
||||
// This is called by gr-change-actions every time the rebase dialog is
|
||||
// re-opened. Unlike other autocompletes that make a request with each
|
||||
// updated input, this one gets all recent changes once and then filters
|
||||
// them by the input. The query is re-run each time the dialog is opened
|
||||
// in case there are new/updated changes in the generic query since the
|
||||
// last time it was run.
|
||||
fetchRecentChanges() {
|
||||
return this.$.restAPI.getChanges(null, `is:open -age:90d`)
|
||||
.then(response => {
|
||||
const changes = [];
|
||||
for (const key in response) {
|
||||
if (!response.hasOwnProperty(key)) { continue; }
|
||||
changes.push({
|
||||
name: `${response[key]._number}: ${response[key].subject}`,
|
||||
value: response[key]._number,
|
||||
});
|
||||
}
|
||||
this._recentChanges = changes;
|
||||
return this._recentChanges;
|
||||
});
|
||||
},
|
||||
|
||||
_getRecentChanges() {
|
||||
if (this._recentChanges) {
|
||||
return Promise.resolve(this._recentChanges);
|
||||
}
|
||||
return this.fetchRecentChanges();
|
||||
},
|
||||
|
||||
_getChangeSuggestions(input) {
|
||||
return this._getRecentChanges().then(changes =>
|
||||
this._filterChanges(input, changes));
|
||||
},
|
||||
|
||||
_filterChanges(input, changes) {
|
||||
return changes.filter(change => change.name.includes(input) &&
|
||||
change.value !== this.changeNumber);
|
||||
},
|
||||
|
||||
_displayParentOption(rebaseOnCurrent, hasParent) {
|
||||
return hasParent && rebaseOnCurrent;
|
||||
},
|
||||
@@ -58,11 +106,13 @@
|
||||
|
||||
_handleConfirmTap(e) {
|
||||
e.preventDefault();
|
||||
this._inputText = '';
|
||||
this.fire('confirm', null, {bubbles: false});
|
||||
},
|
||||
|
||||
_handleCancelTap(e) {
|
||||
e.preventDefault();
|
||||
this._inputText = '';
|
||||
this.fire('cancel', null, {bubbles: false});
|
||||
},
|
||||
|
||||
@@ -85,6 +135,10 @@
|
||||
this.base = null;
|
||||
},
|
||||
|
||||
_handleBaseSelected(e) {
|
||||
this.base = e.detail.value;
|
||||
},
|
||||
|
||||
_handleEnterChangeNumberTap() {
|
||||
this.$.rebaseOnOtherInput.checked = true;
|
||||
},
|
||||
|
||||
@@ -34,9 +34,15 @@ limitations under the License.
|
||||
<script>
|
||||
suite('gr-confirm-rebase-dialog tests', () => {
|
||||
let element;
|
||||
let sandbox;
|
||||
|
||||
setup(() => {
|
||||
element = fixture('basic');
|
||||
sandbox = sinon.sandbox.create();
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
test('controls with parent and rebase on current available', () => {
|
||||
@@ -82,5 +88,88 @@ limitations under the License.
|
||||
assert.isTrue(element.$.rebaseOnTip.hasAttribute('hidden'));
|
||||
assert.isFalse(element.$.tipUpToDateMsg.hasAttribute('hidden'));
|
||||
});
|
||||
|
||||
test('input cleared on cancel or submit', () => {
|
||||
element._inputText = '123';
|
||||
element.$.confirmDialog.fire('confirm');
|
||||
assert.equal(element._inputText, '');
|
||||
|
||||
element._inputText = '123';
|
||||
element.$.confirmDialog.fire('cancel');
|
||||
assert.equal(element._inputText, '');
|
||||
});
|
||||
|
||||
suite('parent suggestions', () => {
|
||||
let recentChanges;
|
||||
setup(() => {
|
||||
recentChanges = [
|
||||
{
|
||||
name: '123: my first awesome change',
|
||||
value: 123,
|
||||
},
|
||||
{
|
||||
name: '124: my second awesome change',
|
||||
value: 124,
|
||||
},
|
||||
{
|
||||
name: '245: my third awesome change',
|
||||
value: 245,
|
||||
},
|
||||
];
|
||||
|
||||
sandbox.stub(element.$.restAPI, 'getChanges').returns(Promise.resolve(
|
||||
[
|
||||
{
|
||||
_number: 123,
|
||||
subject: 'my first awesome change',
|
||||
},
|
||||
{
|
||||
_number: 124,
|
||||
subject: 'my second awesome change',
|
||||
},
|
||||
{
|
||||
_number: 245,
|
||||
subject: 'my third awesome change',
|
||||
},
|
||||
]
|
||||
));
|
||||
});
|
||||
|
||||
test('_getRecentChanges', () => {
|
||||
sandbox.spy(element, '_getRecentChanges');
|
||||
return element._getRecentChanges().then(() => {
|
||||
assert.deepEqual(element._recentChanges, recentChanges);
|
||||
assert.equal(element.$.restAPI.getChanges.callCount, 1);
|
||||
// When called a second time, should not re-request recent changes.
|
||||
element._getRecentChanges();
|
||||
}).then(() => {
|
||||
assert.equal(element._getRecentChanges.callCount, 2);
|
||||
assert.equal(element.$.restAPI.getChanges.callCount, 1);
|
||||
});
|
||||
});
|
||||
|
||||
test('_filterChanges', () => {
|
||||
assert.equal(element._filterChanges('123', recentChanges).length, 1);
|
||||
assert.equal(element._filterChanges('12', recentChanges).length, 2);
|
||||
assert.equal(element._filterChanges('awesome', recentChanges).length,
|
||||
3);
|
||||
assert.equal(element._filterChanges('third', recentChanges).length,
|
||||
1);
|
||||
|
||||
element.changeNumber = 123;
|
||||
assert.equal(element._filterChanges('123', recentChanges).length, 0);
|
||||
assert.equal(element._filterChanges('124', recentChanges).length, 1);
|
||||
assert.equal(element._filterChanges('awesome', recentChanges).length,
|
||||
2);
|
||||
});
|
||||
|
||||
test('input text change triggers function', () => {
|
||||
sandbox.spy(element, '_getRecentChanges');
|
||||
element._inputText = '1';
|
||||
assert.isTrue(element._getRecentChanges.calledOnce);
|
||||
element._inputText = '12';
|
||||
assert.isTrue(element._getRecentChanges.calledTwice);
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user