blob: 9e6b53a7354075658695d927fc7e31ab8020c0e4 [file] [log] [blame]
Dmitrii Filippovacd39a22020-04-02 10:31:43 +02001/**
2 * @license
3 * Copyright (C) 2015 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
Andrew Bonventre547b8ab2015-12-01 01:02:00 -050017
Dmitrii Filippovacd39a22020-04-02 10:31:43 +020018import '../../../test/common-test-setup-karma.js';
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010019import '../../edit/gr-edit-constants.js';
20import './gr-change-view.js';
Dmitrii Filippove903bbf2020-05-06 12:57:39 +020021import {PrimaryTab, SecondaryTab, ChangeStatus} from '../../../constants/constants.js';
Tao Zhou4fd32d52020-04-06 17:23:10 +020022
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010023import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
Dmitrii Filippov56e58de2020-04-03 18:07:10 +020024import {KeyboardShortcutBinder} from '../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.js';
Dmitrii Filippov0028b582020-03-24 11:58:55 +010025import {GrEditConstants} from '../../edit/gr-edit-constants.js';
Dmitrii Filippovf97fc6e2020-04-07 09:59:22 +020026import {_testOnly_resetEndpoints} from '../../shared/gr-js-api-interface/gr-plugin-endpoints.js';
Dmitrii Filippovcd1a0692020-03-24 15:55:51 +010027import {util} from '../../../scripts/util.js';
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +020028import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
Dmitrii Filippovf97fc6e2020-04-07 09:59:22 +020029import {pluginLoader} from '../../shared/gr-js-api-interface/gr-plugin-loader.js';
Dmitrii Filippov5f25dc02020-04-07 18:49:00 +020030import {_testOnly_initGerritPluginApi} from '../../shared/gr-js-api-interface/gr-gerrit.js';
Dmitrii Filippov35aea692020-04-07 12:14:11 +020031
Dmitrii Filippovacd39a22020-04-02 10:31:43 +020032import 'lodash/lodash.js';
33
Dmitrii Filippov5f25dc02020-04-07 18:49:00 +020034const pluginApi = _testOnly_initGerritPluginApi();
Dmitrii Filippovacd39a22020-04-02 10:31:43 +020035const fixture = fixtureFromElement('gr-change-view');
Dmitrii Filippov0028b582020-03-24 11:58:55 +010036
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010037suite('gr-change-view tests', () => {
Dmitrii Filippov56e58de2020-04-03 18:07:10 +020038 const kb = KeyboardShortcutBinder;
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010039 kb.bindShortcut(kb.Shortcut.SEND_REPLY, 'ctrl+enter');
40 kb.bindShortcut(kb.Shortcut.REFRESH_CHANGE, 'shift+r');
41 kb.bindShortcut(kb.Shortcut.OPEN_REPLY_DIALOG, 'a');
42 kb.bindShortcut(kb.Shortcut.OPEN_DOWNLOAD_DIALOG, 'd');
43 kb.bindShortcut(kb.Shortcut.TOGGLE_DIFF_MODE, 'm');
44 kb.bindShortcut(kb.Shortcut.TOGGLE_CHANGE_STAR, 's');
45 kb.bindShortcut(kb.Shortcut.UP_TO_DASHBOARD, 'u');
46 kb.bindShortcut(kb.Shortcut.EXPAND_ALL_MESSAGES, 'x');
47 kb.bindShortcut(kb.Shortcut.COLLAPSE_ALL_MESSAGES, 'z');
48 kb.bindShortcut(kb.Shortcut.OPEN_DIFF_PREFS, ',');
49 kb.bindShortcut(kb.Shortcut.EDIT_TOPIC, 't');
Logan Hanks5cde0842018-10-05 15:56:10 -070050
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010051 let element;
52 let sandbox;
53 let navigateToChangeStub;
54 const TEST_SCROLL_TOP_PX = 100;
Andrew Bonventre547b8ab2015-12-01 01:02:00 -050055
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010056 const ROBOT_COMMENTS_LIMIT = 10;
57
Tao Zhou18738b92020-05-04 19:44:51 +020058 // TODO: should have a mock service to generate VALID fake data
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010059 const THREADS = [
60 {
61 comments: [
62 {
63 __path: '/COMMIT_MSG',
64 author: {
65 _account_id: 1000000,
66 name: 'user',
67 username: 'user',
68 },
69 patch_set: 2,
70 robot_id: 'rb1',
Tao Zhou18738b92020-05-04 19:44:51 +020071 id: 'ecf0b9fa_fe1a5f62',
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010072 line: 5,
73 updated: '2018-02-08 18:49:18.000000000',
74 message: 'test',
75 unresolved: true,
76 },
77 {
78 __path: '/COMMIT_MSG',
79 author: {
80 _account_id: 1000000,
81 name: 'user',
82 username: 'user',
83 },
84 patch_set: 4,
Tao Zhou18738b92020-05-04 19:44:51 +020085 id: 'ecf0b9fa_fe1a5f62_1',
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010086 line: 5,
87 updated: '2018-02-08 18:49:18.000000000',
88 message: 'test',
89 unresolved: true,
90 },
91 {
92 id: '503008e2_0ab203ee',
93 path: '/COMMIT_MSG',
94 line: 5,
95 in_reply_to: 'ecf0b9fa_fe1a5f62',
96 updated: '2018-02-13 22:48:48.018000000',
97 message: 'draft',
98 unresolved: false,
99 __draft: true,
100 __draftID: '0.m683trwff68',
101 __editing: false,
102 patch_set: '2',
103 },
104 ],
105 patchNum: 4,
106 path: '/COMMIT_MSG',
107 line: 5,
108 rootId: 'ecf0b9fa_fe1a5f62',
109 start_datetime: '2018-02-08 18:49:18.000000000',
110 },
111 {
112 comments: [
113 {
114 __path: '/COMMIT_MSG',
115 author: {
116 _account_id: 1000000,
117 name: 'user',
118 username: 'user',
119 },
120 patch_set: 3,
121 id: 'ecf0b9fa_fe5f62',
122 robot_id: 'rb2',
123 line: 5,
124 updated: '2018-02-08 18:49:18.000000000',
125 message: 'test',
126 unresolved: true,
127 },
128 {
129 __path: 'test.txt',
130 author: {
131 _account_id: 1000000,
132 name: 'user',
133 username: 'user',
134 },
135 patch_set: 3,
136 id: '09a9fb0a_1484e6cf',
137 side: 'PARENT',
138 updated: '2018-02-13 22:47:19.000000000',
139 message: 'Some comment on another patchset.',
140 unresolved: false,
141 },
142 ],
143 patchNum: 3,
144 path: 'test.txt',
145 rootId: '09a9fb0a_1484e6cf',
146 start_datetime: '2018-02-13 22:47:19.000000000',
147 commentSide: 'PARENT',
148 },
149 {
150 comments: [
151 {
152 __path: '/COMMIT_MSG',
153 author: {
154 _account_id: 1000000,
155 name: 'user',
156 username: 'user',
157 },
158 patch_set: 2,
159 id: '8caddf38_44770ec1',
160 line: 4,
161 updated: '2018-02-13 22:48:40.000000000',
162 message: 'Another unresolved comment',
163 unresolved: true,
164 },
165 ],
166 patchNum: 2,
167 path: '/COMMIT_MSG',
168 line: 4,
169 rootId: '8caddf38_44770ec1',
170 start_datetime: '2018-02-13 22:48:40.000000000',
171 },
172 {
173 comments: [
174 {
175 __path: '/COMMIT_MSG',
176 author: {
177 _account_id: 1000000,
178 name: 'user',
179 username: 'user',
180 },
181 patch_set: 2,
182 id: 'scaddf38_44770ec1',
183 line: 4,
184 updated: '2018-02-14 22:48:40.000000000',
185 message: 'Yet another unresolved comment',
186 unresolved: true,
187 },
188 ],
189 patchNum: 2,
190 path: '/COMMIT_MSG',
191 line: 4,
192 rootId: 'scaddf38_44770ec1',
193 start_datetime: '2018-02-14 22:48:40.000000000',
194 },
195 {
196 comments: [
197 {
198 id: 'zcf0b9fa_fe1a5f62',
199 path: '/COMMIT_MSG',
200 line: 6,
201 updated: '2018-02-15 22:48:48.018000000',
202 message: 'resolved draft',
203 unresolved: false,
204 __draft: true,
205 __draftID: '0.m683trwff68',
206 __editing: false,
207 patch_set: '2',
208 },
209 ],
210 patchNum: 4,
211 path: '/COMMIT_MSG',
212 line: 6,
213 rootId: 'zcf0b9fa_fe1a5f62',
214 start_datetime: '2018-02-09 18:49:18.000000000',
215 },
216 {
217 comments: [
218 {
219 __path: '/COMMIT_MSG',
220 author: {
221 _account_id: 1000000,
222 name: 'user',
223 username: 'user',
224 },
225 patch_set: 4,
226 id: 'rc1',
227 line: 5,
228 updated: '2019-02-08 18:49:18.000000000',
229 message: 'test',
230 unresolved: true,
231 robot_id: 'rc1',
232 },
233 ],
234 patchNum: 4,
235 path: '/COMMIT_MSG',
236 line: 5,
237 rootId: 'rc1',
238 start_datetime: '2019-02-08 18:49:18.000000000',
239 },
240 {
241 comments: [
242 {
243 __path: '/COMMIT_MSG',
244 author: {
245 _account_id: 1000000,
246 name: 'user',
247 username: 'user',
248 },
249 patch_set: 4,
250 id: 'rc2',
251 line: 5,
252 updated: '2019-03-08 18:49:18.000000000',
253 message: 'test',
254 unresolved: true,
255 robot_id: 'rc2',
256 },
257 {
258 __path: '/COMMIT_MSG',
259 author: {
260 _account_id: 1000000,
261 name: 'user',
262 username: 'user',
263 },
264 patch_set: 4,
265 id: 'c2_1',
266 line: 5,
267 updated: '2019-03-08 18:49:18.000000000',
268 message: 'test',
269 unresolved: true,
270 },
271 ],
272 patchNum: 4,
273 path: '/COMMIT_MSG',
274 line: 5,
275 rootId: 'rc2',
276 start_datetime: '2019-03-08 18:49:18.000000000',
277 },
278 ];
279
280 setup(() => {
281 sandbox = sinon.sandbox.create();
282 stub('gr-endpoint-decorator', {
283 _import: sandbox.stub().returns(Promise.resolve()),
284 });
Dmitrii Filippovf97fc6e2020-04-07 09:59:22 +0200285 // Since pluginEndpoints are global, must reset state.
286 _testOnly_resetEndpoints();
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +0200287 navigateToChangeStub = sandbox.stub(GerritNav, 'navigateToChange');
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100288 stub('gr-rest-api-interface', {
289 getConfig() { return Promise.resolve({test: 'config'}); },
290 getAccount() { return Promise.resolve(null); },
291 getDiffComments() { return Promise.resolve({}); },
292 getDiffRobotComments() { return Promise.resolve({}); },
293 getDiffDrafts() { return Promise.resolve({}); },
294 _fetchSharedCacheURL() { return Promise.resolve({}); },
295 });
Dmitrii Filippovacd39a22020-04-02 10:31:43 +0200296 element = fixture.instantiate();
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100297 sandbox.stub(element.$.actions, 'reload').returns(Promise.resolve());
Dmitrii Filippov35aea692020-04-07 12:14:11 +0200298 pluginLoader.loadPlugins([]);
Dmitrii Filippov5f25dc02020-04-07 18:49:00 +0200299 pluginApi.install(
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100300 plugin => {
301 plugin.registerDynamicCustomComponent(
302 'change-view-tab-header',
303 'gr-checks-change-view-tab-header-view'
304 );
305 plugin.registerDynamicCustomComponent(
306 'change-view-tab-content',
307 'gr-checks-view'
308 );
309 },
310 '0.1',
311 'http://some/plugins/url.html'
312 );
313 });
314
315 teardown(done => {
316 flush(() => {
317 sandbox.restore();
318 done();
319 });
320 });
321
322 const getCustomCssValue =
323 cssParam => util.getComputedStyleValue(cssParam, element);
324
325 test('_handleMessageAnchorTap', () => {
326 element._changeNum = '1';
327 element._patchRange = {
328 basePatchNum: 'PARENT',
329 patchNum: 1,
Dhruv Srivastava97fb2342020-01-27 17:04:14 +0100330 };
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +0200331 const getUrlStub = sandbox.stub(GerritNav, 'getUrlForChange');
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100332 const replaceStateStub = sandbox.stub(history, 'replaceState');
333 element._handleMessageAnchorTap({detail: {id: 'a12345'}});
Dhruv Srivastava97fb2342020-01-27 17:04:14 +0100334
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100335 assert.equal(getUrlStub.lastCall.args[4], '#message-a12345');
336 assert.isTrue(replaceStateStub.called);
337 });
Dhruv Srivastavab41e20e2020-01-27 20:25:21 +0100338
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100339 suite('plugins adding to file tab', () => {
340 setup(done => {
341 // Resolving it here instead of during setup() as other tests depend
342 // on flush() not being called during setup.
343 flush(() => done());
Andrew Bonventre547b8ab2015-12-01 01:02:00 -0500344 });
345
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100346 test('plugin added tab shows up as a dynamic endpoint', () => {
347 assert(element._dynamicTabHeaderEndpoints.includes(
348 'change-view-tab-header-url'));
349 const paperTabs = element.shadowRoot.querySelector('#primaryTabs');
350 // 3 Tabs are : Files, Plugin, Findings
351 assert.equal(paperTabs.querySelectorAll('paper-tab').length, 3);
352 assert.equal(paperTabs.querySelectorAll('paper-tab')[1].dataset.name,
353 'change-view-tab-header-url');
354 });
355
Tao Zhou4fd32d52020-04-06 17:23:10 +0200356 test('_setActivePrimaryTab switched tab correctly', done => {
357 element._setActivePrimaryTab({detail:
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100358 {tab: 'change-view-tab-header-url'}});
Becky Siegelba3f5892017-05-12 12:28:13 -0700359 flush(() => {
Tao Zhou4fd32d52020-04-06 17:23:10 +0200360 assert.equal(element._activeTabs[0], 'change-view-tab-header-url');
361 done();
362 });
363 });
364
365 test('show-primary-tab switched primary tab correctly', done => {
366 element.fire('show-primary-tab', {tab: 'change-view-tab-header-url'});
367 flush(() => {
368 assert.equal(element._activeTabs[0], 'change-view-tab-header-url');
369 done();
370 });
371 });
372
373 test('param change should switch primary tab correctly', done => {
Dmitrii Filippove903bbf2020-05-06 12:57:39 +0200374 assert.equal(element._activeTabs[0], PrimaryTab.FILES);
Tao Zhou4fd32d52020-04-06 17:23:10 +0200375 const queryMap = new Map();
Dmitrii Filippove903bbf2020-05-06 12:57:39 +0200376 queryMap.set('tab', PrimaryTab.FINDINGS);
Tao Zhou4fd32d52020-04-06 17:23:10 +0200377 // view is required
378 element.params = Object.assign(
379 {
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +0200380 view: GerritNav.View.CHANGE,
Tao Zhou4fd32d52020-04-06 17:23:10 +0200381 },
382 element.params, {queryMap});
383 flush(() => {
Dmitrii Filippove903bbf2020-05-06 12:57:39 +0200384 assert.equal(element._activeTabs[0], PrimaryTab.FINDINGS);
Tao Zhou4fd32d52020-04-06 17:23:10 +0200385 done();
386 });
387 });
388
389 test('invalid param change should not switch primary tab', done => {
Dmitrii Filippove903bbf2020-05-06 12:57:39 +0200390 assert.equal(element._activeTabs[0], PrimaryTab.FILES);
Tao Zhou4fd32d52020-04-06 17:23:10 +0200391 const queryMap = new Map();
392 queryMap.set('tab', 'random');
393 // view is required
394 element.params = Object.assign(
395 {
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +0200396 view: GerritNav.View.CHANGE,
Tao Zhou4fd32d52020-04-06 17:23:10 +0200397 },
398 element.params, {queryMap});
399 flush(() => {
Dmitrii Filippove903bbf2020-05-06 12:57:39 +0200400 assert.equal(element._activeTabs[0], PrimaryTab.FILES);
Viktar Donich29e1ce52017-03-28 17:02:44 -0700401 done();
402 });
Kasper Nilssonf0743732016-10-18 13:01:10 -0700403 });
404
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100405 test('switching tab sets _selectedTabPluginEndpoint', done => {
406 const paperTabs = element.shadowRoot.querySelector('#primaryTabs');
407 MockInteractions.tap(paperTabs.querySelectorAll('paper-tab')[1]);
408 flush(() => {
409 assert.equal(element._selectedTabPluginEndpoint,
410 'change-view-tab-content-url');
411 done();
412 });
413 });
414 });
Paladox none7acb7ac2017-11-09 18:06:18 +0000415
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100416 suite('keyboard shortcuts', () => {
417 test('t to add topic', () => {
418 const editStub = sandbox.stub(element.$.metadata, 'editTopic');
419 MockInteractions.pressAndReleaseKeyOn(element, 83, null, 't');
420 assert(editStub.called);
421 });
422
423 test('S should toggle the CL star', () => {
424 const starStub = sandbox.stub(element.$.changeStar, 'toggleStar');
425 MockInteractions.pressAndReleaseKeyOn(element, 83, null, 's');
426 assert(starStub.called);
427 });
428
429 test('U should navigate to root if no backPage set', () => {
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +0200430 const relativeNavStub = sandbox.stub(GerritNav,
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100431 'navigateToRelativeUrl');
432 MockInteractions.pressAndReleaseKeyOn(element, 85, null, 'u');
433 assert.isTrue(relativeNavStub.called);
434 assert.isTrue(relativeNavStub.lastCall.calledWithExactly(
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +0200435 GerritNav.getUrlForRoot()));
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100436 });
437
438 test('U should navigate to backPage if set', () => {
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +0200439 const relativeNavStub = sandbox.stub(GerritNav,
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100440 'navigateToRelativeUrl');
441 element.backPage = '/dashboard/self';
442 MockInteractions.pressAndReleaseKeyOn(element, 85, null, 'u');
443 assert.isTrue(relativeNavStub.called);
444 assert.isTrue(relativeNavStub.lastCall.calledWithExactly(
445 '/dashboard/self'));
446 });
447
448 test('A fires an error event when not logged in', done => {
449 sandbox.stub(element, '_getLoggedIn').returns(Promise.resolve(false));
450 const loggedInErrorSpy = sandbox.spy();
451 element.addEventListener('show-auth-required', loggedInErrorSpy);
452 MockInteractions.pressAndReleaseKeyOn(element, 65, null, 'a');
453 flush(() => {
454 assert.isFalse(element.$.replyOverlay.opened);
455 assert.isTrue(loggedInErrorSpy.called);
456 done();
457 });
458 });
459
460 test('shift A does not open reply overlay', done => {
461 sandbox.stub(element, '_getLoggedIn').returns(Promise.resolve(true));
462 MockInteractions.pressAndReleaseKeyOn(element, 65, 'shift', 'a');
463 flush(() => {
464 assert.isFalse(element.$.replyOverlay.opened);
465 done();
466 });
467 });
468
469 test('A toggles overlay when logged in', done => {
470 sandbox.stub(element, '_getLoggedIn').returns(Promise.resolve(true));
471 sandbox.stub(element.$.replyDialog, 'fetchChangeUpdates')
472 .returns(Promise.resolve({isLatest: true}));
473 element._change = {labels: {}};
474 const openSpy = sandbox.spy(element, '_openReplyDialog');
475
476 MockInteractions.pressAndReleaseKeyOn(element, 65, null, 'a');
477 flush(() => {
478 assert.isTrue(element.$.replyOverlay.opened);
479 element.$.replyOverlay.close();
480 assert.isFalse(element.$.replyOverlay.opened);
481 assert(openSpy.lastCall.calledWithExactly(
482 element.$.replyDialog.FocusTarget.ANY),
483 '_openReplyDialog should have been passed ANY');
484 assert.equal(openSpy.callCount, 1);
485 done();
486 });
487 });
488
489 test('fullscreen-overlay-opened hides content', () => {
490 element._loggedIn = true;
491 element._loading = false;
492 element._change = {
493 owner: {_account_id: 1},
494 labels: {},
495 actions: {
496 abandon: {
497 enabled: true,
498 label: 'Abandon',
499 method: 'POST',
500 title: 'Abandon',
501 },
502 },
503 };
504 sandbox.spy(element, '_handleHideBackgroundContent');
Tao Zhou0a3c9862020-04-08 14:45:37 +0200505 element.$.replyDialog.dispatchEvent(
506 new CustomEvent('fullscreen-overlay-opened', {
507 composed: true, bubbles: true,
508 }));
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100509 assert.isTrue(element._handleHideBackgroundContent.called);
510 assert.isTrue(element.$.mainContent.classList.contains('overlayOpen'));
511 assert.equal(getComputedStyle(element.$.actions).display, 'flex');
512 });
513
514 test('fullscreen-overlay-closed shows content', () => {
515 element._loggedIn = true;
516 element._loading = false;
517 element._change = {
518 owner: {_account_id: 1},
519 labels: {},
520 actions: {
521 abandon: {
522 enabled: true,
523 label: 'Abandon',
524 method: 'POST',
525 title: 'Abandon',
526 },
527 },
528 };
529 sandbox.spy(element, '_handleShowBackgroundContent');
Tao Zhou0a3c9862020-04-08 14:45:37 +0200530 element.$.replyDialog.dispatchEvent(
531 new CustomEvent('fullscreen-overlay-closed', {
532 composed: true, bubbles: true,
533 }));
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100534 assert.isTrue(element._handleShowBackgroundContent.called);
535 assert.isFalse(element.$.mainContent.classList.contains('overlayOpen'));
536 });
537
538 test('expand all messages when expand-diffs fired', () => {
539 const handleExpand =
540 sandbox.stub(element.$.fileList, 'expandAllDiffs');
Tao Zhou0a3c9862020-04-08 14:45:37 +0200541 element.$.fileListHeader.dispatchEvent(
542 new CustomEvent('expand-diffs', {
543 composed: true, bubbles: true,
544 }));
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100545 assert.isTrue(handleExpand.called);
546 });
547
548 test('collapse all messages when collapse-diffs fired', () => {
549 const handleCollapse =
550 sandbox.stub(element.$.fileList, 'collapseAllDiffs');
Tao Zhou0a3c9862020-04-08 14:45:37 +0200551 element.$.fileListHeader.dispatchEvent(
552 new CustomEvent('collapse-diffs', {
553 composed: true, bubbles: true,
554 }));
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100555 assert.isTrue(handleCollapse.called);
556 });
557
558 test('X should expand all messages', done => {
559 flush(() => {
560 const handleExpand = sandbox.stub(element.messagesList,
561 'handleExpandCollapse');
562 MockInteractions.pressAndReleaseKeyOn(element, 88, null, 'x');
563 assert(handleExpand.calledWith(true));
564 done();
565 });
566 });
567
568 test('Z should collapse all messages', done => {
569 flush(() => {
570 const handleExpand = sandbox.stub(element.messagesList,
571 'handleExpandCollapse');
572 MockInteractions.pressAndReleaseKeyOn(element, 90, null, 'z');
573 assert(handleExpand.calledWith(false));
574 done();
575 });
576 });
577
578 test('shift + R should fetch and navigate to the latest patch set',
579 done => {
580 element._changeNum = '42';
581 element._patchRange = {
582 basePatchNum: 'PARENT',
583 patchNum: 1,
584 };
585 element._change = {
586 change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca',
587 _number: 42,
588 revisions: {
589 rev1: {_number: 1, commit: {parents: []}},
590 },
591 current_revision: 'rev1',
592 status: 'NEW',
593 labels: {},
594 actions: {},
595 };
596
597 navigateToChangeStub.restore();
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +0200598 navigateToChangeStub = sandbox.stub(GerritNav, 'navigateToChange',
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100599 (change, patchNum, basePatchNum) => {
600 assert.equal(change, element._change);
601 assert.isUndefined(patchNum);
602 assert.isUndefined(basePatchNum);
603 done();
604 });
605
606 MockInteractions.pressAndReleaseKeyOn(element, 82, 'shift', 'r');
607 });
608
609 test('d should open download overlay', () => {
610 const stub = sandbox.stub(element.$.downloadOverlay, 'open');
611 MockInteractions.pressAndReleaseKeyOn(element, 68, null, 'd');
612 assert.isTrue(stub.called);
613 });
614
615 test(', should open diff preferences', () => {
616 const stub = sandbox.stub(
617 element.$.fileList.$.diffPreferencesDialog, 'open');
618 element._loggedIn = false;
619 element.disableDiffPrefs = true;
620 MockInteractions.pressAndReleaseKeyOn(element, 188, null, ',');
621 assert.isFalse(stub.called);
622
623 element._loggedIn = true;
624 MockInteractions.pressAndReleaseKeyOn(element, 188, null, ',');
625 assert.isFalse(stub.called);
626
627 element.disableDiffPrefs = false;
628 MockInteractions.pressAndReleaseKeyOn(element, 188, null, ',');
629 assert.isTrue(stub.called);
630 });
631
632 test('m should toggle diff mode', () => {
633 sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
634 const setModeStub = sandbox.stub(element.$.fileListHeader,
635 'setDiffViewMode');
636 const e = {preventDefault: () => {}};
637 flushAsynchronousOperations();
638
639 element.viewState.diffMode = 'SIDE_BY_SIDE';
640 element._handleToggleDiffMode(e);
641 assert.isTrue(setModeStub.calledWith('UNIFIED_DIFF'));
642
643 element.viewState.diffMode = 'UNIFIED_DIFF';
644 element._handleToggleDiffMode(e);
645 assert.isTrue(setModeStub.calledWith('SIDE_BY_SIDE'));
646 });
647 });
648
649 suite('reloading drafts', () => {
650 let reloadStub;
651 const drafts = {
652 'testfile.txt': [
653 {
654 patch_set: 5,
655 id: 'dd2982f5_c01c9e6a',
656 line: 1,
657 updated: '2017-11-08 18:47:45.000000000',
658 message: 'test',
659 unresolved: true,
660 },
661 ],
662 };
663 setup(() => {
664 // Fake computeDraftCount as its required for ChangeComments,
665 // see gr-comment-api#reloadDrafts.
666 reloadStub = sandbox.stub(element.$.commentAPI, 'reloadDrafts')
667 .returns(Promise.resolve({
668 drafts,
669 getAllThreadsForChange: () => ([]),
670 computeDraftCount: () => 1,
671 }));
672 });
673
674 test('drafts are reloaded when reload-drafts fired', done => {
Tao Zhou0a3c9862020-04-08 14:45:37 +0200675 element.$.fileList.dispatchEvent(
676 new CustomEvent('reload-drafts', {
677 detail: {
678 resolve: () => {
679 assert.isTrue(reloadStub.called);
680 assert.deepEqual(element._diffDrafts, drafts);
681 done();
682 },
683 },
684 composed: true, bubbles: true,
685 }));
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100686 });
687
688 test('drafts are reloaded when comment-refresh fired', () => {
Tao Zhou0a3c9862020-04-08 14:45:37 +0200689 element.dispatchEvent(
690 new CustomEvent('comment-refresh', {
691 composed: true, bubbles: true,
692 }));
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100693 assert.isTrue(reloadStub.called);
694 });
695 });
696
Tao Zhou18738b92020-05-04 19:44:51 +0200697 suite('_recomputeComments', () => {
698 setup(() => {
699 // Fake computeDraftCount as its required for ChangeComments,
700 // see gr-comment-api#reloadDrafts.
701 sandbox.stub(element.$.commentAPI, 'reloadDrafts')
702 .returns(Promise.resolve({
703 drafts: {},
704 getAllThreadsForChange: () => THREADS,
705 computeDraftCount: () => 0,
706 }));
707 });
708
709 test('draft threads should be a new copy with correct states', done => {
710 element.$.fileList.dispatchEvent(
711 new CustomEvent('reload-drafts', {
712 detail: {
713 resolve: () => {
714 assert.equal(element._draftCommentThreads.length, 2);
715 assert.equal(
716 element._draftCommentThreads[0].rootId,
717 THREADS[0].rootId
718 );
719 assert.notEqual(
720 element._draftCommentThreads[0].comments,
721 THREADS[0].comments
722 );
723 assert.notEqual(
724 element._draftCommentThreads[0].comments[0],
725 THREADS[0].comments[0]
726 );
727 assert.isTrue(
728 element._draftCommentThreads[0]
729 .comments
730 .slice(0, 2)
731 .every(c => c.collapsed === true)
732 );
733
734 assert.isTrue(
735 element._draftCommentThreads[0]
736 .comments[2]
737 .collapsed === false
738 );
739 done();
740 },
741 },
742 composed: true, bubbles: true,
743 }));
744 });
745 });
746
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100747 test('diff comments modified', () => {
748 sandbox.spy(element, '_handleReloadCommentThreads');
749 return element._reloadComments().then(() => {
Tao Zhou0a3c9862020-04-08 14:45:37 +0200750 element.dispatchEvent(
751 new CustomEvent('diff-comments-modified', {
752 composed: true, bubbles: true,
753 }));
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100754 assert.isTrue(element._handleReloadCommentThreads.called);
755 });
756 });
757
758 test('thread list modified', () => {
759 sandbox.spy(element, '_handleReloadDiffComments');
Dmitrii Filippove903bbf2020-05-06 12:57:39 +0200760 element._activeTabs = [PrimaryTab.FILES, SecondaryTab.COMMENT_THREADS];
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100761 flushAsynchronousOperations();
762
763 return element._reloadComments().then(() => {
Tao Zhou0a3c9862020-04-08 14:45:37 +0200764 element.threadList.dispatchEvent(
765 new CustomEvent('thread-list-modified', {
766 composed: true, bubbles: true,
767 }));
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100768 assert.isTrue(element._handleReloadDiffComments.called);
769
770 let draftStub = sinon.stub(element._changeComments, 'computeDraftCount')
771 .returns(1);
772 assert.equal(element._computeTotalCommentCounts(5,
773 element._changeComments), '5 unresolved, 1 draft');
774 assert.equal(element._computeTotalCommentCounts(0,
775 element._changeComments), '1 draft');
776 draftStub.restore();
777 draftStub = sinon.stub(element._changeComments, 'computeDraftCount')
778 .returns(0);
779 assert.equal(element._computeTotalCommentCounts(0,
780 element._changeComments), '');
781 assert.equal(element._computeTotalCommentCounts(1,
782 element._changeComments), '1 unresolved');
783 draftStub.restore();
784 draftStub = sinon.stub(element._changeComments, 'computeDraftCount')
785 .returns(2);
786 assert.equal(element._computeTotalCommentCounts(1,
787 element._changeComments), '1 unresolved, 2 drafts');
788 draftStub.restore();
789 });
790 });
791
792 suite('thread list and change log tabs', () => {
793 setup(() => {
Kasper Nilsson9c1a3db2018-10-19 15:11:07 -0700794 element._changeNum = '1';
795 element._patchRange = {
796 basePatchNum: 'PARENT',
797 patchNum: 1,
798 };
Becky Siegel9b03dd22017-10-26 14:57:32 -0700799 element._change = {
800 change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca',
801 revisions: {
Wyatt Allena744e232017-11-08 15:33:48 -0800802 rev2: {_number: 2, commit: {parents: []}},
803 rev1: {_number: 1, commit: {parents: []}},
804 rev13: {_number: 13, commit: {parents: []}},
805 rev3: {_number: 3, commit: {parents: []}},
Kasper Nilssonf0743732016-10-18 13:01:10 -0700806 },
807 current_revision: 'rev3',
808 status: 'NEW',
809 labels: {
810 test: {
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100811 all: [],
Kasper Nilssonf0743732016-10-18 13:01:10 -0700812 default_value: 0,
813 values: [],
814 approved: {},
815 },
816 },
817 };
Kasper Nilsson34a5d892018-04-11 11:10:53 -0700818 sandbox.stub(element.$.relatedChanges, 'reload');
819 sandbox.stub(element, '_reload').returns(Promise.resolve());
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100820 sandbox.spy(element, '_paramsChanged');
821 element.params = {view: 'change', changeNum: '1'};
Kasper Nilsson34a5d892018-04-11 11:10:53 -0700822 });
Kasper Nilssonbbd28672018-08-01 10:23:23 -0700823
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100824 test('tab switch works correctly', done => {
825 assert.isTrue(element._paramsChanged.called);
Dmitrii Filippove903bbf2020-05-06 12:57:39 +0200826 assert.equal(element._activeTabs[1], SecondaryTab.CHANGE_LOG);
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100827
828 const commentTab = element.shadowRoot.querySelector(
829 'paper-tab.commentThreads'
830 );
831 // Switch to comment thread tab
832 MockInteractions.tap(commentTab);
Dmitrii Filippove903bbf2020-05-06 12:57:39 +0200833 assert.equal(element._activeTabs[1], SecondaryTab.COMMENT_THREADS);
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100834
835 // Switch back to 'Change Log' tab
836 element._paramsChanged(element.params);
837 flush(() => {
Dmitrii Filippove903bbf2020-05-06 12:57:39 +0200838 assert.equal(element._activeTabs[1], SecondaryTab.CHANGE_LOG);
Tao Zhou4fd32d52020-04-06 17:23:10 +0200839 done();
840 });
841 });
842
843 test('show-secondary-tab event works', () => {
Dmitrii Filippove903bbf2020-05-06 12:57:39 +0200844 assert.equal(element._activeTabs[1], SecondaryTab.CHANGE_LOG);
Tao Zhou4fd32d52020-04-06 17:23:10 +0200845 // Switch to comment thread tab
Dmitrii Filippove903bbf2020-05-06 12:57:39 +0200846 element.fire('show-secondary-tab', {tab: SecondaryTab.COMMENT_THREADS});
847 assert.equal(element._activeTabs[1], SecondaryTab.COMMENT_THREADS);
Tao Zhou4fd32d52020-04-06 17:23:10 +0200848 });
849
850 test('param change should switched secondary tab correctly', done => {
Dmitrii Filippove903bbf2020-05-06 12:57:39 +0200851 assert.equal(element._activeTabs[1], SecondaryTab.CHANGE_LOG);
Tao Zhou4fd32d52020-04-06 17:23:10 +0200852 const queryMap = new Map();
Dmitrii Filippove903bbf2020-05-06 12:57:39 +0200853 queryMap.set('secondaryTab', SecondaryTab.COMMENT_THREADS);
Tao Zhou4fd32d52020-04-06 17:23:10 +0200854 // view is required
855 element.params = Object.assign(
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +0200856 {view: GerritNav.View.CHANGE},
Tao Zhou4fd32d52020-04-06 17:23:10 +0200857 element.params, {queryMap}
858 );
859 flush(() => {
Dmitrii Filippove903bbf2020-05-06 12:57:39 +0200860 assert.equal(element._activeTabs[1], SecondaryTab.COMMENT_THREADS);
Tao Zhou4fd32d52020-04-06 17:23:10 +0200861 done();
862 });
863 });
864
865 test('invalid secondaryTab should not switch tab', done => {
Dmitrii Filippove903bbf2020-05-06 12:57:39 +0200866 assert.equal(element._activeTabs[1], SecondaryTab.CHANGE_LOG);
Tao Zhou4fd32d52020-04-06 17:23:10 +0200867 const queryMap = new Map();
868 queryMap.set('secondaryTab', 'random');
869 // view is required
870 element.params = Object.assign({
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +0200871 view: GerritNav.View.CHANGE,
Tao Zhou4fd32d52020-04-06 17:23:10 +0200872 }, element.params, {queryMap});
873 flush(() => {
Dmitrii Filippove903bbf2020-05-06 12:57:39 +0200874 assert.equal(element._activeTabs[1], SecondaryTab.CHANGE_LOG);
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100875 done();
876 });
877 });
878 });
879
880 suite('Findings comment tab', () => {
881 setup(done => {
Kasper Nilssonbbd28672018-08-01 10:23:23 -0700882 element._change = {
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100883 change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca',
884 revisions: {
885 rev2: {_number: 2, commit: {parents: []}},
886 rev1: {_number: 1, commit: {parents: []}},
887 rev13: {_number: 13, commit: {parents: []}},
888 rev3: {_number: 3, commit: {parents: []}},
889 rev4: {_number: 4, commit: {parents: []}},
890 },
891 current_revision: 'rev4',
Kasper Nilssonbbd28672018-08-01 10:23:23 -0700892 };
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100893 element._commentThreads = THREADS;
894 const paperTabs = element.shadowRoot.querySelector('#primaryTabs');
895 MockInteractions.tap(paperTabs.querySelectorAll('paper-tab')[2]);
896 flush(() => {
897 done();
898 });
Kasper Nilssonbbd28672018-08-01 10:23:23 -0700899 });
Milutin Kristofic7a86db12019-08-07 15:26:13 +0200900
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100901 test('robot comments count per patchset', () => {
902 const count = element._robotCommentCountPerPatchSet(THREADS);
903 const expectedCount = {
904 2: 1,
905 3: 1,
906 4: 2,
907 };
908 assert.deepEqual(count, expectedCount);
909 assert.equal(element._computeText({_number: 2}, THREADS),
910 'Patchset 2 (1 finding)');
911 assert.equal(element._computeText({_number: 4}, THREADS),
912 'Patchset 4 (2 findings)');
913 assert.equal(element._computeText({_number: 5}, THREADS),
914 'Patchset 5');
915 });
Milutin Kristofic7a86db12019-08-07 15:26:13 +0200916
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100917 test('only robot comments are rendered', () => {
918 assert.equal(element._robotCommentThreads.length, 2);
919 assert.equal(element._robotCommentThreads[0].comments[0].robot_id,
920 'rc1');
921 assert.equal(element._robotCommentThreads[1].comments[0].robot_id,
922 'rc2');
923 });
924
925 test('changing patchsets resets robot comments', done => {
926 element.set('_change.current_revision', 'rev3');
927 flush(() => {
928 assert.equal(element._robotCommentThreads.length, 1);
929 done();
930 });
931 });
932
933 test('Show more button is hidden', () => {
934 assert.isNull(element.shadowRoot.querySelector('.show-robot-comments'));
935 });
936
937 suite('robot comments show more button', () => {
938 setup(done => {
939 const arr = [];
940 for (let i = 0; i <= 30; i++) {
941 arr.push(...THREADS);
942 }
943 element._commentThreads = arr;
Milutin Kristofic7a86db12019-08-07 15:26:13 +0200944 flush(() => {
Milutin Kristofic7a86db12019-08-07 15:26:13 +0200945 done();
946 });
947 });
948
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100949 test('Show more button is rendered', () => {
950 assert.isOk(element.shadowRoot.querySelector('.show-robot-comments'));
951 assert.equal(element._robotCommentThreads.length,
952 ROBOT_COMMENTS_LIMIT);
953 });
954
955 test('Clicking show more button renders all comments', done => {
956 MockInteractions.tap(element.shadowRoot.querySelector(
957 '.show-robot-comments'));
Milutin Kristofic7a86db12019-08-07 15:26:13 +0200958 flush(() => {
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100959 assert.equal(element._robotCommentThreads.length, 62);
Milutin Kristofic7a86db12019-08-07 15:26:13 +0200960 done();
961 });
962 });
963 });
Andrew Bonventre547b8ab2015-12-01 01:02:00 -0500964 });
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100965
966 test('reply button is not visible when logged out', () => {
967 assert.equal(getComputedStyle(element.$.replyBtn).display, 'none');
968 element._loggedIn = true;
969 assert.notEqual(getComputedStyle(element.$.replyBtn).display, 'none');
970 });
971
972 test('download tap calls _handleOpenDownloadDialog', () => {
973 sandbox.stub(element, '_handleOpenDownloadDialog');
Tao Zhou0a3c9862020-04-08 14:45:37 +0200974 element.$.actions.dispatchEvent(
975 new CustomEvent('download-tap', {
976 composed: true, bubbles: true,
977 }));
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100978 assert.isTrue(element._handleOpenDownloadDialog.called);
979 });
980
981 test('fetches the server config on attached', done => {
982 flush(() => {
983 assert.equal(element._serverConfig.test, 'config');
984 done();
985 });
986 });
987
988 test('_changeStatuses', () => {
989 sandbox.stub(element, 'changeStatuses').returns(
990 ['Merged', 'WIP']);
991 element._loading = false;
992 element._change = {
993 change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca',
994 revisions: {
995 rev2: {_number: 2},
996 rev1: {_number: 1},
997 rev13: {_number: 13},
998 rev3: {_number: 3},
999 },
1000 current_revision: 'rev3',
1001 labels: {
1002 test: {
1003 all: [],
1004 default_value: 0,
1005 values: [],
1006 approved: {},
1007 },
1008 },
1009 };
1010 element._mergeable = true;
1011 const expectedStatuses = ['Merged', 'WIP'];
1012 assert.deepEqual(element._changeStatuses, expectedStatuses);
1013 assert.equal(element._changeStatus, expectedStatuses.join(', '));
1014 flushAsynchronousOperations();
1015 const statusChips = dom(element.root)
1016 .querySelectorAll('gr-change-status');
1017 assert.equal(statusChips.length, 2);
1018 });
1019
1020 test('diff preferences open when open-diff-prefs is fired', () => {
1021 const overlayOpenStub = sandbox.stub(element.$.fileList,
1022 'openDiffPrefs');
Tao Zhou0a3c9862020-04-08 14:45:37 +02001023 element.$.fileListHeader.dispatchEvent(
1024 new CustomEvent('open-diff-prefs', {
1025 composed: true, bubbles: true,
1026 }));
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01001027 assert.isTrue(overlayOpenStub.called);
1028 });
1029
1030 test('_prepareCommitMsgForLinkify', () => {
1031 let commitMessage = 'R=test@google.com';
1032 let result = element._prepareCommitMsgForLinkify(commitMessage);
1033 assert.equal(result, 'R=\u200Btest@google.com');
1034
1035 commitMessage = 'R=test@google.com\nR=test@google.com';
1036 result = element._prepareCommitMsgForLinkify(commitMessage);
1037 assert.equal(result, 'R=\u200Btest@google.com\nR=\u200Btest@google.com');
1038
1039 commitMessage = 'CC=test@google.com';
1040 result = element._prepareCommitMsgForLinkify(commitMessage);
1041 assert.equal(result, 'CC=\u200Btest@google.com');
1042 }),
1043
1044 test('_isSubmitEnabled', () => {
1045 assert.isFalse(element._isSubmitEnabled({}));
1046 assert.isFalse(element._isSubmitEnabled({submit: {}}));
1047 assert.isTrue(element._isSubmitEnabled(
1048 {submit: {enabled: true}}));
1049 });
1050
1051 test('_reload is called when an approved label is removed', () => {
1052 const vote = {_account_id: 1, name: 'bojack', value: 1};
1053 element._changeNum = '42';
1054 element._patchRange = {
1055 basePatchNum: 'PARENT',
1056 patchNum: 1,
1057 };
1058 element._change = {
1059 change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca',
1060 owner: {email: 'abc@def'},
1061 revisions: {
1062 rev2: {_number: 2, commit: {parents: []}},
1063 rev1: {_number: 1, commit: {parents: []}},
1064 rev13: {_number: 13, commit: {parents: []}},
1065 rev3: {_number: 3, commit: {parents: []}},
1066 },
1067 current_revision: 'rev3',
1068 status: 'NEW',
1069 labels: {
1070 test: {
1071 all: [vote],
1072 default_value: 0,
1073 values: [],
1074 approved: {},
1075 },
1076 },
1077 };
1078 flushAsynchronousOperations();
1079 const reloadStub = sandbox.stub(element, '_reload');
1080 element.splice('_change.labels.test.all', 0, 1);
1081 assert.isFalse(reloadStub.called);
1082 element._change.labels.test.all.push(vote);
1083 element._change.labels.test.all.push(vote);
1084 element._change.labels.test.approved = vote;
1085 flushAsynchronousOperations();
1086 element.splice('_change.labels.test.all', 0, 2);
1087 assert.isTrue(reloadStub.called);
1088 assert.isTrue(reloadStub.calledOnce);
1089 });
1090
1091 test('reply button has updated count when there are drafts', () => {
1092 const getLabel = element._computeReplyButtonLabel;
1093
1094 assert.equal(getLabel(null, false), 'Reply');
Ben Rohlfs8a3995e2020-04-03 15:03:57 +00001095 assert.equal(getLabel(null, true), 'Start review');
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01001096
1097 const changeRecord = {base: null};
1098 assert.equal(getLabel(changeRecord, false), 'Reply');
1099
1100 changeRecord.base = {};
1101 assert.equal(getLabel(changeRecord, false), 'Reply');
1102
1103 changeRecord.base = {
1104 'file1.txt': [{}],
1105 'file2.txt': [{}, {}],
1106 };
1107 assert.equal(getLabel(changeRecord, false), 'Reply (3)');
1108 });
1109
1110 test('start review button when owner of WIP change', () => {
Ben Rohlfs8a3995e2020-04-03 15:03:57 +00001111 assert.equal(
1112 element._computeReplyButtonLabel(null, true),
1113 'Start review');
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01001114 });
1115
1116 test('comment events properly update diff drafts', () => {
1117 element._patchRange = {
1118 basePatchNum: 'PARENT',
1119 patchNum: 2,
1120 };
1121 const draft = {
1122 __draft: true,
1123 id: 'id1',
1124 path: '/foo/bar.txt',
1125 text: 'hello',
1126 };
1127 element._handleCommentSave({detail: {comment: draft}});
1128 draft.patch_set = 2;
1129 assert.deepEqual(element._diffDrafts, {'/foo/bar.txt': [draft]});
1130 draft.patch_set = null;
1131 draft.text = 'hello, there';
1132 element._handleCommentSave({detail: {comment: draft}});
1133 draft.patch_set = 2;
1134 assert.deepEqual(element._diffDrafts, {'/foo/bar.txt': [draft]});
1135 const draft2 = {
1136 __draft: true,
1137 id: 'id2',
1138 path: '/foo/bar.txt',
1139 text: 'hola',
1140 };
1141 element._handleCommentSave({detail: {comment: draft2}});
1142 draft2.patch_set = 2;
1143 assert.deepEqual(element._diffDrafts, {'/foo/bar.txt': [draft, draft2]});
1144 draft.patch_set = null;
1145 element._handleCommentDiscard({detail: {comment: draft}});
1146 draft.patch_set = 2;
1147 assert.deepEqual(element._diffDrafts, {'/foo/bar.txt': [draft2]});
1148 element._handleCommentDiscard({detail: {comment: draft2}});
1149 assert.deepEqual(element._diffDrafts, {});
1150 });
1151
1152 test('change num change', () => {
1153 element._changeNum = null;
1154 element._patchRange = {
1155 basePatchNum: 'PARENT',
1156 patchNum: 2,
1157 };
1158 element._change = {
1159 change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca',
1160 labels: {},
1161 };
1162 element.viewState.changeNum = null;
1163 element.viewState.diffMode = 'UNIFIED';
1164 assert.equal(element.viewState.numFilesShown, 200);
1165 assert.equal(element._numFilesShown, 200);
1166 element._numFilesShown = 150;
1167 flushAsynchronousOperations();
1168 assert.equal(element.viewState.diffMode, 'UNIFIED');
1169 assert.equal(element.viewState.numFilesShown, 150);
1170
1171 element._changeNum = '1';
1172 element.params = {changeNum: '1'};
1173 element._change.newProp = '1';
1174 flushAsynchronousOperations();
1175 assert.equal(element.viewState.diffMode, 'UNIFIED');
1176 assert.equal(element.viewState.changeNum, '1');
1177
1178 element._changeNum = '2';
1179 element.params = {changeNum: '2'};
1180 element._change.newProp = '2';
1181 flushAsynchronousOperations();
1182 assert.equal(element.viewState.diffMode, 'UNIFIED');
1183 assert.equal(element.viewState.changeNum, '2');
1184 assert.equal(element.viewState.numFilesShown, 200);
1185 assert.equal(element._numFilesShown, 200);
1186 });
1187
1188 test('_setDiffViewMode is called with reset when new change is loaded',
1189 () => {
1190 sandbox.stub(element, '_setDiffViewMode');
1191 element.viewState = {changeNum: 1};
1192 element._changeNum = 2;
1193 element._resetFileListViewState();
1194 assert.isTrue(
1195 element._setDiffViewMode.lastCall.calledWithExactly(true));
1196 });
1197
1198 test('diffViewMode is propagated from file list header', () => {
1199 element.viewState = {diffMode: 'UNIFIED'};
1200 element.$.fileListHeader.diffViewMode = 'SIDE_BY_SIDE';
1201 assert.equal(element.viewState.diffMode, 'SIDE_BY_SIDE');
1202 });
1203
1204 test('diffMode defaults to side by side without preferences', done => {
1205 sandbox.stub(element.$.restAPI, 'getPreferences').returns(
1206 Promise.resolve({}));
1207 // No user prefs or diff view mode set.
1208
1209 element._setDiffViewMode().then(() => {
1210 assert.equal(element.viewState.diffMode, 'SIDE_BY_SIDE');
1211 done();
1212 });
1213 });
1214
1215 test('diffMode defaults to preference when not already set', done => {
1216 sandbox.stub(element.$.restAPI, 'getPreferences').returns(
1217 Promise.resolve({default_diff_view: 'UNIFIED'}));
1218
1219 element._setDiffViewMode().then(() => {
1220 assert.equal(element.viewState.diffMode, 'UNIFIED');
1221 done();
1222 });
1223 });
1224
1225 test('existing diffMode overrides preference', done => {
1226 element.viewState.diffMode = 'SIDE_BY_SIDE';
1227 sandbox.stub(element.$.restAPI, 'getPreferences').returns(
1228 Promise.resolve({default_diff_view: 'UNIFIED'}));
1229 element._setDiffViewMode().then(() => {
1230 assert.equal(element.viewState.diffMode, 'SIDE_BY_SIDE');
1231 done();
1232 });
1233 });
1234
1235 test('don’t reload entire page when patchRange changes', () => {
1236 const reloadStub = sandbox.stub(element, '_reload',
1237 () => Promise.resolve());
1238 const reloadPatchDependentStub = sandbox.stub(element,
1239 '_reloadPatchNumDependentResources',
1240 () => Promise.resolve());
1241 const relatedClearSpy = sandbox.spy(element.$.relatedChanges, 'clear');
1242 const collapseStub = sandbox.stub(element.$.fileList, 'collapseAllDiffs');
1243
1244 const value = {
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +02001245 view: GerritNav.View.CHANGE,
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01001246 patchNum: '1',
1247 };
1248 element._paramsChanged(value);
1249 assert.isTrue(reloadStub.calledOnce);
1250 assert.isTrue(relatedClearSpy.calledOnce);
1251
1252 element._initialLoadComplete = true;
1253
1254 value.basePatchNum = '1';
1255 value.patchNum = '2';
1256 element._paramsChanged(value);
1257 assert.isFalse(reloadStub.calledTwice);
1258 assert.isTrue(reloadPatchDependentStub.calledOnce);
1259 assert.isTrue(relatedClearSpy.calledOnce);
1260 assert.isTrue(collapseStub.calledTwice);
1261 });
1262
1263 test('reload entire page when patchRange doesnt change', () => {
1264 const reloadStub = sandbox.stub(element, '_reload',
1265 () => Promise.resolve());
1266 const collapseStub = sandbox.stub(element.$.fileList, 'collapseAllDiffs');
1267 const value = {
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +02001268 view: GerritNav.View.CHANGE,
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01001269 };
1270 element._paramsChanged(value);
1271 assert.isTrue(reloadStub.calledOnce);
1272 element._initialLoadComplete = true;
1273 element._paramsChanged(value);
1274 assert.isTrue(reloadStub.calledTwice);
1275 assert.isTrue(collapseStub.calledTwice);
1276 });
1277
1278 test('related changes are updated and new patch selected after rebase',
1279 done => {
1280 element._changeNum = '42';
1281 sandbox.stub(element, 'computeLatestPatchNum', () => 1);
1282 sandbox.stub(element, '_reload',
1283 () => Promise.resolve());
1284 const e = {detail: {action: 'rebase'}};
1285 element._handleReloadChange(e).then(() => {
1286 assert.isTrue(navigateToChangeStub.lastCall.calledWithExactly(
1287 element._change));
1288 done();
1289 });
1290 });
1291
1292 test('related changes are not updated after other action', done => {
1293 sandbox.stub(element, '_reload', () => Promise.resolve());
1294 sandbox.stub(element.$.relatedChanges, 'reload');
1295 const e = {detail: {action: 'abandon'}};
1296 element._handleReloadChange(e).then(() => {
1297 assert.isFalse(navigateToChangeStub.called);
1298 done();
1299 });
1300 });
1301
1302 test('_computeMergedCommitInfo', () => {
1303 const dummyRevs = {
1304 1: {commit: {commit: 1}},
1305 2: {commit: {}},
1306 };
1307 assert.deepEqual(element._computeMergedCommitInfo(0, dummyRevs), {});
1308 assert.deepEqual(element._computeMergedCommitInfo(1, dummyRevs),
1309 dummyRevs[1].commit);
1310
1311 // Regression test for issue 5337.
1312 const commit = element._computeMergedCommitInfo(2, dummyRevs);
1313 assert.notDeepEqual(commit, dummyRevs[2]);
1314 assert.deepEqual(commit, {commit: 2});
1315 });
1316
1317 test('_computeCopyTextForTitle', () => {
1318 const change = {
1319 _number: 123,
1320 subject: 'test subject',
1321 revisions: {
1322 rev1: {_number: 1},
1323 rev3: {_number: 3},
1324 },
1325 current_revision: 'rev3',
1326 };
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +02001327 sandbox.stub(GerritNav, 'getUrlForChange')
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01001328 .returns('/change/123');
1329 assert.equal(
1330 element._computeCopyTextForTitle(change),
Paladox noned619ebc2020-03-22 00:19:41 +00001331 `123: test subject | http://${location.host}/change/123`
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01001332 );
1333 });
1334
1335 test('get latest revision', () => {
1336 let change = {
1337 revisions: {
1338 rev1: {_number: 1},
1339 rev3: {_number: 3},
1340 },
1341 current_revision: 'rev3',
1342 };
1343 assert.equal(element._getLatestRevisionSHA(change), 'rev3');
1344 change = {
1345 revisions: {
1346 rev1: {_number: 1},
1347 },
1348 };
1349 assert.equal(element._getLatestRevisionSHA(change), 'rev1');
1350 });
1351
1352 test('show commit message edit button', () => {
1353 const _change = {
Dmitrii Filippov4e4522e2020-05-06 12:50:49 +02001354 status: ChangeStatus.MERGED,
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01001355 };
1356 assert.isTrue(element._computeHideEditCommitMessage(false, false, {}));
1357 assert.isTrue(element._computeHideEditCommitMessage(true, true, {}));
1358 assert.isTrue(element._computeHideEditCommitMessage(false, true, {}));
1359 assert.isFalse(element._computeHideEditCommitMessage(true, false, {}));
1360 assert.isTrue(element._computeHideEditCommitMessage(true, false,
1361 _change));
1362 assert.isTrue(element._computeHideEditCommitMessage(true, false, {},
1363 true));
1364 assert.isFalse(element._computeHideEditCommitMessage(true, false, {},
1365 false));
1366 });
1367
1368 test('_handleCommitMessageSave trims trailing whitespace', () => {
1369 const putStub = sandbox.stub(element.$.restAPI, 'putChangeCommitMessage')
1370 .returns(Promise.resolve({}));
1371
1372 const mockEvent = content => { return {detail: {content}}; };
1373
1374 element._handleCommitMessageSave(mockEvent('test \n test '));
1375 assert.equal(putStub.lastCall.args[1], 'test\n test');
1376
1377 element._handleCommitMessageSave(mockEvent(' test\ntest'));
1378 assert.equal(putStub.lastCall.args[1], ' test\ntest');
1379
1380 element._handleCommitMessageSave(mockEvent('\n\n\n\n\n\n\n\n'));
1381 assert.equal(putStub.lastCall.args[1], '\n\n\n\n\n\n\n\n');
1382 });
1383
1384 test('_computeChangeIdCommitMessageError', () => {
1385 let commitMessage =
1386 'Change-Id: I4ce18b2395bca69d7a9aa48bf4554faa56282483';
1387 let change = {change_id: 'I4ce18b2395bca69d7a9aa48bf4554faa56282483'};
1388 assert.equal(
1389 element._computeChangeIdCommitMessageError(commitMessage, change),
1390 null);
1391
1392 change = {change_id: 'I4ce18b2395bca69d7a9aa48bf4554faa56282484'};
1393 assert.equal(
1394 element._computeChangeIdCommitMessageError(commitMessage, change),
1395 'mismatch');
1396
1397 commitMessage = 'This is the greatest change.';
1398 assert.equal(
1399 element._computeChangeIdCommitMessageError(commitMessage, change),
1400 'missing');
1401 });
1402
1403 test('multiple change Ids in commit message picks last', () => {
1404 const commitMessage = [
1405 'Change-Id: I4ce18b2395bca69d7a9aa48bf4554faa56282484',
1406 'Change-Id: I4ce18b2395bca69d7a9aa48bf4554faa56282483',
1407 ].join('\n');
1408 let change = {change_id: 'I4ce18b2395bca69d7a9aa48bf4554faa56282483'};
1409 assert.equal(
1410 element._computeChangeIdCommitMessageError(commitMessage, change),
1411 null);
1412 change = {change_id: 'I4ce18b2395bca69d7a9aa48bf4554faa56282484'};
1413 assert.equal(
1414 element._computeChangeIdCommitMessageError(commitMessage, change),
1415 'mismatch');
1416 });
1417
1418 test('does not count change Id that starts mid line', () => {
1419 const commitMessage = [
1420 'Change-Id: I4ce18b2395bca69d7a9aa48bf4554faa56282484',
1421 'Change-Id: I4ce18b2395bca69d7a9aa48bf4554faa56282483',
1422 ].join(' and ');
1423 let change = {change_id: 'I4ce18b2395bca69d7a9aa48bf4554faa56282484'};
1424 assert.equal(
1425 element._computeChangeIdCommitMessageError(commitMessage, change),
1426 null);
1427 change = {change_id: 'I4ce18b2395bca69d7a9aa48bf4554faa56282483'};
1428 assert.equal(
1429 element._computeChangeIdCommitMessageError(commitMessage, change),
1430 'mismatch');
1431 });
1432
1433 test('_computeTitleAttributeWarning', () => {
1434 let changeIdCommitMessageError = 'missing';
1435 assert.equal(
1436 element._computeTitleAttributeWarning(changeIdCommitMessageError),
1437 'No Change-Id in commit message');
1438
1439 changeIdCommitMessageError = 'mismatch';
1440 assert.equal(
1441 element._computeTitleAttributeWarning(changeIdCommitMessageError),
1442 'Change-Id mismatch');
1443 });
1444
1445 test('_computeChangeIdClass', () => {
1446 let changeIdCommitMessageError = 'missing';
1447 assert.equal(
1448 element._computeChangeIdClass(changeIdCommitMessageError), '');
1449
1450 changeIdCommitMessageError = 'mismatch';
1451 assert.equal(
1452 element._computeChangeIdClass(changeIdCommitMessageError), 'warning');
1453 });
1454
1455 test('topic is coalesced to null', done => {
1456 sandbox.stub(element, '_changeChanged');
1457 sandbox.stub(element.$.restAPI, 'getChangeDetail', () => Promise.resolve({
1458 id: '123456789',
1459 labels: {},
1460 current_revision: 'foo',
1461 revisions: {foo: {commit: {}}},
1462 }));
1463
1464 element._getChangeDetail().then(() => {
1465 assert.isNull(element._change.topic);
1466 done();
1467 });
1468 });
1469
1470 test('commit sha is populated from getChangeDetail', done => {
1471 sandbox.stub(element, '_changeChanged');
1472 sandbox.stub(element.$.restAPI, 'getChangeDetail', () => Promise.resolve({
1473 id: '123456789',
1474 labels: {},
1475 current_revision: 'foo',
1476 revisions: {foo: {commit: {}}},
1477 }));
1478
1479 element._getChangeDetail().then(() => {
1480 assert.equal('foo', element._commitInfo.commit);
1481 done();
1482 });
1483 });
1484
1485 test('edit is added to change', () => {
1486 sandbox.stub(element, '_changeChanged');
1487 sandbox.stub(element.$.restAPI, 'getChangeDetail', () => Promise.resolve({
1488 id: '123456789',
1489 labels: {},
1490 current_revision: 'foo',
1491 revisions: {foo: {commit: {}}},
1492 }));
1493 sandbox.stub(element, '_getEdit', () => Promise.resolve({
1494 base_patch_set_number: 1,
1495 commit: {commit: 'bar'},
1496 }));
1497 element._patchRange = {};
1498
1499 return element._getChangeDetail().then(() => {
1500 const revs = element._change.revisions;
1501 assert.equal(Object.keys(revs).length, 2);
1502 assert.deepEqual(revs['foo'], {commit: {commit: 'foo'}});
1503 assert.deepEqual(revs['bar'], {
1504 _number: element.EDIT_NAME,
1505 basePatchNum: 1,
1506 commit: {commit: 'bar'},
1507 fetch: undefined,
1508 });
1509 });
1510 });
1511
1512 test('_getBasePatchNum', () => {
1513 const _change = {
1514 _number: 42,
1515 revisions: {
1516 '98da160735fb81604b4c40e93c368f380539dd0e': {
1517 _number: 1,
1518 commit: {
1519 parents: [],
1520 },
1521 },
1522 },
1523 };
1524 const _patchRange = {
1525 basePatchNum: 'PARENT',
1526 };
1527 assert.equal(element._getBasePatchNum(_change, _patchRange), 'PARENT');
1528
1529 element._prefs = {
1530 default_base_for_merges: 'FIRST_PARENT',
1531 };
1532
1533 const _change2 = {
1534 _number: 42,
1535 revisions: {
1536 '98da160735fb81604b4c40e93c368f380539dd0e': {
1537 _number: 1,
1538 commit: {
1539 parents: [
1540 {
1541 commit: '6e12bdf1176eb4ab24d8491ba3b6d0704409cde8',
1542 subject: 'test',
1543 },
1544 {
1545 commit: '22f7db4754b5d9816fc581f3d9a6c0ef8429c841',
1546 subject: 'test3',
1547 },
1548 ],
1549 },
1550 },
1551 },
1552 };
1553 assert.equal(element._getBasePatchNum(_change2, _patchRange), -1);
1554
1555 _patchRange.patchNum = 1;
1556 assert.equal(element._getBasePatchNum(_change2, _patchRange), 'PARENT');
1557 });
1558
1559 test('_openReplyDialog called with `ANY` when coming from tap event',
1560 () => {
1561 const openStub = sandbox.stub(element, '_openReplyDialog');
1562 element._serverConfig = {};
1563 MockInteractions.tap(element.$.replyBtn);
1564 assert(openStub.lastCall.calledWithExactly(
1565 element.$.replyDialog.FocusTarget.ANY),
1566 '_openReplyDialog should have been passed ANY');
1567 assert.equal(openStub.callCount, 1);
1568 });
1569
1570 test('_openReplyDialog called with `BODY` when coming from message reply' +
1571 'event', done => {
1572 flush(() => {
1573 const openStub = sandbox.stub(element, '_openReplyDialog');
Tao Zhou0a3c9862020-04-08 14:45:37 +02001574 element.messagesList.dispatchEvent(
1575 new CustomEvent('reply', {
1576 detail:
1577 {message: {message: 'text'}},
1578 composed: true, bubbles: true,
1579 }));
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01001580 assert(openStub.lastCall.calledWithExactly(
1581 element.$.replyDialog.FocusTarget.BODY),
1582 '_openReplyDialog should have been passed BODY');
1583 assert.equal(openStub.callCount, 1);
1584 done();
1585 });
1586 });
1587
1588 test('reply dialog focus can be controlled', () => {
1589 const FocusTarget = element.$.replyDialog.FocusTarget;
1590 const openStub = sandbox.stub(element, '_openReplyDialog');
1591
1592 const e = {detail: {}};
1593 element._handleShowReplyDialog(e);
1594 assert(openStub.lastCall.calledWithExactly(FocusTarget.REVIEWERS),
1595 '_openReplyDialog should have been passed REVIEWERS');
1596 assert.equal(openStub.callCount, 1);
1597
1598 e.detail.value = {ccsOnly: true};
1599 element._handleShowReplyDialog(e);
1600 assert(openStub.lastCall.calledWithExactly(FocusTarget.CCS),
1601 '_openReplyDialog should have been passed CCS');
1602 assert.equal(openStub.callCount, 2);
1603 });
1604
1605 test('getUrlParameter functionality', () => {
1606 const locationStub = sandbox.stub(element, '_getLocationSearch');
1607
1608 locationStub.returns('?test');
1609 assert.equal(element._getUrlParameter('test'), 'test');
1610 locationStub.returns('?test2=12&test=3');
1611 assert.equal(element._getUrlParameter('test'), 'test');
1612 locationStub.returns('');
1613 assert.isNull(element._getUrlParameter('test'));
1614 locationStub.returns('?');
1615 assert.isNull(element._getUrlParameter('test'));
1616 locationStub.returns('?test2');
1617 assert.isNull(element._getUrlParameter('test'));
1618 });
1619
1620 test('revert dialog opened with revert param', done => {
1621 sandbox.stub(element.$.restAPI, 'getLoggedIn', () => Promise.resolve(true));
Dmitrii Filippovf97fc6e2020-04-07 09:59:22 +02001622 sandbox.stub(pluginLoader, 'awaitPluginsLoaded', () => Promise.resolve());
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01001623
1624 element._patchRange = {
1625 basePatchNum: 'PARENT',
1626 patchNum: 2,
1627 };
1628 element._change = {
1629 change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca',
1630 revisions: {
1631 rev1: {_number: 1, commit: {parents: []}},
1632 rev2: {_number: 2, commit: {parents: []}},
1633 },
1634 current_revision: 'rev1',
Dmitrii Filippov4e4522e2020-05-06 12:50:49 +02001635 status: ChangeStatus.MERGED,
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01001636 labels: {},
1637 actions: {},
1638 };
1639
1640 sandbox.stub(element, '_getUrlParameter',
1641 param => {
1642 assert.equal(param, 'revert');
1643 return param;
1644 });
1645
1646 sandbox.stub(element.$.actions, 'showRevertDialog',
1647 done);
1648
1649 element._maybeShowRevertDialog();
Dmitrii Filippovf97fc6e2020-04-07 09:59:22 +02001650 assert.isTrue(pluginLoader.awaitPluginsLoaded.called);
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01001651 });
1652
1653 suite('scroll related tests', () => {
1654 test('document scrolling calls function to set scroll height', done => {
1655 const originalHeight = document.body.scrollHeight;
1656 const scrollStub = sandbox.stub(element, '_handleScroll',
1657 () => {
1658 assert.isTrue(scrollStub.called);
1659 document.body.style.height = originalHeight + 'px';
1660 scrollStub.restore();
1661 done();
1662 });
1663 document.body.style.height = '10000px';
1664 element._handleScroll();
1665 });
1666
1667 test('scrollTop is set correctly', () => {
1668 element.viewState = {scrollTop: TEST_SCROLL_TOP_PX};
1669
1670 sandbox.stub(element, '_reload', () => {
1671 // When element is reloaded, ensure that the history
1672 // state has the scrollTop set earlier. This will then
1673 // be reset.
1674 assert.isTrue(element.viewState.scrollTop == TEST_SCROLL_TOP_PX);
1675 return Promise.resolve({});
1676 });
1677
1678 // simulate reloading component, which is done when route
1679 // changes to match a regex of change view type.
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +02001680 element._paramsChanged({view: GerritNav.View.CHANGE});
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01001681 });
1682
1683 test('scrollTop is reset when new change is loaded', () => {
1684 element._resetFileListViewState();
1685 assert.equal(element.viewState.scrollTop, 0);
1686 });
1687 });
1688
1689 suite('reply dialog tests', () => {
1690 setup(() => {
1691 sandbox.stub(element.$.replyDialog, '_draftChanged');
1692 sandbox.stub(element.$.replyDialog, 'fetchChangeUpdates',
1693 () => Promise.resolve({isLatest: true}));
1694 element._change = {labels: {}};
1695 });
1696
1697 test('reply from comment adds quote text', () => {
1698 const e = {detail: {message: {message: 'quote text'}}};
1699 element._handleMessageReply(e);
1700 assert.equal(element.$.replyDialog.quote, '> quote text\n\n');
1701 });
1702
1703 test('reply from comment replaces quote text', () => {
1704 element.$.replyDialog.draft = '> old quote text\n\n some draft text';
1705 element.$.replyDialog.quote = '> old quote text\n\n';
1706 const e = {detail: {message: {message: 'quote text'}}};
1707 element._handleMessageReply(e);
1708 assert.equal(element.$.replyDialog.quote, '> quote text\n\n');
1709 });
1710
1711 test('reply from same comment preserves quote text', () => {
1712 element.$.replyDialog.draft = '> quote text\n\n some draft text';
1713 element.$.replyDialog.quote = '> quote text\n\n';
1714 const e = {detail: {message: {message: 'quote text'}}};
1715 element._handleMessageReply(e);
1716 assert.equal(element.$.replyDialog.draft,
1717 '> quote text\n\n some draft text');
1718 assert.equal(element.$.replyDialog.quote, '> quote text\n\n');
1719 });
1720
1721 test('reply from top of page contains previous draft', () => {
1722 const div = document.createElement('div');
1723 element.$.replyDialog.draft = '> quote text\n\n some draft text';
1724 element.$.replyDialog.quote = '> quote text\n\n';
1725 const e = {target: div, preventDefault: sandbox.spy()};
1726 element._handleReplyTap(e);
1727 assert.equal(element.$.replyDialog.draft,
1728 '> quote text\n\n some draft text');
1729 assert.equal(element.$.replyDialog.quote, '> quote text\n\n');
1730 });
1731 });
1732
1733 test('reply button is disabled until server config is loaded', () => {
1734 assert.isTrue(element._replyDisabled);
1735 element._serverConfig = {};
1736 assert.isFalse(element._replyDisabled);
1737 });
1738
1739 suite('commit message expand/collapse', () => {
1740 setup(() => {
1741 sandbox.stub(element, 'fetchChangeUpdates',
1742 () => Promise.resolve({isLatest: false}));
1743 });
1744
1745 test('commitCollapseToggle hidden for short commit message', () => {
1746 element._latestCommitMessage = '';
1747 assert.isTrue(element.$.commitCollapseToggle.hasAttribute('hidden'));
1748 });
1749
1750 test('commitCollapseToggle shown for long commit message', () => {
1751 element._latestCommitMessage = _.times(31, String).join('\n');
1752 assert.isFalse(element.$.commitCollapseToggle.hasAttribute('hidden'));
1753 });
1754
1755 test('commitCollapseToggle functions', () => {
1756 element._latestCommitMessage = _.times(35, String).join('\n');
1757 assert.isTrue(element._commitCollapsed);
1758 assert.isTrue(element._commitCollapsible);
1759 assert.isTrue(
1760 element.$.commitMessageEditor.hasAttribute('collapsed'));
1761 MockInteractions.tap(element.$.commitCollapseToggleButton);
1762 assert.isFalse(element._commitCollapsed);
1763 assert.isTrue(element._commitCollapsible);
1764 assert.isFalse(
1765 element.$.commitMessageEditor.hasAttribute('collapsed'));
1766 });
1767 });
1768
1769 suite('related changes expand/collapse', () => {
1770 let updateHeightSpy;
1771 setup(() => {
1772 updateHeightSpy = sandbox.spy(element, '_updateRelatedChangeMaxHeight');
1773 });
1774
1775 test('relatedChangesToggle shown height greater than changeInfo height',
1776 () => {
1777 assert.isFalse(element.$.relatedChangesToggle.classList
1778 .contains('showToggle'));
1779 sandbox.stub(element, '_getOffsetHeight', () => 50);
1780 sandbox.stub(element, '_getScrollHeight', () => 60);
1781 sandbox.stub(element, '_getLineHeight', () => 5);
1782 sandbox.stub(window, 'matchMedia', () => { return {matches: true}; });
1783 element.$.relatedChanges.dispatchEvent(
1784 new CustomEvent('new-section-loaded'));
1785 assert.isTrue(element.$.relatedChangesToggle.classList
1786 .contains('showToggle'));
1787 assert.equal(updateHeightSpy.callCount, 1);
1788 });
1789
1790 test('relatedChangesToggle hidden height less than changeInfo height',
1791 () => {
1792 assert.isFalse(element.$.relatedChangesToggle.classList
1793 .contains('showToggle'));
1794 sandbox.stub(element, '_getOffsetHeight', () => 50);
1795 sandbox.stub(element, '_getScrollHeight', () => 40);
1796 sandbox.stub(element, '_getLineHeight', () => 5);
1797 sandbox.stub(window, 'matchMedia', () => { return {matches: true}; });
1798 element.$.relatedChanges.dispatchEvent(
1799 new CustomEvent('new-section-loaded'));
1800 assert.isFalse(element.$.relatedChangesToggle.classList
1801 .contains('showToggle'));
1802 assert.equal(updateHeightSpy.callCount, 1);
1803 });
1804
1805 test('relatedChangesToggle functions', () => {
1806 sandbox.stub(element, '_getOffsetHeight', () => 50);
1807 sandbox.stub(window, 'matchMedia', () => { return {matches: false}; });
1808 element._relatedChangesLoading = false;
1809 assert.isTrue(element._relatedChangesCollapsed);
1810 assert.isTrue(
1811 element.$.relatedChanges.classList.contains('collapsed'));
1812 MockInteractions.tap(element.$.relatedChangesToggleButton);
1813 assert.isFalse(element._relatedChangesCollapsed);
1814 assert.isFalse(
1815 element.$.relatedChanges.classList.contains('collapsed'));
1816 });
1817
1818 test('_updateRelatedChangeMaxHeight without commit toggle', () => {
1819 sandbox.stub(element, '_getOffsetHeight', () => 50);
1820 sandbox.stub(element, '_getLineHeight', () => 12);
1821 sandbox.stub(window, 'matchMedia', () => { return {matches: false}; });
1822
1823 // 50 (existing height) - 30 (extra height) = 20 (adjusted height).
1824 // 20 (max existing height) % 12 (line height) = 6 (remainder).
1825 // 20 (adjusted height) - 8 (remainder) = 12 (max height to set).
1826
1827 element._updateRelatedChangeMaxHeight();
1828 assert.equal(getCustomCssValue('--relation-chain-max-height'),
1829 '12px');
1830 assert.equal(getCustomCssValue('--related-change-btn-top-padding'),
1831 '');
1832 });
1833
1834 test('_updateRelatedChangeMaxHeight with commit toggle', () => {
1835 element._latestCommitMessage = _.times(31, String).join('\n');
1836 sandbox.stub(element, '_getOffsetHeight', () => 50);
1837 sandbox.stub(element, '_getLineHeight', () => 12);
1838 sandbox.stub(window, 'matchMedia', () => { return {matches: false}; });
1839
1840 // 50 (existing height) % 12 (line height) = 2 (remainder).
1841 // 50 (existing height) - 2 (remainder) = 48 (max height to set).
1842
1843 element._updateRelatedChangeMaxHeight();
1844 assert.equal(getCustomCssValue('--relation-chain-max-height'),
1845 '48px');
1846 assert.equal(getCustomCssValue('--related-change-btn-top-padding'),
1847 '2px');
1848 });
1849
1850 test('_updateRelatedChangeMaxHeight in small screen mode', () => {
1851 element._latestCommitMessage = _.times(31, String).join('\n');
1852 sandbox.stub(element, '_getOffsetHeight', () => 50);
1853 sandbox.stub(element, '_getLineHeight', () => 12);
1854 sandbox.stub(window, 'matchMedia', () => { return {matches: true}; });
1855
1856 element._updateRelatedChangeMaxHeight();
1857
1858 // 400 (new height) % 12 (line height) = 4 (remainder).
1859 // 400 (new height) - 4 (remainder) = 396.
1860
1861 assert.equal(getCustomCssValue('--relation-chain-max-height'),
1862 '396px');
1863 });
1864
1865 test('_updateRelatedChangeMaxHeight in medium screen mode', () => {
1866 element._latestCommitMessage = _.times(31, String).join('\n');
1867 sandbox.stub(element, '_getOffsetHeight', () => 50);
1868 sandbox.stub(element, '_getLineHeight', () => 12);
1869 sandbox.stub(window, 'matchMedia', () => {
1870 if (window.matchMedia.lastCall.args[0] === '(max-width: 75em)') {
1871 return {matches: true};
1872 } else {
1873 return {matches: false};
1874 }
1875 });
1876
1877 // 100 (new height) % 12 (line height) = 4 (remainder).
1878 // 100 (new height) - 4 (remainder) = 96.
1879 element._updateRelatedChangeMaxHeight();
1880 assert.equal(getCustomCssValue('--relation-chain-max-height'),
1881 '96px');
1882 });
1883
1884 suite('update checks', () => {
1885 setup(() => {
1886 sandbox.spy(element, '_startUpdateCheckTimer');
1887 sandbox.stub(element, 'async', f => {
1888 // Only fire the async callback one time.
1889 if (element.async.callCount > 1) { return; }
1890 f.call(element);
1891 });
1892 });
1893
1894 test('_startUpdateCheckTimer negative delay', () => {
1895 sandbox.stub(element, 'fetchChangeUpdates');
1896
1897 element._serverConfig = {change: {update_delay: -1}};
1898
1899 assert.isTrue(element._startUpdateCheckTimer.called);
1900 assert.isFalse(element.fetchChangeUpdates.called);
1901 });
1902
1903 test('_startUpdateCheckTimer up-to-date', () => {
1904 sandbox.stub(element, 'fetchChangeUpdates',
1905 () => Promise.resolve({isLatest: true}));
1906
1907 element._serverConfig = {change: {update_delay: 12345}};
1908
1909 assert.isTrue(element._startUpdateCheckTimer.called);
1910 assert.isTrue(element.fetchChangeUpdates.called);
1911 assert.equal(element.async.lastCall.args[1], 12345 * 1000);
1912 });
1913
1914 test('_startUpdateCheckTimer out-of-date shows an alert', done => {
1915 sandbox.stub(element, 'fetchChangeUpdates',
1916 () => Promise.resolve({isLatest: false}));
1917 element.addEventListener('show-alert', e => {
1918 assert.equal(e.detail.message,
1919 'A newer patch set has been uploaded');
1920 done();
1921 });
1922 element._serverConfig = {change: {update_delay: 12345}};
1923 });
1924
1925 test('_startUpdateCheckTimer new status shows an alert', done => {
1926 sandbox.stub(element, 'fetchChangeUpdates')
1927 .returns(Promise.resolve({
1928 isLatest: true,
Dmitrii Filippov4e4522e2020-05-06 12:50:49 +02001929 newStatus: ChangeStatus.MERGED,
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01001930 }));
1931 element.addEventListener('show-alert', e => {
1932 assert.equal(e.detail.message, 'This change has been merged');
1933 done();
1934 });
1935 element._serverConfig = {change: {update_delay: 12345}};
1936 });
1937
1938 test('_startUpdateCheckTimer new messages shows an alert', done => {
1939 sandbox.stub(element, 'fetchChangeUpdates')
1940 .returns(Promise.resolve({
1941 isLatest: true,
1942 newMessages: true,
1943 }));
1944 element.addEventListener('show-alert', e => {
1945 assert.equal(e.detail.message,
1946 'There are new messages on this change');
1947 done();
1948 });
1949 element._serverConfig = {change: {update_delay: 12345}};
1950 });
1951 });
1952
1953 test('canStartReview computation', () => {
1954 const change1 = {};
1955 const change2 = {
1956 actions: {
1957 ready: {
1958 enabled: true,
1959 },
1960 },
1961 };
1962 const change3 = {
1963 actions: {
1964 ready: {
1965 label: 'Ready for Review',
1966 },
1967 },
1968 };
1969 assert.isFalse(element._computeCanStartReview(change1));
1970 assert.isTrue(element._computeCanStartReview(change2));
1971 assert.isFalse(element._computeCanStartReview(change3));
1972 });
1973 });
1974
1975 test('header class computation', () => {
1976 assert.equal(element._computeHeaderClass(), 'header');
1977 assert.equal(element._computeHeaderClass(true), 'header editMode');
1978 });
1979
1980 test('_maybeScrollToMessage', done => {
1981 flush(() => {
1982 const scrollStub = sandbox.stub(element.messagesList,
1983 'scrollToMessage');
1984
1985 element._maybeScrollToMessage('');
1986 assert.isFalse(scrollStub.called);
1987 element._maybeScrollToMessage('message');
1988 assert.isFalse(scrollStub.called);
1989 element._maybeScrollToMessage('#message-TEST');
1990 assert.isTrue(scrollStub.called);
1991 assert.equal(scrollStub.lastCall.args[0], 'TEST');
1992 done();
1993 });
1994 });
1995
1996 test('topic update reloads related changes', () => {
1997 sandbox.stub(element.$.relatedChanges, 'reload');
1998 element.dispatchEvent(new CustomEvent('topic-changed'));
1999 assert.isTrue(element.$.relatedChanges.reload.calledOnce);
2000 });
2001
2002 test('_computeEditMode', () => {
2003 const callCompute = (range, params) =>
2004 element._computeEditMode({base: range}, {base: params});
2005 assert.isFalse(callCompute({}, {}));
2006 assert.isTrue(callCompute({}, {edit: true}));
2007 assert.isFalse(callCompute({basePatchNum: 'PARENT', patchNum: 1}, {}));
2008 assert.isFalse(callCompute({basePatchNum: 'edit', patchNum: 1}, {}));
2009 assert.isTrue(callCompute({basePatchNum: 1, patchNum: 'edit'}, {}));
2010 });
2011
2012 test('_processEdit', () => {
2013 element._patchRange = {};
2014 const change = {
2015 current_revision: 'foo',
2016 revisions: {foo: {commit: {}, actions: {cherrypick: {enabled: true}}}},
2017 };
2018 let mockChange;
2019
2020 // With no edit, mockChange should be unmodified.
2021 element._processEdit(mockChange = _.cloneDeep(change), null);
2022 assert.deepEqual(mockChange, change);
2023
2024 // When edit is not based on the latest PS, current_revision should be
2025 // unmodified.
2026 const edit = {
2027 base_patch_set_number: 1,
2028 commit: {commit: 'bar'},
2029 fetch: true,
2030 };
2031 element._processEdit(mockChange = _.cloneDeep(change), edit);
2032 assert.notDeepEqual(mockChange, change);
2033 assert.equal(mockChange.revisions.bar._number, element.EDIT_NAME);
2034 assert.equal(mockChange.current_revision, change.current_revision);
2035 assert.deepEqual(mockChange.revisions.bar.commit, {commit: 'bar'});
2036 assert.notOk(mockChange.revisions.bar.actions);
2037
2038 edit.base_revision = 'foo';
2039 element._processEdit(mockChange = _.cloneDeep(change), edit);
2040 assert.notDeepEqual(mockChange, change);
2041 assert.equal(mockChange.current_revision, 'bar');
2042 assert.deepEqual(mockChange.revisions.bar.actions,
2043 mockChange.revisions.foo.actions);
2044
2045 // If _patchRange.patchNum is defined, do not load edit.
2046 element._patchRange.patchNum = 'baz';
2047 change.current_revision = 'baz';
2048 element._processEdit(mockChange = _.cloneDeep(change), edit);
2049 assert.equal(element._patchRange.patchNum, 'baz');
2050 assert.notOk(mockChange.revisions.bar.actions);
2051 });
2052
2053 test('file-action-tap handling', () => {
2054 element._patchRange = {
2055 basePatchNum: 'PARENT',
2056 patchNum: 1,
2057 };
2058 const fileList = element.$.fileList;
2059 const Actions = GrEditConstants.Actions;
2060 const controls = element.$.fileListHeader.$.editControls;
2061 sandbox.stub(controls, 'openDeleteDialog');
2062 sandbox.stub(controls, 'openRenameDialog');
2063 sandbox.stub(controls, 'openRestoreDialog');
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +02002064 sandbox.stub(GerritNav, 'getEditUrlForDiff');
2065 sandbox.stub(GerritNav, 'navigateToRelativeUrl');
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01002066
2067 // Delete
2068 fileList.dispatchEvent(new CustomEvent('file-action-tap', {
2069 detail: {action: Actions.DELETE.id, path: 'foo'},
2070 bubbles: true,
2071 composed: true,
2072 }));
2073 flushAsynchronousOperations();
2074
2075 assert.isTrue(controls.openDeleteDialog.called);
2076 assert.equal(controls.openDeleteDialog.lastCall.args[0], 'foo');
2077
2078 // Restore
2079 fileList.dispatchEvent(new CustomEvent('file-action-tap', {
2080 detail: {action: Actions.RESTORE.id, path: 'foo'},
2081 bubbles: true,
2082 composed: true,
2083 }));
2084 flushAsynchronousOperations();
2085
2086 assert.isTrue(controls.openRestoreDialog.called);
2087 assert.equal(controls.openRestoreDialog.lastCall.args[0], 'foo');
2088
2089 // Rename
2090 fileList.dispatchEvent(new CustomEvent('file-action-tap', {
2091 detail: {action: Actions.RENAME.id, path: 'foo'},
2092 bubbles: true,
2093 composed: true,
2094 }));
2095 flushAsynchronousOperations();
2096
2097 assert.isTrue(controls.openRenameDialog.called);
2098 assert.equal(controls.openRenameDialog.lastCall.args[0], 'foo');
2099
2100 // Open
2101 fileList.dispatchEvent(new CustomEvent('file-action-tap', {
2102 detail: {action: Actions.OPEN.id, path: 'foo'},
2103 bubbles: true,
2104 composed: true,
2105 }));
2106 flushAsynchronousOperations();
2107
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +02002108 assert.isTrue(GerritNav.getEditUrlForDiff.called);
2109 assert.equal(GerritNav.getEditUrlForDiff.lastCall.args[1], 'foo');
2110 assert.equal(GerritNav.getEditUrlForDiff.lastCall.args[2], '1');
2111 assert.isTrue(GerritNav.navigateToRelativeUrl.called);
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01002112 });
2113
2114 test('_selectedRevision updates when patchNum is changed', () => {
2115 const revision1 = {_number: 1, commit: {parents: []}};
2116 const revision2 = {_number: 2, commit: {parents: []}};
2117 sandbox.stub(element.$.restAPI, 'getChangeDetail').returns(
2118 Promise.resolve({
2119 revisions: {
2120 aaa: revision1,
2121 bbb: revision2,
2122 },
2123 labels: {},
2124 actions: {},
2125 current_revision: 'bbb',
2126 change_id: 'loremipsumdolorsitamet',
2127 }));
2128 sandbox.stub(element, '_getEdit').returns(Promise.resolve());
2129 sandbox.stub(element, '_getPreferences').returns(Promise.resolve({}));
2130 element._patchRange = {patchNum: '2'};
2131 return element._getChangeDetail().then(() => {
2132 assert.strictEqual(element._selectedRevision, revision2);
2133
2134 element.set('_patchRange.patchNum', '1');
2135 assert.strictEqual(element._selectedRevision, revision1);
2136 });
2137 });
2138
2139 test('_selectedRevision is assigned when patchNum is edit', () => {
2140 const revision1 = {_number: 1, commit: {parents: []}};
2141 const revision2 = {_number: 2, commit: {parents: []}};
2142 const revision3 = {_number: 'edit', commit: {parents: []}};
2143 sandbox.stub(element.$.restAPI, 'getChangeDetail').returns(
2144 Promise.resolve({
2145 revisions: {
2146 aaa: revision1,
2147 bbb: revision2,
2148 ccc: revision3,
2149 },
2150 labels: {},
2151 actions: {},
2152 current_revision: 'ccc',
2153 change_id: 'loremipsumdolorsitamet',
2154 }));
2155 sandbox.stub(element, '_getEdit').returns(Promise.resolve());
2156 sandbox.stub(element, '_getPreferences').returns(Promise.resolve({}));
2157 element._patchRange = {patchNum: 'edit'};
2158 return element._getChangeDetail().then(() => {
2159 assert.strictEqual(element._selectedRevision, revision3);
2160 });
2161 });
2162
2163 test('_sendShowChangeEvent', () => {
2164 element._change = {labels: {}};
2165 element._patchRange = {patchNum: 4};
2166 element._mergeable = true;
2167 const showStub = sandbox.stub(element.$.jsAPI, 'handleEvent');
2168 element._sendShowChangeEvent();
2169 assert.isTrue(showStub.calledOnce);
2170 assert.equal(
2171 showStub.lastCall.args[0], element.$.jsAPI.EventType.SHOW_CHANGE);
2172 assert.deepEqual(showStub.lastCall.args[1], {
2173 change: {labels: {}},
2174 patchNum: 4,
2175 info: {mergeable: true},
2176 });
2177 });
2178
2179 suite('_handleEditTap', () => {
2180 let fireEdit;
2181
2182 setup(() => {
2183 fireEdit = () => {
2184 element.$.actions.dispatchEvent(new CustomEvent('edit-tap'));
2185 };
2186 navigateToChangeStub.restore();
2187
2188 element._change = {revisions: {rev1: {_number: 1}}};
2189 });
2190
2191 test('edit exists in revisions', done => {
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +02002192 sandbox.stub(GerritNav, 'navigateToChange', (...args) => {
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01002193 assert.equal(args.length, 2);
2194 assert.equal(args[1], element.EDIT_NAME); // patchNum
2195 done();
2196 });
2197
2198 element.set('_change.revisions.rev2', {_number: element.EDIT_NAME});
2199 flushAsynchronousOperations();
2200
2201 fireEdit();
2202 });
2203
2204 test('no edit exists in revisions, non-latest patchset', done => {
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +02002205 sandbox.stub(GerritNav, 'navigateToChange', (...args) => {
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01002206 assert.equal(args.length, 4);
2207 assert.equal(args[1], 1); // patchNum
2208 assert.equal(args[3], true); // opt_isEdit
2209 done();
2210 });
2211
2212 element.set('_change.revisions.rev2', {_number: 2});
2213 element._patchRange = {patchNum: 1};
2214 flushAsynchronousOperations();
2215
2216 fireEdit();
2217 });
2218
2219 test('no edit exists in revisions, latest patchset', done => {
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +02002220 sandbox.stub(GerritNav, 'navigateToChange', (...args) => {
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01002221 assert.equal(args.length, 4);
2222 // No patch should be specified when patchNum == latest.
2223 assert.isNotOk(args[1]); // patchNum
2224 assert.equal(args[3], true); // opt_isEdit
2225 done();
2226 });
2227
2228 element.set('_change.revisions.rev2', {_number: 2});
2229 element._patchRange = {patchNum: 2};
2230 flushAsynchronousOperations();
2231
2232 fireEdit();
2233 });
2234 });
2235
2236 test('_handleStopEditTap', done => {
2237 sandbox.stub(element.$.metadata, '_computeLabelNames');
2238 navigateToChangeStub.restore();
2239 sandbox.stub(element, 'computeLatestPatchNum').returns(1);
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +02002240 sandbox.stub(GerritNav, 'navigateToChange', (...args) => {
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01002241 assert.equal(args.length, 2);
2242 assert.equal(args[1], 1); // patchNum
2243 done();
2244 });
2245
2246 element._patchRange = {patchNum: 1};
2247 element.$.actions.dispatchEvent(new CustomEvent('stop-edit-tap',
2248 {bubbles: false}));
2249 });
2250
2251 suite('plugin endpoints', () => {
2252 test('endpoint params', done => {
2253 element._change = {labels: {}};
2254 element._selectedRevision = {};
2255 let hookEl;
2256 let plugin;
Dmitrii Filippov5f25dc02020-04-07 18:49:00 +02002257 pluginApi.install(
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01002258 p => {
2259 plugin = p;
2260 plugin.hook('change-view-integration').getLastAttached()
2261 .then(
2262 el => hookEl = el);
2263 },
2264 '0.1',
2265 'http://some/plugins/url.html');
2266 flush(() => {
2267 assert.strictEqual(hookEl.plugin, plugin);
2268 assert.strictEqual(hookEl.change, element._change);
2269 assert.strictEqual(hookEl.revision, element._selectedRevision);
2270 done();
2271 });
2272 });
2273 });
2274
2275 suite('_getMergeability', () => {
2276 let getMergeableStub;
2277
2278 setup(() => {
2279 element._change = {labels: {}};
2280 getMergeableStub = sandbox.stub(element.$.restAPI, 'getMergeable')
2281 .returns(Promise.resolve({mergeable: true}));
2282 });
2283
2284 test('merged change', () => {
2285 element._mergeable = null;
Dmitrii Filippov4e4522e2020-05-06 12:50:49 +02002286 element._change.status = ChangeStatus.MERGED;
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01002287 return element._getMergeability().then(() => {
2288 assert.isFalse(element._mergeable);
2289 assert.isFalse(getMergeableStub.called);
2290 });
2291 });
2292
2293 test('abandoned change', () => {
2294 element._mergeable = null;
Dmitrii Filippov4e4522e2020-05-06 12:50:49 +02002295 element._change.status = ChangeStatus.ABANDONED;
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01002296 return element._getMergeability().then(() => {
2297 assert.isFalse(element._mergeable);
2298 assert.isFalse(getMergeableStub.called);
2299 });
2300 });
2301
2302 test('open change', () => {
2303 element._mergeable = null;
2304 return element._getMergeability().then(() => {
2305 assert.isTrue(element._mergeable);
2306 assert.isTrue(getMergeableStub.called);
2307 });
2308 });
2309 });
2310
2311 test('_paramsChanged sets in projectLookup', () => {
2312 sandbox.stub(element.$.relatedChanges, 'reload');
2313 sandbox.stub(element, '_reload').returns(Promise.resolve());
2314 const setStub = sandbox.stub(element.$.restAPI, 'setInProjectLookup');
2315 element._paramsChanged({
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +02002316 view: GerritNav.View.CHANGE,
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01002317 changeNum: 101,
2318 project: 'test-project',
2319 });
2320 assert.isTrue(setStub.calledOnce);
2321 assert.isTrue(setStub.calledWith(101, 'test-project'));
2322 });
2323
2324 test('_handleToggleStar called when star is tapped', () => {
2325 element._change = {
2326 owner: {_account_id: 1},
2327 starred: false,
2328 };
2329 element._loggedIn = true;
2330 const stub = sandbox.stub(element, '_handleToggleStar');
2331 flushAsynchronousOperations();
2332
2333 MockInteractions.tap(element.$.changeStar.shadowRoot
2334 .querySelector('button'));
2335 assert.isTrue(stub.called);
2336 });
2337
2338 suite('gr-reporting tests', () => {
2339 setup(() => {
2340 element._patchRange = {
2341 basePatchNum: 'PARENT',
2342 patchNum: 1,
2343 };
2344 sandbox.stub(element, '_getChangeDetail').returns(Promise.resolve());
2345 sandbox.stub(element, '_getProjectConfig').returns(Promise.resolve());
2346 sandbox.stub(element, '_reloadComments').returns(Promise.resolve());
2347 sandbox.stub(element, '_getMergeability').returns(Promise.resolve());
2348 sandbox.stub(element, '_getLatestCommitMessage')
2349 .returns(Promise.resolve());
2350 });
2351
2352 test('don\'t report changedDisplayed on reply', done => {
2353 const changeDisplayStub =
Milutin Kristoficda88b332020-03-24 10:19:12 +01002354 sandbox.stub(element.reporting, 'changeDisplayed');
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01002355 const changeFullyLoadedStub =
Milutin Kristoficda88b332020-03-24 10:19:12 +01002356 sandbox.stub(element.reporting, 'changeFullyLoaded');
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01002357 element._handleReplySent();
2358 flush(() => {
2359 assert.isFalse(changeDisplayStub.called);
2360 assert.isFalse(changeFullyLoadedStub.called);
2361 done();
2362 });
2363 });
2364
2365 test('report changedDisplayed on _paramsChanged', done => {
2366 const changeDisplayStub =
Milutin Kristoficda88b332020-03-24 10:19:12 +01002367 sandbox.stub(element.reporting, 'changeDisplayed');
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01002368 const changeFullyLoadedStub =
Milutin Kristoficda88b332020-03-24 10:19:12 +01002369 sandbox.stub(element.reporting, 'changeFullyLoaded');
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01002370 element._paramsChanged({
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +02002371 view: GerritNav.View.CHANGE,
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01002372 changeNum: 101,
2373 project: 'test-project',
2374 });
2375 flush(() => {
2376 assert.isTrue(changeDisplayStub.called);
2377 assert.isTrue(changeFullyLoadedStub.called);
2378 done();
2379 });
2380 });
2381 });
2382});