Merge "Utilize gr-patch-range-select in the change view"

This commit is contained in:
Becky Siegel 2017-09-14 21:19:11 +00:00 committed by Gerrit Code Review
commit bf5ace98c2
10 changed files with 325 additions and 346 deletions

View File

@ -637,8 +637,6 @@
this._patchRange.patchNum ||
this.computeLatestPatchNum(this._allPatchSets));
this.$.fileListHeader.updateSelected();
const title = change.subject + ' (' + change.change_id.substr(0, 9) + ')';
this.fire('title-change', {title});
},

View File

@ -18,6 +18,7 @@ limitations under the License.
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
<link rel="import" href="../../diff/gr-patch-range-select/gr-patch-range-select.html">
<link rel="import" href="../../shared/gr-editable-label/gr-editable-label.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../shared/gr-select/gr-select.html">
@ -54,9 +55,6 @@ limitations under the License.
.latestPatchContainer {
display: none;
}
.patchSetSelect {
max-width: 8em;
}
gr-editable-label.descriptionLabel {
max-width: 100%;
}
@ -94,8 +92,8 @@ limitations under the License.
.separator {
margin: 0 .25em;
}
.patchSetSelect {
max-width: 8em;
.expandInline {
padding-right: .25em;
}
.editLoaded .hideOnEdit {
display: none;
@ -103,32 +101,23 @@ limitations under the License.
.editLoaded .showOnEdit {
display: initial;
}
@media screen and (max-width: 50em) {
.desktop {
display: none;
}
}
</style>
<div class$="patchInfo-header [[_computeEditLoadedClass(editLoaded)]] [[_computePatchInfoClass(patchRange.patchNum, allPatchSets)]]">
<div class="patchInfo-header-wrapper">
<label class="patchSelectLabel" for="patchSetSelect">
Patch set
</label>
<gr-select
id="patchSetSelect"
bind-value="{{_selectedPatchSet}}"
class="patchSetSelect"
on-change="_handlePatchChange">
<select>
<template is="dom-repeat" items="[[allPatchSets]]"
as="patchNum">
<option
value$="[[patchNum.num]]"
disabled$="[[_computePatchSetDisabled(patchNum.num, patchRange.basePatchNum, revisions)]]">
[[patchNum.num]]
/
[[computeLatestPatchNum(allPatchSets)]]
[[_computePatchSetCommentsString(comments, patchNum.num)]]
[[_computePatchSetDescription(change, patchNum.num)]]
</option>
</template>
</select>
</gr-select>
<gr-patch-range-select
id="rangeSelect"
comments="[[comments]]"
change-num="[[changeNum]]"
patch-range="[[patchRange]]"
available-patches="[[allPatchSets]]"
revisions="[[change.revisions]]"
on-patch-range-change="_handlePatchChange">
</gr-patch-range-select>
/
<gr-commit-info
change="[[change]]"
@ -149,7 +138,7 @@ limitations under the License.
<gr-editable-label
id="descriptionLabel"
class="descriptionLabel"
value="[[_computePatchSetDescription(change, _selectedPatchSet)]]"
value="[[_computePatchSetDescription(change, patchRange.patchNum)]]"
placeholder="[[_computeDescriptionPlaceholder(_descriptionReadOnly)]]"
read-only="[[_descriptionReadOnly]]"
on-changed="_handleDescriptionChanged"></gr-editable-label>
@ -194,27 +183,6 @@ limitations under the License.
<option value="UNIFIED_DIFF">Unified</option>
</select>
</gr-select>
<span class="separator">/</span>
<label>
Diff against
<gr-select id="patchChange" bind-value="{{_diffAgainst}}"
class="patchSetSelect" on-change="_handleBasePatchChange">
<select>
<option value="PARENT">Base</option>
<template
is="dom-repeat"
items="[[allPatchSets]]"
as="patchNum">
<option
disabled$="[[_computeBasePatchDisabled(patchNum.num, patchRange.patchNum, revisions)]]"
value$="[[patchNum.num]]">
[[patchNum.num]]
[[patchNum.desc]]
</option>
</template>
</select>
</gr-select>
</label>
</div>
</div>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>

View File

@ -23,6 +23,7 @@
properties: {
account: Object,
allPatchSets: Array,
/** @type {?} */
change: Object,
changeNum: String,
changeUrl: String,
@ -38,10 +39,7 @@
notify: true,
},
/** @type {?} */
patchRange: {
type: Object,
observer: 'updateSelected',
},
patchRange: Object,
revisions: Array,
// Caps the number of files that can be shown and have the 'show diffs' /
// 'hide diffs' buttons still be functional.
@ -54,8 +52,6 @@
type: Boolean,
computed: '_computeDescriptionReadOnly(loggedIn, change, account)',
},
_selectedPatchSet: String,
_diffAgainst: String,
},
behaviors: [
@ -70,11 +66,6 @@
this.fire('collapse-diffs');
},
updateSelected() {
this._selectedPatchSet = this.patchRange.patchNum;
this._diffAgainst = this.patchRange.basePatchNum;
},
_computeDescriptionPlaceholder(readOnly) {
return (readOnly ? 'No' : 'Add a') + ' patch set description';
},
@ -107,10 +98,10 @@
_handleDescriptionChanged(e) {
const desc = e.detail.trim();
const rev = this.getRevisionByPatchNum(this.change.revisions,
this._selectedPatchSet);
this.patchRange.patchNum);
const sha = this._getPatchsetHash(this.change.revisions, rev);
this.$.restAPI.setDescription(this.changeNum,
this._selectedPatchSet, desc)
this.patchRange.patchNum, desc)
.then(res => {
if (res.ok) {
this.set(['_change', 'revisions', sha, 'description'], desc);
@ -127,63 +118,6 @@
return !loggedIn || !prefs;
},
// Copied from gr-file-list
_getCommentsForPath(comments, patchNum, path) {
return (comments[path] || []).filter(c => {
return this.patchNumEquals(c.patch_set, patchNum);
});
},
// Copied from gr-file-list
_computeUnresolvedNum(comments, drafts, patchNum, path) {
comments = this._getCommentsForPath(comments, patchNum, path);
drafts = this._getCommentsForPath(drafts, patchNum, path);
comments = comments.concat(drafts);
// Create an object where every comment ID is the key of an unresolved
// comment.
const idMap = comments.reduce((acc, comment) => {
if (comment.unresolved) {
acc[comment.id] = true;
}
return acc;
}, {});
// Set false for the comments that are marked as parents.
for (const comment of comments) {
idMap[comment.in_reply_to] = false;
}
// The unresolved comments are the comments that still have true.
const unresolvedLeaves = Object.keys(idMap).filter(key => {
return idMap[key];
});
return unresolvedLeaves.length;
},
_computePatchSetCommentsString(allComments, patchNum) {
let numComments = 0;
let numUnresolved = 0;
for (const file in allComments) {
if (allComments.hasOwnProperty(file)) {
numComments += this._getCommentsForPath(
allComments, patchNum, file).length;
numUnresolved += this._computeUnresolvedNum(
allComments, {}, patchNum, file);
}
}
let commentsStr = '';
if (numComments > 0) {
commentsStr = '(' + numComments + ' comments';
if (numUnresolved > 0) {
commentsStr += ', ' + numUnresolved + ' unresolved';
}
commentsStr += ')';
}
return commentsStr;
},
_fileListActionsVisible(shownFileCount, maxFilesForBulkActions) {
return shownFileCount <= maxFilesForBulkActions;
@ -211,7 +145,7 @@
* always include the patch range, even if the requested patchNum is
* known to be the latest.
*/
_changePatchNum(patchNum, basePatchNum, opt_forceParams) {
_changePatchNum(basePatchNum, patchNum, opt_forceParams) {
if (!opt_forceParams) {
let currentPatchNum;
if (this.change.current_revision) {
@ -230,12 +164,8 @@
basePatchNum);
},
_handleBasePatchChange(e) {
this._changePatchNum(this._selectedPatchSet, e.target.value, true);
},
_handlePatchChange(e) {
this._changePatchNum(e.target.value, this._diffAgainst, true);
this._changePatchNum(e.detail.leftPatch, e.detail.rightPatch, true);
},
_handlePrefsTap(e) {

View File

@ -123,42 +123,6 @@ limitations under the License.
false);
});
test('_computePatchSetCommentsString', () => {
// Test string with unresolved comments.
comments = {
foo: [{
id: '27dcee4d_f7b77cfa',
message: 'test',
patch_set: 1,
unresolved: true,
}],
bar: [{
id: '27dcee4d_f7b77cfa',
message: 'test',
patch_set: 1,
},
{
id: '27dcee4d_f7b77cfa',
message: 'test',
patch_set: 1,
}],
abc: [],
};
assert.equal(element._computePatchSetCommentsString(comments, 1),
'(3 comments, 1 unresolved)');
// Test string with no unresolved comments.
delete comments['foo'];
assert.equal(element._computePatchSetCommentsString(comments, 1),
'(2 comments)');
// Test string with no comments.
delete comments['bar'];
assert.equal(element._computePatchSetCommentsString(comments, 1), '');
});
test('_handleDescriptionChanged', () => {
const putDescStub = sandbox.stub(element.$.restAPI, 'setDescription')
.returns(Promise.resolve({ok: true}));
@ -169,7 +133,6 @@ limitations under the License.
basePatchNum: 'PARENT',
patchNum: 1,
};
element._selectedPatchNum = '1';
element.change = {
change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca',
revisions: {
@ -195,130 +158,6 @@ limitations under the License.
assert.equal(putDescStub.args[0][2], 'test2');
});
test('patch num change', done => {
element.patchRange = {
basePatchNum: 'PARENT',
patchNum: 2,
};
element.allPatchSets = [
{num: 1},
{num: 2},
{num: 3},
{num: 13},
];
flushAsynchronousOperations();
const selectEl = element.$$('.patchInfo-header gr-select');
assert.ok(selectEl);
const optionEls = Polymer.dom(element.root).querySelectorAll(
'.patchInfo-header option');
assert.equal(optionEls.length, 4);
const select = element.$$('.patchInfo-header #patchSetSelect').bindValue;
assert.notEqual(select, 1);
assert.equal(select, 2);
assert.notEqual(select, 3);
assert.equal(optionEls[3].value, 13);
let numEvents = 0;
selectEl.addEventListener('change', e => {
assert.equal(element.diffViewMode, 'SIDE_BY_SIDE');
numEvents++;
if (numEvents == 1) {
assert.isTrue(navigateToChangeStub.lastCall.calledWithExactly(
element.change, '1', 'PARENT'));
selectEl.nativeSelect.value = '3';
element.fire('change', {}, {node: selectEl.nativeSelect});
} else if (numEvents == 2) {
assert.isTrue(navigateToChangeStub.lastCall.calledWithExactly(
element.change, '3', 'PARENT'));
done();
}
});
selectEl.nativeSelect.value = '1';
element.fire('change', {}, {node: selectEl.nativeSelect});
});
test('patch num change with missing current_revision', done => {
element.patchRange = {
basePatchNum: 'PARENT',
patchNum: 2,
};
element.allPatchSets = [
{num: 1},
{num: 2},
{num: 3},
{num: 13},
];
flushAsynchronousOperations();
const selectEl = element.$$('.patchInfo-header gr-select');
assert.ok(selectEl);
const optionEls = Polymer.dom(element.root).querySelectorAll(
'.patchInfo-header option');
assert.equal(optionEls.length, 4);
assert.notEqual(
element.$$('.patchInfo-header #patchSetSelect').bindValue, 1);
assert.equal(
element.$$('.patchInfo-header #patchSetSelect').bindValue, 2);
assert.notEqual(
element.$$('.patchInfo-header #patchSetSelect').bindValue, 3);
assert.equal(optionEls[3].value, 13);
let numEvents = 0;
selectEl.addEventListener('change', e => {
numEvents++;
if (numEvents == 1) {
assert.isTrue(navigateToChangeStub.lastCall.calledWithExactly(
element.change, '1', 'PARENT'));
selectEl.nativeSelect.value = '3';
element.fire('change', {}, {node: selectEl.nativeSelect});
} else if (numEvents == 2) {
assert.isTrue(navigateToChangeStub.lastCall.calledWithExactly(
element.change, '3', 'PARENT'));
done();
}
});
selectEl.nativeSelect.value = '1';
element.fire('change', {}, {node: selectEl.nativeSelect});
});
test('diff against dropdown', done => {
element.revisions = [
{commit: {}},
{commit: {}},
{commit: {}},
{commit: {}},
];
element.allPatchSets = [
{num: 1},
{num: 2},
{num: 3},
{num: 'edit'},
];
element.patchRange = {
basePatchNum: 'PARENT',
patchNum: '3',
};
flush(() => {
const selectEl = element.$.patchChange;
assert.equal(selectEl.nativeSelect.value, 'PARENT');
assert.isTrue(element.$$('#patchChange option[value="3"]')
.hasAttribute('disabled'));
selectEl.addEventListener('change', () => {
assert.equal(selectEl.nativeSelect.value, 'edit');
assert(navigateToChangeStub.lastCall.calledWithExactly(
element.change, '3', 'edit'),
'Should navigate to /c/42/edit..3');
done();
});
selectEl.nativeSelect.value = 'edit';
element.fire('change', {}, {node: selectEl.nativeSelect});
});
});
test('expandAllDiffs called when expand button clicked', () => {
element.shownFileCount = 1;
flushAsynchronousOperations();
@ -367,6 +206,15 @@ limitations under the License.
assert.equal(select.nativeSelect.value, 'UNIFIED_DIFF');
});
test('_changePatchNum called when range select changes', () => {
const leftPatch = 1;
const rightPatch = 2;
sandbox.stub(element, '_changePatchNum');
element.$.rangeSelect.fire('patch-range-change', {leftPatch, rightPatch});
assert.isTrue(element._changePatchNum.lastCall
.calledWithExactly(1, 2, true));
});
test('include base patch when not parent', () => {
element.changeNum = '42';
element.patchRange = {
@ -385,13 +233,13 @@ limitations under the License.
labels: {},
};
element._changePatchNum(13, 2);
element._changePatchNum(2, 13);
assert.isTrue(navigateToChangeStub.lastCall.calledWithExactly(
element.change, 13, 2));
element.patchRange.basePatchNum = 'PARENT';
element._changePatchNum(3, 'PARENT');
element._changePatchNum('PARENT', 3);
assert.isTrue(navigateToChangeStub.lastCall.calledWithExactly(
element.change, 3, 'PARENT'));
});

View File

@ -37,6 +37,9 @@ limitations under the License.
:host {
background-color: var(--view-background-color);
}
gr-patch-range-select {
display: block;
}
gr-diff {
border: none;
}
@ -271,12 +274,13 @@ limitations under the License.
<div class="subHeader">
<div class="patchRangeLeft">
<gr-patch-range-select
path="[[_path]]"
id="rangeSelect"
change-num="[[_changeNum]]"
patch-range="[[_patchRange]]"
files-weblinks="[[_filesWeblinks]]"
available-patches="[[_computeAvailablePatches(_change.revisions, _change.revisions.*)]]"
revisions="[[_change.revisions]]">
revisions="[[_change.revisions]]"
on-patch-range-change="_handlePatchChange">
</gr-patch-range-select>
<span class="download desktop">
<span class="separator">/</span>

View File

@ -580,7 +580,9 @@
},
_computeAvailablePatches(revs) {
return this.sortRevisions(Object.values(revs)).map(e => e._number);
return this.sortRevisions(Object.values(revs)).map(e => {
return {num: e._number};
});
},
/**
@ -672,6 +674,13 @@
this.$.dropdown.open();
},
_handlePatchChange(e) {
const rightPatch = e.detail.rightPatch;
const leftPatch = e.detail.leftPatch;
Gerrit.Nav.navigateToDiff(
this._change, this._path, rightPatch, leftPatch);
},
_handlePrefsTap(e) {
e.preventDefault();
this.$.diffPreferences.open();

View File

@ -446,6 +446,18 @@ limitations under the License.
});
});
test('_handlePatchChange calls navigateToDiff correctly', () => {
const leftPatch = 'PARENT';
const rightPatch = '3';
const navigateStub = sandbox.stub(Gerrit.Nav, 'navigateToDiff');
element._change = {_number: 321, project: 'foo/bar'};
element._path = 'path/to/file.txt';
element.$.rangeSelect.fire('patch-range-change', {leftPatch, rightPatch});
assert(navigateStub.lastCall.calledWithExactly(element._change,
element._path, rightPatch, leftPatch));
});
test('download link', () => {
element._changeNum = '42';
element._patchRange = {

View File

@ -22,9 +22,6 @@ limitations under the License.
<dom-module id="gr-patch-range-select">
<template>
<style include="shared-styles">
:host {
display: block;
}
.patchRange {
display: inline-block;
}
@ -46,11 +43,12 @@ limitations under the License.
on-change="_handlePatchChange">
<select>
<option value="PARENT">Base</option>
<template is="dom-repeat" items="{{availablePatches}}" as="patchNum">
<option value$="[[patchNum]]"
disabled$="[[_computeLeftDisabled(patchNum, patchRange)]]">
[[patchNum]]
[[_computePatchSetDescription(revisions, patchNum)]]
<template is="dom-repeat" items="{{availablePatches}}" as="basePatchNum">
<option value$="[[basePatchNum.num]]"
disabled$="[[_computeLeftDisabled(basePatchNum.num, patchRange.patchNum, _sortedRevisions)]]">
[[basePatchNum.num]]
[[_computePatchSetCommentsString(comments, basePatchNum.num)]]
[[_computePatchSetDescription(revisions, basePatchNum.num)]]
</option>
</template>
</select>
@ -68,10 +66,11 @@ limitations under the License.
on-change="_handlePatchChange">
<select>
<template is="dom-repeat" items="{{availablePatches}}" as="patchNum">
<option value$="[[patchNum]]"
disabled$="[[_computeRightDisabled(patchNum, patchRange)]]">
[[patchNum]]
[[_computePatchSetDescription(revisions, patchNum)]]
<option value$="[[patchNum.num]]"
disabled$="[[_computeRightDisabled(patchNum.num, patchRange.basePatchNum, _sortedRevisions)]]">
[[patchNum.num]]
[[_computePatchSetCommentsString(comments, patchNum.num)]]
[[_computePatchSetDescription(revisions, patchNum.num)]]
</option>
</template>
</select>

View File

@ -17,26 +17,36 @@
// Maximum length for patch set descriptions.
const PATCH_DESC_MAX_LENGTH = 500;
/**
* Fired when the patch range changes
*
* @event patch-range-change
*
* @property {string} leftPatch
* @property {string} rightPatch
*/
Polymer({
is: 'gr-patch-range-select',
properties: {
availablePatches: Array,
changeNum: String,
comments: Array,
/** @type {{ meta_a: !Array, meta_b: !Array}} */
filesWeblinks: Object,
path: String,
patchRange: {
type: Object,
observer: '_updateSelected',
},
/** @type {?} */
patchRange: Object,
revisions: Object,
_sortedRevisions: Array,
_rightSelected: String,
_leftSelected: String,
},
observers: ['_updateSortedRevisions(revisions.*)'],
observers: [
'_updateSortedRevisions(revisions.*)',
'_updateSelected(patchRange.*)',
],
behaviors: [Gerrit.PatchSetBehavior],
@ -53,24 +63,20 @@
_handlePatchChange(e) {
const leftPatch = this._leftSelected;
const rightPatch = this._rightSelected;
let rangeStr = rightPatch;
if (leftPatch != 'PARENT') {
rangeStr = leftPatch + '..' + rangeStr;
}
page.show('/c/' + this.changeNum + '/' + rangeStr + '/' + this.path);
this.fire('patch-range-change', {rightPatch, leftPatch});
e.target.blur();
},
_computeLeftDisabled(patchNum, patchRange) {
return this.findSortedIndex(patchNum, this._sortedRevisions) >=
this.findSortedIndex(patchRange.patchNum, this._sortedRevisions);
_computeLeftDisabled(basePatchNum, patchNum, sortedRevisions) {
return this.findSortedIndex(basePatchNum, sortedRevisions) >=
this.findSortedIndex(patchNum, sortedRevisions);
},
_computeRightDisabled(patchNum, patchRange) {
if (patchRange.basePatchNum == 'PARENT') { return false; }
_computeRightDisabled(patchNum, basePatchNum, sortedRevisions) {
if (basePatchNum == 'PARENT') { return false; }
return this.findSortedIndex(patchNum, this._sortedRevisions) <=
this.findSortedIndex(patchRange.basePatchNum, this._sortedRevisions);
return this.findSortedIndex(patchNum, sortedRevisions) <=
this.findSortedIndex(basePatchNum, sortedRevisions);
},
// On page load, the dom-if for options getting added occurs after
@ -86,6 +92,68 @@
this.$.leftPatchSelect.value = this._leftSelected;
},
// Copied from gr-file-list
// @todo(beckysiegel) clean up.
_getCommentsForPath(comments, patchNum, path) {
return (comments[path] || []).filter(c => {
return this.patchNumEquals(c.patch_set, patchNum);
});
},
// Copied from gr-file-list
// @todo(beckysiegel) clean up.
_computeUnresolvedNum(comments, drafts, patchNum, path) {
comments = this._getCommentsForPath(comments, patchNum, path);
drafts = this._getCommentsForPath(drafts, patchNum, path);
comments = comments.concat(drafts);
// Create an object where every comment ID is the key of an unresolved
// comment.
const idMap = comments.reduce((acc, comment) => {
if (comment.unresolved) {
acc[comment.id] = true;
}
return acc;
}, {});
// Set false for the comments that are marked as parents.
for (const comment of comments) {
idMap[comment.in_reply_to] = false;
}
// The unresolved comments are the comments that still have true.
const unresolvedLeaves = Object.keys(idMap).filter(key => {
return idMap[key];
});
return unresolvedLeaves.length;
},
_computePatchSetCommentsString(allComments, patchNum) {
// todo (beckysiegel) get comment strings for diff view also.
if (!allComments) { return ''; }
let numComments = 0;
let numUnresolved = 0;
for (const file in allComments) {
if (allComments.hasOwnProperty(file)) {
numComments += this._getCommentsForPath(
allComments, patchNum, file).length;
numUnresolved += this._computeUnresolvedNum(
allComments, {}, patchNum, file);
}
}
let commentsStr = '';
if (numComments > 0) {
commentsStr = '(' + numComments + ' comments';
if (numUnresolved > 0) {
commentsStr += ', ' + numUnresolved + ' unresolved';
}
commentsStr += ')';
}
return commentsStr;
},
_computePatchSetDescription(revisions, patchNum) {
const rev = this.getRevisionByPatchNum(revisions, patchNum);
return (rev && rev.description) ?

View File

@ -50,38 +50,105 @@ limitations under the License.
basePatchNum: 'PARENT',
patchNum: '3',
};
element._sortedRevisions = [
const sortedRevisions = [
{_number: 1},
{_number: 2},
{_number: element.EDIT_NAME, basePatchNum: 2},
{_number: 3},
];
for (const patchNum of ['1', '2', '3']) {
assert.isFalse(element._computeRightDisabled(patchNum, patchRange));
assert.isFalse(element._computeRightDisabled(patchNum,
patchRange.basePatchNum, sortedRevisions));
}
for (const patchNum of ['PARENT', '1', '2']) {
assert.isFalse(element._computeLeftDisabled(patchNum, patchRange));
assert.isFalse(element._computeLeftDisabled(patchNum,
patchRange.patchNum, sortedRevisions));
}
assert.isTrue(element._computeLeftDisabled('3', patchRange));
assert.isTrue(element._computeLeftDisabled('3', patchRange.patchNum));
patchRange.basePatchNum = element.EDIT_NAME;
assert.isTrue(element._computeLeftDisabled('3', patchRange));
assert.isTrue(element._computeRightDisabled('1', patchRange));
assert.isTrue(element._computeRightDisabled('2', patchRange));
assert.isFalse(element._computeRightDisabled('3', patchRange));
assert.isTrue(element._computeRightDisabled(element.EDIT_NAME, patchRange));
assert.isTrue(element._computeLeftDisabled('3', patchRange.patchNum,
sortedRevisions));
assert.isTrue(element._computeRightDisabled('1', patchRange.basePatchNum,
sortedRevisions));
assert.isTrue(element._computeRightDisabled('2', patchRange.basePatchNum,
sortedRevisions));
assert.isFalse(element._computeRightDisabled('3', patchRange.basePatchNum,
sortedRevisions));
assert.isTrue(element._computeRightDisabled(element.EDIT_NAME,
patchRange.basePatchNum, sortedRevisions));
});
test('navigation', done => {
test('_updateSelected called with subproperty changes', () => {
sandbox.stub(element, '_updateSelected');
element.patchRange = {patchNum: 1, basePatchNum: 'PARENT'};
assert.equal(element._updateSelected.callCount, 1);
element.set('patchRange.patchNum', 2);
assert.equal(element._updateSelected.callCount, 2);
element.set('patchRange.basePatchNum', 1);
assert.equal(element._updateSelected.callCount, 3);
});
test('_computeLeftDisabled called when patchNum updates', () => {
element.revisions = [
{commit: {}},
{commit: {}},
{commit: {}},
{commit: {}},
];
element.availablePatches = [
{num: 1},
{num: 2},
{num: 3},
{num: 'edit'},
];
element.patchRange = {patchNum: 2, basePatchNum: 'PARENT'};
flushAsynchronousOperations();
// Should be recomputed for each available patch
sandbox.stub(element, '_computeLeftDisabled');
element.set('patchRange.patchNum', '1');
assert.equal(element._computeLeftDisabled.callCount, 4);
});
test('_computeRightDisabled called when basePatchNum updates', () => {
element.revisions = [
{commit: {}},
{commit: {}},
{commit: {}},
{commit: {}},
];
element.availablePatches = [
{num: 1},
{num: 2},
{num: 3},
{num: 'edit'},
];
element.patchRange = {patchNum: 2, basePatchNum: 'PARENT'};
flushAsynchronousOperations();
// Should be recomputed for each available patch
sandbox.stub(element, '_computeRightDisabled');
element.set('patchRange.basePatchNum', '1');
assert.equal(element._computeRightDisabled.callCount, 4);
});
test('changes in patch range fire event', done => {
sandbox.stub(element, '_computeLeftDisabled').returns(false);
sandbox.stub(element, '_computeRightDisabled').returns(false);
const showStub = sandbox.stub(page, 'show');
const patchRangeChangedStub = sandbox.stub();
element.addEventListener('patch-range-change', patchRangeChangedStub);
const leftSelectEl = element.$.leftPatchSelect;
const rightSelectEl = element.$.rightPatchSelect;
const blurSpy = sandbox.spy(leftSelectEl, 'blur');
element.changeNum = '42';
element.path = 'path/to/file.txt';
element.availablePatches = ['1', '2', '3'];
element.availablePatches =
[{num: '1'}, {num: '2'}, {num: '3'}, {num: 'edit'}];
element.patchRange = {
basePatchNum: 'PARENT',
patchNum: '3',
@ -91,25 +158,66 @@ limitations under the License.
let numEvents = 0;
leftSelectEl.addEventListener('change', e => {
numEvents++;
if (numEvents == 1) {
assert(showStub.lastCall.calledWithExactly(
'/c/42/3/path/to/file.txt'),
'Should navigate to /c/42/3/path/to/file.txt');
leftSelectEl.nativeSelect.value = '1';
if (numEvents === 1) {
assert.deepEqual(patchRangeChangedStub.lastCall.args[0].detail,
{rightPatch: '3', leftPatch: 'PARENT'});
leftSelectEl.nativeSelect.value = 'edit';
element.fire('change', {}, {node: leftSelectEl});
assert(blurSpy.called, 'Dropdown should be blurred after selection');
} else if (numEvents == 2) {
assert(showStub.lastCall.calledWithExactly(
'/c/42/1..3/path/to/file.txt'),
'Should navigate to /c/42/1..3/path/to/file.txt');
done();
} else if (numEvents === 2) {
assert.deepEqual(patchRangeChangedStub.lastCall.args[0].detail,
{rightPatch: '3', leftPatch: 'edit'});
rightSelectEl.nativeSelect.value = '1';
element.fire('change', {}, {node: rightSelectEl});
}
});
rightSelectEl.addEventListener('change', e => {
assert.deepEqual(patchRangeChangedStub.lastCall.args[0].detail,
{rightPatch: '1', leftPatch: 'edit'});
done();
});
leftSelectEl.nativeSelect.value = 'PARENT';
rightSelectEl.nativeSelect.value = '3';
element.fire('change', {}, {node: leftSelectEl});
});
test('diff against dropdown', done => {
element.revisions = [
{commit: {}},
{commit: {}},
{commit: {}},
{commit: {}},
];
element.availablePatches = [
{num: 1},
{num: 2},
{num: 3},
{num: 'edit'},
];
element.patchRange = {
basePatchNum: 'PARENT',
patchNum: '3',
};
const patchRangeChangedStub = sandbox.stub();
element.addEventListener('patch-range-change', patchRangeChangedStub);
flush(() => {
const selectEl = element.$.leftPatchSelect;
assert.equal(selectEl.nativeSelect.value, 'PARENT');
assert.isTrue(element.$$('#leftPatchSelect option[value="3"]')
.hasAttribute('disabled'));
selectEl.addEventListener('change', () => {
assert.equal(selectEl.nativeSelect.value, 'edit');
assert.deepEqual(patchRangeChangedStub.lastCall.args[0].detail,
{leftPatch: 'edit', rightPatch: '3'});
done();
});
selectEl.nativeSelect.value = 'edit';
element.fire('change', {}, {node: selectEl.nativeSelect});
});
});
test('filesWeblinks', () => {
element.filesWeblinks = {
meta_a: [
@ -132,5 +240,40 @@ limitations under the License.
assert.equal(
domApi.querySelector('a[href="ba.r"]').textContent, 'bar');
});
test('_computePatchSetCommentsString', () => {
// Test string with unresolved comments.
comments = {
foo: [{
id: '27dcee4d_f7b77cfa',
message: 'test',
patch_set: 1,
unresolved: true,
}],
bar: [{
id: '27dcee4d_f7b77cfa',
message: 'test',
patch_set: 1,
},
{
id: '27dcee4d_f7b77cfa',
message: 'test',
patch_set: 1,
}],
abc: [],
};
assert.equal(element._computePatchSetCommentsString(comments, 1),
'(3 comments, 1 unresolved)');
// Test string with no unresolved comments.
delete comments['foo'];
assert.equal(element._computePatchSetCommentsString(comments, 1),
'(2 comments)');
// Test string with no comments.
delete comments['bar'];
assert.equal(element._computePatchSetCommentsString(comments, 1), '');
});
});
</script>