| /** |
| * @license |
| * Copyright (C) 2018 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-thread-list'; |
| import {CommentSide, SpecialFilePath} from '../../../constants/constants'; |
| import {CommentTabState} from '../../../types/events'; |
| import { |
| compareThreads, |
| GrThreadList, |
| __testOnly_SortDropdownState, |
| } from './gr-thread-list'; |
| import {queryAll} from '../../../test/test-utils'; |
| import {accountOrGroupKey} from '../../../utils/account-util'; |
| import {tap} from '@polymer/iron-test-helpers/mock-interactions'; |
| import { |
| createAccountDetailWithId, |
| createParsedChange, |
| createThread, |
| } from '../../../test/test-data-generators'; |
| import { |
| AccountId, |
| NumericChangeId, |
| PatchSetNum, |
| Timestamp, |
| } from '../../../api/rest-api'; |
| import {RobotId, UrlEncodedCommentId} from '../../../types/common'; |
| import {CommentThread} from '../../../utils/comment-util'; |
| import {query, queryAndAssert} from '../../../utils/common-util'; |
| import {GrAccountLabel} from '../../shared/gr-account-label/gr-account-label'; |
| |
| const basicFixture = fixtureFromElement('gr-thread-list'); |
| |
| suite('gr-thread-list tests', () => { |
| let element: GrThreadList; |
| |
| setup(async () => { |
| element = basicFixture.instantiate(); |
| element.changeNum = 123 as NumericChangeId; |
| element.change = createParsedChange(); |
| element.account = createAccountDetailWithId(); |
| element.threads = [ |
| { |
| comments: [ |
| { |
| path: '/COMMIT_MSG', |
| author: { |
| _account_id: 1000001 as AccountId, |
| name: 'user', |
| username: 'user', |
| }, |
| patch_set: 4 as PatchSetNum, |
| id: 'ecf0b9fa_fe1a5f62' as UrlEncodedCommentId, |
| line: 5, |
| updated: '2015-12-01 15:15:15.000000000' as Timestamp, |
| message: 'test', |
| unresolved: true, |
| }, |
| { |
| id: '503008e2_0ab203ee' as UrlEncodedCommentId, |
| path: '/COMMIT_MSG', |
| line: 5, |
| in_reply_to: 'ecf0b9fa_fe1a5f62' as UrlEncodedCommentId, |
| updated: '2015-12-01 15:16:15.000000000' as Timestamp, |
| message: 'draft', |
| unresolved: true, |
| __draft: true, |
| patch_set: '2' as PatchSetNum, |
| }, |
| ], |
| patchNum: 4 as PatchSetNum, |
| path: '/COMMIT_MSG', |
| line: 5, |
| rootId: 'ecf0b9fa_fe1a5f62' as UrlEncodedCommentId, |
| commentSide: CommentSide.REVISION, |
| }, |
| { |
| comments: [ |
| { |
| path: 'test.txt', |
| author: { |
| _account_id: 1000002 as AccountId, |
| name: 'user', |
| username: 'user', |
| }, |
| patch_set: 3 as PatchSetNum, |
| id: '09a9fb0a_1484e6cf' as UrlEncodedCommentId, |
| updated: '2015-12-02 15:16:15.000000000' as Timestamp, |
| message: 'Some comment on another patchset.', |
| unresolved: false, |
| }, |
| ], |
| patchNum: 3 as PatchSetNum, |
| path: 'test.txt', |
| rootId: '09a9fb0a_1484e6cf' as UrlEncodedCommentId, |
| commentSide: CommentSide.REVISION, |
| }, |
| { |
| comments: [ |
| { |
| path: '/COMMIT_MSG', |
| author: { |
| _account_id: 1000002 as AccountId, |
| name: 'user', |
| username: 'user', |
| }, |
| patch_set: 2 as PatchSetNum, |
| id: '8caddf38_44770ec1' as UrlEncodedCommentId, |
| updated: '2015-12-03 15:16:15.000000000' as Timestamp, |
| message: 'Another unresolved comment', |
| unresolved: false, |
| }, |
| ], |
| patchNum: 2 as PatchSetNum, |
| path: '/COMMIT_MSG', |
| rootId: '8caddf38_44770ec1' as UrlEncodedCommentId, |
| commentSide: CommentSide.REVISION, |
| }, |
| { |
| comments: [ |
| { |
| path: '/COMMIT_MSG', |
| author: { |
| _account_id: 1000003 as AccountId, |
| name: 'user', |
| username: 'user', |
| }, |
| patch_set: 2 as PatchSetNum, |
| id: 'scaddf38_44770ec1' as UrlEncodedCommentId, |
| line: 4, |
| updated: '2015-12-04 15:16:15.000000000' as Timestamp, |
| message: 'Yet another unresolved comment', |
| unresolved: true, |
| }, |
| ], |
| patchNum: 2 as PatchSetNum, |
| path: '/COMMIT_MSG', |
| line: 4, |
| rootId: 'scaddf38_44770ec1' as UrlEncodedCommentId, |
| commentSide: CommentSide.REVISION, |
| }, |
| { |
| comments: [ |
| { |
| id: 'zcf0b9fa_fe1a5f62' as UrlEncodedCommentId, |
| path: '/COMMIT_MSG', |
| line: 6, |
| updated: '2015-12-05 15:16:15.000000000' as Timestamp, |
| message: 'resolved draft', |
| unresolved: false, |
| __draft: true, |
| patch_set: '2' as PatchSetNum, |
| }, |
| ], |
| patchNum: 4 as PatchSetNum, |
| path: '/COMMIT_MSG', |
| line: 6, |
| rootId: 'zcf0b9fa_fe1a5f62' as UrlEncodedCommentId, |
| commentSide: CommentSide.REVISION, |
| }, |
| { |
| comments: [ |
| { |
| id: 'patchset_level_1' as UrlEncodedCommentId, |
| path: SpecialFilePath.PATCHSET_LEVEL_COMMENTS, |
| updated: '2015-12-06 15:16:15.000000000' as Timestamp, |
| message: 'patchset comment 1', |
| unresolved: false, |
| patch_set: '2' as PatchSetNum, |
| }, |
| ], |
| patchNum: 2 as PatchSetNum, |
| path: SpecialFilePath.PATCHSET_LEVEL_COMMENTS, |
| rootId: 'patchset_level_1' as UrlEncodedCommentId, |
| commentSide: CommentSide.REVISION, |
| }, |
| { |
| comments: [ |
| { |
| id: 'patchset_level_2' as UrlEncodedCommentId, |
| path: SpecialFilePath.PATCHSET_LEVEL_COMMENTS, |
| updated: '2015-12-07 15:16:15.000000000' as Timestamp, |
| message: 'patchset comment 2', |
| unresolved: false, |
| patch_set: '3' as PatchSetNum, |
| }, |
| ], |
| patchNum: 3 as PatchSetNum, |
| path: SpecialFilePath.PATCHSET_LEVEL_COMMENTS, |
| rootId: 'patchset_level_2' as UrlEncodedCommentId, |
| commentSide: CommentSide.REVISION, |
| }, |
| { |
| comments: [ |
| { |
| path: '/COMMIT_MSG', |
| author: { |
| _account_id: 1000000 as AccountId, |
| name: 'user', |
| username: 'user', |
| }, |
| patch_set: 4 as PatchSetNum, |
| id: 'rc1' as UrlEncodedCommentId, |
| line: 5, |
| updated: '2015-12-08 15:16:15.000000000' as Timestamp, |
| message: 'test', |
| unresolved: true, |
| robot_id: 'rc1' as RobotId, |
| }, |
| ], |
| patchNum: 4 as PatchSetNum, |
| path: '/COMMIT_MSG', |
| line: 5, |
| rootId: 'rc1' as UrlEncodedCommentId, |
| commentSide: CommentSide.REVISION, |
| }, |
| { |
| comments: [ |
| { |
| path: '/COMMIT_MSG', |
| author: { |
| _account_id: 1000000 as AccountId, |
| name: 'user', |
| username: 'user', |
| }, |
| patch_set: 4 as PatchSetNum, |
| id: 'rc2' as UrlEncodedCommentId, |
| line: 7, |
| updated: '2015-12-09 15:16:15.000000000' as Timestamp, |
| message: 'test', |
| unresolved: true, |
| robot_id: 'rc2' as RobotId, |
| }, |
| { |
| path: '/COMMIT_MSG', |
| author: { |
| _account_id: 1000000 as AccountId, |
| name: 'user', |
| username: 'user', |
| }, |
| patch_set: 4 as PatchSetNum, |
| id: 'c2_1' as UrlEncodedCommentId, |
| line: 5, |
| updated: '2015-12-10 15:16:15.000000000' as Timestamp, |
| message: 'test', |
| unresolved: true, |
| }, |
| ], |
| patchNum: 4 as PatchSetNum, |
| path: '/COMMIT_MSG', |
| line: 7, |
| rootId: 'rc2' as UrlEncodedCommentId, |
| commentSide: CommentSide.REVISION, |
| }, |
| ]; |
| await element.updateComplete; |
| }); |
| |
| suite('sort threads', () => { |
| test('sort all threads', () => { |
| element.sortDropdownValue = __testOnly_SortDropdownState.FILES; |
| assert.equal(element.getDisplayedThreads().length, 9); |
| const expected: UrlEncodedCommentId[] = [ |
| 'patchset_level_2' as UrlEncodedCommentId, // Posted on Patchset 3 |
| 'patchset_level_1' as UrlEncodedCommentId, // Posted on Patchset 2 |
| '8caddf38_44770ec1' as UrlEncodedCommentId, // File level on COMMIT_MSG |
| 'scaddf38_44770ec1' as UrlEncodedCommentId, // Line 4 on COMMIT_MSG |
| 'rc1' as UrlEncodedCommentId, // Line 5 on COMMIT_MESSAGE newer |
| 'ecf0b9fa_fe1a5f62' as UrlEncodedCommentId, // Line 5 on COMMIT_MESSAGE older |
| 'zcf0b9fa_fe1a5f62' as UrlEncodedCommentId, // Line 6 on COMMIT_MSG |
| 'rc2' as UrlEncodedCommentId, // Line 7 on COMMIT_MSG |
| '09a9fb0a_1484e6cf' as UrlEncodedCommentId, // File level on test.txt |
| ]; |
| const actual = element.getDisplayedThreads().map(t => t.rootId); |
| assert.sameOrderedMembers(actual, expected); |
| }); |
| |
| test('sort all threads by timestamp', () => { |
| element.sortDropdownValue = __testOnly_SortDropdownState.TIMESTAMP; |
| assert.equal(element.getDisplayedThreads().length, 9); |
| const expected: UrlEncodedCommentId[] = [ |
| 'rc2' as UrlEncodedCommentId, |
| 'rc1' as UrlEncodedCommentId, |
| 'patchset_level_2' as UrlEncodedCommentId, |
| 'patchset_level_1' as UrlEncodedCommentId, |
| 'zcf0b9fa_fe1a5f62' as UrlEncodedCommentId, |
| 'scaddf38_44770ec1' as UrlEncodedCommentId, |
| '8caddf38_44770ec1' as UrlEncodedCommentId, |
| '09a9fb0a_1484e6cf' as UrlEncodedCommentId, |
| 'ecf0b9fa_fe1a5f62' as UrlEncodedCommentId, |
| ]; |
| const actual = element.getDisplayedThreads().map(t => t.rootId); |
| assert.sameOrderedMembers(actual, expected); |
| }); |
| }); |
| |
| test('renders', async () => { |
| await element.updateComplete; |
| expect(element).shadowDom.to.equal(/* HTML */ ` |
| <div class="header"> |
| <span class="sort-text">Sort By:</span> |
| <gr-dropdown-list id="sortDropdown"></gr-dropdown-list> |
| <span class="separator"></span> |
| <span class="filter-text">Filter By:</span> |
| <gr-dropdown-list id="filterDropdown"></gr-dropdown-list> |
| <span class="author-text">From:</span> |
| <gr-account-label |
| deselected="" |
| selectionchipstyle="" |
| nostatusicons="" |
| ></gr-account-label> |
| <gr-account-label |
| deselected="" |
| selectionchipstyle="" |
| nostatusicons="" |
| ></gr-account-label> |
| <gr-account-label |
| deselected="" |
| selectionchipstyle="" |
| nostatusicons="" |
| ></gr-account-label> |
| <gr-account-label |
| deselected="" |
| selectionchipstyle="" |
| nostatusicons="" |
| ></gr-account-label> |
| <gr-account-label |
| deselected="" |
| selectionchipstyle="" |
| nostatusicons="" |
| ></gr-account-label> |
| </div> |
| <div id="threads" part="threads"> |
| <gr-comment-thread |
| show-file-name="" |
| show-file-path="" |
| ></gr-comment-thread> |
| <gr-comment-thread show-file-path=""></gr-comment-thread> |
| <div class="thread-separator"></div> |
| <gr-comment-thread |
| show-file-name="" |
| show-file-path="" |
| ></gr-comment-thread> |
| <gr-comment-thread show-file-path=""></gr-comment-thread> |
| <div class="thread-separator"></div> |
| <gr-comment-thread |
| has-draft="" |
| show-file-name="" |
| show-file-path="" |
| ></gr-comment-thread> |
| <gr-comment-thread show-file-path=""></gr-comment-thread> |
| <gr-comment-thread show-file-path=""></gr-comment-thread> |
| <div class="thread-separator"></div> |
| <gr-comment-thread |
| show-file-name="" |
| show-file-path="" |
| ></gr-comment-thread> |
| <div class="thread-separator"></div> |
| <gr-comment-thread |
| has-draft="" |
| show-file-name="" |
| show-file-path="" |
| ></gr-comment-thread> |
| </div> |
| `); |
| }); |
| |
| test('renders empty', async () => { |
| element.threads = []; |
| await element.updateComplete; |
| expect(queryAndAssert(element, 'div#threads')).dom.to.equal(/* HTML */ ` |
| <div id="threads" part="threads"> |
| <div><span>No comments</span></div> |
| </div> |
| `); |
| }); |
| |
| test('tapping single author chips', async () => { |
| element.account = createAccountDetailWithId(1); |
| await element.updateComplete; |
| const chips = Array.from( |
| queryAll<GrAccountLabel>(element, 'gr-account-label') |
| ); |
| const authors = chips.map(chip => accountOrGroupKey(chip.account!)).sort(); |
| assert.deepEqual(authors, [ |
| 1 as AccountId, |
| 1000000 as AccountId, |
| 1000001 as AccountId, |
| 1000002 as AccountId, |
| 1000003 as AccountId, |
| ]); |
| assert.equal(element.threads.length, 9); |
| assert.equal(element.getDisplayedThreads().length, 9); |
| |
| const chip = chips.find(chip => chip.account!._account_id === 1000001); |
| tap(chip!); |
| await element.updateComplete; |
| |
| assert.equal(element.threads.length, 9); |
| assert.equal(element.getDisplayedThreads().length, 1); |
| assert.equal( |
| element.getDisplayedThreads()[0].comments[0].author?._account_id, |
| 1000001 as AccountId |
| ); |
| |
| tap(chip!); |
| await element.updateComplete; |
| assert.equal(element.threads.length, 9); |
| assert.equal(element.getDisplayedThreads().length, 9); |
| }); |
| |
| test('tapping multiple author chips', async () => { |
| element.account = createAccountDetailWithId(1); |
| await element.updateComplete; |
| const chips = Array.from( |
| queryAll<GrAccountLabel>(element, 'gr-account-label') |
| ); |
| |
| tap(chips.find(chip => chip.account?._account_id === 1000001)!); |
| tap(chips.find(chip => chip.account?._account_id === 1000002)!); |
| await element.updateComplete; |
| |
| assert.equal(element.threads.length, 9); |
| assert.equal(element.getDisplayedThreads().length, 3); |
| assert.equal( |
| element.getDisplayedThreads()[0].comments[0].author?._account_id, |
| 1000002 as AccountId |
| ); |
| assert.equal( |
| element.getDisplayedThreads()[1].comments[0].author?._account_id, |
| 1000002 as AccountId |
| ); |
| assert.equal( |
| element.getDisplayedThreads()[2].comments[0].author?._account_id, |
| 1000001 as AccountId |
| ); |
| }); |
| |
| test('show all comments', async () => { |
| const event = new CustomEvent('value-changed', { |
| detail: {value: CommentTabState.SHOW_ALL}, |
| }); |
| element.handleCommentsDropdownValueChange(event); |
| await element.updateComplete; |
| assert.equal(element.getDisplayedThreads().length, 9); |
| }); |
| |
| test('unresolved shows all unresolved comments', async () => { |
| const event = new CustomEvent('value-changed', { |
| detail: {value: CommentTabState.UNRESOLVED}, |
| }); |
| element.handleCommentsDropdownValueChange(event); |
| await element.updateComplete; |
| assert.equal(element.getDisplayedThreads().length, 4); |
| }); |
| |
| test('toggle drafts only shows threads with draft comments', async () => { |
| const event = new CustomEvent('value-changed', { |
| detail: {value: CommentTabState.DRAFTS}, |
| }); |
| element.handleCommentsDropdownValueChange(event); |
| await element.updateComplete; |
| assert.equal(element.getDisplayedThreads().length, 2); |
| }); |
| |
| suite('hideDropdown', () => { |
| test('header hidden for hideDropdown=true', async () => { |
| element.hideDropdown = true; |
| await element.updateComplete; |
| assert.isUndefined(query(element, '.header')); |
| }); |
| |
| test('header shown for hideDropdown=false', async () => { |
| element.hideDropdown = false; |
| await element.updateComplete; |
| assert.isDefined(query(element, '.header')); |
| }); |
| }); |
| |
| suite('empty thread', () => { |
| setup(async () => { |
| element.threads = []; |
| await element.updateComplete; |
| }); |
| |
| test('default empty message should show', () => { |
| const threadsEl = queryAndAssert(element, '#threads'); |
| assert.isTrue(threadsEl.textContent?.trim().includes('No comments')); |
| }); |
| }); |
| }); |
| |
| suite('compareThreads', () => { |
| let t1: CommentThread; |
| let t2: CommentThread; |
| |
| const sortPredicate = (thread1: CommentThread, thread2: CommentThread) => |
| compareThreads(thread1, thread2); |
| |
| const checkOrder = (expected: CommentThread[]) => { |
| assert.sameOrderedMembers([t1, t2].sort(sortPredicate), expected); |
| assert.sameOrderedMembers([t2, t1].sort(sortPredicate), expected); |
| }; |
| |
| setup(() => { |
| t1 = createThread({}); |
| t2 = createThread({}); |
| }); |
| |
| test('patchset-level before file comments', () => { |
| t1.path = SpecialFilePath.PATCHSET_LEVEL_COMMENTS; |
| t2.path = SpecialFilePath.COMMIT_MESSAGE; |
| checkOrder([t1, t2]); |
| }); |
| |
| test('paths lexicographically', () => { |
| t1.path = 'a.txt'; |
| t2.path = 'b.txt'; |
| checkOrder([t1, t2]); |
| }); |
| |
| test('patchsets in reverse order', () => { |
| t1.patchNum = 2 as PatchSetNum; |
| t2.patchNum = 3 as PatchSetNum; |
| checkOrder([t2, t1]); |
| }); |
| |
| test('file level comment before line', () => { |
| t1.line = 123; |
| t2.line = 'FILE'; |
| checkOrder([t2, t1]); |
| }); |
| |
| test('comments sorted by line', () => { |
| t1.line = 123; |
| t2.line = 321; |
| checkOrder([t1, t2]); |
| }); |
| }); |