summaryrefslogtreecommitdiffstats
path: root/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.ts')
-rw-r--r--polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.ts620
1 files changed, 620 insertions, 0 deletions
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.ts b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.ts
new file mode 100644
index 0000000000..b9cb616d29
--- /dev/null
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.ts
@@ -0,0 +1,620 @@
+/**
+ * @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';
+import './gr-messages-list';
+import {createCommentApiMockWithTemplateElement} from '../../../test/mocks/comment-api';
+import {CombinedMessage, GrMessagesList, TEST_ONLY} from './gr-messages-list';
+import {MessageTag} from '../../../constants/constants';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
+import {
+ query,
+ queryAll,
+ queryAndAssert,
+ stubRestApi,
+} from '../../../test/test-utils';
+import {GrMessage} from '../gr-message/gr-message';
+import {
+ AccountId,
+ ChangeMessageId,
+ ChangeMessageInfo,
+ EmailAddress,
+ LabelNameToInfoMap,
+ NumericChangeId,
+ PatchSetNum,
+ ReviewInputTag,
+ Timestamp,
+ UrlEncodedCommentId,
+} from '../../../types/common';
+import * as MockInteractions from '@polymer/iron-test-helpers/mock-interactions';
+import {assertIsDefined} from '../../../utils/common-util';
+
+createCommentApiMockWithTemplateElement(
+ 'gr-messages-list-comment-mock-api',
+ html` <gr-messages-list id="messagesList"></gr-messages-list> `
+);
+
+const basicFixture = fixtureFromTemplate(html`
+ <gr-messages-list-comment-mock-api>
+ <gr-messages-list></gr-messages-list>
+ </gr-messages-list-comment-mock-api>
+`);
+
+const author = {
+ _account_id: 42 as AccountId,
+ name: 'Marvin the Paranoid Android',
+ email: 'marvin@sirius.org' as EmailAddress,
+};
+
+const createComment = function () {
+ return {
+ id: '1a2b3c4d' as UrlEncodedCommentId,
+ message: 'some random test text',
+ change_message_id: '8a7b6c5d',
+ updated: '2016-01-01 01:02:03.000000000' as Timestamp,
+ line: 1,
+ patch_set: 1 as PatchSetNum,
+ author,
+ };
+};
+
+const randomMessage = function (opt_params?: ChangeMessageInfo) {
+ const params = opt_params || ({} as ChangeMessageInfo);
+ const author1 = {
+ _account_id: 1115495 as AccountId,
+ name: 'Andrew Bonventre',
+ email: 'andybons@chromium.org' as EmailAddress,
+ };
+ return {
+ id: (params.id || Math.random().toString()) as ChangeMessageId,
+ date: (params.date || '2016-01-12 20:28:33.038000') as Timestamp,
+ message: params.message || Math.random().toString(),
+ _revision_number: (params._revision_number || 1) as PatchSetNum,
+ author: params.author || author1,
+ tag: params.tag,
+ };
+};
+
+function generateRandomMessages(count: number) {
+ return new Array(count)
+ .fill(undefined)
+ .map(() => randomMessage()) as ChangeMessageInfo[];
+}
+
+suite('gr-messages-list tests', () => {
+ let element: GrMessagesList;
+ let messages: ChangeMessageInfo[];
+
+ let commentApiWrapper: any;
+
+ const getMessages = function () {
+ return queryAll<GrMessage>(element, 'gr-message');
+ };
+
+ const MESSAGE_ID_0 = '1234ccc949c6d482b061be6a28e10782abf0e7af';
+ const MESSAGE_ID_1 = '8c19ccc949c6d482b061be6a28e10782abf0e7af';
+ const MESSAGE_ID_2 = 'e7bfdbc842f6b6d8064bc68e0f52b673f40c0ca5';
+
+ const comments = {
+ file1: [
+ {
+ ...createComment(),
+ change_message_id: MESSAGE_ID_0,
+ in_reply_to: '6505d749_f0bec0aa' as UrlEncodedCommentId,
+ author: {
+ email: 'some@email.com' as EmailAddress,
+ _account_id: 123 as AccountId,
+ },
+ },
+ {
+ ...createComment(),
+ id: '2b3c4d5e' as UrlEncodedCommentId,
+ change_message_id: MESSAGE_ID_1,
+ in_reply_to: 'c5912363_6b820105' as UrlEncodedCommentId,
+ },
+ {
+ ...createComment(),
+ id: '2b3c4d5e' as UrlEncodedCommentId,
+ change_message_id: MESSAGE_ID_1,
+ in_reply_to: '6505d749_f0bec0aa' as UrlEncodedCommentId,
+ },
+ {
+ ...createComment(),
+ id: '34ed05d749_10ed44b2' as UrlEncodedCommentId,
+ change_message_id: MESSAGE_ID_2,
+ },
+ ],
+ file2: [
+ {
+ ...createComment(),
+ change_message_id: MESSAGE_ID_1,
+ in_reply_to: 'c5912363_4b7d450a' as UrlEncodedCommentId,
+ id: '450a935e_4f260d25' as UrlEncodedCommentId,
+ },
+ ],
+ };
+
+ suite('basic tests', () => {
+ setup(async () => {
+ 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 = queryAndAssert<GrMessagesList>(
+ commentApiWrapper,
+ '#messagesList'
+ );
+ await element.getCommentsModel().reloadComments(0 as NumericChangeId);
+ element.messages = messages;
+ await flush();
+ });
+
+ test('expand/collapse all', async () => {
+ let allMessageEls = getMessages();
+ for (const message of allMessageEls) {
+ assertIsDefined(message.message);
+ message.message = {...message.message, expanded: false};
+ await message.updateComplete;
+ }
+ MockInteractions.tap(allMessageEls[1]);
+ assert.isTrue(allMessageEls[1].message?.expanded);
+
+ MockInteractions.tap(queryAndAssert(element, '#collapse-messages'));
+ allMessageEls = getMessages();
+ for (const message of allMessageEls) {
+ assert.isTrue(message.message?.expanded);
+ }
+
+ MockInteractions.tap(queryAndAssert(element, '#collapse-messages'));
+ allMessageEls = getMessages();
+ for (const message of allMessageEls) {
+ assert.isFalse(message.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.message?.expanded).length === 0
+ );
+
+ // Press 'z' -> all collapsed
+ element.handleExpandCollapse(false);
+ assert.isTrue(
+ [...getMessages()].filter(m => m.message?.expanded).length === 0
+ );
+
+ // Press 'x' -> all expanded
+ element.handleExpandCollapse(true);
+ assert.isTrue(
+ [...getMessages()].filter(m => !m.message?.expanded).length === 0
+ );
+
+ // Press 'z' -> all collapsed
+ element.handleExpandCollapse(false);
+ assert.isTrue(
+ [...getMessages()].filter(m => m.message?.expanded).length === 0
+ );
+ });
+
+ test('showAllActivity does not appear when all msgs are important', () => {
+ assert.isOk(query(element, '#showAllActivityToggleContainer'));
+ assert.isNotOk(query(element, '.showAllActivityToggle'));
+ });
+
+ test('scroll to message', async () => {
+ const allMessageEls = getMessages();
+ for (const message of allMessageEls) {
+ assertIsDefined(message.message);
+ message.message = {...message.message, expanded: false};
+ }
+
+ const scrollToStub = sinon.stub(window, 'scrollTo');
+ const highlightStub = sinon.stub(element, '_highlightEl');
+
+ await element.scrollToMessage('invalid');
+
+ for (const message of allMessageEls) {
+ assertIsDefined(message.message);
+ assert.isFalse(
+ message.message.expanded,
+ 'expected gr-message to not be expanded'
+ );
+ }
+
+ const messageID = messages[1].id;
+ await element.scrollToMessage(messageID);
+ assert.isTrue(
+ queryAndAssert<GrMessage>(element, `[data-message-id="${messageID}"]`)
+ .message?.expanded
+ );
+
+ assert.isTrue(scrollToStub.calledOnce);
+ assert.isTrue(highlightStub.calledOnce);
+ });
+
+ test('scroll to message offscreen', async () => {
+ const scrollToStub = sinon.stub(window, 'scrollTo');
+ const highlightStub = sinon.stub(element, '_highlightEl');
+ element.messages = generateRandomMessages(25);
+ await element.updateComplete;
+ assert.isFalse(scrollToStub.called);
+ assert.isFalse(highlightStub.called);
+
+ const messageID = element.messages[1].id;
+ await element.scrollToMessage(messageID);
+ assert.isTrue(scrollToStub.calledOnce);
+ assert.isTrue(highlightStub.calledOnce);
+ assert.isTrue(
+ queryAndAssert<GrMessage>(element, `[data-message-id="${messageID}"]`)
+ .message?.expanded
+ );
+ });
+
+ test('associating messages with comments', () => {
+ // Have to type as any otherwise fails with
+ // Argument of type 'ChangeMessageInfo[]' is not assignable to
+ // parameter of type 'ConcatArray<never>'.
+ const messages = ([] as any).concat(
+ randomMessage(),
+ {
+ _index: 5,
+ _revision_number: 4 as PatchSetNum,
+ message: 'Uploaded patch set 4.',
+ date: '2016-09-28 13:36:33.000000000' as Timestamp,
+ author,
+ id: '8c19ccc949c6d482b061be6a28e10782abf0e7af' as ChangeMessageId,
+ } as CombinedMessage,
+ {
+ _index: 6,
+ _revision_number: 4 as PatchSetNum,
+ message: 'Patch Set 4:\n\n(6 comments)',
+ date: '2016-09-28 13:36:33.000000000' as Timestamp,
+ author,
+ id: 'e7bfdbc842f6b6d8064bc68e0f52b673f40c0ca5' as ChangeMessageId,
+ } as CombinedMessage
+ );
+ 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 as PatchSetNum,
+ message: 'Uploaded patch set 4.',
+ date: '2016-09-28 13:36:33.000000000' as Timestamp,
+ author,
+ id: '8c19ccc949c6d482b061be6a28e10782abf0e7af' as ChangeMessageId,
+ },
+ ];
+ 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' as ReviewInputTag;
+ 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 as ReviewInputTag;
+ assert.equal(TEST_ONLY.computeTag(m), MessageTag.TAG_NEW_PATCHSET);
+ });
+
+ test('updateTag remove postfix', () => {
+ const m = randomMessage();
+ m.tag = 'something~withpostfix' as ReviewInputTag;
+ assert.equal(TEST_ONLY.computeTag(m), 'something');
+ });
+
+ test('updateTag with robot comments', () => {
+ const m = randomMessage();
+ (m as any).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 as PatchSetNum);
+ assert.equal(TEST_ONLY.computeRevision(m2, [m1, m2]), 1 as PatchSetNum);
+ });
+
+ test('setRevisionNumber reviewer updates', () => {
+ const m1 = randomMessage({
+ ...randomMessage(),
+ tag: MessageTag.TAG_REVIEWER_UPDATE as ReviewInputTag,
+ date: '2020-01-01 10:00:00.000000000' as Timestamp,
+ });
+ m1._revision_number = 0 as PatchSetNum;
+ const m2 = randomMessage({
+ ...randomMessage(),
+ date: '2020-01-02 10:00:00.000000000' as Timestamp,
+ });
+ m2._revision_number = 1 as PatchSetNum;
+ const m3 = randomMessage({
+ ...randomMessage(),
+ tag: MessageTag.TAG_REVIEWER_UPDATE as ReviewInputTag,
+ date: '2020-01-03 10:00:00.000000000' as Timestamp,
+ });
+ m3._revision_number = 0 as PatchSetNum;
+ const m4 = randomMessage({
+ ...randomMessage(),
+ date: '2020-01-04 10:00:00.000000000' as Timestamp,
+ });
+ m4._revision_number = 2 as PatchSetNum;
+ const m5 = randomMessage({
+ ...randomMessage(),
+ tag: MessageTag.TAG_REVIEWER_UPDATE as ReviewInputTag,
+ date: '2020-01-05 10:00:00.000000000' as Timestamp,
+ });
+ m5._revision_number = 0 as PatchSetNum;
+ const allMessages = [m1, m2, m3, m4, m5];
+ assert.equal(TEST_ONLY.computeRevision(m1, allMessages), undefined);
+ assert.equal(
+ TEST_ONLY.computeRevision(m2, allMessages),
+ 1 as PatchSetNum
+ );
+ assert.equal(
+ TEST_ONLY.computeRevision(m3, allMessages),
+ 1 as PatchSetNum
+ );
+ assert.equal(
+ TEST_ONLY.computeRevision(m4, allMessages),
+ 2 as PatchSetNum
+ );
+ assert.equal(
+ TEST_ONLY.computeRevision(m5, allMessages),
+ 2 as PatchSetNum
+ );
+ });
+
+ 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({
+ ...randomMessage(),
+ tag: 'autogenerated:gerrit1' as ReviewInputTag,
+ });
+ const m3 = randomMessage({
+ ...randomMessage(),
+ tag: 'autogenerated:gerrit2' as ReviewInputTag,
+ });
+ 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({
+ ...randomMessage(),
+ tag: 'auto' as ReviewInputTag,
+ _revision_number: 2 as PatchSetNum,
+ });
+ const m2 = randomMessage({
+ ...randomMessage(),
+ tag: 'auto' as ReviewInputTag,
+ _revision_number: 1 as PatchSetNum,
+ });
+ const m3 = randomMessage({
+ ...randomMessage(),
+ tag: 'auto' as ReviewInputTag,
+ });
+ 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({
+ ...randomMessage(),
+ tag: MessageTag.TAG_NEW_PATCHSET as ReviewInputTag,
+ _revision_number: 1 as PatchSetNum,
+ });
+ const m2 = randomMessage({
+ ...randomMessage(),
+ tag: MessageTag.TAG_NEW_WIP_PATCHSET as ReviewInputTag,
+ _revision_number: 2 as PatchSetNum,
+ });
+ element.messages = [m1, m2];
+ flush();
+ assert.isFalse((m1 as CombinedMessage).isImportant);
+ assert.isTrue((m2 as CombinedMessage).isImportant);
+ });
+
+ test('messages without author do not throw', () => {
+ const messages = [
+ {
+ _index: 5,
+ _revision_number: 4 as PatchSetNum,
+ message: 'Uploaded patch set 4.',
+ date: '2016-09-28 13:36:33.000000000' as Timestamp,
+ id: '8c19ccc949c6d482b061be6a28e10782abf0e7af' as ChangeMessageId,
+ },
+ ];
+ 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: GrMessagesList;
+ let messages: ChangeMessageInfo[];
+
+ let commentApiWrapper: any;
+
+ setup(() => {
+ stubRestApi('getLoggedIn').returns(Promise.resolve(false));
+ stubRestApi('getDiffComments').returns(Promise.resolve({}));
+ stubRestApi('getDiffRobotComments').returns(Promise.resolve({}));
+ stubRestApi('getDiffDrafts').returns(Promise.resolve({}));
+
+ messages = [
+ randomMessage(),
+ randomMessage({
+ ...randomMessage(),
+ tag: 'auto' as ReviewInputTag,
+ _revision_number: 2 as PatchSetNum,
+ }),
+ randomMessage({
+ ...randomMessage(),
+ tag: 'auto' as ReviewInputTag,
+ _revision_number: 3 as PatchSetNum,
+ }),
+ ];
+
+ // Element must be wrapped in an element with direct access to the
+ // comment API.
+ commentApiWrapper = basicFixture.instantiate();
+ element = queryAndAssert<GrMessagesList>(
+ commentApiWrapper,
+ '#messagesList'
+ );
+ element.messages = messages;
+ flush();
+ });
+
+ test('hide autogenerated button is not hidden', () => {
+ const toggle = queryAndAssert(element, '.showAllActivityToggle');
+ assert.isOk(toggle);
+ });
+
+ test('one unimportant message is hidden initially', () => {
+ const displayedMsgs = queryAll<GrMessage>(element, 'gr-message');
+ assert.equal(displayedMsgs.length, 2);
+ });
+
+ test('unimportant messages hidden after toggle', () => {
+ element._showAllActivity = true;
+ const toggle = queryAndAssert(element, '.showAllActivityToggle');
+ assert.isOk(toggle);
+ MockInteractions.tap(toggle);
+ flush();
+ const displayedMsgs = queryAll<GrMessage>(element, 'gr-message');
+ assert.equal(displayedMsgs.length, 2);
+ });
+
+ test('unimportant messages shown after toggle', () => {
+ element._showAllActivity = false;
+ const toggle = queryAndAssert(element, '.showAllActivityToggle');
+ assert.isOk(toggle);
+ MockInteractions.tap(toggle);
+ flush();
+ const displayedMsgs = queryAll<GrMessage>(element, 'gr-message');
+ assert.equal(displayedMsgs.length, 3);
+ });
+
+ test('_computeLabelExtremes', () => {
+ const computeSpy = sinon.spy(element, '_computeLabelExtremes');
+
+ // Have to type as any to be able to use null.
+ element.labels = null as any;
+ 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': {}}},
+ } as LabelNameToInfoMap;
+ 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': {}}},
+ } as LabelNameToInfoMap;
+ 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': {}}},
+ } as LabelNameToInfoMap;
+ assert.equal(computeSpy.callCount, 7);
+ assert.deepEqual(computeSpy.lastCall.returnValue, {
+ 'my-label': {min: -12, max: -12},
+ 'other-label': {min: -1, max: 1},
+ });
+ });
+ });
+});