blob: d22c7d097b396e74a348652c68753ccbe6b84cf9 [file] [log] [blame]
/**
* @license
* Copyright (C) 2020 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.
*/
import '../../../test/common-test-setup-karma.js';
import '../../diff/gr-comment-api/gr-comment-api.js';
import './gr-messages-list.js';
import {createCommentApiMockWithTemplateElement} from '../../../test/mocks/comment-api.js';
import {TEST_ONLY} from './gr-messages-list.js';
import {MessageTag} from '../../../constants/constants.js';
import {html} from '@polymer/polymer/lib/utils/html-tag.js';
import {stubRestApi} from '../../../test/test-utils.js';
createCommentApiMockWithTemplateElement(
'gr-messages-list-comment-mock-api', html`
<gr-messages-list
id="messagesList"
change-comments="[[_changeComments]]"></gr-messages-list>
<gr-comment-api id="commentAPI"></gr-comment-api>
`);
const basicFixture = fixtureFromTemplate(html`
<gr-messages-list-comment-mock-api>
<gr-messages-list></gr-messages-list>
</gr-messages-list-comment-mock-api>
`);
const randomMessage = function(opt_params) {
const params = opt_params || {};
const author1 = {
_account_id: 1115495,
name: 'Andrew Bonventre',
email: 'andybons@chromium.org',
};
return {
id: params.id || Math.random().toString(),
date: params.date || '2016-01-12 20:28:33.038000',
message: params.message || Math.random().toString(),
_revision_number: params._revision_number || 1,
author: params.author || author1,
tag: params.tag,
};
};
function generateRandomMessages(count) {
return new Array(count).fill()
.map(() => randomMessage());
}
suite('gr-messages-list tests', () => {
let element;
let messages;
let commentApiWrapper;
const getMessages = function() {
return element.root.querySelectorAll('gr-message');
};
const MESSAGE_ID_0 = '1234ccc949c6d482b061be6a28e10782abf0e7af';
const MESSAGE_ID_1 = '8c19ccc949c6d482b061be6a28e10782abf0e7af';
const MESSAGE_ID_2 = 'e7bfdbc842f6b6d8064bc68e0f52b673f40c0ca5';
const author = {
_account_id: 42,
name: 'Marvin the Paranoid Android',
email: 'marvin@sirius.org',
};
const createComment = function() {
return {
id: '1a2b3c4d',
message: 'some random test text',
change_message_id: '8a7b6c5d',
updated: '2016-01-01 01:02:03.000000000',
line: 1,
patch_set: 1,
author,
};
};
const comments = {
file1: [
{
...createComment(),
change_message_id: MESSAGE_ID_0,
in_reply_to: '6505d749_f0bec0aa',
author: {
email: 'some@email.com',
_account_id: 123,
},
},
{
...createComment(),
id: '2b3c4d5e',
change_message_id: MESSAGE_ID_1,
in_reply_to: 'c5912363_6b820105',
},
{
...createComment(),
id: '2b3c4d5e',
change_message_id: MESSAGE_ID_1,
in_reply_to: '6505d749_f0bec0aa',
},
{
...createComment(),
id: '34ed05d749_10ed44b2',
change_message_id: MESSAGE_ID_2,
},
],
file2: [
{
...createComment(),
change_message_id: MESSAGE_ID_1,
in_reply_to: 'c5912363_4b7d450a',
id: '450a935e_4f260d25',
},
],
};
suite('basic tests', () => {
setup(() => {
stubRestApi('getConfig').returns(Promise.resolve({}));
stubRestApi('getLoggedIn').returns(Promise.resolve(false));
stubRestApi('getDiffComments').returns(Promise.resolve(comments));
stubRestApi('getDiffRobotComments').returns(Promise.resolve({}));
stubRestApi('getDiffDrafts').returns(Promise.resolve({}));
messages = generateRandomMessages(3);
// Element must be wrapped in an element with direct access to the
// comment API.
commentApiWrapper = basicFixture.instantiate();
element = commentApiWrapper.$.messagesList;
element.messages = messages;
// Stub methods on the changeComments object after changeComments has
// been initialized.
return commentApiWrapper.loadComments();
});
test('expand/collapse all', () => {
let allMessageEls = getMessages();
for (const message of allMessageEls) {
message._expanded = false;
}
MockInteractions.tap(allMessageEls[1]);
assert.isTrue(allMessageEls[1]._expanded);
MockInteractions.tap(element.shadowRoot
.querySelector('#collapse-messages'));
allMessageEls = getMessages();
for (const message of allMessageEls) {
assert.isTrue(message._expanded);
}
MockInteractions.tap(element.shadowRoot
.querySelector('#collapse-messages'));
allMessageEls = getMessages();
for (const message of allMessageEls) {
assert.isFalse(message._expanded);
}
});
test('expand/collapse from external keypress', () => {
// Start with one expanded message. -> not all collapsed
element.scrollToMessage(messages[1].id);
assert.isFalse([...getMessages()].filter(m => m._expanded).length == 0);
// Press 'z' -> all collapsed
element.handleExpandCollapse(false);
assert.isTrue([...getMessages()].filter(m => m._expanded).length == 0);
// Press 'x' -> all expanded
element.handleExpandCollapse(true);
assert.isTrue([...getMessages()].filter(m => !m._expanded).length == 0);
// Press 'z' -> all collapsed
element.handleExpandCollapse(false);
assert.isTrue([...getMessages()].filter(m => m._expanded).length == 0);
});
test('showAllActivity does not appear when all msgs are important', () => {
assert.isOk(element.shadowRoot
.querySelector('#showAllActivityToggleContainer'));
assert.isNotOk(element.shadowRoot
.querySelector('.showAllActivityToggle'));
});
test('scroll to message', () => {
const allMessageEls = getMessages();
for (const message of allMessageEls) {
message.set('message.expanded', false);
}
const scrollToStub = sinon.stub(window, 'scrollTo');
const highlightStub = sinon.stub(element, '_highlightEl');
element.scrollToMessage('invalid');
for (const message of allMessageEls) {
assert.isFalse(message._expanded,
'expected gr-message to not be expanded');
}
const messageID = messages[1].id;
element.scrollToMessage(messageID);
assert.isTrue(
element.shadowRoot
.querySelector('[data-message-id="' + messageID + '"]')
._expanded);
assert.isTrue(scrollToStub.calledOnce);
assert.isTrue(highlightStub.calledOnce);
});
test('scroll to message offscreen', () => {
const scrollToStub = sinon.stub(window, 'scrollTo');
const highlightStub = sinon.stub(element, '_highlightEl');
element.messages = generateRandomMessages(25);
flush();
assert.isFalse(scrollToStub.called);
assert.isFalse(highlightStub.called);
const messageID = element.messages[1].id;
element.scrollToMessage(messageID);
assert.isTrue(scrollToStub.calledOnce);
assert.isTrue(highlightStub.calledOnce);
assert.isTrue(
element.shadowRoot
.querySelector('[data-message-id="' + messageID + '"]')
._expanded);
});
test('associating messages with comments', () => {
const messages = [].concat(
randomMessage(),
{
_index: 5,
_revision_number: 4,
message: 'Uploaded patch set 4.',
date: '2016-09-28 13:36:33.000000000',
author,
id: '8c19ccc949c6d482b061be6a28e10782abf0e7af',
},
{
_index: 6,
_revision_number: 4,
message: 'Patch Set 4:\n\n(6 comments)',
date: '2016-09-28 13:36:33.000000000',
author,
id: 'e7bfdbc842f6b6d8064bc68e0f52b673f40c0ca5',
}
);
element.messages = messages;
flush();
const messageElements = getMessages();
assert.equal(messageElements.length, messages.length);
assert.deepEqual(messageElements[1].message, messages[1]);
assert.deepEqual(messageElements[2].message, messages[2]);
});
test('threads', () => {
const messages = [
{
_index: 5,
_revision_number: 4,
message: 'Uploaded patch set 4.',
date: '2016-09-28 13:36:33.000000000',
author,
id: '8c19ccc949c6d482b061be6a28e10782abf0e7af',
},
];
element.messages = messages;
flush();
const messageElements = getMessages();
// threads
assert.equal(
messageElements[0].message.commentThreads.length,
3);
// first thread contains 1 comment
assert.equal(
messageElements[0].message.commentThreads[0].comments.length,
1);
});
test('updateTag human message', () => {
const m = randomMessage();
assert.equal(TEST_ONLY.computeTag(m), undefined);
});
test('updateTag nothing to change', () => {
const m = randomMessage();
const tag = 'something-normal';
m.tag = tag;
assert.equal(TEST_ONLY.computeTag(m), tag);
});
test('updateTag TAG_NEW_WIP_PATCHSET', () => {
const m = randomMessage();
m.tag = MessageTag.TAG_NEW_WIP_PATCHSET;
assert.equal(TEST_ONLY.computeTag(m), MessageTag.TAG_NEW_PATCHSET);
});
test('updateTag remove postfix', () => {
const m = randomMessage();
m.tag = 'something~withpostfix';
assert.equal(TEST_ONLY.computeTag(m), 'something');
});
test('updateTag with robot comments', () => {
const m = randomMessage();
m.commentThreads = [{
comments: [{
robot_id: 'id314',
change_message_id: m.id,
}],
}];
assert.notEqual(TEST_ONLY.computeTag(m), undefined);
});
test('setRevisionNumber nothing to change', () => {
const m1 = randomMessage();
const m2 = randomMessage();
assert.equal(TEST_ONLY.computeRevision(m1, [m1, m2]), 1);
assert.equal(TEST_ONLY.computeRevision(m2, [m1, m2]), 1);
});
test('setRevisionNumber reviewer updates', () => {
const m1 = randomMessage(
{
tag: MessageTag.TAG_REVIEWER_UPDATE,
date: '2020-01-01 10:00:00.000000000',
});
m1._revision_number = undefined;
const m2 = randomMessage(
{
date: '2020-01-02 10:00:00.000000000',
});
m2._revision_number = 1;
const m3 = randomMessage(
{
tag: MessageTag.TAG_REVIEWER_UPDATE,
date: '2020-01-03 10:00:00.000000000',
});
m3._revision_number = undefined;
const m4 = randomMessage(
{
date: '2020-01-04 10:00:00.000000000',
});
m4._revision_number = 2;
const m5 = randomMessage(
{
tag: MessageTag.TAG_REVIEWER_UPDATE,
date: '2020-01-05 10:00:00.000000000',
});
m5._revision_number = undefined;
const allMessages = [m1, m2, m3, m4, m5];
assert.equal(TEST_ONLY.computeRevision(m1, allMessages), undefined);
assert.equal(TEST_ONLY.computeRevision(m2, allMessages), 1);
assert.equal(TEST_ONLY.computeRevision(m3, allMessages), 1);
assert.equal(TEST_ONLY.computeRevision(m4, allMessages), 2);
assert.equal(TEST_ONLY.computeRevision(m5, allMessages), 2);
});
test('isImportant human message', () => {
const m = randomMessage();
assert.isTrue(TEST_ONLY.computeIsImportant(m, []));
assert.isTrue(TEST_ONLY.computeIsImportant(m, [m]));
});
test('isImportant even with a tag', () => {
const m1 = randomMessage();
const m2 = randomMessage({tag: 'autogenerated:gerrit1'});
const m3 = randomMessage({tag: 'autogenerated:gerrit2'});
assert.isTrue(TEST_ONLY.computeIsImportant(m2, []));
assert.isTrue(TEST_ONLY.computeIsImportant(m1, [m1, m2, m3]));
assert.isTrue(TEST_ONLY.computeIsImportant(m2, [m1, m2, m3]));
assert.isTrue(TEST_ONLY.computeIsImportant(m3, [m1, m2, m3]));
});
test('isImportant filters same tag and older revision', () => {
const m1 = randomMessage({tag: 'auto', _revision_number: 2});
const m2 = randomMessage({tag: 'auto', _revision_number: 1});
const m3 = randomMessage({tag: 'auto'});
assert.isTrue(TEST_ONLY.computeIsImportant(m1, [m1]));
assert.isTrue(TEST_ONLY.computeIsImportant(m2, [m2]));
assert.isTrue(TEST_ONLY.computeIsImportant(m1, [m1, m2]));
assert.isFalse(TEST_ONLY.computeIsImportant(m2, [m1, m2]));
assert.isTrue(TEST_ONLY.computeIsImportant(m1, [m1, m3]));
assert.isFalse(TEST_ONLY.computeIsImportant(m3, [m1, m3]));
assert.isTrue(TEST_ONLY.computeIsImportant(m1, [m1, m2, m3]));
assert.isFalse(TEST_ONLY.computeIsImportant(m2, [m1, m2, m3]));
assert.isFalse(TEST_ONLY.computeIsImportant(m3, [m1, m2, m3]));
});
test('isImportant is evaluated after tag update', () => {
const m1 = randomMessage(
{tag: MessageTag.TAG_NEW_PATCHSET, _revision_number: 1});
const m2 = randomMessage(
{tag: MessageTag.TAG_NEW_WIP_PATCHSET, _revision_number: 2});
element.messages = [m1, m2];
flush();
assert.isFalse(m1.isImportant);
assert.isTrue(m2.isImportant);
});
test('messages without author do not throw', () => {
const messages = [{
_index: 5,
_revision_number: 4,
message: 'Uploaded patch set 4.',
date: '2016-09-28 13:36:33.000000000',
id: '8c19ccc949c6d482b061be6a28e10782abf0e7af',
}];
element.messages = messages;
flush();
const messageEls = getMessages();
assert.equal(messageEls.length, 1);
assert.equal(messageEls[0].message.message, messages[0].message);
});
});
suite('gr-messages-list automate tests', () => {
let element;
let messages;
let commentApiWrapper;
setup(() => {
stubRestApi('getConfig').returns(Promise.resolve({}));
stubRestApi('getLoggedIn').returns(Promise.resolve(false));
stubRestApi('getDiffComments').returns(Promise.resolve({}));
stubRestApi('getDiffRobotComments').returns(Promise.resolve({}));
stubRestApi('getDiffDrafts').returns(Promise.resolve({}));
messages = [
randomMessage(),
randomMessage({tag: 'auto', _revision_number: 2}),
randomMessage({tag: 'auto', _revision_number: 3}),
];
// Element must be wrapped in an element with direct access to the
// comment API.
commentApiWrapper = basicFixture.instantiate();
element = commentApiWrapper.$.messagesList;
sinon.spy(commentApiWrapper.$.commentAPI, 'loadAll');
element.messages = messages;
// Stub methods on the changeComments object after changeComments has
// been initialized.
return commentApiWrapper.loadComments();
});
test('hide autogenerated button is not hidden', () => {
const toggle = element.root.querySelector('.showAllActivityToggle');
assert.isOk(toggle);
});
test('one unimportant message is hidden initially', () => {
const displayedMsgs = element.root.querySelectorAll('gr-message');
assert.equal(displayedMsgs.length, 2);
});
test('unimportant messages hidden after toggle', () => {
element._showAllActivity = true;
const toggle = element.root.querySelector('.showAllActivityToggle');
assert.isOk(toggle);
MockInteractions.tap(toggle);
flush();
const displayedMsgs = element.root.querySelectorAll('gr-message');
assert.equal(displayedMsgs.length, 2);
});
test('unimportant messages shown after toggle', () => {
element._showAllActivity = false;
const toggle = element.root.querySelector('.showAllActivityToggle');
assert.isOk(toggle);
MockInteractions.tap(toggle);
flush();
const displayedMsgs = element.root.querySelectorAll('gr-message');
assert.equal(displayedMsgs.length, 3);
});
test('_computeLabelExtremes', () => {
const computeSpy = sinon.spy(element, '_computeLabelExtremes');
element.labels = null;
assert.isTrue(computeSpy.calledOnce);
assert.deepEqual(computeSpy.lastCall.returnValue, {});
element.labels = {};
assert.isTrue(computeSpy.calledTwice);
assert.deepEqual(computeSpy.lastCall.returnValue, {});
element.labels = {'my-label': {}};
assert.isTrue(computeSpy.calledThrice);
assert.deepEqual(computeSpy.lastCall.returnValue, {});
element.labels = {'my-label': {values: {}}};
assert.equal(computeSpy.callCount, 4);
assert.deepEqual(computeSpy.lastCall.returnValue, {});
element.labels = {'my-label': {values: {'-12': {}}}};
assert.equal(computeSpy.callCount, 5);
assert.deepEqual(computeSpy.lastCall.returnValue,
{'my-label': {min: -12, max: -12}});
element.labels = {
'my-label': {values: {'-2': {}, '-1': {}, '0': {}, '+1': {}, '+2': {}}},
};
assert.equal(computeSpy.callCount, 6);
assert.deepEqual(computeSpy.lastCall.returnValue,
{'my-label': {min: -2, max: 2}});
element.labels = {
'my-label': {values: {'-12': {}}},
'other-label': {values: {'-1': {}, ' 0': {}, '+1': {}}},
};
assert.equal(computeSpy.callCount, 7);
assert.deepEqual(computeSpy.lastCall.returnValue, {
'my-label': {min: -12, max: -12},
'other-label': {min: -1, max: 1},
});
});
});
});