Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

428 lines
13 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<!--
@license
Copyright (C) 2015 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.
-->
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-patch-range-select</title>
<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
<script src="../../../bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<script src="../../../bower_components/page/page.js"></script>
<link rel="import" href="../../diff/gr-comment-api/gr-comment-api.html">
<link rel="import" href="../../shared/gr-rest-api-interface/mock-diff-response_test.html">
<link rel="import" href="../../shared/revision-info/revision-info.html">
<link rel="import" href="gr-patch-range-select.html">
<script>void(0);</script>
<dom-module id="comment-api-mock">
<template>
<gr-patch-range-select id="patchRange" auto
change-comments="[[_changeComments]]"></gr-patch-range-select>
<gr-comment-api id="commentAPI"></gr-comment-api>
</template>
<script src="../../diff/gr-comment-api/gr-comment-api-mock.js"></script>
</dom-module>
<test-fixture id="basic">
<template>
<comment-api-mock></comment-api-mock>
</template>
</test-fixture>
<script>
suite('gr-patch-range-select tests', () => {
let element;
let sandbox;
let commentApiWrapper;
function getInfo(revisions) {
const revisionObj = {};
for (let i = 0; i < revisions.length; i++) {
revisionObj[i] = revisions[i];
}
return new Gerrit.RevisionInfo({revisions: revisionObj});
}
setup(() => {
sandbox = sinon.sandbox.create();
stub('gr-rest-api-interface', {
getDiffComments() { return Promise.resolve({}); },
getDiffRobotComments() { return Promise.resolve({}); },
getDiffDrafts() { return Promise.resolve({}); },
});
// Element must be wrapped in an element with direct access to the
// comment API.
commentApiWrapper = fixture('basic');
element = commentApiWrapper.$.patchRange;
// Stub methods on the changeComments object after changeComments has
// been initalized.
return commentApiWrapper.loadComments();
});
teardown(() => sandbox.restore());
test('enabled/disabled options', () => {
const patchRange = {
basePatchNum: 'PARENT',
patchNum: '3',
};
const sortedRevisions = [
{_number: 3},
{_number: element.EDIT_NAME, basePatchNum: 2},
{_number: 2},
{_number: 1},
];
for (const patchNum of ['1', '2', '3']) {
assert.isFalse(element._computeRightDisabled(patchRange.basePatchNum,
patchNum, sortedRevisions));
}
for (const basePatchNum of ['1', '2']) {
assert.isFalse(element._computeLeftDisabled(basePatchNum,
patchRange.patchNum, sortedRevisions));
}
assert.isTrue(element._computeLeftDisabled('3', patchRange.patchNum));
patchRange.basePatchNum = element.EDIT_NAME;
assert.isTrue(element._computeLeftDisabled('3', patchRange.patchNum,
sortedRevisions));
assert.isTrue(element._computeRightDisabled(patchRange.basePatchNum, '1',
sortedRevisions));
assert.isTrue(element._computeRightDisabled(patchRange.basePatchNum, '2',
sortedRevisions));
assert.isFalse(element._computeRightDisabled(patchRange.basePatchNum, '3',
sortedRevisions));
assert.isTrue(element._computeRightDisabled(patchRange.basePatchNum,
element.EDIT_NAME, sortedRevisions));
});
test('_computeBaseDropdownContent', () => {
const availablePatches = [
{num: 'edit'},
{num: 3},
{num: 2},
{num: 1},
];
const revisions = [
{
commit: {parents: []},
_number: 2,
description: 'description',
},
{commit: {parents: []}},
{commit: {parents: []}},
{commit: {parents: []}},
];
element.revisionInfo = getInfo(revisions);
const patchNum = 1;
const sortedRevisions = [
{_number: 3, created: 'Mon, 01 Jan 2001 00:00:00 GMT'},
{_number: element.EDIT_NAME, basePatchNum: 2},
{_number: 2, description: 'description'},
{_number: 1},
];
const expectedResult = [
{
disabled: true,
triggerText: 'Patchset edit',
text: 'Patchset edit',
mobileText: 'edit',
bottomText: '',
value: 'edit',
},
{
disabled: true,
triggerText: 'Patchset 3',
text: 'Patchset 3',
mobileText: '3',
bottomText: '',
value: 3,
date: 'Mon, 01 Jan 2001 00:00:00 GMT',
},
{
disabled: true,
triggerText: 'Patchset 2',
text: 'Patchset 2',
mobileText: '2 description',
bottomText: 'description',
value: 2,
},
{
disabled: true,
triggerText: 'Patchset 1',
text: 'Patchset 1',
mobileText: '1',
bottomText: '',
value: 1,
},
{
text: 'Base',
value: 'PARENT',
},
];
assert.deepEqual(element._computeBaseDropdownContent(availablePatches,
patchNum, sortedRevisions, element.changeComments,
element.revisionInfo),
expectedResult);
});
test('_computeBaseDropdownContent called when patchNum updates', () => {
element.revisions = [
{commit: {parents: []}},
{commit: {parents: []}},
{commit: {parents: []}},
{commit: {parents: []}},
];
element.revisionInfo = getInfo(element.revisions);
element.availablePatches = [
{num: 1},
{num: 2},
{num: 3},
{num: 'edit'},
];
element.patchNum = 2;
element.basePatchNum = 'PARENT';
flushAsynchronousOperations();
sandbox.stub(element, '_computeBaseDropdownContent');
// Should be recomputed for each available patch
element.set('patchNum', 1);
assert.equal(element._computeBaseDropdownContent.callCount, 1);
});
test('_computeBaseDropdownContent called when changeComments update',
done => {
element.revisions = [
{commit: {parents: []}},
{commit: {parents: []}},
{commit: {parents: []}},
{commit: {parents: []}},
];
element.revisionInfo = getInfo(element.revisions);
element.availablePatches = [
{num: 'edit'},
{num: 3},
{num: 2},
{num: 1},
];
element.patchNum = 2;
element.basePatchNum = 'PARENT';
flushAsynchronousOperations();
// Should be recomputed for each available patch
sandbox.stub(element, '_computeBaseDropdownContent');
assert.equal(element._computeBaseDropdownContent.callCount, 0);
commentApiWrapper.loadComments().then().then(() => {
assert.equal(element._computeBaseDropdownContent.callCount, 1);
done();
});
});
test('_computePatchDropdownContent called when basePatchNum updates', () => {
element.revisions = [
{commit: {parents: []}},
{commit: {parents: []}},
{commit: {parents: []}},
{commit: {parents: []}},
];
element.revisionInfo = getInfo(element.revisions);
element.availablePatches = [
{num: 1},
{num: 2},
{num: 3},
{num: 'edit'},
];
element.patchNum = 2;
element.basePatchNum = 'PARENT';
flushAsynchronousOperations();
// Should be recomputed for each available patch
sandbox.stub(element, '_computePatchDropdownContent');
element.set('basePatchNum', 1);
assert.equal(element._computePatchDropdownContent.callCount, 1);
});
test('_computePatchDropdownContent called when comments update', done => {
element.revisions = [
{commit: {parents: []}},
{commit: {parents: []}},
{commit: {parents: []}},
{commit: {parents: []}},
];
element.revisionInfo = getInfo(element.revisions);
element.availablePatches = [
{num: 1},
{num: 2},
{num: 3},
{num: 'edit'},
];
element.patchNum = 2;
element.basePatchNum = 'PARENT';
flushAsynchronousOperations();
// Should be recomputed for each available patch
sandbox.stub(element, '_computePatchDropdownContent');
assert.equal(element._computePatchDropdownContent.callCount, 0);
commentApiWrapper.loadComments().then().then(() => {
done();
});
});
test('_computePatchDropdownContent', () => {
const availablePatches = [
{num: 'edit'},
{num: 3},
{num: 2},
{num: 1},
];
const basePatchNum = 1;
const sortedRevisions = [
{_number: 3, created: 'Mon, 01 Jan 2001 00:00:00 GMT'},
{_number: element.EDIT_NAME, basePatchNum: 2},
{_number: 2, description: 'description'},
{_number: 1},
];
const expectedResult = [
{
disabled: false,
triggerText: 'edit',
text: 'edit',
mobileText: 'edit',
bottomText: '',
value: 'edit',
},
{
disabled: false,
triggerText: 'Patchset 3',
text: 'Patchset 3',
mobileText: '3',
bottomText: '',
value: 3,
date: 'Mon, 01 Jan 2001 00:00:00 GMT',
},
{
disabled: false,
triggerText: 'Patchset 2',
text: 'Patchset 2',
mobileText: '2 description',
bottomText: 'description',
value: 2,
},
{
disabled: true,
triggerText: 'Patchset 1',
text: 'Patchset 1',
mobileText: '1',
bottomText: '',
value: 1,
},
];
assert.deepEqual(element._computePatchDropdownContent(availablePatches,
basePatchNum, sortedRevisions, element.changeComments),
expectedResult);
});
test('filesWeblinks', () => {
element.filesWeblinks = {
meta_a: [
{
name: 'foo',
url: 'f.oo',
},
],
meta_b: [
{
name: 'bar',
url: 'ba.r',
},
],
};
flushAsynchronousOperations();
const domApi = Polymer.dom(element.root);
assert.equal(
domApi.querySelector('a[href="f.oo"]').textContent, 'foo');
assert.equal(
domApi.querySelector('a[href="ba.r"]').textContent, 'bar');
});
test('_computePatchSetCommentsString', () => {
// Test string with unresolved comments.
element.changeComments._comments = {
foo: [{
id: '27dcee4d_f7b77cfa',
message: 'test',
patch_set: 1,
unresolved: true,
Count unresolved threads within thread groups rather than by leaves Diff comments are threaded together based on the in_reply_to relation (which potentially expresses a tree structure) but are always displayed linearly in the UI. This means that some comments in the middle of a linear thread may be actually stored as leaves of a tree. For example, the following thread of comments can be created if comments two and three are created at nearly the same time. Comment 1: thread root, unresolved, ┣ Comment 2: in reply to comment 1, unresolved, ┗ Comment 3: also in reply to comment 1, unresolved, ┗ Comment 4: in reply to comment 3, resolved Because the thread is flattened, the resolved state of the thread should be determined by the state of the chronologically latest comment (#4), resulting in this thread being considered as resolved. However, in a couple of locations, the resolved state is counted differently. Namely, it finds the "leaf" comments -- that is, the comments that are not marked as the parent of any other comment -- and the number of unresolved threads is determined as the number of unresolved leaves. This approach was used by: - ChangeComments#computeUnresolvedNum in the UI, which determines the string stating the number of unresolved threads in a file row. - ChangeData#unresolvedCommentCount in the Java server code, which determines, among other things, the value of the unresolved_comment_count change detail property, as well as the Prolog fact used by the Prolog recipe that requires all comments to be resolved before a change can be submitted. Instead, the unresolved thread logic is modified to group comments into flat threads, and consider the resolved state of each one based on the chronologically final comment, irregardless of the leaves. Bug: Issue 8472 Change-Id: I2788fdb22ecfd56f0b3da763790a7732ec73be33
2018-03-01 14:25:06 -08:00
updated: '2017-10-11 20:48:40.000000000',
}],
bar: [{
id: '27dcee4d_f7b77cfa',
message: 'test',
patch_set: 1,
Count unresolved threads within thread groups rather than by leaves Diff comments are threaded together based on the in_reply_to relation (which potentially expresses a tree structure) but are always displayed linearly in the UI. This means that some comments in the middle of a linear thread may be actually stored as leaves of a tree. For example, the following thread of comments can be created if comments two and three are created at nearly the same time. Comment 1: thread root, unresolved, ┣ Comment 2: in reply to comment 1, unresolved, ┗ Comment 3: also in reply to comment 1, unresolved, ┗ Comment 4: in reply to comment 3, resolved Because the thread is flattened, the resolved state of the thread should be determined by the state of the chronologically latest comment (#4), resulting in this thread being considered as resolved. However, in a couple of locations, the resolved state is counted differently. Namely, it finds the "leaf" comments -- that is, the comments that are not marked as the parent of any other comment -- and the number of unresolved threads is determined as the number of unresolved leaves. This approach was used by: - ChangeComments#computeUnresolvedNum in the UI, which determines the string stating the number of unresolved threads in a file row. - ChangeData#unresolvedCommentCount in the Java server code, which determines, among other things, the value of the unresolved_comment_count change detail property, as well as the Prolog fact used by the Prolog recipe that requires all comments to be resolved before a change can be submitted. Instead, the unresolved thread logic is modified to group comments into flat threads, and consider the resolved state of each one based on the chronologically final comment, irregardless of the leaves. Bug: Issue 8472 Change-Id: I2788fdb22ecfd56f0b3da763790a7732ec73be33
2018-03-01 14:25:06 -08:00
updated: '2017-10-12 20:48:40.000000000',
},
{
id: '27dcee4d_f7b77cfa',
message: 'test',
patch_set: 1,
Count unresolved threads within thread groups rather than by leaves Diff comments are threaded together based on the in_reply_to relation (which potentially expresses a tree structure) but are always displayed linearly in the UI. This means that some comments in the middle of a linear thread may be actually stored as leaves of a tree. For example, the following thread of comments can be created if comments two and three are created at nearly the same time. Comment 1: thread root, unresolved, ┣ Comment 2: in reply to comment 1, unresolved, ┗ Comment 3: also in reply to comment 1, unresolved, ┗ Comment 4: in reply to comment 3, resolved Because the thread is flattened, the resolved state of the thread should be determined by the state of the chronologically latest comment (#4), resulting in this thread being considered as resolved. However, in a couple of locations, the resolved state is counted differently. Namely, it finds the "leaf" comments -- that is, the comments that are not marked as the parent of any other comment -- and the number of unresolved threads is determined as the number of unresolved leaves. This approach was used by: - ChangeComments#computeUnresolvedNum in the UI, which determines the string stating the number of unresolved threads in a file row. - ChangeData#unresolvedCommentCount in the Java server code, which determines, among other things, the value of the unresolved_comment_count change detail property, as well as the Prolog fact used by the Prolog recipe that requires all comments to be resolved before a change can be submitted. Instead, the unresolved thread logic is modified to group comments into flat threads, and consider the resolved state of each one based on the chronologically final comment, irregardless of the leaves. Bug: Issue 8472 Change-Id: I2788fdb22ecfd56f0b3da763790a7732ec73be33
2018-03-01 14:25:06 -08:00
updated: '2017-10-13 20:48:40.000000000',
}],
abc: [],
};
assert.equal(element._computePatchSetCommentsString(
element.changeComments, 1), ' (3 comments, 1 unresolved)');
// Test string with no unresolved comments.
delete element.changeComments._comments['foo'];
assert.equal(element._computePatchSetCommentsString(
element.changeComments, 1), ' (2 comments)');
// Test string with no comments.
delete element.changeComments._comments['bar'];
assert.equal(element._computePatchSetCommentsString(
element.changeComments, 1), '');
});
test('patch-range-change fires', () => {
const handler = sandbox.stub();
element.basePatchNum = 1;
element.patchNum = 3;
element.addEventListener('patch-range-change', handler);
element.$.basePatchDropdown._handleValueChange(2, [{value: 2}]);
assert.isTrue(handler.calledOnce);
assert.deepEqual(handler.lastCall.args[0].detail,
{basePatchNum: 2, patchNum: 3});
// BasePatchNum should not have changed, due to one-way data binding.
element.$.patchNumDropdown._handleValueChange('edit', [{value: 'edit'}]);
assert.deepEqual(handler.lastCall.args[0].detail,
{basePatchNum: 1, patchNum: 'edit'});
});
});
</script>