| /** |
| * @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'; |
| import './gr-message'; |
| import {GerritNav} from '../../core/gr-navigation/gr-navigation'; |
| import { |
| createChange, |
| createChangeMessage, |
| createComment, |
| createRevisions, |
| } from '../../../test/test-data-generators'; |
| import { |
| query, |
| queryAll, |
| queryAndAssert, |
| stubRestApi, |
| } from '../../../test/test-utils'; |
| import {GrMessage} from './gr-message'; |
| import { |
| AccountId, |
| BasePatchSetNum, |
| ChangeMessageId, |
| EmailAddress, |
| NumericChangeId, |
| PatchSetNum, |
| ReviewInputTag, |
| Timestamp, |
| UrlEncodedCommentId, |
| } from '../../../types/common'; |
| import {tap} from '@polymer/iron-test-helpers/mock-interactions'; |
| import { |
| ChangeMessageDeletedEventDetail, |
| ReplyEventDetail, |
| } from '../../../types/events'; |
| import {GrButton} from '../../shared/gr-button/gr-button'; |
| import {CommentSide} from '../../../constants/constants'; |
| import {SinonStubbedMember} from 'sinon/pkg/sinon-esm'; |
| |
| const basicFixture = fixtureFromElement('gr-message'); |
| |
| suite('gr-message tests', () => { |
| let element: GrMessage; |
| |
| suite('when admin and logged in', () => { |
| setup(done => { |
| stubRestApi('getIsAdmin').returns(Promise.resolve(true)); |
| element = basicFixture.instantiate(); |
| flush(done); |
| }); |
| |
| test('reply event', done => { |
| element.message = { |
| ...createChangeMessage(), |
| id: '47c43261_55aa2c41' as ChangeMessageId, |
| author: { |
| _account_id: 1115495 as AccountId, |
| name: 'Andrew Bonventre', |
| email: 'andybons@chromium.org' as EmailAddress, |
| }, |
| date: '2016-01-12 20:24:49.448000000' as Timestamp, |
| message: 'Uploaded patch set 1.', |
| _revision_number: 1 as PatchSetNum, |
| expanded: true, |
| }; |
| |
| element.addEventListener('reply', (e: CustomEvent<ReplyEventDetail>) => { |
| assert.deepEqual(e.detail.message, element.message); |
| done(); |
| }); |
| flush(); |
| assert.isFalse( |
| queryAndAssert<HTMLElement>(element, '.replyActionContainer').hidden |
| ); |
| tap(queryAndAssert(element, '.replyBtn')); |
| }); |
| |
| test('can see delete button', () => { |
| element.message = { |
| ...createChangeMessage(), |
| id: '47c43261_55aa2c41' as ChangeMessageId, |
| author: { |
| _account_id: 1115495 as AccountId, |
| name: 'Andrew Bonventre', |
| email: 'andybons@chromium.org' as EmailAddress, |
| }, |
| date: '2016-01-12 20:24:49.448000000' as Timestamp, |
| message: 'Uploaded patch set 1.', |
| _revision_number: 1 as PatchSetNum, |
| expanded: true, |
| }; |
| |
| flush(); |
| assert.isFalse(queryAndAssert<HTMLElement>(element, '.deleteBtn').hidden); |
| }); |
| |
| test('delete change message', done => { |
| element.changeNum = 314159 as NumericChangeId; |
| element.message = { |
| ...createChangeMessage(), |
| id: '47c43261_55aa2c41' as ChangeMessageId, |
| author: { |
| _account_id: 1115495 as AccountId, |
| name: 'Andrew Bonventre', |
| email: 'andybons@chromium.org' as EmailAddress, |
| }, |
| date: '2016-01-12 20:24:49.448000000' as Timestamp, |
| message: 'Uploaded patch set 1.', |
| _revision_number: 1 as PatchSetNum, |
| expanded: true, |
| }; |
| |
| element.addEventListener( |
| 'change-message-deleted', |
| (e: CustomEvent<ChangeMessageDeletedEventDetail>) => { |
| assert.deepEqual(e.detail.message, element.message); |
| assert.isFalse( |
| (queryAndAssert(element, '.deleteBtn') as GrButton).disabled |
| ); |
| done(); |
| } |
| ); |
| flush(); |
| tap(queryAndAssert(element, '.deleteBtn')); |
| assert.isTrue( |
| (queryAndAssert(element, '.deleteBtn') as GrButton).disabled |
| ); |
| }); |
| |
| test('autogenerated prefix hiding', () => { |
| element.message = { |
| ...createChangeMessage(), |
| tag: 'autogenerated:gerrit:test' as ReviewInputTag, |
| expanded: false, |
| }; |
| |
| assert.isTrue(element.isAutomated); |
| assert.isFalse(element.hidden); |
| |
| element.hideAutomated = true; |
| |
| assert.isTrue(element.hidden); |
| }); |
| |
| test('reviewer message treated as autogenerated', () => { |
| element.message = { |
| ...createChangeMessage(), |
| tag: 'autogenerated:gerrit:test' as ReviewInputTag, |
| reviewer: {}, |
| expanded: false, |
| }; |
| |
| assert.isTrue(element.isAutomated); |
| assert.isFalse(element.hidden); |
| |
| element.hideAutomated = true; |
| |
| assert.isTrue(element.hidden); |
| }); |
| |
| test('batch reviewer message treated as autogenerated', () => { |
| element.message = { |
| ...createChangeMessage(), |
| type: 'REVIEWER_UPDATE', |
| reviewer: {}, |
| expanded: false, |
| }; |
| |
| assert.isTrue(element.isAutomated); |
| assert.isFalse(element.hidden); |
| |
| element.hideAutomated = true; |
| |
| assert.isTrue(element.hidden); |
| }); |
| |
| test('tag that is not autogenerated prefix does not hide', () => { |
| element.message = { |
| ...createChangeMessage(), |
| tag: 'something' as ReviewInputTag, |
| expanded: false, |
| }; |
| |
| assert.isFalse(element.isAutomated); |
| assert.isFalse(element.hidden); |
| |
| element.hideAutomated = true; |
| |
| assert.isFalse(element.hidden); |
| }); |
| |
| test('reply button hidden unless logged in', () => { |
| const message = { |
| ...createChangeMessage(), |
| message: 'Uploaded patch set 1.', |
| expanded: false, |
| }; |
| assert.isFalse(element._computeShowReplyButton(message, false)); |
| assert.isTrue(element._computeShowReplyButton(message, true)); |
| }); |
| |
| test('_computeShowOnBehalfOf', () => { |
| const message = { |
| ...createChangeMessage(), |
| message: '...', |
| expanded: false, |
| }; |
| assert.isNotOk(element._computeShowOnBehalfOf(message)); |
| message.author = {_account_id: 1115495 as AccountId}; |
| assert.isNotOk(element._computeShowOnBehalfOf(message)); |
| message.real_author = {_account_id: 1115495 as AccountId}; |
| assert.isNotOk(element._computeShowOnBehalfOf(message)); |
| message.real_author._account_id = 123456 as AccountId; |
| assert.isOk(element._computeShowOnBehalfOf(message)); |
| message.updated_by = message.author; |
| delete message.author; |
| assert.isOk(element._computeShowOnBehalfOf(message)); |
| delete message.updated_by; |
| assert.isNotOk(element._computeShowOnBehalfOf(message)); |
| }); |
| |
| ['Trybot-Ready', 'Tryjob-Request', 'Commit-Queue'].forEach(label => { |
| test(`${label} ignored for color voting`, () => { |
| element.message = { |
| ...createChangeMessage(), |
| author: {}, |
| expanded: false, |
| message: `Patch Set 1: ${label}+1`, |
| }; |
| assert.isNotOk(query(element, '.negativeVote')); |
| assert.isNotOk(query(element, '.positiveVote')); |
| }); |
| }); |
| |
| test('clicking on date link fires event', () => { |
| element.message = { |
| ...createChangeMessage(), |
| type: 'REVIEWER_UPDATE', |
| reviewer: {}, |
| id: '47c43261_55aa2c41' as ChangeMessageId, |
| expanded: false, |
| }; |
| flush(); |
| const stub = sinon.stub(); |
| element.addEventListener('message-anchor-tap', stub); |
| const dateEl = queryAndAssert(element, '.date'); |
| assert.ok(dateEl); |
| tap(dateEl); |
| |
| assert.isTrue(stub.called); |
| assert.deepEqual(stub.lastCall.args[0].detail, {id: element.message.id}); |
| }); |
| |
| suite('uploaded patchset X message navigates to X - 1 vs X', () => { |
| let navStub: SinonStubbedMember<typeof GerritNav.navigateToChange>; |
| setup(() => { |
| element.change = {...createChange(), revisions: createRevisions(4)}; |
| navStub = sinon.stub(GerritNav, 'navigateToChange'); |
| }); |
| |
| test('Patchset 1 navigates to Base', () => { |
| element.message = { |
| ...createChangeMessage(), |
| message: 'Uploaded patch set 1.', |
| }; |
| element._handleViewPatchsetDiff(new MouseEvent('click')); |
| assert.isTrue( |
| navStub.calledWithExactly( |
| element.change!, |
| 1 as PatchSetNum, |
| 'PARENT' as BasePatchSetNum |
| ) |
| ); |
| }); |
| |
| test('Patchset X navigates to X vs X - 1', () => { |
| element.message = { |
| ...createChangeMessage(), |
| message: 'Uploaded patch set 2.', |
| }; |
| element._handleViewPatchsetDiff(new MouseEvent('click')); |
| assert.isTrue( |
| navStub.calledWithExactly( |
| element.change!, |
| 2 as PatchSetNum, |
| 1 as BasePatchSetNum |
| ) |
| ); |
| |
| element.message = { |
| ...createChangeMessage(), |
| message: 'Uploaded patch set 200.', |
| }; |
| element._handleViewPatchsetDiff(new MouseEvent('click')); |
| assert.isTrue( |
| navStub.calledWithExactly( |
| element.change!, |
| 200 as PatchSetNum, |
| 199 as BasePatchSetNum |
| ) |
| ); |
| }); |
| |
| test('Commit message updated', () => { |
| element.message = { |
| ...createChangeMessage(), |
| message: 'Commit message updated.', |
| }; |
| element._handleViewPatchsetDiff(new MouseEvent('click')); |
| assert.isTrue( |
| navStub.calledWithExactly( |
| element.change!, |
| 4 as PatchSetNum, |
| 3 as BasePatchSetNum |
| ) |
| ); |
| }); |
| |
| test('Merged patchset change message', () => { |
| element.message = { |
| ...createChangeMessage(), |
| message: 'abcd↵3 is the latest approved patch-set.↵abc', |
| }; |
| element._handleViewPatchsetDiff(new MouseEvent('click')); |
| assert.isTrue( |
| navStub.calledWithExactly( |
| element.change!, |
| 4 as PatchSetNum, |
| 3 as BasePatchSetNum |
| ) |
| ); |
| }); |
| }); |
| |
| suite('compute messages', () => { |
| test('empty', () => { |
| assert.equal( |
| element._computeMessageContent(true, '', '' as ReviewInputTag), |
| '' |
| ); |
| assert.equal( |
| element._computeMessageContent(false, '', '' as ReviewInputTag), |
| '' |
| ); |
| }); |
| |
| test('new patchset', () => { |
| const original = 'Uploaded patch set 1.'; |
| const tag = 'autogenerated:gerrit:newPatchSet' as ReviewInputTag; |
| let actual = element._computeMessageContent(true, original, tag); |
| assert.equal( |
| actual, |
| element._computeMessageContentCollapsed(original, tag, []) |
| ); |
| assert.equal(actual, original); |
| actual = element._computeMessageContent(false, original, tag); |
| assert.equal(actual, original); |
| }); |
| |
| test('new patchset rebased', () => { |
| const original = 'Patch Set 27: Patch Set 26 was rebased'; |
| const tag = 'autogenerated:gerrit:newPatchSet' as ReviewInputTag; |
| const expected = 'Patch Set 26 was rebased'; |
| let actual = element._computeMessageContent(true, original, tag); |
| assert.equal(actual, expected); |
| assert.equal( |
| actual, |
| element._computeMessageContentCollapsed(original, tag, []) |
| ); |
| actual = element._computeMessageContent(false, original, tag); |
| assert.equal(actual, expected); |
| }); |
| |
| test('ready for review', () => { |
| const original = 'Patch Set 1:\n\nThis change is ready for review.'; |
| const tag = undefined; |
| const expected = 'This change is ready for review.'; |
| let actual = element._computeMessageContent(true, original, tag); |
| assert.equal(actual, expected); |
| assert.equal( |
| actual, |
| element._computeMessageContentCollapsed(original, tag, []) |
| ); |
| actual = element._computeMessageContent(false, original, tag); |
| assert.equal(actual, expected); |
| }); |
| |
| test('vote', () => { |
| const original = 'Patch Set 1: Code-Style+1'; |
| const tag = undefined; |
| const expected = ''; |
| let actual = element._computeMessageContent(true, original, tag); |
| assert.equal(actual, expected); |
| actual = element._computeMessageContent(false, original, tag); |
| assert.equal(actual, expected); |
| }); |
| |
| test('comments', () => { |
| const original = 'Patch Set 1:\n\n(3 comments)'; |
| const tag = undefined; |
| const expected = ''; |
| let actual = element._computeMessageContent(true, original, tag); |
| assert.equal(actual, expected); |
| actual = element._computeMessageContent(false, original, tag); |
| assert.equal(actual, expected); |
| }); |
| }); |
| |
| test('votes', () => { |
| element.message = { |
| ...createChangeMessage(), |
| author: {}, |
| expanded: false, |
| message: 'Patch Set 1: Verified+1 Code-Review-2 Trybot-Label3+1 Blub+1', |
| }; |
| element.labelExtremes = { |
| Verified: {max: 1, min: -1}, |
| 'Code-Review': {max: 2, min: -2}, |
| 'Trybot-Label3': {max: 3, min: 0}, |
| }; |
| flush(); |
| const scoreChips = queryAll(element, '.score'); |
| assert.equal(scoreChips.length, 3); |
| |
| assert.isTrue(scoreChips[0].classList.contains('positive')); |
| assert.isTrue(scoreChips[0].classList.contains('max')); |
| |
| assert.isTrue(scoreChips[1].classList.contains('negative')); |
| assert.isTrue(scoreChips[1].classList.contains('min')); |
| |
| assert.isTrue(scoreChips[2].classList.contains('positive')); |
| assert.isFalse(scoreChips[2].classList.contains('min')); |
| }); |
| |
| test('Uploaded patch set X', () => { |
| element.message = { |
| ...createChangeMessage(), |
| author: {}, |
| expanded: false, |
| message: |
| 'Uploaded patch set 1:' + |
| 'Verified+1 Code-Review-2 Trybot-Label3+1 Blub+1', |
| }; |
| element.labelExtremes = { |
| Verified: {max: 1, min: -1}, |
| 'Code-Review': {max: 2, min: -2}, |
| 'Trybot-Label3': {max: 3, min: 0}, |
| }; |
| flush(); |
| const scoreChips = queryAll(element, '.score'); |
| assert.equal(scoreChips.length, 3); |
| |
| assert.isTrue(scoreChips[0].classList.contains('positive')); |
| assert.isTrue(scoreChips[0].classList.contains('max')); |
| |
| assert.isTrue(scoreChips[1].classList.contains('negative')); |
| assert.isTrue(scoreChips[1].classList.contains('min')); |
| |
| assert.isTrue(scoreChips[2].classList.contains('positive')); |
| assert.isFalse(scoreChips[2].classList.contains('min')); |
| }); |
| |
| test('removed votes', () => { |
| element.message = { |
| ...createChangeMessage(), |
| author: {}, |
| expanded: false, |
| message: 'Patch Set 1: Verified+1 -Code-Review -Commit-Queue', |
| }; |
| element.labelExtremes = { |
| Verified: {max: 1, min: -1}, |
| 'Code-Review': {max: 2, min: -2}, |
| 'Commit-Queue': {max: 3, min: 0}, |
| }; |
| flush(); |
| const scoreChips = queryAll(element, '.score'); |
| assert.equal(scoreChips.length, 3); |
| |
| assert.isTrue(scoreChips[1].classList.contains('removed')); |
| assert.isTrue(scoreChips[2].classList.contains('removed')); |
| }); |
| |
| test('false negative vote', () => { |
| element.message = { |
| ...createChangeMessage(), |
| author: {}, |
| expanded: false, |
| message: 'Patch Set 1: Cherry Picked from branch stable-2.14.', |
| }; |
| element.labelExtremes = {}; |
| const scoreChips = element.root!.querySelectorAll('.score'); |
| assert.equal(scoreChips.length, 0); |
| }); |
| }); |
| |
| suite('when not logged in', () => { |
| setup(done => { |
| stubRestApi('getLoggedIn').returns(Promise.resolve(false)); |
| stubRestApi('getIsAdmin').returns(Promise.resolve(false)); |
| element = basicFixture.instantiate(); |
| flush(done); |
| }); |
| |
| test('reply and delete button should be hidden', () => { |
| element.message = { |
| ...createChangeMessage(), |
| id: '47c43261_55aa2c41' as ChangeMessageId, |
| author: { |
| _account_id: 1115495 as AccountId, |
| name: 'Andrew Bonventre', |
| email: 'andybons@chromium.org' as EmailAddress, |
| }, |
| date: '2016-01-12 20:24:49.448000000' as Timestamp, |
| message: 'Uploaded patch set 1.', |
| _revision_number: 1 as PatchSetNum, |
| expanded: true, |
| }; |
| |
| flush(); |
| assert.isTrue( |
| queryAndAssert<HTMLElement>(element, '.replyActionContainer').hidden |
| ); |
| assert.isTrue(queryAndAssert<HTMLElement>(element, '.deleteBtn').hidden); |
| }); |
| }); |
| |
| suite('patchset comment summary', () => { |
| setup(() => { |
| element = basicFixture.instantiate(); |
| element.message = { |
| ...createChangeMessage(), |
| id: '6a07f64a82f96e7337ca5f7f84cfc73abf8ac2a3' as ChangeMessageId, |
| }; |
| }); |
| |
| test('single patchset comment posted', () => { |
| const threads = [ |
| { |
| comments: [ |
| { |
| ...createComment(), |
| change_message_id: '6a07f64a82f96e7337ca5f7f84cfc73abf8ac2a3' as ChangeMessageId, |
| patch_set: 1 as PatchSetNum, |
| id: 'e365b138_bed65caa' as UrlEncodedCommentId, |
| updated: '2020-05-15 13:35:56.000000000' as Timestamp, |
| message: 'testing the load', |
| unresolved: false, |
| path: '/PATCHSET_LEVEL', |
| collapsed: false, |
| }, |
| ], |
| patchNum: 1 as PatchSetNum, |
| path: '/PATCHSET_LEVEL', |
| rootId: 'e365b138_bed65caa' as UrlEncodedCommentId, |
| commentSide: CommentSide.REVISION, |
| }, |
| ]; |
| assert.equal( |
| element._computeMessageContentCollapsed('', undefined, threads), |
| 'testing the load' |
| ); |
| assert.equal(element._computeMessageContent(false, '', undefined), ''); |
| }); |
| |
| test('single patchset comment with reply', () => { |
| const threads = [ |
| { |
| comments: [ |
| { |
| ...createComment(), |
| patch_set: 1 as PatchSetNum, |
| id: 'e365b138_bed65caa' as UrlEncodedCommentId, |
| updated: '2020-05-15 13:35:56.000000000' as Timestamp, |
| message: 'testing the load', |
| unresolved: false, |
| path: '/PATCHSET_LEVEL', |
| collapsed: false, |
| }, |
| { |
| change_message_id: '6a07f64a82f96e7337ca5f7f84cfc73abf8ac2a3', |
| patch_set: 1 as PatchSetNum, |
| id: 'd6efcc85_4cbbb6f4' as UrlEncodedCommentId, |
| in_reply_to: 'e365b138_bed65caa' as UrlEncodedCommentId, |
| updated: '2020-05-15 16:55:28.000000000' as Timestamp, |
| message: 'n', |
| unresolved: false, |
| path: '/PATCHSET_LEVEL', |
| __draft: true, |
| collapsed: true, |
| }, |
| ], |
| patchNum: 1 as PatchSetNum, |
| path: '/PATCHSET_LEVEL', |
| rootId: 'e365b138_bed65caa' as UrlEncodedCommentId, |
| commentSide: CommentSide.REVISION, |
| }, |
| ]; |
| assert.equal( |
| element._computeMessageContentCollapsed('', undefined, threads), |
| 'n' |
| ); |
| assert.equal(element._computeMessageContent(false, '', undefined), ''); |
| }); |
| }); |
| |
| suite('when logged in but not admin', () => { |
| setup(async () => { |
| stubRestApi('getIsAdmin').returns(Promise.resolve(false)); |
| element = basicFixture.instantiate(); |
| await flush(); |
| }); |
| |
| test('can see reply but not delete button', () => { |
| element.message = { |
| ...createChangeMessage(), |
| id: '47c43261_55aa2c41' as ChangeMessageId, |
| author: { |
| _account_id: 1115495 as AccountId, |
| name: 'Andrew Bonventre', |
| email: 'andybons@chromium.org' as EmailAddress, |
| }, |
| date: '2016-01-12 20:24:49.448000000' as Timestamp, |
| message: 'Uploaded patch set 1.', |
| _revision_number: 1 as PatchSetNum, |
| expanded: true, |
| }; |
| |
| flush(); |
| assert.isFalse( |
| queryAndAssert<HTMLElement>(element, '.replyActionContainer').hidden |
| ); |
| assert.isTrue(queryAndAssert<HTMLElement>(element, '.deleteBtn').hidden); |
| }); |
| |
| test('reply button shown when message is updated', () => { |
| element.message = undefined; |
| flush(); |
| let replyEl = query(element, '.replyActionContainer'); |
| // We don't even expect the button to show up in the DOM when the message |
| // is undefined. |
| assert.isNotOk(replyEl); |
| |
| element.message = { |
| ...createChangeMessage(), |
| id: '47c43261_55aa2c41' as ChangeMessageId, |
| author: { |
| _account_id: 1115495 as AccountId, |
| name: 'Andrew Bonventre', |
| email: 'andybons@chromium.org' as EmailAddress, |
| }, |
| date: '2016-01-12 20:24:49.448000000' as Timestamp, |
| message: 'not empty', |
| _revision_number: 1 as PatchSetNum, |
| expanded: true, |
| }; |
| flush(); |
| replyEl = queryAndAssert(element, '.replyActionContainer'); |
| assert.isOk(replyEl); |
| assert.isFalse((replyEl as HTMLElement).hidden); |
| }); |
| }); |
| }); |