| <!DOCTYPE html> | 
 | <!-- | 
 | 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. | 
 | --> | 
 |  | 
 | <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> | 
 | <title>gr-change-view</title> | 
 |  | 
 | <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script> | 
 | <script src="../../../bower_components/web-component-tester/browser.js"></script> | 
 | <link rel="import" href="../../../test/common-test-setup.html"/> | 
 | <script src="../../../bower_components/page/page.js"></script> | 
 |  | 
 | <link rel="import" href="gr-change-view.html"> | 
 |  | 
 | <script>void(0);</script> | 
 |  | 
 | <test-fixture id="basic"> | 
 |   <template> | 
 |     <gr-change-view></gr-change-view> | 
 |   </template> | 
 | </test-fixture> | 
 |  | 
 | <test-fixture id="blank"> | 
 |   <template> | 
 |     <div></div> | 
 |   </template> | 
 | </test-fixture> | 
 |  | 
 | <script> | 
 |   suite('gr-change-view tests', () => { | 
 |     let element; | 
 |     let sandbox; | 
 |     let navigateToChangeStub; | 
 |     const TEST_SCROLL_TOP_PX = 100; | 
 |  | 
 |     setup(() => { | 
 |       sandbox = sinon.sandbox.create(); | 
 |       navigateToChangeStub = sandbox.stub(Gerrit.Nav, 'navigateToChange'); | 
 |       stub('gr-rest-api-interface', { | 
 |         getConfig() { return Promise.resolve({test: 'config'}); }, | 
 |         getAccount() { return Promise.resolve(null); }, | 
 |         _fetchSharedCacheURL() { return Promise.resolve({}); }, | 
 |       }); | 
 |       element = fixture('basic'); | 
 |     }); | 
 |  | 
 |     teardown(done => { | 
 |       flush(() => { | 
 |         sandbox.restore(); | 
 |         done(); | 
 |       }); | 
 |     }); | 
 |  | 
 |     suite('keyboard shortcuts', () => { | 
 |       setup(() => { | 
 |         sandbox.stub(element, '_updateSortedRevisions'); | 
 |       }); | 
 |  | 
 |       test('S should toggle the CL star', () => { | 
 |         const starStub = sandbox.stub(element.$.changeStar, 'toggleStar'); | 
 |         MockInteractions.pressAndReleaseKeyOn(element, 83, null, 's'); | 
 |         assert(starStub.called); | 
 |       }); | 
 |  | 
 |       test('U should navigate to / if no backPage set', () => { | 
 |         const relativeNavStub = sandbox.stub(Gerrit.Nav, | 
 |             'navigateToRelativeUrl'); | 
 |         MockInteractions.pressAndReleaseKeyOn(element, 85, null, 'u'); | 
 |         assert.isTrue(relativeNavStub.called); | 
 |         assert.isTrue(relativeNavStub.lastCall.calledWithExactly('/')); | 
 |       }); | 
 |  | 
 |       test('U should navigate to backPage if set', () => { | 
 |         const relativeNavStub = sandbox.stub(Gerrit.Nav, | 
 |             'navigateToRelativeUrl'); | 
 |         element.backPage = '/dashboard/self'; | 
 |         MockInteractions.pressAndReleaseKeyOn(element, 85, null, 'u'); | 
 |         assert.isTrue(relativeNavStub.called); | 
 |         assert.isTrue(relativeNavStub.lastCall.calledWithExactly( | 
 |             '/dashboard/self')); | 
 |       }); | 
 |  | 
 |       test('A fires an error event when not logged in', done => { | 
 |         sandbox.stub(element, '_getLoggedIn').returns(Promise.resolve(false)); | 
 |         const loggedInErrorSpy = sandbox.spy(); | 
 |         element.addEventListener('show-auth-required', loggedInErrorSpy); | 
 |         MockInteractions.pressAndReleaseKeyOn(element, 65, null, 'a'); | 
 |         flush(() => { | 
 |           assert.isFalse(element.$.replyOverlay.opened); | 
 |           assert.isTrue(loggedInErrorSpy.called); | 
 |           done(); | 
 |         }); | 
 |       }); | 
 |  | 
 |       test('shift A does not open reply overlay', done => { | 
 |         sandbox.stub(element, '_getLoggedIn').returns(Promise.resolve(true)); | 
 |         MockInteractions.pressAndReleaseKeyOn(element, 65, 'shift', 'a'); | 
 |         flush(() => { | 
 |           assert.isFalse(element.$.replyOverlay.opened); | 
 |           done(); | 
 |         }); | 
 |       }); | 
 |  | 
 |       test('A toggles overlay when logged in', done => { | 
 |         sandbox.stub(element, '_getLoggedIn').returns(Promise.resolve(true)); | 
 |         sandbox.stub(element.$.replyDialog, 'fetchIsLatestKnown') | 
 |             .returns(Promise.resolve(true)); | 
 |         element._change = {labels: {}}; | 
 |         MockInteractions.pressAndReleaseKeyOn(element, 65, null, 'a'); | 
 |         flush(() => { | 
 |           assert.isTrue(element.$.replyOverlay.opened); | 
 |           element.$.replyOverlay.close(); | 
 |           assert.isFalse(element.$.replyOverlay.opened); | 
 |           done(); | 
 |         }); | 
 |       }); | 
 |  | 
 |       test('fullscreen-overlay-opened hides content', () => { | 
 |         element._loggedIn = true; | 
 |         element._loading = false; | 
 |         element._change = { | 
 |           owner: {_account_id: 1}, | 
 |           labels: {}, | 
 |           actions: { | 
 |             abandon: { | 
 |               enabled: true, | 
 |               label: 'Abandon', | 
 |               method: 'POST', | 
 |               title: 'Abandon', | 
 |             }, | 
 |           }, | 
 |         }; | 
 |         sandbox.spy(element, '_handleHideBackgroundContent'); | 
 |         element.$.replyDialog.fire('fullscreen-overlay-opened'); | 
 |         assert.isTrue(element._handleHideBackgroundContent.called); | 
 |         assert.isTrue(element.$.mainContent.classList.contains('overlayOpen')); | 
 |         assert.equal(getComputedStyle(element.$.actions).display, 'block'); | 
 |       }); | 
 |  | 
 |       test('fullscreen-overlay-closed shows content', () => { | 
 |         element._loggedIn = true; | 
 |         element._loading = false; | 
 |         element._change = { | 
 |           owner: {_account_id: 1}, | 
 |           labels: {}, | 
 |           actions: { | 
 |             abandon: { | 
 |               enabled: true, | 
 |               label: 'Abandon', | 
 |               method: 'POST', | 
 |               title: 'Abandon', | 
 |             }, | 
 |           }, | 
 |         }; | 
 |         sandbox.spy(element, '_handleShowBackgroundContent'); | 
 |         element.$.replyDialog.fire('fullscreen-overlay-closed'); | 
 |         assert.isTrue(element._handleShowBackgroundContent.called); | 
 |         assert.isFalse(element.$.mainContent.classList.contains('overlayOpen')); | 
 |       }); | 
 |  | 
 |       test('expand all messages when expand-diffs fired', () => { | 
 |         const handleExpand = | 
 |             sandbox.stub(element.$.fileList, 'expandAllDiffs'); | 
 |         element.$.fileListHeader.fire('expand-diffs'); | 
 |         assert.isTrue(handleExpand.called); | 
 |       }); | 
 |  | 
 |       test('collapse all messages when collapse-diffs fired', () => { | 
 |         const handleCollapse = | 
 |         sandbox.stub(element.$.fileList, 'collapseAllDiffs'); | 
 |         element.$.fileListHeader.fire('collapse-diffs'); | 
 |         assert.isTrue(handleCollapse.called); | 
 |       }); | 
 |  | 
 |       test('X should expand all messages', () => { | 
 |         const handleExpand = | 
 |             sandbox.stub(element.$.messageList, 'handleExpandCollapse'); | 
 |         MockInteractions.pressAndReleaseKeyOn(element, 88, null, 'x'); | 
 |         assert(handleExpand.calledWith(true)); | 
 |       }); | 
 |  | 
 |       test('Z should collapse all messages', () => { | 
 |         const handleExpand = | 
 |             sandbox.stub(element.$.messageList, 'handleExpandCollapse'); | 
 |         MockInteractions.pressAndReleaseKeyOn(element, 90, null, 'z'); | 
 |         assert(handleExpand.calledWith(false)); | 
 |       }); | 
 |  | 
 |       test('shift + R should fetch and navigate to the latest patch set', | 
 |           done => { | 
 |             element._changeNum = '42'; | 
 |             element._patchRange = { | 
 |               basePatchNum: 'PARENT', | 
 |               patchNum: 1, | 
 |             }; | 
 |             element._change = { | 
 |               change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca', | 
 |               _number: 42, | 
 |               revisions: { | 
 |                 rev1: {_number: 1}, | 
 |               }, | 
 |               current_revision: 'rev1', | 
 |               status: 'NEW', | 
 |               labels: {}, | 
 |               actions: {}, | 
 |             }; | 
 |  | 
 |             sandbox.stub(element.$.actions, 'reload'); | 
 |  | 
 |             navigateToChangeStub.restore(); | 
 |             navigateToChangeStub = sandbox.stub(Gerrit.Nav, 'navigateToChange', | 
 |                 (change, patchNum, basePatchNum) => { | 
 |                   assert.equal(change, element._change); | 
 |                   assert.isUndefined(patchNum); | 
 |                   assert.isUndefined(basePatchNum); | 
 |                   done(); | 
 |                 }); | 
 |  | 
 |             MockInteractions.pressAndReleaseKeyOn(element, 82, 'shift', 'r'); | 
 |           }); | 
 |  | 
 |       test('d should open download overlay', () => { | 
 |         const stub = sandbox.stub(element.$.downloadOverlay, 'open'); | 
 |         MockInteractions.pressAndReleaseKeyOn(element, 68, null, 'd'); | 
 |         assert.isTrue(stub.called); | 
 |       }); | 
 |  | 
 |       test(', should open diff preferences', () => { | 
 |         const stub = sandbox.stub(element.$.fileList.$.diffPreferences, 'open'); | 
 |         MockInteractions.pressAndReleaseKeyOn(element, 188, null, ','); | 
 |         assert.isTrue(stub.called); | 
 |       }); | 
 |     }); | 
 |  | 
 |     test('download tap calls _handleOpenDownloadDialog', () => { | 
 |       sandbox.stub(element, '_handleOpenDownloadDialog'); | 
 |       element.$.actions.fire('download-tap'); | 
 |       assert.isTrue(element._handleOpenDownloadDialog.called); | 
 |     }); | 
 |  | 
 |     test('fetches the server config on attached', done => { | 
 |       flush(() => { | 
 |         assert.equal(element._serverConfig.test, 'config'); | 
 |         done(); | 
 |       }); | 
 |     }); | 
 |  | 
 |     test('diff preferences open when open-diff-prefs is fired', () => { | 
 |       const overlayOpenStub = sandbox.stub(element.$.fileList, | 
 |           'openDiffPrefs'); | 
 |       element.$.fileListHeader.fire('open-diff-prefs'); | 
 |       assert.isTrue(overlayOpenStub.called); | 
 |     }); | 
 |  | 
 |     test('_prepareCommitMsgForLinkify', () => { | 
 |       let commitMessage = 'R=test@google.com'; | 
 |       let result = element._prepareCommitMsgForLinkify(commitMessage); | 
 |       assert.equal(result, 'R=\u200Btest@google.com'); | 
 |  | 
 |       commitMessage = 'R=test@google.com\nR=test@google.com'; | 
 |       result = element._prepareCommitMsgForLinkify(commitMessage); | 
 |       assert.equal(result, 'R=\u200Btest@google.com\nR=\u200Btest@google.com'); | 
 |  | 
 |       commitMessage = 'CC=test@google.com'; | 
 |       result = element._prepareCommitMsgForLinkify(commitMessage); | 
 |       assert.equal(result, 'CC=\u200Btest@google.com'); | 
 |     }), | 
 |  | 
 |     test('_updateRebaseAction', () => { | 
 |       const currentRevisionActions = { | 
 |         cherrypick: { | 
 |           enabled: true, | 
 |           label: 'Cherry Pick', | 
 |           method: 'POST', | 
 |           title: 'cherrypick', | 
 |         }, | 
 |         rebase: { | 
 |           enabled: true, | 
 |           label: 'Rebase', | 
 |           method: 'POST', | 
 |           title: 'Rebase onto tip of branch or parent change', | 
 |         }, | 
 |       }; | 
 |  | 
 |       // Rebase enabled should always end up true. | 
 |       // When rebase is enabled initially, rebaseOnCurrent should be set to | 
 |       // true. | 
 |       assert.equal(element._updateRebaseAction(currentRevisionActions), | 
 |           currentRevisionActions); | 
 |  | 
 |       assert.isTrue(currentRevisionActions.rebase.enabled); | 
 |       assert.isTrue(currentRevisionActions.rebase.rebaseOnCurrent); | 
 |  | 
 |       delete currentRevisionActions.rebase.enabled; | 
 |  | 
 |       // When rebase is not enabled initially, rebaseOnCurrent should be set to | 
 |       // false. | 
 |       assert.equal(element._updateRebaseAction(currentRevisionActions), | 
 |           currentRevisionActions); | 
 |  | 
 |       assert.isTrue(currentRevisionActions.rebase.enabled); | 
 |       assert.isFalse(currentRevisionActions.rebase.rebaseOnCurrent); | 
 |     }); | 
 |  | 
 |     test('_reload is called when an approved label is removed', () => { | 
 |       const vote = {_account_id: 1, name: 'bojack', value: 1}; | 
 |       element._changeNum = '42'; | 
 |       element._patchRange = { | 
 |         basePatchNum: 'PARENT', | 
 |         patchNum: 1, | 
 |       }; | 
 |       element._change = { | 
 |         change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca', | 
 |         revisions: { | 
 |           rev2: {_number: 2}, | 
 |           rev1: {_number: 1}, | 
 |           rev13: {_number: 13}, | 
 |           rev3: {_number: 3}, | 
 |         }, | 
 |         current_revision: 'rev3', | 
 |         status: 'NEW', | 
 |         labels: { | 
 |           test: { | 
 |             all: [vote], | 
 |             default_value: 0, | 
 |             values: [], | 
 |             approved: {}, | 
 |           }, | 
 |         }, | 
 |       }; | 
 |       flushAsynchronousOperations(); | 
 |       const reloadStub = sandbox.stub(element, '_reload'); | 
 |       element.splice('_change.labels.test.all', 0, 1); | 
 |       assert.isFalse(reloadStub.called); | 
 |       element._change.labels.test.all.push(vote); | 
 |       element._change.labels.test.all.push(vote); | 
 |       element._change.labels.test.approved = vote; | 
 |       flushAsynchronousOperations(); | 
 |       element.splice('_change.labels.test.all', 0, 2); | 
 |       assert.isTrue(reloadStub.called); | 
 |       assert.isTrue(reloadStub.calledOnce); | 
 |     }); | 
 |  | 
 |     test('reply button has updated count when there are drafts', () => { | 
 |       const getLabel = element._computeReplyButtonLabel; | 
 |  | 
 |       assert.equal(getLabel(null, false), 'Reply'); | 
 |       assert.equal(getLabel(null, true), 'Start review'); | 
 |  | 
 |       const changeRecord = {base: null}; | 
 |       assert.equal(getLabel(changeRecord, false), 'Reply'); | 
 |  | 
 |       changeRecord.base = {}; | 
 |       assert.equal(getLabel(changeRecord, false), 'Reply'); | 
 |  | 
 |       changeRecord.base = { | 
 |         'file1.txt': [{}], | 
 |         'file2.txt': [{}, {}], | 
 |       }; | 
 |       assert.equal(getLabel(changeRecord, false), 'Reply (3)'); | 
 |     }); | 
 |  | 
 |     test('start review button when owner of WIP change', () => { | 
 |       assert.equal( | 
 |           element._computeReplyButtonLabel(null, true), | 
 |           'Start review'); | 
 |     }); | 
 |  | 
 |     test('comment events properly update diff drafts', () => { | 
 |       element._patchRange = { | 
 |         basePatchNum: 'PARENT', | 
 |         patchNum: 2, | 
 |       }; | 
 |       const draft = { | 
 |         __draft: true, | 
 |         id: 'id1', | 
 |         path: '/foo/bar.txt', | 
 |         text: 'hello', | 
 |       }; | 
 |       element._handleCommentSave({target: {comment: draft}}); | 
 |       draft.patch_set = 2; | 
 |       assert.deepEqual(element._diffDrafts, {'/foo/bar.txt': [draft]}); | 
 |       draft.patch_set = null; | 
 |       draft.text = 'hello, there'; | 
 |       element._handleCommentSave({target: {comment: draft}}); | 
 |       draft.patch_set = 2; | 
 |       assert.deepEqual(element._diffDrafts, {'/foo/bar.txt': [draft]}); | 
 |       const draft2 = { | 
 |         __draft: true, | 
 |         id: 'id2', | 
 |         path: '/foo/bar.txt', | 
 |         text: 'hola', | 
 |       }; | 
 |       element._handleCommentSave({target: {comment: draft2}}); | 
 |       draft2.patch_set = 2; | 
 |       assert.deepEqual(element._diffDrafts, {'/foo/bar.txt': [draft, draft2]}); | 
 |       draft.patch_set = null; | 
 |       element._handleCommentDiscard({target: {comment: draft}}); | 
 |       draft.patch_set = 2; | 
 |       assert.deepEqual(element._diffDrafts, {'/foo/bar.txt': [draft2]}); | 
 |       element._handleCommentDiscard({target: {comment: draft2}}); | 
 |       assert.deepEqual(element._diffDrafts, {}); | 
 |     }); | 
 |  | 
 |     test('change num change', () => { | 
 |       sandbox.stub(element, '_updateSortedRevisions'); | 
 |       element._changeNum = null; | 
 |       element._patchRange = { | 
 |         basePatchNum: 'PARENT', | 
 |         patchNum: 2, | 
 |       }; | 
 |       element._change = { | 
 |         change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca', | 
 |         labels: {}, | 
 |       }; | 
 |       element.viewState.changeNum = null; | 
 |       element.viewState.diffMode = 'UNIFIED'; | 
 |       assert.equal(element.viewState.numFilesShown, 200); | 
 |       assert.equal(element._numFilesShown, 200); | 
 |       element._numFilesShown = 150; | 
 |       flushAsynchronousOperations(); | 
 |       assert.equal(element.viewState.diffMode, 'UNIFIED'); | 
 |       assert.equal(element.viewState.numFilesShown, 150); | 
 |  | 
 |       element._changeNum = '1'; | 
 |       element.params = {changeNum: '1'}; | 
 |       element._change.newProp = '1'; | 
 |       flushAsynchronousOperations(); | 
 |       assert.equal(element.viewState.diffMode, 'UNIFIED'); | 
 |       assert.equal(element.viewState.changeNum, '1'); | 
 |  | 
 |       element._changeNum = '2'; | 
 |       element.params = {changeNum: '2'}; | 
 |       element._change.newProp = '2'; | 
 |       flushAsynchronousOperations(); | 
 |       assert.equal(element.viewState.diffMode, 'UNIFIED'); | 
 |       assert.equal(element.viewState.changeNum, '2'); | 
 |       assert.equal(element.viewState.numFilesShown, 200); | 
 |       assert.equal(element._numFilesShown, 200); | 
 |     }); | 
 |  | 
 |     test('_setDiffViewMode is called with reset when new change is loaded', | 
 |         () => { | 
 |           sandbox.stub(element, '_setDiffViewMode'); | 
 |           element.viewState = {changeNum: 1}; | 
 |           element._changeNum = 2; | 
 |           element._resetFileListViewState(); | 
 |           assert.isTrue( | 
 |               element._setDiffViewMode.lastCall.calledWithExactly(true)); | 
 |         }); | 
 |  | 
 |     test('diffViewMode is propagated from file list header', () => { | 
 |       element.viewState = {diffMode: 'UNIFIED'}; | 
 |       element.$.fileListHeader.diffViewMode = 'SIDE_BY_SIDE'; | 
 |       assert.equal(element.viewState.diffMode, 'SIDE_BY_SIDE'); | 
 |     }); | 
 |  | 
 |     test('diffMode defaults to side by side without preferences', done => { | 
 |       sandbox.stub(element.$.restAPI, 'getPreferences').returns( | 
 |           Promise.resolve({})); | 
 |       // No user prefs or diff view mode set. | 
 |  | 
 |       element._setDiffViewMode().then(() => { | 
 |         assert.equal(element.viewState.diffMode, 'SIDE_BY_SIDE'); | 
 |         done(); | 
 |       }); | 
 |     }); | 
 |  | 
 |     test('diffMode defaults to preference when not already set', done => { | 
 |       sandbox.stub(element.$.restAPI, 'getPreferences').returns( | 
 |           Promise.resolve({default_diff_view: 'UNIFIED'})); | 
 |  | 
 |       element._setDiffViewMode().then(() => { | 
 |         assert.equal(element.viewState.diffMode, 'UNIFIED'); | 
 |         done(); | 
 |       }); | 
 |     }); | 
 |  | 
 |     test('existing diffMode overrides preference', done => { | 
 |       element.viewState.diffMode = 'SIDE_BY_SIDE'; | 
 |       sandbox.stub(element.$.restAPI, 'getPreferences').returns( | 
 |           Promise.resolve({default_diff_view: 'UNIFIED'})); | 
 |       element._setDiffViewMode().then(() => { | 
 |         assert.equal(element.viewState.diffMode, 'SIDE_BY_SIDE'); | 
 |         done(); | 
 |       }); | 
 |     }); | 
 |  | 
 |     test('don’t reload entire page when patchRange changes', () => { | 
 |       const reloadStub = sandbox.stub(element, '_reload', | 
 |           () => { return Promise.resolve(); }); | 
 |       const reloadPatchDependentStub = sandbox.stub(element, | 
 |           '_reloadPatchNumDependentResources', | 
 |           () => { return Promise.resolve(); }); | 
 |       const relatedClearSpy = sandbox.spy(element.$.relatedChanges, 'clear'); | 
 |       const collapseStub = sandbox.stub(element.$.fileList, 'collapseAllDiffs'); | 
 |  | 
 |       const value = { | 
 |         view: Gerrit.Nav.View.CHANGE, | 
 |         patchNum: '1', | 
 |       }; | 
 |       element._paramsChanged(value); | 
 |       assert.isTrue(reloadStub.calledOnce); | 
 |       assert.isTrue(relatedClearSpy.calledOnce); | 
 |  | 
 |       element._initialLoadComplete = true; | 
 |  | 
 |       value.basePatchNum = '1'; | 
 |       value.patchNum = '2'; | 
 |       element._paramsChanged(value); | 
 |       assert.isFalse(reloadStub.calledTwice); | 
 |       assert.isTrue(reloadPatchDependentStub.calledOnce); | 
 |       assert.isTrue(relatedClearSpy.calledOnce); | 
 |       assert.isTrue(collapseStub.calledTwice); | 
 |     }); | 
 |  | 
 |     test('reload entire page when patchRange doesnt change', () => { | 
 |       const reloadStub = sandbox.stub(element, '_reload', | 
 |           () => { return Promise.resolve(); }); | 
 |       const collapseStub = sandbox.stub(element.$.fileList, 'collapseAllDiffs'); | 
 |       const value = { | 
 |         view: Gerrit.Nav.View.CHANGE, | 
 |       }; | 
 |       element._paramsChanged(value); | 
 |       assert.isTrue(reloadStub.calledOnce); | 
 |       element._initialLoadComplete = true; | 
 |       element._paramsChanged(value); | 
 |       assert.isTrue(reloadStub.calledTwice); | 
 |       assert.isTrue(collapseStub.calledTwice); | 
 |     }); | 
 |  | 
 |     test('related changes are updated and new patch selected after rebase', | 
 |         done => { | 
 |           element._changeNum = '42'; | 
 |           sandbox.stub(element, 'computeLatestPatchNum', () => { | 
 |             return 1; | 
 |           }); | 
 |           sandbox.stub(element, '_reload', | 
 |               () => { return Promise.resolve(); }); | 
 |           const e = {detail: {action: 'rebase'}}; | 
 |           element._handleReloadChange(e).then(() => { | 
 |             assert.isTrue(navigateToChangeStub.lastCall.calledWithExactly( | 
 |                 element._change)); | 
 |             done(); | 
 |           }); | 
 |         }); | 
 |  | 
 |     test('related changes are not updated after other action', done => { | 
 |       sandbox.stub(element, '_reload', () => { return Promise.resolve(); }); | 
 |       sandbox.stub(element.$.relatedChanges, 'reload'); | 
 |       const e = {detail: {action: 'abandon'}}; | 
 |       element._handleReloadChange(e).then(() => { | 
 |         assert.isFalse(navigateToChangeStub.called); | 
 |         done(); | 
 |       }); | 
 |     }); | 
 |  | 
 |     test('_computeMergedCommitInfo', () => { | 
 |       const dummyRevs = { | 
 |         1: {commit: {commit: 1}}, | 
 |         2: {commit: {}}, | 
 |       }; | 
 |       assert.deepEqual(element._computeMergedCommitInfo(0, dummyRevs), {}); | 
 |       assert.deepEqual(element._computeMergedCommitInfo(1, dummyRevs), | 
 |           dummyRevs[1].commit); | 
 |  | 
 |       // Regression test for issue 5337. | 
 |       const commit = element._computeMergedCommitInfo(2, dummyRevs); | 
 |       assert.notDeepEqual(commit, dummyRevs[2]); | 
 |       assert.deepEqual(commit, {commit: 2}); | 
 |     }); | 
 |  | 
 |     test('get latest revision', () => { | 
 |       let change = { | 
 |         revisions: { | 
 |           rev1: {_number: 1}, | 
 |           rev3: {_number: 3}, | 
 |         }, | 
 |         current_revision: 'rev3', | 
 |       }; | 
 |       assert.equal(element._getLatestRevisionSHA(change), 'rev3'); | 
 |       change = { | 
 |         revisions: { | 
 |           rev1: {_number: 1}, | 
 |         }, | 
 |       }; | 
 |       assert.equal(element._getLatestRevisionSHA(change), 'rev1'); | 
 |     }); | 
 |  | 
 |     test('show commit message edit button', () => { | 
 |       const _change = { | 
 |         status: element.ChangeStatus.MERGED, | 
 |       }; | 
 |       assert.isTrue(element._computeHideEditCommitMessage(false, false, {})); | 
 |       assert.isTrue(element._computeHideEditCommitMessage(true, true, {})); | 
 |       assert.isTrue(element._computeHideEditCommitMessage(false, true, {})); | 
 |       assert.isFalse(element._computeHideEditCommitMessage(true, false, {})); | 
 |       assert.isTrue(element._computeHideEditCommitMessage(true, false, | 
 |           _change)); | 
 |     }); | 
 |  | 
 |     test('_computeChangeIdCommitMessageError', () => { | 
 |       let commitMessage = | 
 |         'Change-Id: I4ce18b2395bca69d7a9aa48bf4554faa56282483'; | 
 |       let change = {change_id: 'I4ce18b2395bca69d7a9aa48bf4554faa56282483'}; | 
 |       assert.equal( | 
 |           element._computeChangeIdCommitMessageError(commitMessage, change), | 
 |           null); | 
 |  | 
 |       change = {change_id: 'I4ce18b2395bca69d7a9aa48bf4554faa56282484'}; | 
 |       assert.equal( | 
 |           element._computeChangeIdCommitMessageError(commitMessage, change), | 
 |           'mismatch'); | 
 |  | 
 |       commitMessage = 'This is the greatest change.'; | 
 |       assert.equal( | 
 |           element._computeChangeIdCommitMessageError(commitMessage, change), | 
 |           'missing'); | 
 |     }); | 
 |  | 
 |     test('multiple change Ids in commit message picks last', () => { | 
 |       const commitMessage = [ | 
 |         'Change-Id: I4ce18b2395bca69d7a9aa48bf4554faa56282484', | 
 |         'Change-Id: I4ce18b2395bca69d7a9aa48bf4554faa56282483', | 
 |       ].join('\n'); | 
 |       let change = {change_id: 'I4ce18b2395bca69d7a9aa48bf4554faa56282483'}; | 
 |       assert.equal( | 
 |           element._computeChangeIdCommitMessageError(commitMessage, change), | 
 |           null); | 
 |       change = {change_id: 'I4ce18b2395bca69d7a9aa48bf4554faa56282484'}; | 
 |       assert.equal( | 
 |           element._computeChangeIdCommitMessageError(commitMessage, change), | 
 |           'mismatch'); | 
 |     }); | 
 |  | 
 |     test('does not count change Id that starts mid line', () => { | 
 |       const commitMessage = [ | 
 |         'Change-Id: I4ce18b2395bca69d7a9aa48bf4554faa56282484', | 
 |         'Change-Id: I4ce18b2395bca69d7a9aa48bf4554faa56282483', | 
 |       ].join(' and '); | 
 |       let change = {change_id: 'I4ce18b2395bca69d7a9aa48bf4554faa56282484'}; | 
 |       assert.equal( | 
 |           element._computeChangeIdCommitMessageError(commitMessage, change), | 
 |           null); | 
 |       change = {change_id: 'I4ce18b2395bca69d7a9aa48bf4554faa56282483'}; | 
 |       assert.equal( | 
 |           element._computeChangeIdCommitMessageError(commitMessage, change), | 
 |           'mismatch'); | 
 |     }); | 
 |  | 
 |     test('_computeTitleAttributeWarning', () => { | 
 |       let changeIdCommitMessageError = 'missing'; | 
 |       assert.equal( | 
 |           element._computeTitleAttributeWarning(changeIdCommitMessageError), | 
 |           'No Change-Id in commit message'); | 
 |  | 
 |       changeIdCommitMessageError = 'mismatch'; | 
 |       assert.equal( | 
 |           element._computeTitleAttributeWarning(changeIdCommitMessageError), | 
 |           'Change-Id mismatch'); | 
 |     }); | 
 |  | 
 |     test('_computeChangeIdClass', () => { | 
 |       let changeIdCommitMessageError = 'missing'; | 
 |       assert.equal( | 
 |           element._computeChangeIdClass(changeIdCommitMessageError), ''); | 
 |  | 
 |       changeIdCommitMessageError = 'mismatch'; | 
 |       assert.equal( | 
 |           element._computeChangeIdClass(changeIdCommitMessageError), 'warning'); | 
 |     }); | 
 |  | 
 |     test('topic is coalesced to null', done => { | 
 |       sandbox.stub(element, '_changeChanged'); | 
 |       sandbox.stub(element.$.restAPI, 'getChangeDetail', () => { | 
 |         return Promise.resolve({ | 
 |           id: '123456789', | 
 |           labels: {}, | 
 |           current_revision: 'foo', | 
 |           revisions: {foo: {commit: {}}}, | 
 |         }); | 
 |       }); | 
 |  | 
 |       element._getChangeDetail().then(() => { | 
 |         assert.isNull(element._change.topic); | 
 |         done(); | 
 |       }); | 
 |     }); | 
 |  | 
 |     test('commit sha is populated from getChangeDetail', done => { | 
 |       sandbox.stub(element, '_changeChanged'); | 
 |       sandbox.stub(element.$.restAPI, 'getChangeDetail', () => { | 
 |         return Promise.resolve({ | 
 |           id: '123456789', | 
 |           labels: {}, | 
 |           current_revision: 'foo', | 
 |           revisions: {foo: {commit: {}}}, | 
 |         }); | 
 |       }); | 
 |  | 
 |       element._getChangeDetail().then(() => { | 
 |         assert.equal('foo', element._commitInfo.commit); | 
 |         done(); | 
 |       }); | 
 |     }); | 
 |  | 
 |     test('edit is added to change', () => { | 
 |       sandbox.stub(element, '_changeChanged'); | 
 |       sandbox.stub(element.$.restAPI, 'getChangeDetail', () => { | 
 |         return Promise.resolve({ | 
 |           id: '123456789', | 
 |           labels: {}, | 
 |           current_revision: 'foo', | 
 |           revisions: {foo: {commit: {}}}, | 
 |         }); | 
 |       }); | 
 |       sandbox.stub(element, '_getEdit', () => { | 
 |         return Promise.resolve({ | 
 |           base_patch_set_number: 1, | 
 |           commit: {commit: 'bar'}, | 
 |         }); | 
 |       }); | 
 |       element._patchRange = {}; | 
 |  | 
 |       return element._getChangeDetail().then(() => { | 
 |         const revs = element._change.revisions; | 
 |         assert.equal(Object.keys(revs).length, 2); | 
 |         assert.deepEqual(revs['foo'], {commit: {commit: 'foo'}}); | 
 |         assert.deepEqual(revs['bar'], { | 
 |           _number: element.EDIT_NAME, | 
 |           basePatchNum: 1, | 
 |           commit: {commit: 'bar'}, | 
 |           fetch: undefined, | 
 |         }); | 
 |       }); | 
 |     }); | 
 |  | 
 |     test('reply dialog focus can be controlled', () => { | 
 |       const FocusTarget = element.$.replyDialog.FocusTarget; | 
 |       const openStub = sandbox.stub(element, '_openReplyDialog'); | 
 |  | 
 |       const e = {detail: {}}; | 
 |       element._handleShowReplyDialog(e); | 
 |       assert(openStub.lastCall.calledWithExactly(FocusTarget.REVIEWERS), | 
 |           '_openReplyDialog should have been passed REVIEWERS'); | 
 |  | 
 |       e.detail.value = {ccsOnly: true}; | 
 |       element._handleShowReplyDialog(e); | 
 |       assert(openStub.lastCall.calledWithExactly(FocusTarget.CCS), | 
 |           '_openReplyDialog should have been passed CCS'); | 
 |     }); | 
 |  | 
 |     test('getUrlParameter functionality', () => { | 
 |       const locationStub = sandbox.stub(element, '_getLocationSearch'); | 
 |  | 
 |       locationStub.returns('?test'); | 
 |       assert.equal(element._getUrlParameter('test'), 'test'); | 
 |       locationStub.returns('?test2=12&test=3'); | 
 |       assert.equal(element._getUrlParameter('test'), 'test'); | 
 |       locationStub.returns(''); | 
 |       assert.isNull(element._getUrlParameter('test')); | 
 |       locationStub.returns('?'); | 
 |       assert.isNull(element._getUrlParameter('test')); | 
 |       locationStub.returns('?test2'); | 
 |       assert.isNull(element._getUrlParameter('test')); | 
 |     }); | 
 |  | 
 |     test('revert dialog opened with revert param', done => { | 
 |       sandbox.stub(element.$.restAPI, 'getLoggedIn', () => { | 
 |         return Promise.resolve(true); | 
 |       }); | 
 |       sandbox.stub(Gerrit, 'awaitPluginsLoaded', () => { | 
 |         return Promise.resolve(); | 
 |       }); | 
 |  | 
 |       element._patchRange = { | 
 |         basePatchNum: 'PARENT', | 
 |         patchNum: 2, | 
 |       }; | 
 |       element._change = { | 
 |         change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca', | 
 |         revisions: { | 
 |           rev1: {_number: 1}, | 
 |           rev2: {_number: 2}, | 
 |         }, | 
 |         current_revision: 'rev1', | 
 |         status: element.ChangeStatus.MERGED, | 
 |         labels: {}, | 
 |         actions: {}, | 
 |       }; | 
 |  | 
 |       sandbox.stub(element, '_getUrlParameter', | 
 |           param => { | 
 |             assert.equal(param, 'revert'); | 
 |             return param; | 
 |           }); | 
 |  | 
 |       sandbox.stub(element.$.actions, 'showRevertDialog', | 
 |           done); | 
 |  | 
 |       element._maybeShowRevertDialog(); | 
 |       assert.isTrue(Gerrit.awaitPluginsLoaded.called); | 
 |     }); | 
 |  | 
 |     suite('scroll related tests', () => { | 
 |       test('document scrolling calls function to set scroll height', done => { | 
 |         const originalHeight = document.body.scrollHeight; | 
 |         const scrollStub = sandbox.stub(element, '_handleScroll', | 
 |             () => { | 
 |               assert.isTrue(scrollStub.called); | 
 |               document.body.style.height = originalHeight + 'px'; | 
 |               scrollStub.restore(); | 
 |               done(); | 
 |             }); | 
 |         document.body.style.height = '10000px'; | 
 |         element._handleScroll(); | 
 |       }); | 
 |  | 
 |       test('scrollTop is set correctly', () => { | 
 |         element.viewState = {scrollTop: TEST_SCROLL_TOP_PX}; | 
 |  | 
 |         sandbox.stub(element, '_reload', () => { | 
 |           // When element is reloaded, ensure that the history | 
 |           // state has the scrollTop set earlier. This will then | 
 |           // be reset. | 
 |           assert.isTrue(element.viewState.scrollTop == TEST_SCROLL_TOP_PX); | 
 |           return Promise.resolve({}); | 
 |         }); | 
 |  | 
 |         // simulate reloading component, which is done when route | 
 |         // changes to match a regex of change view type. | 
 |         element._paramsChanged({view: Gerrit.Nav.View.CHANGE}); | 
 |       }); | 
 |  | 
 |       test('scrollTop is reset when new change is loaded', () => { | 
 |         element._resetFileListViewState(); | 
 |         assert.equal(element.viewState.scrollTop, 0); | 
 |       }); | 
 |     }); | 
 |  | 
 |     suite('reply dialog tests', () => { | 
 |       setup(() => { | 
 |         sandbox.stub(element.$.replyDialog, '_draftChanged'); | 
 |         sandbox.stub(element, '_updateSortedRevisions'); | 
 |         sandbox.stub(element.$.replyDialog, 'fetchIsLatestKnown', | 
 |             () => { return Promise.resolve(true); }); | 
 |         element._change = {labels: {}}; | 
 |       }); | 
 |  | 
 |       test('reply from comment adds quote text', () => { | 
 |         const e = {detail: {message: {message: 'quote text'}}}; | 
 |         element._handleMessageReply(e); | 
 |         assert.equal(element.$.replyDialog.draft, '> quote text\n\n'); | 
 |         assert.equal(element.$.replyDialog.quote, '> quote text\n\n'); | 
 |       }); | 
 |  | 
 |       test('reply from comment replaces quote text', () => { | 
 |         element.$.replyDialog.draft = '> old quote text\n\n some draft text'; | 
 |         element.$.replyDialog.quote = '> old quote text\n\n'; | 
 |         const e = {detail: {message: {message: 'quote text'}}}; | 
 |         element._handleMessageReply(e); | 
 |         assert.equal(element.$.replyDialog.draft, '> quote text\n\n'); | 
 |         assert.equal(element.$.replyDialog.quote, '> quote text\n\n'); | 
 |       }); | 
 |  | 
 |       test('reply from same comment preserves quote text', () => { | 
 |         element.$.replyDialog.draft = '> quote text\n\n some draft text'; | 
 |         element.$.replyDialog.quote = '> quote text\n\n'; | 
 |         const e = {detail: {message: {message: 'quote text'}}}; | 
 |         element._handleMessageReply(e); | 
 |         assert.equal(element.$.replyDialog.draft, | 
 |             '> quote text\n\n some draft text'); | 
 |         assert.equal(element.$.replyDialog.quote, '> quote text\n\n'); | 
 |       }); | 
 |  | 
 |       test('reply from top of page contains previous draft', () => { | 
 |         const div = document.createElement('div'); | 
 |         element.$.replyDialog.draft = '> quote text\n\n some draft text'; | 
 |         element.$.replyDialog.quote = '> quote text\n\n'; | 
 |         const e = {target: div, preventDefault: sandbox.spy()}; | 
 |         element._handleReplyTap(e); | 
 |         assert.equal(element.$.replyDialog.draft, | 
 |             '> quote text\n\n some draft text'); | 
 |         assert.equal(element.$.replyDialog.quote, '> quote text\n\n'); | 
 |       }); | 
 |     }); | 
 |  | 
 |     test('reply button is disabled until server config is loaded', () => { | 
 |       assert.isTrue(element._replyDisabled); | 
 |       element._serverConfig = {}; | 
 |       assert.isFalse(element._replyDisabled); | 
 |     }); | 
 |  | 
 |     suite('commit message expand/collapse', () => { | 
 |       setup(() => { | 
 |         sandbox.stub(element, 'fetchIsLatestKnown', | 
 |             () => { return Promise.resolve(false); }); | 
 |       }); | 
 |  | 
 |       test('commitCollapseToggle hidden for short commit message', () => { | 
 |         element._latestCommitMessage = ''; | 
 |         assert.isTrue(element.$.commitCollapseToggle.hasAttribute('hidden')); | 
 |       }); | 
 |  | 
 |       test('commitCollapseToggle shown for long commit message', () => { | 
 |         element._latestCommitMessage = _.times(31, String).join('\n'); | 
 |         assert.isFalse(element.$.commitCollapseToggle.hasAttribute('hidden')); | 
 |       }); | 
 |  | 
 |       test('commitCollapseToggle functions', () => { | 
 |         element._latestCommitMessage = _.times(31, String).join('\n'); | 
 |         assert.isTrue(element._commitCollapsed); | 
 |         assert.isTrue( | 
 |             element.$.commitMessage.classList.contains('collapsed')); | 
 |         MockInteractions.tap(element.$.commitCollapseToggleButton); | 
 |         assert.isFalse(element._commitCollapsed); | 
 |         assert.isFalse( | 
 |             element.$.commitMessage.classList.contains('collapsed')); | 
 |       }); | 
 |     }); | 
 |  | 
 |     suite('related changes expand/collapse', () => { | 
 |       let updateHeightSpy; | 
 |       setup(() => { | 
 |         updateHeightSpy = sandbox.spy(element, '_updateRelatedChangeMaxHeight'); | 
 |       }); | 
 |  | 
 |       test('relatedChangesToggle shown height greater than changeInfo height', | 
 |           () => { | 
 |             assert.isFalse(element.$.relatedChangesToggle.classList | 
 |                 .contains('showToggle')); | 
 |             sandbox.stub(element, '_getOffsetHeight', () => 50); | 
 |             sandbox.stub(element, '_getScrollHeight', () => 60); | 
 |             sandbox.stub(element, '_getLineHeight', () => 5); | 
 |             sandbox.stub(window, 'matchMedia', () => ({matches: true})); | 
 |             element._relatedChangesLoading = false; | 
 |             assert.isTrue(element.$.relatedChangesToggle.classList | 
 |                 .contains('showToggle')); | 
 |             assert.equal(updateHeightSpy.callCount, 1); | 
 |           }); | 
 |  | 
 |       test('relatedChangesToggle hidden height less than changeInfo height', | 
 |           () => { | 
 |             assert.isFalse(element.$.relatedChangesToggle.classList | 
 |                 .contains('showToggle')); | 
 |             sandbox.stub(element, '_getOffsetHeight', () => 50); | 
 |             sandbox.stub(element, '_getScrollHeight', () => 40); | 
 |             sandbox.stub(element, '_getLineHeight', () => 5); | 
 |             sandbox.stub(window, 'matchMedia', () => ({matches: true})); | 
 |             element._relatedChangesLoading = false; | 
 |             assert.isFalse(element.$.relatedChangesToggle.classList | 
 |                 .contains('showToggle')); | 
 |             assert.equal(updateHeightSpy.callCount, 1); | 
 |           }); | 
 |  | 
 |       test('relatedChangesToggle functions', () => { | 
 |         sandbox.stub(element, '_getOffsetHeight', () => 50); | 
 |         sandbox.stub(window, 'matchMedia', () => ({matches: false})); | 
 |         element._relatedChangesLoading = false; | 
 |         assert.isTrue(element._relatedChangesCollapsed); | 
 |         assert.isTrue( | 
 |             element.$.relatedChanges.classList.contains('collapsed')); | 
 |         MockInteractions.tap(element.$.relatedChangesToggleButton); | 
 |         assert.isFalse(element._relatedChangesCollapsed); | 
 |         assert.isFalse( | 
 |             element.$.relatedChanges.classList.contains('collapsed')); | 
 |       }); | 
 |  | 
 |       test('_updateRelatedChangeMaxHeight without commit toggle', () => { | 
 |         sandbox.stub(element, '_getOffsetHeight', () => 50); | 
 |         sandbox.stub(element, '_getLineHeight', () => 12); | 
 |         sandbox.stub(window, 'matchMedia', () => ({matches: false})); | 
 |  | 
 |         // 50 (existing height) - 30 (extra height) = 20 (adjusted height). | 
 |         // 20 (max existing height)  % 12 (line height) = 6 (remainder). | 
 |         // 20 (adjusted height) - 8 (remainder) = 12 (max height to set). | 
 |  | 
 |         element._updateRelatedChangeMaxHeight(); | 
 |         assert.equal(element.customStyle['--relation-chain-max-height'], | 
 |             '12px'); | 
 |         assert.equal(element.customStyle['--related-change-btn-top-padding'], | 
 |             undefined); | 
 |       }); | 
 |  | 
 |       test('_updateRelatedChangeMaxHeight with commit toggle', () => { | 
 |         element._latestCommitMessage = _.times(31, String).join('\n'); | 
 |         sandbox.stub(element, '_getOffsetHeight', () => 50); | 
 |         sandbox.stub(element, '_getLineHeight', () => 12); | 
 |         sandbox.stub(window, 'matchMedia', () => ({matches: false})); | 
 |  | 
 |         // 50 (existing height) % 12 (line height) = 2 (remainder). | 
 |         // 50 (existing height)  - 2 (remainder) = 48 (max height to set). | 
 |  | 
 |         element._updateRelatedChangeMaxHeight(); | 
 |         assert.equal(element.customStyle['--relation-chain-max-height'], | 
 |             '48px'); | 
 |         assert.equal(element.customStyle['--related-change-btn-top-padding'], | 
 |             '2px'); | 
 |       }); | 
 |  | 
 |       test('_updateRelatedChangeMaxHeight in small screen mode', () => { | 
 |         element._latestCommitMessage = _.times(31, String).join('\n'); | 
 |         sandbox.stub(element, '_getOffsetHeight', () => 50); | 
 |         sandbox.stub(element, '_getLineHeight', () => 12); | 
 |         sandbox.stub(window, 'matchMedia', () => ({matches: true})); | 
 |  | 
 |         element._updateRelatedChangeMaxHeight(); | 
 |  | 
 |         // 400 (new height) % 12 (line height) = 4 (remainder). | 
 |         // 400 (new height) - 4 (remainder) = 396. | 
 |  | 
 |         assert.equal(element.customStyle['--relation-chain-max-height'], | 
 |             '396px'); | 
 |       }); | 
 |  | 
 |       test('_updateRelatedChangeMaxHeight in medium screen mode', () => { | 
 |         element._latestCommitMessage = _.times(31, String).join('\n'); | 
 |         sandbox.stub(element, '_getOffsetHeight', () => 50); | 
 |         sandbox.stub(element, '_getLineHeight', () => 12); | 
 |         sandbox.stub(window, 'matchMedia', () => { | 
 |           if (window.matchMedia.lastCall.args[0] === '(max-width: 60em)') { | 
 |             return {matches: true}; | 
 |           } else { | 
 |             return {matches: false}; | 
 |           } | 
 |         }); | 
 |  | 
 |         // 100 (new height) % 12 (line height) = 4 (remainder). | 
 |         // 100 (new height) - 4 (remainder) = 96. | 
 |         element._updateRelatedChangeMaxHeight(); | 
 |         assert.equal(element.customStyle['--relation-chain-max-height'], | 
 |             '96px'); | 
 |       }); | 
 |  | 
 |  | 
 |       suite('update checks', () => { | 
 |         setup(() => { | 
 |           sandbox.spy(element, '_startUpdateCheckTimer'); | 
 |           sandbox.stub(element, 'async', f => { | 
 |             // Only fire the async callback one time. | 
 |             if (element.async.callCount > 1) { return; } | 
 |             f.call(element); | 
 |           }); | 
 |         }); | 
 |  | 
 |         test('_startUpdateCheckTimer negative delay', () => { | 
 |           sandbox.stub(element, 'fetchIsLatestKnown'); | 
 |  | 
 |           element._serverConfig = {change: {update_delay: -1}}; | 
 |  | 
 |           assert.isTrue(element._startUpdateCheckTimer.called); | 
 |           assert.isFalse(element.fetchIsLatestKnown.called); | 
 |         }); | 
 |  | 
 |         test('_startUpdateCheckTimer up-to-date', () => { | 
 |           sandbox.stub(element, 'fetchIsLatestKnown', | 
 |               () => { return Promise.resolve(true); }); | 
 |  | 
 |           element._serverConfig = {change: {update_delay: 12345}}; | 
 |  | 
 |           assert.isTrue(element._startUpdateCheckTimer.called); | 
 |           assert.isTrue(element.fetchIsLatestKnown.called); | 
 |           assert.equal(element.async.lastCall.args[1], 12345 * 1000); | 
 |         }); | 
 |  | 
 |         test('_startUpdateCheckTimer out-of-date shows an alert', done => { | 
 |           sandbox.stub(element, 'fetchIsLatestKnown', | 
 |               () => { return Promise.resolve(false); }); | 
 |           element.addEventListener('show-alert', () => { | 
 |             done(); | 
 |           }); | 
 |           element._serverConfig = {change: {update_delay: 12345}}; | 
 |         }); | 
 |       }); | 
 |  | 
 |       test('canStartReview computation', () => { | 
 |         const change1 = {}; | 
 |         const change2 = { | 
 |           actions: { | 
 |             ready: { | 
 |               enabled: true, | 
 |             }, | 
 |           }, | 
 |         }; | 
 |         const change3 = { | 
 |           actions: { | 
 |             ready: { | 
 |               label: 'Ready for Review', | 
 |             }, | 
 |           }, | 
 |         }; | 
 |         assert.isFalse(element._computeCanStartReview(change1)); | 
 |         assert.isTrue(element._computeCanStartReview(change2)); | 
 |         assert.isFalse(element._computeCanStartReview(change3)); | 
 |       }); | 
 |  | 
 |       test('header class computation', () => { | 
 |         assert.equal(element._computeHeaderClass({}), 'header'); | 
 |         assert.equal(element._computeHeaderClass({work_in_progress: true}), | 
 |             'header wip'); | 
 |       }); | 
 |     }); | 
 |  | 
 |     test('_maybeScrollToMessage', () => { | 
 |       const scrollStub = sandbox.stub(element.$.messageList, 'scrollToMessage'); | 
 |  | 
 |       element._maybeScrollToMessage(''); | 
 |       assert.isFalse(scrollStub.called); | 
 |  | 
 |       element._maybeScrollToMessage('message'); | 
 |       assert.isFalse(scrollStub.called); | 
 |  | 
 |       element._maybeScrollToMessage('#message-TEST'); | 
 |       assert.isTrue(scrollStub.called); | 
 |       assert.equal(scrollStub.lastCall.args[0], 'TEST'); | 
 |     }); | 
 |  | 
 |     test('topic update reloads related changes', () => { | 
 |       sandbox.stub(element.$.relatedChanges, 'reload'); | 
 |       element.dispatchEvent(new CustomEvent('topic-changed')); | 
 |       assert.isTrue(element.$.relatedChanges.reload.calledOnce); | 
 |     }); | 
 |  | 
 |     test('_computeEditLoaded', () => { | 
 |       const callCompute = range => element._computeEditLoaded({base: range}); | 
 |       assert.isFalse(callCompute({})); | 
 |       assert.isFalse(callCompute({basePatchNum: 'PARENT', patchNum: 1})); | 
 |       assert.isFalse(callCompute({basePatchNum: 'edit', patchNum: 1})); | 
 |       assert.isTrue(callCompute({basePatchNum: 1, patchNum: 'edit'})); | 
 |     }); | 
 |  | 
 |     test('_processEdit', () => { | 
 |       element._patchRange = {}; | 
 |       const change = { | 
 |         current_revision: 'foo', | 
 |         revisions: {foo: {commit: {}, actions: {cherrypick: {enabled: true}}}}, | 
 |       }; | 
 |       let mockChange; | 
 |  | 
 |       // With no edit, mockChange should be unmodified. | 
 |       element._processEdit(mockChange = _.cloneDeep(change), null); | 
 |       assert.deepEqual(mockChange, change); | 
 |  | 
 |       // When edit is not based on the latest PS, current_revision should be | 
 |       // unmodified. | 
 |       const edit = { | 
 |         base_patch_set_number: 1, | 
 |         commit: {commit: 'bar'}, | 
 |         fetch: true, | 
 |       }; | 
 |       element._processEdit(mockChange = _.cloneDeep(change), edit); | 
 |       assert.notDeepEqual(mockChange, change); | 
 |       assert.equal(mockChange.revisions.bar._number, element.EDIT_NAME); | 
 |       assert.equal(mockChange.current_revision, change.current_revision); | 
 |       assert.deepEqual(mockChange.revisions.bar.commit, {commit: 'bar'}); | 
 |       assert.notOk(mockChange.revisions.bar.actions); | 
 |  | 
 |       edit.base_revision = 'foo'; | 
 |       element._processEdit(mockChange = _.cloneDeep(change), edit); | 
 |       assert.notDeepEqual(mockChange, change); | 
 |       assert.equal(mockChange.current_revision, 'bar'); | 
 |       assert.deepEqual(mockChange.revisions.bar.actions, | 
 |           mockChange.revisions.foo.actions); | 
 |  | 
 |       // If _patchRange.patchNum is defined, do not load edit. | 
 |       element._patchRange.patchNum = 'baz'; | 
 |       change.current_revision = 'baz'; | 
 |       element._processEdit(mockChange = _.cloneDeep(change), edit); | 
 |       assert.equal(element._patchRange.patchNum, 'baz'); | 
 |       assert.notOk(mockChange.revisions.bar.actions); | 
 |     }); | 
 |  | 
 |     test('_editLoaded set when patchNum is an edit', () => { | 
 |       sandbox.stub(element, 'computeLatestPatchNum').returns('2'); | 
 |       element._patchRange = {patchNum: element.EDIT_NAME}; | 
 |  | 
 |       assert.isTrue(element._editLoaded); | 
 |       element.set('_patchRange.patchNum', 1); | 
 |  | 
 |       assert.isFalse(element._editLoaded); | 
 |     }); | 
 |   }); | 
 | </script> |