| /** |
| * @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.js'; |
| import './gr-thread-list.js'; |
| import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js'; |
| import {SpecialFilePath} from '../../../constants/constants.js'; |
| |
| const basicFixture = fixtureFromElement('gr-thread-list'); |
| |
| suite('gr-thread-list tests', () => { |
| let element; |
| |
| let threadElements; |
| |
| function getVisibleThreads() { |
| return [...dom(element.root) |
| .querySelectorAll('gr-comment-thread')] |
| .filter(e => e.style.display !== 'none'); |
| } |
| |
| setup(done => { |
| element = basicFixture.instantiate(); |
| element.changeNum = 123; |
| element.change = { |
| project: 'testRepo', |
| }; |
| element.threads = [ |
| { |
| comments: [ |
| { |
| path: '/COMMIT_MSG', |
| author: { |
| _account_id: 1000000, |
| name: 'user', |
| username: 'user', |
| }, |
| patch_set: 4, |
| id: 'ecf0b9fa_fe1a5f62', |
| line: 5, |
| updated: '2018-02-08 18:49:18.000000000', |
| message: 'test', |
| unresolved: true, |
| }, |
| { |
| id: '503008e2_0ab203ee', |
| path: '/COMMIT_MSG', |
| line: 5, |
| in_reply_to: 'ecf0b9fa_fe1a5f62', |
| updated: '2018-02-13 22:48:48.018000000', |
| message: 'draft', |
| unresolved: true, |
| __draft: true, |
| __draftID: '0.m683trwff68', |
| __editing: false, |
| patch_set: '2', |
| }, |
| ], |
| patchNum: 4, |
| path: '/COMMIT_MSG', |
| line: 5, |
| rootId: 'ecf0b9fa_fe1a5f62', |
| start_datetime: '2018-02-08 18:49:18.000000000', |
| }, |
| { |
| comments: [ |
| { |
| path: 'test.txt', |
| author: { |
| _account_id: 1000000, |
| name: 'user', |
| username: 'user', |
| }, |
| patch_set: 3, |
| id: '09a9fb0a_1484e6cf', |
| side: 'PARENT', |
| updated: '2018-02-13 22:47:19.000000000', |
| message: 'Some comment on another patchset.', |
| unresolved: false, |
| }, |
| ], |
| patchNum: 3, |
| path: 'test.txt', |
| rootId: '09a9fb0a_1484e6cf', |
| start_datetime: '2018-02-13 22:47:19.000000000', |
| commentSide: 'PARENT', |
| }, |
| { |
| comments: [ |
| { |
| path: '/COMMIT_MSG', |
| author: { |
| _account_id: 1000000, |
| name: 'user', |
| username: 'user', |
| }, |
| patch_set: 2, |
| id: '8caddf38_44770ec1', |
| updated: '2018-02-13 22:48:40.000000000', |
| message: 'Another unresolved comment', |
| unresolved: false, |
| }, |
| ], |
| patchNum: 2, |
| path: '/COMMIT_MSG', |
| rootId: '8caddf38_44770ec1', |
| start_datetime: '2018-02-13 22:48:40.000000000', |
| }, |
| { |
| comments: [ |
| { |
| path: '/COMMIT_MSG', |
| author: { |
| _account_id: 1000000, |
| name: 'user', |
| username: 'user', |
| }, |
| patch_set: 2, |
| id: 'scaddf38_44770ec1', |
| line: 4, |
| updated: '2018-02-14 22:48:40.000000000', |
| message: 'Yet another unresolved comment', |
| unresolved: true, |
| }, |
| ], |
| patchNum: 2, |
| path: '/COMMIT_MSG', |
| line: 4, |
| rootId: 'scaddf38_44770ec1', |
| start_datetime: '2018-02-14 22:48:40.000000000', |
| }, |
| { |
| comments: [ |
| { |
| id: 'zcf0b9fa_fe1a5f62', |
| path: '/COMMIT_MSG', |
| line: 6, |
| updated: '2018-02-15 22:48:48.018000000', |
| message: 'resolved draft', |
| unresolved: false, |
| __draft: true, |
| __draftID: '0.m683trwff69', |
| __editing: false, |
| patch_set: '2', |
| }, |
| ], |
| patchNum: 4, |
| path: '/COMMIT_MSG', |
| line: 6, |
| rootId: 'zcf0b9fa_fe1a5f62', |
| start_datetime: '2018-02-09 18:49:18.000000000', |
| }, |
| { |
| comments: [ |
| { |
| id: 'patchset_level_1', |
| path: SpecialFilePath.PATCHSET_LEVEL_COMMENTS, |
| updated: '2018-02-15 22:48:48.018000000', |
| message: 'patchset comment 1', |
| unresolved: false, |
| __editing: false, |
| patch_set: '2', |
| }, |
| ], |
| patchNum: 2, |
| path: SpecialFilePath.PATCHSET_LEVEL_COMMENTS, |
| rootId: 'patchset_level_1', |
| start_datetime: '2018-02-09 18:49:18.000000000', |
| }, |
| { |
| comments: [ |
| { |
| id: 'patchset_level_2', |
| path: SpecialFilePath.PATCHSET_LEVEL_COMMENTS, |
| updated: '2018-02-15 22:48:48.018000000', |
| message: 'patchset comment 2', |
| unresolved: false, |
| __editing: false, |
| patch_set: '3', |
| }, |
| ], |
| patchNum: 3, |
| path: SpecialFilePath.PATCHSET_LEVEL_COMMENTS, |
| rootId: 'patchset_level_2', |
| start_datetime: '2018-02-09 18:49:18.000000000', |
| }, |
| { |
| comments: [ |
| { |
| path: '/COMMIT_MSG', |
| author: { |
| _account_id: 1000000, |
| name: 'user', |
| username: 'user', |
| }, |
| patch_set: 4, |
| id: 'rc1', |
| line: 5, |
| updated: '2019-02-08 18:49:18.000000000', |
| message: 'test', |
| unresolved: true, |
| robot_id: 'rc1', |
| }, |
| ], |
| patchNum: 4, |
| path: '/COMMIT_MSG', |
| line: 5, |
| rootId: 'rc1', |
| start_datetime: '2019-02-08 18:49:18.000000000', |
| }, |
| { |
| comments: [ |
| { |
| path: '/COMMIT_MSG', |
| author: { |
| _account_id: 1000000, |
| name: 'user', |
| username: 'user', |
| }, |
| patch_set: 4, |
| id: 'rc2', |
| line: 7, |
| updated: '2019-03-08 18:49:18.000000000', |
| message: 'test', |
| unresolved: true, |
| robot_id: 'rc2', |
| }, |
| { |
| path: '/COMMIT_MSG', |
| author: { |
| _account_id: 1000000, |
| name: 'user', |
| username: 'user', |
| }, |
| patch_set: 4, |
| id: 'c2_1', |
| line: 5, |
| updated: '2019-03-08 18:49:18.000000000', |
| message: 'test', |
| unresolved: true, |
| }, |
| ], |
| patchNum: 4, |
| path: '/COMMIT_MSG', |
| line: 7, |
| rootId: 'rc2', |
| start_datetime: '2019-03-08 18:49:18.000000000', |
| }, |
| ]; |
| |
| // use flush to render all (bypass initial-count set on dom-repeat) |
| flush(() => { |
| threadElements = dom(element.root) |
| .querySelectorAll('gr-comment-thread'); |
| done(); |
| }); |
| }); |
| |
| test('draft toggle only appears when logged in', () => { |
| assert.equal(getComputedStyle(element.shadowRoot |
| .querySelector('.draftToggle')).display, |
| 'none'); |
| element.loggedIn = true; |
| assert.notEqual(getComputedStyle(element.shadowRoot |
| .querySelector('.draftToggle')).display, |
| 'none'); |
| }); |
| |
| test('show all threads by default', () => { |
| assert.equal(dom(element.root) |
| .querySelectorAll('gr-comment-thread').length, element.threads.length); |
| assert.equal(getVisibleThreads().length, element.threads.length); |
| }); |
| |
| test('show unresolved threads if unresolvedOnly is set', done => { |
| element.unresolvedOnly = true; |
| flush(); |
| const unresolvedThreads = element.threads.filter(t => t.comments.some( |
| c => c.unresolved |
| )); |
| assert.equal(getVisibleThreads().length, unresolvedThreads.length); |
| done(); |
| }); |
| |
| test('showing file name takes visible threads into account', () => { |
| assert.equal(element._isFirstThreadWithFileName(element._sortedThreads, |
| element._sortedThreads[2], element.unresolvedOnly, element._draftsOnly, |
| element.onlyShowRobotCommentsWithHumanReply), true); |
| element.unresolvedOnly = true; |
| assert.equal(element._isFirstThreadWithFileName(element._sortedThreads, |
| element._sortedThreads[2], element.unresolvedOnly, element._draftsOnly, |
| element.onlyShowRobotCommentsWithHumanReply), false); |
| }); |
| |
| test('onlyShowRobotCommentsWithHumanReply ', () => { |
| element.onlyShowRobotCommentsWithHumanReply = true; |
| flush(); |
| assert.equal( |
| getVisibleThreads().length, |
| element.threads.length - 1); |
| assert.isNotOk(getVisibleThreads().find(th => th.rootId === 'rc1')); |
| }); |
| |
| suite('_compareThreads', () => { |
| test('patchset comes before any other file', () => { |
| const t1 = {thread: {path: SpecialFilePath.PATCHSET_LEVEL_COMMENTS}}; |
| const t2 = {thread: {path: SpecialFilePath.COMMIT_MESSAGE}}; |
| |
| t1.patchNum = t2.patchNum = 1; |
| t1.unresolved = t2.unresolved = t1.hasDraft = t2.hasDraft = false; |
| assert.equal(element._compareThreads(t1, t2), -1); |
| assert.equal(element._compareThreads(t2, t1), 1); |
| |
| // assigning values to properties such that t2 should come first |
| t1.patchNum = 1; |
| t2.patchNum = 2; |
| t1.unresolved = t1.hasDraft = false; |
| t2.unresolved = t2.unresolved = true; |
| assert.equal(element._compareThreads(t1, t2), -1); |
| assert.equal(element._compareThreads(t2, t1), 1); |
| }); |
| |
| test('file path is compared lexicographically', () => { |
| const t1 = {thread: {path: 'a.txt'}}; |
| const t2 = {thread: {path: 'b.txt'}}; |
| t1.patchNum = t2.patchNum = 1; |
| t1.unresolved = t2.unresolved = t1.hasDraft = t2.hasDraft = false; |
| assert.equal(element._compareThreads(t1, t2), -1); |
| assert.equal(element._compareThreads(t2, t1), 1); |
| |
| t1.patchNum = 1; |
| t2.patchNum = 2; |
| t1.unresolved = t1.hasDraft = false; |
| t2.unresolved = t2.unresolved = true; |
| assert.equal(element._compareThreads(t1, t2), -1); |
| assert.equal(element._compareThreads(t2, t1), 1); |
| }); |
| |
| test('patchset comments sorted by reverse patchset', () => { |
| const t1 = {thread: {path: SpecialFilePath.PATCHSET_LEVEL_COMMENTS, |
| patchNum: 1}}; |
| const t2 = {thread: {path: SpecialFilePath.PATCHSET_LEVEL_COMMENTS, |
| patchNum: 2}}; |
| t1.unresolved = t2.unresolved = t1.hasDraft = t2.hasDraft = false; |
| assert.equal(element._compareThreads(t1, t2), 1); |
| assert.equal(element._compareThreads(t2, t1), -1); |
| |
| t1.unresolved = t1.hasDraft = false; |
| t2.unresolved = t2.unresolved = true; |
| assert.equal(element._compareThreads(t1, t2), 1); |
| assert.equal(element._compareThreads(t2, t1), -1); |
| }); |
| |
| test('patchset comments with same patchset picks unresolved first', () => { |
| const t1 = {thread: {path: SpecialFilePath.PATCHSET_LEVEL_COMMENTS, |
| patchNum: 1}, unresolved: true}; |
| const t2 = {thread: {path: SpecialFilePath.PATCHSET_LEVEL_COMMENTS, |
| patchNum: 1}, unresolved: false}; |
| t1.hasDraft = t2.hasDraft = false; |
| assert.equal(element._compareThreads(t1, t2), -1); |
| assert.equal(element._compareThreads(t2, t1), 1); |
| }); |
| |
| test('file level comment before line', () => { |
| const t1 = {thread: {path: 'a.txt', line: 2}}; |
| const t2 = {thread: {path: 'a.txt'}}; |
| t1.patchNum = t2.patchNum = 1; |
| t1.unresolved = t2.unresolved = t1.hasDraft = t2.hasDraft = false; |
| assert.equal(element._compareThreads(t1, t2), 1); |
| assert.equal(element._compareThreads(t2, t1), -1); |
| |
| // give preference to t1 in unresolved/draft properties |
| t1.unresolved = t1.hasDraft = true; |
| t2.unresolved = t2.unresolved = false; |
| assert.equal(element._compareThreads(t1, t2), 1); |
| assert.equal(element._compareThreads(t2, t1), -1); |
| }); |
| |
| test('comments sorted by line', () => { |
| const t1 = {thread: {path: 'a.txt', line: 2}}; |
| const t2 = {thread: {path: 'a.txt', line: 3}}; |
| t1.patchNum = t2.patchNum = 1; |
| t1.unresolved = t2.unresolved = t1.hasDraft = t2.hasDraft = false; |
| assert.equal(element._compareThreads(t1, t2), -1); |
| assert.equal(element._compareThreads(t2, t1), 1); |
| |
| t1.unresolved = t1.hasDraft = false; |
| t2.unresolved = t2.unresolved = true; |
| assert.equal(element._compareThreads(t1, t2), -1); |
| assert.equal(element._compareThreads(t2, t1), 1); |
| }); |
| |
| test('comments on same line sorted by reverse patchset', () => { |
| const t1 = {thread: {path: 'a.txt', line: 2, patchNum: 1}}; |
| const t2 = {thread: {path: 'a.txt', line: 2, patchNum: 2}}; |
| t1.unresolved = t2.unresolved = t1.hasDraft = t2.hasDraft = false; |
| assert.equal(element._compareThreads(t1, t2), 1); |
| assert.equal(element._compareThreads(t2, t1), -1); |
| |
| // give preference to t1 in unresolved/draft properties |
| t1.unresolved = t1.hasDraft = true; |
| t2.unresolved = t2.unresolved = false; |
| assert.equal(element._compareThreads(t1, t2), 1); |
| assert.equal(element._compareThreads(t2, t1), -1); |
| }); |
| |
| test('comments on same line & patchset sorted by unresolved first', |
| () => { |
| const t1 = {thread: {path: 'a.txt', line: 2, patchNum: 1}, |
| unresolved: true}; |
| const t2 = {thread: {path: 'a.txt', line: 2, patchNum: 1}, |
| unresolved: false}; |
| t1.patchNum = t2.patchNum = 1; |
| assert.equal(element._compareThreads(t1, t2), -1); |
| assert.equal(element._compareThreads(t2, t1), 1); |
| |
| t2.hasDraft = true; |
| t1.hasDraft = false; |
| assert.equal(element._compareThreads(t1, t2), -1); |
| assert.equal(element._compareThreads(t2, t1), 1); |
| }); |
| |
| test('comments on same line & patchset & unresolved sorted by draft', |
| () => { |
| const t1 = {thread: {path: 'a.txt', line: 2, patchNum: 1}, |
| unresolved: true, hasDraft: false}; |
| const t2 = {thread: {path: 'a.txt', line: 2, patchNum: 1}, |
| unresolved: true, hasDraft: true}; |
| t1.patchNum = t2.patchNum = 1; |
| assert.equal(element._compareThreads(t1, t2), 1); |
| assert.equal(element._compareThreads(t2, t1), -1); |
| }); |
| }); |
| |
| test('_computeSortedThreads', () => { |
| assert.equal(element._sortedThreads.length, 9); |
| const expectedSortedRootIds = [ |
| 'patchset_level_2', // Posted on Patchset 3 |
| 'patchset_level_1', // Posted on Patchset 2 |
| '8caddf38_44770ec1', // File level on COMMIT_MSG |
| 'scaddf38_44770ec1', // Line 4 on COMMIT_MSG |
| 'ecf0b9fa_fe1a5f62', // Line 5 on COMMIT_MESSAGE but with drafts |
| 'rc1', // Line 5 on COMMIT_MESSAGE without drafts |
| 'zcf0b9fa_fe1a5f62', // Line 6 on COMMIT_MSG |
| 'rc2', // Line 7 on COMMIT_MSG |
| '09a9fb0a_1484e6cf', // File level on test.txt |
| ]; |
| element._sortedThreads.forEach((thread, index) => { |
| assert.equal(thread.rootId, expectedSortedRootIds[index]); |
| }); |
| }); |
| |
| test('thread removal and sort again', () => { |
| threadElements[1].dispatchEvent( |
| new CustomEvent('thread-discard', { |
| detail: {rootId: 'rc2'}, |
| composed: true, bubbles: true, |
| })); |
| flush(); |
| assert.equal(element._sortedThreads.length, 8); |
| const expectedSortedRootIds = [ |
| 'patchset_level_2', |
| 'patchset_level_1', |
| '8caddf38_44770ec1', // File level on COMMIT_MSG |
| 'scaddf38_44770ec1', // Line 4 on COMMIT_MSG |
| 'ecf0b9fa_fe1a5f62', // Line 5 on COMMIT_MESSAGE but with drafts |
| 'rc1', // Line 5 on COMMIT_MESSAGE without drafts |
| 'zcf0b9fa_fe1a5f62', // Line 6 on COMMIT_MSG |
| '09a9fb0a_1484e6cf', // File level on test.txt |
| ]; |
| element._sortedThreads.forEach((thread, index) => { |
| assert.equal(thread.rootId, expectedSortedRootIds[index]); |
| }); |
| }); |
| |
| test('modification on thread shold not trigger sort again', () => { |
| const currentSortedThreads = [...element._sortedThreads]; |
| for (const thread of currentSortedThreads) { |
| thread.comments = [...thread.comments]; |
| } |
| const modifiedThreads = [...element.threads]; |
| modifiedThreads[5] = {...modifiedThreads[5]}; |
| modifiedThreads[5].comments = [...modifiedThreads[5].comments, { |
| ...modifiedThreads[5].comments[0], |
| unresolved: false, |
| }]; |
| element.threads = modifiedThreads; |
| assert.notDeepEqual(currentSortedThreads, element._sortedThreads); |
| |
| // exact same order as in _computeSortedThreads |
| const expectedSortedRootIds = [ |
| 'patchset_level_2', |
| 'patchset_level_1', |
| '8caddf38_44770ec1', // File level on COMMIT_MSG |
| 'scaddf38_44770ec1', // Line 4 on COMMIT_MSG |
| 'ecf0b9fa_fe1a5f62', // Line 5 on COMMIT_MESSAGE but with drafts |
| 'rc1', // Line 5 on COMMIT_MESSAGE without drafts |
| 'zcf0b9fa_fe1a5f62', // Line 6 on COMMIT_MSG |
| 'rc2', // Line 7 on COMMIT_MSG |
| '09a9fb0a_1484e6cf', // File level on test.txt |
| ]; |
| element._sortedThreads.forEach((thread, index) => { |
| assert.equal(thread.rootId, expectedSortedRootIds[index]); |
| }); |
| }); |
| |
| test('reset sortedThreads when threads set to undefiend', () => { |
| element.threads = undefined; |
| assert.deepEqual(element._sortedThreads, []); |
| }); |
| |
| test('non-equal length of sortThreads and threads' + |
| ' should trigger sort again', () => { |
| const modifiedThreads = [...element.threads]; |
| const currentSortedThreads = [...element._sortedThreads]; |
| element._sortedThreads = []; |
| element.threads = modifiedThreads; |
| assert.deepEqual(currentSortedThreads, element._sortedThreads); |
| |
| // exact same order as in _computeSortedThreads |
| const expectedSortedRootIds = [ |
| 'patchset_level_2', |
| 'patchset_level_1', |
| '8caddf38_44770ec1', // File level on COMMIT_MSG |
| 'scaddf38_44770ec1', // Line 4 on COMMIT_MSG |
| 'ecf0b9fa_fe1a5f62', // Line 5 on COMMIT_MESSAGE but with drafts |
| 'rc1', // Line 5 on COMMIT_MESSAGE without drafts |
| 'zcf0b9fa_fe1a5f62', // Line 6 on COMMIT_MSG |
| 'rc2', // Line 7 on COMMIT_MSG |
| '09a9fb0a_1484e6cf', // File level on test.txt |
| ]; |
| element._sortedThreads.forEach((thread, index) => { |
| assert.equal(thread.rootId, expectedSortedRootIds[index]); |
| }); |
| }); |
| |
| test('toggle unresolved shows all comments', () => { |
| MockInteractions.tap(element.shadowRoot.querySelector( |
| '#unresolvedToggle')); |
| flush(); |
| assert.equal(getVisibleThreads().length, 4); |
| }); |
| |
| test('toggle drafts only shows threads with draft comments', () => { |
| MockInteractions.tap(element.shadowRoot.querySelector('#draftToggle')); |
| flush(); |
| assert.equal(getVisibleThreads().length, 2); |
| }); |
| |
| test('toggle drafts and unresolved should ignore comments in editing', () => { |
| const modifiedThreads = [...element.threads]; |
| modifiedThreads[5] = {...modifiedThreads[5]}; |
| modifiedThreads[5].comments = [...modifiedThreads[5].comments]; |
| modifiedThreads[5].comments.push({ |
| ...modifiedThreads[5].comments[0], |
| __editing: true, |
| __draft: true, |
| }); |
| element.threads = modifiedThreads; |
| MockInteractions.tap(element.shadowRoot.querySelector('#draftToggle')); |
| MockInteractions.tap(element.shadowRoot.querySelector( |
| '#unresolvedToggle')); |
| flush(); |
| assert.equal(getVisibleThreads().length, 2); |
| }); |
| |
| test('toggle drafts and unresolved only shows threads with drafts and ' + |
| 'publicly unresolved ', () => { |
| MockInteractions.tap(element.shadowRoot.querySelector('#draftToggle')); |
| MockInteractions.tap(element.shadowRoot.querySelector( |
| '#unresolvedToggle')); |
| flush(); |
| assert.equal(getVisibleThreads().length, 1); |
| }); |
| |
| test('modification events are consumed and displatched', () => { |
| sinon.spy(element, '_handleCommentsChanged'); |
| const dispatchSpy = sinon.stub(); |
| element.addEventListener('thread-list-modified', dispatchSpy); |
| threadElements[0].dispatchEvent( |
| new CustomEvent('thread-changed', { |
| detail: { |
| rootId: 'ecf0b9fa_fe1a5f62', path: '/COMMIT_MSG'}, |
| composed: true, bubbles: true, |
| })); |
| assert.isTrue(element._handleCommentsChanged.called); |
| assert.isTrue(dispatchSpy.called); |
| assert.equal(dispatchSpy.lastCall.args[0].detail.rootId, |
| 'ecf0b9fa_fe1a5f62'); |
| assert.equal(dispatchSpy.lastCall.args[0].detail.path, '/COMMIT_MSG'); |
| }); |
| |
| suite('hideToggleButtons', () => { |
| setup(done => { |
| element.hideToggleButtons = true; |
| flush(() => { |
| done(); |
| }); |
| }); |
| |
| test('toggle buttons are hidden', () => { |
| assert.equal(element.shadowRoot.querySelector('.header').style.display, |
| 'none'); |
| }); |
| }); |
| |
| suite('empty thread', () => { |
| setup(done => { |
| element.threads = []; |
| flush(() => { |
| done(); |
| }); |
| }); |
| |
| test('default empty message should show', () => { |
| assert.isTrue( |
| element.shadowRoot.querySelector('#threads').textContent.trim() |
| .includes('No comments.')); |
| }); |
| }); |
| }); |
| |