| /** |
| * @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. |
| */ |
| |
| import '../../../test/common-test-setup-karma'; |
| import './gr-comment-thread'; |
| import {GerritNav} from '../../core/gr-navigation/gr-navigation'; |
| import {SpecialFilePath, Side} from '../../../constants/constants'; |
| import { |
| sortComments, |
| UIComment, |
| UIRobot, |
| isDraft, |
| UIDraft, |
| } from '../../../utils/comment-util'; |
| import {GrCommentThread} from './gr-comment-thread'; |
| import { |
| PatchSetNum, |
| NumericChangeId, |
| UrlEncodedCommentId, |
| Timestamp, |
| RobotId, |
| RobotRunId, |
| RepoName, |
| ConfigInfo, |
| EmailAddress, |
| } from '../../../types/common'; |
| import {GrComment} from '../gr-comment/gr-comment'; |
| import {LineNumber} from '../../diff/gr-diff/gr-diff-line'; |
| import { |
| tap, |
| pressAndReleaseKeyOn, |
| } from '@polymer/iron-test-helpers/mock-interactions'; |
| import {html} from '@polymer/polymer/lib/utils/html-tag.js'; |
| import {stubRestApi, stubStorage} from '../../../test/test-utils'; |
| |
| const basicFixture = fixtureFromElement('gr-comment-thread'); |
| |
| const withCommentFixture = fixtureFromElement('gr-comment-thread'); |
| |
| suite('gr-comment-thread tests', () => { |
| suite('basic test', () => { |
| let element: GrCommentThread; |
| |
| setup(() => { |
| stubRestApi('getLoggedIn').returns(Promise.resolve(false)); |
| |
| element = basicFixture.instantiate(); |
| element.patchNum = 3 as PatchSetNum; |
| element.changeNum = 1 as NumericChangeId; |
| flush(); |
| }); |
| |
| test('renders without patchNum and changeNum', async () => { |
| const fixture = fixtureFromTemplate( |
| html`<gr-comment-thread show-file-path="" path="path/to/file"></gr-change-metadata>` |
| ); |
| fixture.instantiate(); |
| await flush(); |
| }); |
| |
| test('comments are sorted correctly', () => { |
| const comments: UIComment[] = [ |
| { |
| message: 'i like you, too', |
| in_reply_to: 'sallys_confession' as UrlEncodedCommentId, |
| __date: new Date('2015-12-25'), |
| }, |
| { |
| id: 'sallys_confession' as UrlEncodedCommentId, |
| message: 'i like you, jack', |
| updated: '2015-12-24 15:00:20.396000000' as Timestamp, |
| }, |
| { |
| id: 'sally_to_dr_finklestein' as UrlEncodedCommentId, |
| message: 'i’m running away', |
| updated: '2015-10-31 09:00:20.396000000' as Timestamp, |
| }, |
| { |
| id: 'sallys_defiance' as UrlEncodedCommentId, |
| in_reply_to: 'sally_to_dr_finklestein' as UrlEncodedCommentId, |
| message: 'i will poison you so i can get away', |
| updated: '2015-10-31 15:00:20.396000000' as Timestamp, |
| }, |
| { |
| id: 'dr_finklesteins_response' as UrlEncodedCommentId, |
| in_reply_to: 'sally_to_dr_finklestein' as UrlEncodedCommentId, |
| message: 'no i will pull a thread and your arm will fall off', |
| updated: '2015-10-31 11:00:20.396000000' as Timestamp, |
| }, |
| { |
| id: 'sallys_mission' as UrlEncodedCommentId, |
| message: 'i have to find santa', |
| updated: '2015-12-24 15:00:20.396000000' as Timestamp, |
| }, |
| ]; |
| const results = sortComments(comments); |
| assert.deepEqual(results, [ |
| { |
| id: 'sally_to_dr_finklestein' as UrlEncodedCommentId, |
| message: 'i’m running away', |
| updated: '2015-10-31 09:00:20.396000000' as Timestamp, |
| }, |
| { |
| id: 'dr_finklesteins_response' as UrlEncodedCommentId, |
| in_reply_to: 'sally_to_dr_finklestein' as UrlEncodedCommentId, |
| message: 'no i will pull a thread and your arm will fall off', |
| updated: '2015-10-31 11:00:20.396000000' as Timestamp, |
| }, |
| { |
| id: 'sallys_defiance' as UrlEncodedCommentId, |
| in_reply_to: 'sally_to_dr_finklestein' as UrlEncodedCommentId, |
| message: 'i will poison you so i can get away', |
| updated: '2015-10-31 15:00:20.396000000' as Timestamp, |
| }, |
| { |
| id: 'sallys_confession' as UrlEncodedCommentId, |
| message: 'i like you, jack', |
| updated: '2015-12-24 15:00:20.396000000' as Timestamp, |
| }, |
| { |
| id: 'sallys_mission' as UrlEncodedCommentId, |
| message: 'i have to find santa', |
| updated: '2015-12-24 15:00:20.396000000' as Timestamp, |
| }, |
| { |
| message: 'i like you, too' as UrlEncodedCommentId, |
| in_reply_to: 'sallys_confession' as UrlEncodedCommentId, |
| __date: new Date('2015-12-25'), |
| }, |
| ]); |
| }); |
| |
| test('addOrEditDraft w/ edit draft', () => { |
| element.comments = [ |
| { |
| id: 'jacks_reply' as UrlEncodedCommentId, |
| message: 'i like you, too', |
| in_reply_to: 'sallys_confession' as UrlEncodedCommentId, |
| updated: '2015-12-25 15:00:20.396000000' as Timestamp, |
| __draft: true, |
| }, |
| ]; |
| const commentElStub = sinon |
| .stub(element, '_commentElWithDraftID') |
| .callsFake(() => new GrComment()); |
| const addDraftStub = sinon.stub(element, 'addDraft'); |
| |
| element.addOrEditDraft(123); |
| |
| assert.isTrue(commentElStub.called); |
| assert.isFalse(addDraftStub.called); |
| }); |
| |
| test('addOrEditDraft w/o edit draft', () => { |
| element.comments = []; |
| const commentElStub = sinon |
| .stub(element, '_commentElWithDraftID') |
| .callsFake(() => new GrComment()); |
| const addDraftStub = sinon.stub(element, 'addDraft'); |
| |
| element.addOrEditDraft(123); |
| |
| assert.isFalse(commentElStub.called); |
| assert.isTrue(addDraftStub.called); |
| }); |
| |
| test('_shouldDisableAction', () => { |
| let showActions = true; |
| const lastComment: UIComment = {}; |
| assert.equal( |
| element._shouldDisableAction(showActions, lastComment), |
| false |
| ); |
| showActions = false; |
| assert.equal( |
| element._shouldDisableAction(showActions, lastComment), |
| true |
| ); |
| showActions = true; |
| lastComment.__draft = true; |
| assert.equal( |
| element._shouldDisableAction(showActions, lastComment), |
| true |
| ); |
| const robotComment: UIRobot = { |
| id: '1234' as UrlEncodedCommentId, |
| updated: '1234' as Timestamp, |
| robot_id: 'robot_id' as RobotId, |
| robot_run_id: 'robot_run_id' as RobotRunId, |
| properties: {}, |
| fix_suggestions: [], |
| }; |
| assert.equal( |
| element._shouldDisableAction(showActions, robotComment), |
| false |
| ); |
| }); |
| |
| test('_hideActions', () => { |
| let showActions = true; |
| const lastComment: UIComment = {}; |
| assert.equal(element._hideActions(showActions, lastComment), false); |
| showActions = false; |
| assert.equal(element._hideActions(showActions, lastComment), true); |
| showActions = true; |
| lastComment.__draft = true; |
| assert.equal(element._hideActions(showActions, lastComment), true); |
| const robotComment: UIRobot = { |
| id: '1234' as UrlEncodedCommentId, |
| updated: '1234' as Timestamp, |
| robot_id: 'robot_id' as RobotId, |
| robot_run_id: 'robot_run_id' as RobotRunId, |
| properties: {}, |
| fix_suggestions: [], |
| }; |
| assert.equal(element._hideActions(showActions, robotComment), true); |
| }); |
| |
| test('setting project name loads the project config', done => { |
| const projectName = 'foo/bar/baz' as RepoName; |
| const getProjectStub = stubRestApi('getProjectConfig').returns( |
| Promise.resolve({} as ConfigInfo) |
| ); |
| element.projectName = projectName; |
| flush(() => { |
| assert.isTrue(getProjectStub.calledWithExactly(projectName as never)); |
| done(); |
| }); |
| }); |
| |
| test('optionally show file path', () => { |
| // Path info doesn't exist when showFilePath is false. Because it's in a |
| // dom-if it is not yet in the dom. |
| assert.isNotOk(element.shadowRoot?.querySelector('.pathInfo')); |
| |
| const commentStub = sinon.stub(GerritNav, 'getUrlForComment'); |
| element.changeNum = 123 as NumericChangeId; |
| element.projectName = 'test project' as RepoName; |
| element.path = 'path/to/file'; |
| element.patchNum = 3 as PatchSetNum; |
| element.lineNum = 5; |
| element.comments = [{id: 'comment_id' as UrlEncodedCommentId}]; |
| element.showFilePath = true; |
| flush(); |
| assert.isOk(element.shadowRoot?.querySelector('.pathInfo')); |
| assert.notEqual( |
| getComputedStyle(element.shadowRoot!.querySelector('.pathInfo')!) |
| .display, |
| 'none' |
| ); |
| assert.isTrue( |
| commentStub.calledWithExactly( |
| element.changeNum, |
| element.projectName, |
| 'comment_id' as UrlEncodedCommentId |
| ) |
| ); |
| }); |
| |
| test('_computeDisplayPath', () => { |
| let path = 'path/to/file'; |
| assert.equal(element._computeDisplayPath(path), 'path/to/file'); |
| |
| element.lineNum = 5; |
| assert.equal(element._computeDisplayPath(path), 'path/to/file'); |
| |
| element.patchNum = 3 as PatchSetNum; |
| path = SpecialFilePath.PATCHSET_LEVEL_COMMENTS; |
| assert.equal(element._computeDisplayPath(path), 'Patchset'); |
| }); |
| |
| test('_computeDisplayLine', () => { |
| element.lineNum = 5; |
| assert.equal( |
| element._computeDisplayLine(element.lineNum, element.range), |
| '#5' |
| ); |
| |
| element.path = SpecialFilePath.COMMIT_MESSAGE; |
| element.lineNum = 5; |
| assert.equal( |
| element._computeDisplayLine(element.lineNum, element.range), |
| '#5' |
| ); |
| |
| element.lineNum = undefined; |
| element.path = SpecialFilePath.PATCHSET_LEVEL_COMMENTS; |
| assert.equal( |
| element._computeDisplayLine(element.lineNum, element.range), |
| '' |
| ); |
| }); |
| }); |
| }); |
| |
| suite('comment action tests with unresolved thread', () => { |
| let element: GrCommentThread; |
| |
| setup(() => { |
| stubRestApi('getLoggedIn').returns(Promise.resolve(false)); |
| stubRestApi('saveDiffDraft').returns( |
| Promise.resolve(({ |
| headers: {} as Headers, |
| redirected: false, |
| status: 200, |
| statusText: '', |
| type: '' as ResponseType, |
| url: '', |
| ok: true, |
| text() { |
| return Promise.resolve( |
| ")]}'\n" + |
| JSON.stringify({ |
| id: '7afa4931_de3d65bd', |
| path: '/path/to/file.txt', |
| line: 5, |
| in_reply_to: 'baf0414d_60047215' as UrlEncodedCommentId, |
| updated: '2015-12-21 02:01:10.850000000', |
| message: 'Done', |
| }) |
| ); |
| }, |
| } as unknown) as Response) |
| ); |
| stubRestApi('deleteDiffDraft').returns( |
| Promise.resolve(({ok: true} as unknown) as Response) |
| ); |
| element = withCommentFixture.instantiate(); |
| element.patchNum = 1 as PatchSetNum; |
| element.changeNum = 1 as NumericChangeId; |
| element.comments = [ |
| { |
| author: { |
| name: 'Mr. Peanutbutter', |
| email: ('tenn1sballchaser@aol.com' as EmailAddress) as EmailAddress, |
| }, |
| id: 'baf0414d_60047215' as UrlEncodedCommentId, |
| line: 5, |
| message: 'is this a crossover episode!?', |
| updated: '2015-12-08 19:48:33.843000000' as Timestamp, |
| path: '/path/to/file.txt', |
| unresolved: true, |
| patch_set: 3 as PatchSetNum, |
| }, |
| ]; |
| flush(); |
| }); |
| |
| test('reply', () => { |
| const commentEl = element.shadowRoot?.querySelector('gr-comment'); |
| const reportStub = sinon.stub(element.reporting, 'recordDraftInteraction'); |
| assert.ok(commentEl); |
| |
| const replyBtn = element.$.replyBtn; |
| tap(replyBtn); |
| flush(); |
| |
| const drafts = element._orderedComments.filter(c => isDraft(c)); |
| assert.equal(drafts.length, 1); |
| assert.notOk(drafts[0].message, 'message should be empty'); |
| assert.equal( |
| drafts[0].in_reply_to, |
| ('baf0414d_60047215' as UrlEncodedCommentId) as UrlEncodedCommentId |
| ); |
| assert.isTrue(reportStub.calledOnce); |
| }); |
| |
| test('quote reply', () => { |
| const commentEl = element.shadowRoot?.querySelector('gr-comment'); |
| const reportStub = sinon.stub(element.reporting, 'recordDraftInteraction'); |
| assert.ok(commentEl); |
| |
| const quoteBtn = element.$.quoteBtn; |
| tap(quoteBtn); |
| flush(); |
| |
| const drafts = element._orderedComments.filter(c => isDraft(c)); |
| assert.equal(drafts.length, 1); |
| assert.equal(drafts[0].message, '> is this a crossover episode!?\n\n'); |
| assert.equal( |
| drafts[0].in_reply_to, |
| ('baf0414d_60047215' as UrlEncodedCommentId) as UrlEncodedCommentId |
| ); |
| assert.isTrue(reportStub.calledOnce); |
| }); |
| |
| test('quote reply multiline', () => { |
| const reportStub = sinon.stub(element.reporting, 'recordDraftInteraction'); |
| element.comments = [ |
| { |
| author: { |
| name: 'Mr. Peanutbutter', |
| email: ('tenn1sballchaser@aol.com' as EmailAddress) as EmailAddress, |
| }, |
| id: 'baf0414d_60047215' as UrlEncodedCommentId, |
| path: 'test', |
| line: 5, |
| message: 'is this a crossover episode!?\nIt might be!', |
| updated: '2015-12-08 19:48:33.843000000' as Timestamp, |
| }, |
| ]; |
| flush(); |
| |
| const commentEl = element.shadowRoot?.querySelector('gr-comment'); |
| assert.ok(commentEl); |
| |
| const quoteBtn = element.$.quoteBtn; |
| tap(quoteBtn); |
| flush(); |
| |
| const drafts = element._orderedComments.filter(c => isDraft(c)); |
| assert.equal(drafts.length, 1); |
| assert.equal( |
| drafts[0].message, |
| '> is this a crossover episode!?\n> It might be!\n\n' |
| ); |
| assert.equal( |
| drafts[0].in_reply_to, |
| 'baf0414d_60047215' as UrlEncodedCommentId |
| ); |
| assert.isTrue(reportStub.calledOnce); |
| }); |
| |
| test('ack', done => { |
| const reportStub = sinon.stub(element.reporting, 'recordDraftInteraction'); |
| element.changeNum = 42 as NumericChangeId; |
| element.patchNum = 1 as PatchSetNum; |
| |
| const commentEl = element.shadowRoot?.querySelector('gr-comment'); |
| assert.ok(commentEl); |
| |
| const ackBtn = element.shadowRoot?.querySelector('#ackBtn'); |
| assert.isOk(ackBtn); |
| tap(ackBtn!); |
| flush(() => { |
| const drafts = element.comments.filter(c => isDraft(c)); |
| assert.equal(drafts.length, 1); |
| assert.equal(drafts[0].message, 'Ack'); |
| assert.equal( |
| drafts[0].in_reply_to, |
| 'baf0414d_60047215' as UrlEncodedCommentId |
| ); |
| assert.equal(drafts[0].unresolved, false); |
| assert.isTrue(reportStub.calledOnce); |
| done(); |
| }); |
| }); |
| |
| test('done', done => { |
| const reportStub = sinon.stub(element.reporting, 'recordDraftInteraction'); |
| element.changeNum = 42 as NumericChangeId; |
| element.patchNum = 1 as PatchSetNum; |
| const commentEl = element.shadowRoot?.querySelector('gr-comment'); |
| assert.ok(commentEl); |
| |
| const doneBtn = element.shadowRoot?.querySelector('#doneBtn'); |
| assert.isOk(doneBtn); |
| tap(doneBtn!); |
| flush(() => { |
| const drafts = element.comments.filter(c => isDraft(c)); |
| assert.equal(drafts.length, 1); |
| assert.equal(drafts[0].message, 'Done'); |
| assert.equal( |
| drafts[0].in_reply_to, |
| 'baf0414d_60047215' as UrlEncodedCommentId |
| ); |
| assert.isFalse(drafts[0].unresolved); |
| assert.isTrue(reportStub.calledOnce); |
| done(); |
| }); |
| }); |
| |
| test('save', done => { |
| element.changeNum = 42 as NumericChangeId; |
| element.patchNum = 1 as PatchSetNum; |
| element.path = '/path/to/file.txt'; |
| const commentEl = element.shadowRoot?.querySelector('gr-comment'); |
| assert.ok(commentEl); |
| |
| const saveOrDiscardStub = sinon.stub(); |
| element.addEventListener('thread-changed', saveOrDiscardStub); |
| element.shadowRoot?.querySelector('gr-comment')?._fireSave(); |
| |
| flush(() => { |
| assert.isTrue(saveOrDiscardStub.called); |
| assert.equal( |
| saveOrDiscardStub.lastCall.args[0].detail.rootId, |
| 'baf0414d_60047215' |
| ); |
| assert.equal(element.rootId, 'baf0414d_60047215' as UrlEncodedCommentId); |
| assert.equal( |
| saveOrDiscardStub.lastCall.args[0].detail.path, |
| '/path/to/file.txt' |
| ); |
| done(); |
| }); |
| }); |
| |
| test('please fix', done => { |
| element.changeNum = 42 as NumericChangeId; |
| element.patchNum = 1 as PatchSetNum; |
| const commentEl = element.shadowRoot?.querySelector('gr-comment'); |
| assert.ok(commentEl); |
| commentEl!.addEventListener('create-fix-comment', () => { |
| const drafts = element._orderedComments.filter(c => isDraft(c)); |
| assert.equal(drafts.length, 1); |
| assert.equal( |
| drafts[0].message, |
| '> is this a crossover episode!?\n\nPlease fix.' |
| ); |
| assert.equal( |
| drafts[0].in_reply_to, |
| 'baf0414d_60047215' as UrlEncodedCommentId |
| ); |
| assert.isTrue(drafts[0].unresolved); |
| done(); |
| }); |
| commentEl!.dispatchEvent( |
| new CustomEvent('create-fix-comment', { |
| detail: {comment: commentEl!.comment}, |
| composed: true, |
| bubbles: false, |
| }) |
| ); |
| }); |
| |
| test('discard', done => { |
| element.changeNum = 42 as NumericChangeId; |
| element.patchNum = 1 as PatchSetNum; |
| element.path = '/path/to/file.txt'; |
| assert.isOk(element.comments[0]); |
| element.push( |
| 'comments', |
| element._newReply( |
| element.comments[0]!.id as UrlEncodedCommentId, |
| 'it’s pronouced jiff, not giff' |
| ) |
| ); |
| flush(); |
| |
| const saveOrDiscardStub = sinon.stub(); |
| element.addEventListener('thread-changed', saveOrDiscardStub); |
| const draftEl = element.root?.querySelectorAll('gr-comment')[1]; |
| assert.ok(draftEl); |
| draftEl!.addEventListener('comment-discard', () => { |
| const drafts = element.comments.filter(c => isDraft(c)); |
| assert.equal(drafts.length, 0); |
| assert.isTrue(saveOrDiscardStub.called); |
| assert.equal( |
| saveOrDiscardStub.lastCall.args[0].detail.rootId, |
| element.rootId |
| ); |
| assert.equal( |
| saveOrDiscardStub.lastCall.args[0].detail.path, |
| element.path |
| ); |
| done(); |
| }); |
| draftEl!.dispatchEvent( |
| new CustomEvent('comment-discard', { |
| detail: {comment: draftEl!.comment}, |
| composed: true, |
| bubbles: false, |
| }) |
| ); |
| }); |
| |
| test('discard with a single comment still fires event with previous rootId', done => { |
| element.changeNum = 42 as NumericChangeId; |
| element.patchNum = 1 as PatchSetNum; |
| element.path = '/path/to/file.txt'; |
| element.comments = []; |
| element.addOrEditDraft(1 as LineNumber); |
| flush(); |
| const rootId = element.rootId; |
| assert.isOk(rootId); |
| |
| const saveOrDiscardStub = sinon.stub(); |
| element.addEventListener('thread-changed', saveOrDiscardStub); |
| const draftEl = element.root?.querySelectorAll('gr-comment')[0]; |
| assert.ok(draftEl); |
| draftEl!.addEventListener('comment-discard', () => { |
| assert.equal(element.comments.length, 0); |
| assert.isTrue(saveOrDiscardStub.called); |
| assert.equal(saveOrDiscardStub.lastCall.args[0].detail.rootId, rootId); |
| assert.equal( |
| saveOrDiscardStub.lastCall.args[0].detail.path, |
| element.path |
| ); |
| done(); |
| }); |
| draftEl!.dispatchEvent( |
| new CustomEvent('comment-discard', { |
| detail: {comment: draftEl!.comment}, |
| composed: true, |
| bubbles: false, |
| }) |
| ); |
| }); |
| |
| test( |
| 'When not editing other comments, local storage not set' + ' after discard', |
| done => { |
| element.changeNum = 42 as NumericChangeId; |
| element.patchNum = 1 as PatchSetNum; |
| element.comments = [ |
| { |
| author: { |
| name: 'Mr. Peanutbutter', |
| email: 'tenn1sballchaser@aol.com' as EmailAddress, |
| }, |
| id: 'baf0414d_60047215' as UrlEncodedCommentId, |
| path: 'test', |
| line: 5, |
| message: 'is this a crossover episode!?', |
| updated: '2015-12-08 19:48:31.843000000' as Timestamp, |
| }, |
| { |
| author: { |
| name: 'Mr. Peanutbutter', |
| email: 'tenn1sballchaser@aol.com' as EmailAddress, |
| }, |
| __draftID: '1', |
| in_reply_to: 'baf0414d_60047215' as UrlEncodedCommentId, |
| path: 'test', |
| line: 5, |
| message: 'yes', |
| updated: '2015-12-08 19:48:32.843000000' as Timestamp, |
| __draft: true, |
| __editing: true, |
| }, |
| { |
| author: { |
| name: 'Mr. Peanutbutter', |
| email: 'tenn1sballchaser@aol.com' as EmailAddress, |
| }, |
| __draftID: '2', |
| in_reply_to: 'baf0414d_60047215' as UrlEncodedCommentId, |
| path: 'test', |
| line: 5, |
| message: 'no', |
| updated: '2015-12-08 19:48:33.843000000' as Timestamp, |
| __draft: true, |
| }, |
| ]; |
| const storageStub = stubStorage('setDraftComment'); |
| flush(); |
| |
| const draftEl = element.root?.querySelectorAll('gr-comment')[1]; |
| assert.ok(draftEl); |
| draftEl!.addEventListener('comment-discard', () => { |
| assert.isFalse(storageStub.called); |
| storageStub.restore(); |
| done(); |
| }); |
| draftEl!.dispatchEvent( |
| new CustomEvent('comment-discard', { |
| detail: {comment: draftEl!.comment}, |
| composed: true, |
| bubbles: false, |
| }) |
| ); |
| } |
| ); |
| |
| test('comment-update', () => { |
| const commentEl = element.shadowRoot?.querySelector('gr-comment'); |
| const updatedComment = { |
| id: element.comments[0].id, |
| foo: 'bar', |
| }; |
| assert.isOk(commentEl); |
| commentEl!.dispatchEvent( |
| new CustomEvent('comment-update', { |
| detail: {comment: updatedComment}, |
| composed: true, |
| bubbles: true, |
| }) |
| ); |
| assert.strictEqual(element.comments[0], updatedComment); |
| }); |
| |
| suite('jack and sally comment data test consolidation', () => { |
| setup(() => { |
| element.comments = [ |
| { |
| id: 'jacks_reply' as UrlEncodedCommentId, |
| message: 'i like you, too', |
| in_reply_to: 'sallys_confession' as UrlEncodedCommentId, |
| updated: '2015-12-25 15:00:20.396000000' as Timestamp, |
| unresolved: false, |
| }, |
| { |
| id: 'sallys_confession' as UrlEncodedCommentId, |
| in_reply_to: 'nonexistent_comment' as UrlEncodedCommentId, |
| message: 'i like you, jack', |
| updated: '2015-12-24 15:00:20.396000000' as Timestamp, |
| }, |
| { |
| id: 'sally_to_dr_finklestein' as UrlEncodedCommentId, |
| in_reply_to: 'nonexistent_comment' as UrlEncodedCommentId, |
| message: 'i’m running away', |
| updated: '2015-10-31 09:00:20.396000000' as Timestamp, |
| }, |
| { |
| id: 'sallys_defiance' as UrlEncodedCommentId, |
| message: 'i will poison you so i can get away', |
| updated: '2015-10-31 15:00:20.396000000' as Timestamp, |
| }, |
| ]; |
| }); |
| |
| test('orphan replies', () => { |
| assert.equal(4, element._orderedComments.length); |
| }); |
| |
| test('keyboard shortcuts', () => { |
| const expandCollapseStub = sinon.stub(element, '_expandCollapseComments'); |
| pressAndReleaseKeyOn(element, 69, null, 'e'); |
| assert.isTrue(expandCollapseStub.lastCall.calledWith(false)); |
| |
| pressAndReleaseKeyOn(element, 69, 'shift', 'e'); |
| assert.isTrue(expandCollapseStub.lastCall.calledWith(true)); |
| }); |
| |
| test('comment in_reply_to is either null or most recent comment', () => { |
| element._createReplyComment('dummy', true); |
| flush(); |
| assert.equal(element._orderedComments.length, 5); |
| assert.equal( |
| element._orderedComments[4].in_reply_to, |
| 'jacks_reply' as UrlEncodedCommentId |
| ); |
| }); |
| |
| test('resolvable comments', () => { |
| assert.isFalse(element.unresolved); |
| element._createReplyComment('dummy', true, true); |
| flush(); |
| assert.isTrue(element.unresolved); |
| }); |
| |
| test('_setInitialExpandedState with unresolved', () => { |
| element.unresolved = true; |
| element._setInitialExpandedState(); |
| for (let i = 0; i < element.comments.length; i++) { |
| assert.isFalse(element.comments[i].collapsed); |
| } |
| }); |
| |
| test('_setInitialExpandedState without unresolved', () => { |
| element.unresolved = false; |
| element._setInitialExpandedState(); |
| for (let i = 0; i < element.comments.length; i++) { |
| assert.isTrue(element.comments[i].collapsed); |
| } |
| }); |
| |
| test('_setInitialExpandedState with robot_ids', () => { |
| for (let i = 0; i < element.comments.length; i++) { |
| (element.comments[i] as UIRobot).robot_id = '123' as RobotId; |
| } |
| element._setInitialExpandedState(); |
| for (let i = 0; i < element.comments.length; i++) { |
| assert.isFalse(element.comments[i].collapsed); |
| } |
| }); |
| |
| test('_setInitialExpandedState with collapsed state', () => { |
| element.comments[0].collapsed = false; |
| element.unresolved = false; |
| element._setInitialExpandedState(); |
| assert.isFalse(element.comments[0].collapsed); |
| for (let i = 1; i < element.comments.length; i++) { |
| assert.isTrue(element.comments[i].collapsed); |
| } |
| }); |
| }); |
| |
| test('_computeHostClass', () => { |
| assert.equal(element._computeHostClass(true), 'unresolved'); |
| assert.equal(element._computeHostClass(false), ''); |
| }); |
| |
| test('addDraft sets unresolved state correctly', () => { |
| let unresolved = true; |
| element.comments = []; |
| element.addDraft(undefined, undefined, unresolved); |
| assert.equal(element.comments[0].unresolved, true); |
| |
| unresolved = false; // comment should get added as actually resolved. |
| element.comments = []; |
| element.addDraft(undefined, undefined, unresolved); |
| assert.equal(element.comments[0].unresolved, false); |
| |
| element.comments = []; |
| element.addDraft(); |
| assert.equal(element.comments[0].unresolved, true); |
| }); |
| |
| test('_newDraft with root', () => { |
| const draft = element._newDraft(); |
| assert.equal(draft.patch_set, 3 as PatchSetNum); |
| }); |
| |
| test('_newDraft with no root', () => { |
| element.comments = []; |
| element.diffSide = Side.RIGHT; |
| element.patchNum = 2 as PatchSetNum; |
| const draft = element._newDraft(); |
| assert.equal(draft.patch_set, 2 as PatchSetNum); |
| }); |
| |
| test('new comment gets created', () => { |
| element.comments = []; |
| element.addOrEditDraft(1); |
| assert.equal(element.comments.length, 1); |
| // Mock a submitted comment. |
| element.comments[0].id = (element.comments[0] as UIDraft) |
| .__draftID as UrlEncodedCommentId; |
| delete (element.comments[0] as UIDraft).__draft; |
| element.addOrEditDraft(1); |
| assert.equal(element.comments.length, 2); |
| }); |
| |
| test('unresolved label', () => { |
| element.unresolved = false; |
| const label = element.shadowRoot?.querySelector('#unresolvedLabel'); |
| assert.isOk(label); |
| assert.isFalse(label!.hasAttribute('hidden')); |
| element.unresolved = true; |
| assert.isFalse(label!.hasAttribute('hidden')); |
| }); |
| |
| test('draft comments are at the end of orderedComments', () => { |
| element.comments = [ |
| { |
| author: { |
| name: 'Mr. Peanutbutter', |
| email: 'tenn1sballchaser@aol.com' as EmailAddress, |
| }, |
| id: '2' as UrlEncodedCommentId, |
| line: 5, |
| message: 'Earlier draft', |
| updated: '2015-12-08 19:48:33.843000000' as Timestamp, |
| __draft: true, |
| }, |
| { |
| author: { |
| name: 'Mr. Peanutbutter2', |
| email: 'tenn1sballchaser@aol.com' as EmailAddress, |
| }, |
| id: '1' as UrlEncodedCommentId, |
| line: 5, |
| message: 'This comment was left last but is not a draft', |
| updated: '2015-12-10 19:48:33.843000000' as Timestamp, |
| }, |
| { |
| author: { |
| name: 'Mr. Peanutbutter2', |
| email: 'tenn1sballchaser@aol.com' as EmailAddress, |
| }, |
| id: '3' as UrlEncodedCommentId, |
| line: 5, |
| message: 'Later draft', |
| updated: '2015-12-09 19:48:33.843000000' as Timestamp, |
| __draft: true, |
| }, |
| ]; |
| assert.equal(element._orderedComments[0].id, '1' as UrlEncodedCommentId); |
| assert.equal(element._orderedComments[1].id, '2' as UrlEncodedCommentId); |
| assert.equal(element._orderedComments[2].id, '3' as UrlEncodedCommentId); |
| }); |
| |
| test('reflects lineNum and commentSide to attributes', () => { |
| element.lineNum = 7; |
| element.diffSide = Side.LEFT; |
| |
| assert.equal(element.getAttribute('line-num'), '7'); |
| assert.equal(element.getAttribute('diff-side'), Side.LEFT); |
| }); |
| |
| test('reflects range to JSON serialized attribute if set', () => { |
| element.range = { |
| start_line: 4, |
| end_line: 5, |
| start_character: 6, |
| end_character: 7, |
| }; |
| |
| assert.isOk(element.getAttribute('range')); |
| assert.deepEqual(JSON.parse(element.getAttribute('range')!), { |
| start_line: 4, |
| end_line: 5, |
| start_character: 6, |
| end_character: 7, |
| }); |
| }); |
| |
| test('removes range attribute if range is unset', () => { |
| element.range = { |
| start_line: 4, |
| end_line: 5, |
| start_character: 6, |
| end_character: 7, |
| }; |
| element.range = undefined; |
| |
| assert.notOk(element.hasAttribute('range')); |
| }); |
| }); |
| |
| suite('comment action tests on resolved comments', () => { |
| let element: GrCommentThread; |
| |
| setup(() => { |
| stubRestApi('getLoggedIn').returns(Promise.resolve(false)); |
| stubRestApi('saveDiffDraft').returns( |
| Promise.resolve(({ |
| ok: true, |
| text() { |
| return Promise.resolve( |
| ")]}'\n" + |
| JSON.stringify({ |
| id: '7afa4931_de3d65bd', |
| path: '/path/to/file.txt', |
| line: 5, |
| in_reply_to: 'baf0414d_60047215' as UrlEncodedCommentId, |
| updated: '2015-12-21 02:01:10.850000000', |
| message: 'Done', |
| }) |
| ); |
| }, |
| } as unknown) as Response) |
| ); |
| stubRestApi('deleteDiffDraft').returns( |
| Promise.resolve(({ok: true} as unknown) as Response) |
| ); |
| element = withCommentFixture.instantiate(); |
| element.patchNum = 1 as PatchSetNum; |
| element.changeNum = 1 as NumericChangeId; |
| element.comments = [ |
| { |
| author: { |
| name: 'Mr. Peanutbutter', |
| email: 'tenn1sballchaser@aol.com' as EmailAddress, |
| }, |
| id: 'baf0414d_60047215' as UrlEncodedCommentId, |
| line: 5, |
| message: 'is this a crossover episode!?', |
| updated: '2015-12-08 19:48:33.843000000' as Timestamp, |
| path: '/path/to/file.txt', |
| unresolved: false, |
| }, |
| ]; |
| flush(); |
| }); |
| |
| test('ack and done should be hidden', () => { |
| element.changeNum = 42 as NumericChangeId; |
| element.patchNum = 1 as PatchSetNum; |
| |
| const commentEl = element.shadowRoot?.querySelector('gr-comment'); |
| assert.ok(commentEl); |
| |
| const ackBtn = element.shadowRoot?.querySelector('#ackBtn'); |
| const doneBtn = element.shadowRoot?.querySelector('#doneBtn'); |
| assert.equal(ackBtn, null); |
| assert.equal(doneBtn, null); |
| }); |
| |
| test('reply and quote button should be visible', () => { |
| const commentEl = element.shadowRoot?.querySelector('gr-comment'); |
| assert.ok(commentEl); |
| |
| const replyBtn = element.shadowRoot?.querySelector('#replyBtn'); |
| const quoteBtn = element.shadowRoot?.querySelector('#quoteBtn'); |
| assert.ok(replyBtn); |
| assert.ok(quoteBtn); |
| }); |
| }); |