blob: 4f05dfc7652b573b2737123d5aa36036ae665856 [file] [log] [blame]
import {createCommentApiMockWithTemplateElement} from '../../../test/mocks/comment-api';
/**
* @license
* Copyright (C) 2016 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 '../../../test/mocks/comment-api.js';
import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
import {html} from '@polymer/polymer/lib/utils/html-tag.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,
};
};
const randomAutomated = function(opt_params) {
return Object.assign({tag: 'autogenerated:gerrit:replace'},
randomMessage(opt_params));
};
function generateRandomMessages(count) {
return new Array(count).fill()
.map(() => randomMessage());
}
function generateRandomAutomatedMessages(count) {
return new Array(count).fill()
.map(() => randomAutomated());
}
// Returns a shuffled copy of array
export function shuffle(arr) {
const result = [];
for (const item of arr) {
// Random number in the interval [0..array.length]
const j = Math.floor(Math.random() * (arr.length + 1));
if (j < result.length) {
result.push(result[j]);
result[j] = item;
} else {
result.push(item);
}
}
return result;
}
suite('gr-messages-list tests', () => {
let element;
let messages;
let commentApiWrapper;
const getMessages = function() {
return dom(element.root).querySelectorAll('gr-message');
};
const author = {
_account_id: 42,
name: 'Marvin the Paranoid Android',
email: 'marvin@sirius.org',
};
const comments = {
file1: [
{
message: 'message text',
updated: '2016-09-27 00:18:03.000000000',
in_reply_to: '6505d749_f0bec0aa',
line: 62,
id: '6505d749_10ed44b2',
patch_set: 2,
author: {
email: 'some@email.com',
_account_id: 123,
},
},
{
message: 'message text',
updated: '2016-09-27 00:18:03.000000000',
in_reply_to: 'c5912363_6b820105',
line: 42,
id: '450a935e_0f1c05db',
patch_set: 2,
author,
},
{
message: 'message text',
updated: '2016-09-27 00:18:03.000000000',
in_reply_to: '6505d749_f0bec0aa',
line: 62,
id: '6505d749_10ed44b2',
patch_set: 2,
author,
},
],
file2: [
{
message: 'message text',
updated: '2016-09-27 00:18:03.000000000',
in_reply_to: 'c5912363_4b7d450a',
line: 132,
id: '450a935e_4f260d25',
patch_set: 2,
author,
},
],
};
suite('basic tests', () => {
setup(() => {
stub('gr-rest-api-interface', {
getConfig() { return Promise.resolve({}); },
getLoggedIn() { return Promise.resolve(false); },
getDiffComments() { return Promise.resolve(comments); },
getDiffRobotComments() { return Promise.resolve({}); },
getDiffDrafts() { return 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('show some old messages', () => {
assert.isTrue(element.$.messageControlsContainer.hasAttribute('hidden'));
element.messages = generateRandomMessages(26);
flushAsynchronousOperations();
assert.isFalse(element.$.messageControlsContainer.hasAttribute('hidden'));
assert.equal(getMessages().length, 20);
assert.equal(element.$.incrementMessagesBtn.innerText.toUpperCase()
.trim(), 'SHOW 5 MORE');
MockInteractions.tap(element.$.incrementMessagesBtn);
flushAsynchronousOperations();
assert.equal(getMessages().length, 25);
assert.equal(element.$.incrementMessagesBtn.innerText.toUpperCase()
.trim(), 'SHOW 1 MORE');
MockInteractions.tap(element.$.incrementMessagesBtn);
flushAsynchronousOperations();
assert.isTrue(element.$.messageControlsContainer.hasAttribute('hidden'));
assert.equal(getMessages().length, 26);
});
test('show all old messages', () => {
assert.isTrue(element.$.messageControlsContainer.hasAttribute('hidden'));
element.messages = generateRandomMessages(26);
flushAsynchronousOperations();
assert.isFalse(element.$.messageControlsContainer.hasAttribute('hidden'));
assert.equal(getMessages().length, 20);
assert.equal(element.$.oldMessagesBtn.innerText.toUpperCase(),
'SHOW ALL 6 MESSAGES');
MockInteractions.tap(element.$.oldMessagesBtn);
flushAsynchronousOperations();
assert.equal(getMessages().length, 26);
assert.isTrue(element.$.messageControlsContainer.hasAttribute('hidden'));
});
test('message count respects automated', () => {
element.messages = generateRandomAutomatedMessages(10)
.concat(generateRandomMessages(11));
flushAsynchronousOperations();
assert.equal(element.$.oldMessagesBtn.innerText.toUpperCase(),
'SHOW 1 MESSAGE');
assert.isFalse(element.$.messageControlsContainer.hasAttribute('hidden'));
MockInteractions.tap(element.$.automatedMessageToggle);
flushAsynchronousOperations();
assert.isTrue(element.$.messageControlsContainer.hasAttribute('hidden'));
});
test('message count still respects non-automated on toggle', () => {
element.messages = generateRandomMessages(10)
.concat(generateRandomAutomatedMessages(11));
flushAsynchronousOperations();
assert.equal(element.$.oldMessagesBtn.innerText.toUpperCase(),
'SHOW 1 MESSAGE');
assert.isFalse(element.$.messageControlsContainer.hasAttribute('hidden'));
MockInteractions.tap(element.$.automatedMessageToggle);
flushAsynchronousOperations();
assert.equal(element.$.oldMessagesBtn.innerText.toUpperCase(),
'SHOW 1 MESSAGE');
assert.isFalse(element.$.messageControlsContainer.hasAttribute('hidden'));
});
test('show all messages respects expand', () => {
element.messages = generateRandomAutomatedMessages(10)
.concat(generateRandomMessages(11));
flushAsynchronousOperations();
MockInteractions.tap(element.shadowRoot
.querySelector('#collapse-messages')); // Expand all.
flushAsynchronousOperations();
let messages = getMessages();
assert.equal(messages.length, 20);
for (const message of messages) {
assert.isTrue(message._expanded);
}
MockInteractions.tap(element.$.oldMessagesBtn);
flushAsynchronousOperations();
messages = getMessages();
assert.equal(messages.length, 21);
for (const message of messages) {
assert.isTrue(message._expanded);
}
});
test('show all messages respects collapse', () => {
element.messages = generateRandomAutomatedMessages(10)
.concat(generateRandomMessages(11));
flushAsynchronousOperations();
MockInteractions.tap(element.shadowRoot
.querySelector('#collapse-messages')); // Expand all.
MockInteractions.tap(element.shadowRoot
.querySelector('#collapse-messages')); // Collapse all.
flushAsynchronousOperations();
let messages = getMessages();
assert.equal(messages.length, 20);
for (const message of messages) {
assert.isFalse(message._expanded);
}
MockInteractions.tap(element.$.oldMessagesBtn);
flushAsynchronousOperations();
messages = getMessages();
assert.equal(messages.length, 21);
for (const message of messages) {
assert.isFalse(message._expanded);
}
});
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('hide messages does not appear when no automated messages', () => {
assert.isOk(element.shadowRoot
.querySelector('#automatedMessageToggleContainer[hidden]'));
});
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);
flushAsynchronousOperations();
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.equal(element._visibleMessages.length, 24);
assert.isTrue(
element.shadowRoot
.querySelector('[data-message-id="' + messageID + '"]')
._expanded);
});
test('messages', () => {
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;
const isAuthor = function(author, message) {
return message.author._account_id === author._account_id;
};
const isMarvin = isAuthor.bind(null, author);
flushAsynchronousOperations();
const messageElements = getMessages();
assert.equal(messageElements.length, messages.length);
assert.deepEqual(messageElements[1].message, messages[1]);
assert.deepEqual(messageElements[2].message, messages[2]);
assert.deepEqual(messageElements[1].comments.file1,
comments.file1.filter(isMarvin).map(c => {
return {...c,
path: 'file1'};
}));
assert.deepEqual(messageElements[1].comments.file2,
comments.file2.filter(isMarvin).map(c => {
return {...c,
path: 'file2'};
}));
assert.deepEqual(messageElements[2].comments, {});
});
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;
flushAsynchronousOperations();
const messageEls = getMessages();
assert.equal(messageEls.length, 1);
assert.equal(messageEls[0].message.message, messages[0].message);
});
test('hide increment text if increment >= total remaining', () => {
// Test with stubbed return values, as _numRemaining and _getDelta have
// their own tests.
sinon.stub(element, '_getDelta').returns(5);
const remainingStub = sinon.stub(element, '_numRemaining').returns(6);
assert.isFalse(element._computeIncrementHidden(null, null, null));
remainingStub.restore();
sinon.stub(element, '_numRemaining').returns(4);
assert.isTrue(element._computeIncrementHidden(null, null, null));
});
});
suite('gr-messages-list automate tests', () => {
let element;
let messages;
let commentApiWrapper;
const getMessages = function() {
return dom(element.root).querySelectorAll('gr-message');
};
const getHiddenMessages = function() {
return dom(element.root).querySelectorAll('gr-message[hidden]');
};
const randomMessageReviewer = {
reviewer: {},
date: '2016-01-13 20:30:33.038000',
};
setup(() => {
stub('gr-rest-api-interface', {
getConfig() { return Promise.resolve({}); },
getLoggedIn() { return Promise.resolve(false); },
getDiffComments() { return Promise.resolve({}); },
getDiffRobotComments() { return Promise.resolve({}); },
getDiffDrafts() { return Promise.resolve({}); },
});
messages = generateRandomAutomatedMessages(2);
messages.push(randomMessageReviewer);
// 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', () => {
assert.isNotOk(element.shadowRoot
.querySelector('#automatedMessageToggle[hidden]'));
});
test('autogenerated messages are not hidden initially', () => {
const allHiddenMessageEls = getHiddenMessages();
// There are no hidden messages.
assert.isFalse(!!allHiddenMessageEls.length);
});
test('autogenerated messages hidden after comments only toggle', () => {
let allHiddenMessageEls = getHiddenMessages();
element._hideAutomated = false;
MockInteractions.tap(element.$.automatedMessageToggle);
flushAsynchronousOperations();
const allMessageEls = getMessages();
allHiddenMessageEls = getHiddenMessages();
// Autogenerated messages are now hidden.
assert.equal(allHiddenMessageEls.length, allMessageEls.length);
});
test('autogenerated messages not hidden after comments only toggle',
() => {
let allHiddenMessageEls = getHiddenMessages();
element._hideAutomated = true;
MockInteractions.tap(element.$.automatedMessageToggle);
allHiddenMessageEls = getHiddenMessages();
// Autogenerated messages are now hidden.
assert.isFalse(!!allHiddenMessageEls.length);
});
test('_getDelta', () => {
let messages = [randomMessage()];
assert.equal(element._getDelta([], messages, false), 1);
assert.equal(element._getDelta([], messages, true), 1);
messages = generateRandomMessages(7);
assert.equal(element._getDelta([], messages, false), 5);
assert.equal(element._getDelta([], messages, true), 5);
messages = generateRandomMessages(4)
.concat(generateRandomAutomatedMessages(2))
.concat(generateRandomMessages(3));
const dummyArr = generateRandomMessages(2);
assert.equal(element._getDelta(dummyArr, messages, false), 5);
assert.equal(element._getDelta(dummyArr, messages, true), 7);
});
test('_getHumanMessages', () => {
assert.equal(
element._getHumanMessages(
generateRandomAutomatedMessages(5)).length, 0);
assert.equal(
element._getHumanMessages(generateRandomMessages(5)).length, 5);
let messages = shuffle(generateRandomMessages(5)
.concat(generateRandomAutomatedMessages(5)));
messages = element._getHumanMessages(messages);
assert.equal(messages.length, 5);
assert.isFalse(element._hasAutomatedMessages(messages));
});
test('initially show only 20 messages', () => {
sinon.stub(element.reporting, 'reportInteraction').callsFake(
(eventName, details) => {
assert.equal(typeof(eventName), 'string');
if (details) {
assert.equal(typeof(details), 'object');
}
});
const messages = Array.from(Array(23).keys())
.map(() => {
return {};
});
element._processedMessagesChanged(messages);
assert.equal(element._visibleMessages.length, 20);
});
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},
});
});
});
});