blob: d8d49cf5a52ef25b4c59705c2b99b4cd6d92eaca [file] [log] [blame]
Andrew Bonventref8b026d2015-12-09 17:55:54 -05001<!DOCTYPE html>
2<!--
Dave Borowitz8cdc76b2018-03-26 10:04:27 -04003@license
Andrew Bonventref8b026d2015-12-09 17:55:54 -05004Copyright (C) 2015 The Android Open Source Project
5
6Licensed under the Apache License, Version 2.0 (the "License");
7you may not use this file except in compliance with the License.
8You may obtain a copy of the License at
9
10http://www.apache.org/licenses/LICENSE-2.0
11
12Unless required by applicable law or agreed to in writing, software
13distributed under the License is distributed on an "AS IS" BASIS,
14WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15See the License for the specific language governing permissions and
16limitations under the License.
17-->
18
19<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
Andrew Bonventre882043f2016-02-22 18:12:27 -050020<title>gr-reply-dialog</title>
Ole Rehmsen62909352019-05-16 16:10:33 +020021<script src="/test/common-test-setup.js"></script>
22<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
Andrew Bonventref8b026d2015-12-09 17:55:54 -050023
Ole Rehmsenecf0b782019-05-16 11:29:39 +020024<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
Ole Rehmsen31640742019-05-16 11:24:47 +020025<script src="/bower_components/web-component-tester/browser.js"></script>
Mike Samuele07c4b22017-06-02 13:08:19 -040026<link rel="import" href="../../../test/common-test-setup.html"/>
Andrew Bonventre78792e82016-03-04 17:48:22 -050027<link rel="import" href="gr-reply-dialog.html">
Andrew Bonventref8b026d2015-12-09 17:55:54 -050028
Viktar Donich29e1ce52017-03-28 17:02:44 -070029<script>void(0);</script>
30
Andrew Bonventref8b026d2015-12-09 17:55:54 -050031<test-fixture id="basic">
32 <template>
Andrew Bonventre882043f2016-02-22 18:12:27 -050033 <gr-reply-dialog></gr-reply-dialog>
Andrew Bonventref8b026d2015-12-09 17:55:54 -050034 </template>
35</test-fixture>
36
37<script>
Ben Rohlfs9bdbceb2019-07-03 17:06:52 +020038 function cloneableResponse(status, text) {
39 return {
40 ok: false,
41 status,
42 text() {
43 return Promise.resolve(text);
44 },
45 clone() {
46 return {
47 ok: false,
48 status,
49 text() {
50 return Promise.resolve(text);
51 },
52 };
53 },
54 };
55 }
56
Kasper Nilssonad908b12017-05-11 11:26:10 -070057 suite('gr-reply-dialog tests', () => {
58 let element;
59 let changeNum;
60 let patchNum;
Wyatt Allen4f4b3a72016-07-28 12:05:53 -070061
Kasper Nilssonad908b12017-05-11 11:26:10 -070062 let sandbox;
63 let getDraftCommentStub;
64 let setDraftCommentStub;
65 let eraseDraftCommentStub;
Andrew Bonventref8b026d2015-12-09 17:55:54 -050066
Kasper Nilssonad908b12017-05-11 11:26:10 -070067 let lastId = 0;
68 const makeAccount = function() { return {_account_id: lastId++}; };
69 const makeGroup = function() { return {id: lastId++}; };
Kasper Nilssona8271552017-01-17 11:54:40 -080070
Kasper Nilssonad908b12017-05-11 11:26:10 -070071 setup(() => {
Wyatt Allen4f4b3a72016-07-28 12:05:53 -070072 sandbox = sinon.sandbox.create();
73
74 changeNum = 42;
75 patchNum = 1;
76
Andrew Bonventre5c09ba92016-05-02 17:31:23 -040077 stub('gr-rest-api-interface', {
Kasper Nilssonad908b12017-05-11 11:26:10 -070078 getConfig() { return Promise.resolve({}); },
79 getAccount() { return Promise.resolve({}); },
Wyatt Allen7076c1b2018-01-16 15:15:11 -080080 getChange() { return Promise.resolve({}); },
Kasper Nilssonaa746bd2017-09-18 22:12:30 -070081 getChangeSuggestedReviewers() { return Promise.resolve([]); },
Andrew Bonventrecfacb802016-03-29 14:06:39 -040082 });
Wyatt Allen4f4b3a72016-07-28 12:05:53 -070083
Andrew Bonventref8b026d2015-12-09 17:55:54 -050084 element = fixture('basic');
Kasper Nilsson352b68f2016-10-11 11:51:35 -070085 element.change = {
86 _number: changeNum,
87 labels: {
Kasper Nilssonad908b12017-05-11 11:26:10 -070088 'Verified': {
Kasper Nilsson352b68f2016-10-11 11:51:35 -070089 values: {
90 '-1': 'Fails',
91 ' 0': 'No score',
92 '+1': 'Verified',
93 },
94 default_value: 0,
Andrew Bonventref8b026d2015-12-09 17:55:54 -050095 },
Kasper Nilsson352b68f2016-10-11 11:51:35 -070096 'Code-Review': {
97 values: {
98 '-2': 'Do not submit',
99 '-1': 'I would prefer that you didn\'t submit this',
100 ' 0': 'No score',
101 '+1': 'Looks good to me, but someone else must approve',
102 '+2': 'Looks good to me, approved',
103 },
104 default_value: 0,
105 },
Andrew Bonventref8b026d2015-12-09 17:55:54 -0500106 },
Andrew Bonventref8b026d2015-12-09 17:55:54 -0500107 };
Kasper Nilsson352b68f2016-10-11 11:51:35 -0700108 element.patchNum = patchNum;
Andrew Bonventref8b026d2015-12-09 17:55:54 -0500109 element.permittedLabels = {
110 'Code-Review': [
111 '-1',
112 ' 0',
Kasper Nilsson352b68f2016-10-11 11:51:35 -0700113 '+1',
Andrew Bonventref8b026d2015-12-09 17:55:54 -0500114 ],
Kasper Nilssonad908b12017-05-11 11:26:10 -0700115 'Verified': [
Andrew Bonventref8b026d2015-12-09 17:55:54 -0500116 '-1',
117 ' 0',
Kasper Nilsson352b68f2016-10-11 11:51:35 -0700118 '+1',
119 ],
Andrew Bonventref8b026d2015-12-09 17:55:54 -0500120 };
121
Wyatt Allen4f4b3a72016-07-28 12:05:53 -0700122 getDraftCommentStub = sandbox.stub(element.$.storage, 'getDraftComment');
123 setDraftCommentStub = sandbox.stub(element.$.storage, 'setDraftComment');
124 eraseDraftCommentStub = sandbox.stub(element.$.storage,
125 'eraseDraftComment');
126
Wyatt Allenebfe8a72017-11-16 13:32:38 -0800127 sandbox.stub(element, 'fetchChangeUpdates')
Wyatt Allendc554962017-11-16 11:52:50 -0800128 .returns(Promise.resolve({isLatest: true}));
Wyatt Allen6cf58752017-04-24 16:59:07 +0200129
Andrew Bonventref8b026d2015-12-09 17:55:54 -0500130 // Allow the elements created by dom-repeat to be stamped.
131 flushAsynchronousOperations();
132 });
133
Kasper Nilssonad908b12017-05-11 11:26:10 -0700134 teardown(() => {
Wyatt Allen4f4b3a72016-07-28 12:05:53 -0700135 sandbox.restore();
136 });
137
Logan Hanks02c781e2017-07-18 14:18:01 -0700138 function stubSaveReview(jsonResponseProducer) {
139 return sandbox.stub(element, '_saveReview', review => {
Logan Hanks3e0917e2017-07-19 08:32:25 -0700140 return new Promise((resolve, reject) => {
141 try {
142 const result = jsonResponseProducer(review) || {};
143 const resultStr =
144 element.$.restAPI.JSON_PREFIX + JSON.stringify(result);
145 resolve({
146 ok: true,
147 text() {
148 return Promise.resolve(resultStr);
149 },
150 });
151 } catch (err) {
152 reject(err);
153 }
Logan Hanks02c781e2017-07-18 14:18:01 -0700154 });
155 });
156 }
157
Kasper Nilssonad908b12017-05-11 11:26:10 -0700158 test('default to publishing drafts with reply', done => {
Becky Siegel9efcd572017-03-23 08:28:03 -0700159 // Async tick is needed because iron-selector content is distributed and
160 // distributed content requires an observer to be set up.
161 // Note: Double flush seems to be needed in Safari. {@see Issue 4963}.
Kasper Nilssonad908b12017-05-11 11:26:10 -0700162 flush(() => {
163 flush(() => {
Becky Siegel9efcd572017-03-23 08:28:03 -0700164 element.draft = 'I wholeheartedly disapprove';
165
Logan Hanks02c781e2017-07-18 14:18:01 -0700166 stubSaveReview(review => {
Becky Siegel9efcd572017-03-23 08:28:03 -0700167 assert.deepEqual(review, {
168 drafts: 'PUBLISH_ALL_REVISIONS',
Viktar Donich51bdb8d2017-07-14 11:39:24 -0700169 labels: {
170 'Code-Review': 0,
171 'Verified': 0,
172 },
Becky Siegel9efcd572017-03-23 08:28:03 -0700173 message: 'I wholeheartedly disapprove',
174 reviewers: [],
175 });
176 assert.isFalse(element.$.commentList.hidden);
177 done();
Becky Siegel9efcd572017-03-23 08:28:03 -0700178 });
179
180 // This is needed on non-Blink engines most likely due to the ways in
181 // which the dom-repeat elements are stamped.
Kasper Nilssonad908b12017-05-11 11:26:10 -0700182 flush(() => {
Becky Siegel9efcd572017-03-23 08:28:03 -0700183 MockInteractions.tap(element.$$('.send'));
184 });
185 });
186 });
187 });
188
Kasper Nilssonad908b12017-05-11 11:26:10 -0700189 test('keep drafts with reply', done => {
Becky Siegel9efcd572017-03-23 08:28:03 -0700190 MockInteractions.tap(element.$$('#includeComments'));
191 assert.equal(element._includeComments, false);
192
193 // Async tick is needed because iron-selector content is distributed and
194 // distributed content requires an observer to be set up.
195 // Note: Double flush seems to be needed in Safari. {@see Issue 4963}.
Kasper Nilssonad908b12017-05-11 11:26:10 -0700196 flush(() => {
197 flush(() => {
Becky Siegel9efcd572017-03-23 08:28:03 -0700198 element.draft = 'I wholeheartedly disapprove';
199
Logan Hanks02c781e2017-07-18 14:18:01 -0700200 stubSaveReview(review => {
Becky Siegel9efcd572017-03-23 08:28:03 -0700201 assert.deepEqual(review, {
202 drafts: 'KEEP',
Viktar Donich51bdb8d2017-07-14 11:39:24 -0700203 labels: {
204 'Code-Review': 0,
205 'Verified': 0,
206 },
Becky Siegel9efcd572017-03-23 08:28:03 -0700207 message: 'I wholeheartedly disapprove',
208 reviewers: [],
209 });
210 assert.isTrue(element.$.commentList.hidden);
211 done();
Becky Siegel9efcd572017-03-23 08:28:03 -0700212 });
213
214 // This is needed on non-Blink engines most likely due to the ways in
215 // which the dom-repeat elements are stamped.
Kasper Nilssonad908b12017-05-11 11:26:10 -0700216 flush(() => {
Becky Siegel9efcd572017-03-23 08:28:03 -0700217 MockInteractions.tap(element.$$('.send'));
218 });
219 });
220 });
221 });
222
Kasper Nilssonad908b12017-05-11 11:26:10 -0700223 test('label picker', done => {
Becky Siegela499e3d2017-04-06 16:57:13 -0700224 element.draft = 'I wholeheartedly disapprove';
Logan Hanks02c781e2017-07-18 14:18:01 -0700225 stubSaveReview(review => {
Becky Siegela499e3d2017-04-06 16:57:13 -0700226 assert.deepEqual(review, {
227 drafts: 'PUBLISH_ALL_REVISIONS',
228 labels: {
229 'Code-Review': -1,
230 'Verified': -1,
231 },
232 message: 'I wholeheartedly disapprove',
233 reviewers: [],
Urs Wolferb6036942016-03-06 14:57:02 +0100234 });
Becky Siegela499e3d2017-04-06 16:57:13 -0700235 });
236
Kasper Nilssonad908b12017-05-11 11:26:10 -0700237 sandbox.stub(element.$.labelScores, 'getLabelValues', () => {
Becky Siegela499e3d2017-04-06 16:57:13 -0700238 return {
239 'Code-Review': -1,
240 'Verified': -1,
241 };
242 });
243
Kasper Nilssonad908b12017-05-11 11:26:10 -0700244 element.addEventListener('send', () => {
Logan Hanks3e0917e2017-07-19 08:32:25 -0700245 // Flush to ensure properties are updated.
246 flush(() => {
247 assert.isFalse(element.disabled,
248 'Element should be enabled when done sending reply.');
249 assert.equal(element.draft.length, 0);
250 done();
251 });
Becky Siegela499e3d2017-04-06 16:57:13 -0700252 });
253
254 // This is needed on non-Blink engines most likely due to the ways in
255 // which the dom-repeat elements are stamped.
Kasper Nilssonad908b12017-05-11 11:26:10 -0700256 flush(() => {
Becky Siegela499e3d2017-04-06 16:57:13 -0700257 MockInteractions.tap(element.$$('.send'));
258 assert.isTrue(element.disabled);
Urs Wolferb6036942016-03-06 14:57:02 +0100259 });
Andrew Bonventref8b026d2015-12-09 17:55:54 -0500260 });
Logan Hanksa008aa92016-07-22 10:31:12 -0700261
Viktar Donich7d23f222017-05-17 09:25:06 -0700262 test('getlabelValue returns value', done => {
263 flush(() => {
Becky Siegel61613932017-05-30 15:09:20 -0700264 element.$$('gr-label-scores').$$(`gr-label-score-row[name="Verified"]`)
265 .setSelectedValue(-1);
Viktar Donich7d23f222017-05-17 09:25:06 -0700266 assert.equal('-1', element.getLabelValue('Verified'));
267 done();
268 });
269 });
270
271 test('getlabelValue when no score is selected', done => {
272 flush(() => {
Becky Siegel97994152017-12-19 14:31:26 -0800273 element.$$('gr-label-scores')
274 .$$(`gr-label-score-row[name="Code-Review"]`).setSelectedValue(-1);
Viktar Donich51bdb8d2017-07-14 11:39:24 -0700275 assert.strictEqual(element.getLabelValue('Verified'), ' 0');
Viktar Donich7d23f222017-05-17 09:25:06 -0700276 done();
277 });
278 });
279
Dmitrii Filippov2b459762019-09-24 16:26:10 +0200280 test('setlabelValue', done => {
Becky Siegel452a5c62017-05-09 09:59:55 -0700281 element._account = {_account_id: 1};
Dmitrii Filippov2b459762019-09-24 16:26:10 +0200282 flush(() => {
283 const label = 'Verified';
284 const value = '+1';
285 element.setLabelValue(label, value);
286
287 const labels = element.$.labelScores.getLabelValues();
288 assert.deepEqual(labels, {
289 'Code-Review': 0,
290 'Verified': 1,
291 });
292 done();
Viktar Donich51bdb8d2017-07-14 11:39:24 -0700293 });
Becky Siegel452a5c62017-05-09 09:59:55 -0700294 });
295
Logan Hanksa008aa92016-07-22 10:31:12 -0700296 function getActiveElement() {
297 return Polymer.IronOverlayManager.deepActiveElement;
298 }
299
300 function isVisible(el) {
301 assert.ok(el);
302 return getComputedStyle(el).getPropertyValue('display') != 'none';
303 }
304
305 function overlayObserver(mode) {
Kasper Nilssonad908b12017-05-11 11:26:10 -0700306 return new Promise(resolve => {
Logan Hanksa008aa92016-07-22 10:31:12 -0700307 function listener() {
308 element.removeEventListener('iron-overlay-' + mode, listener);
309 resolve();
310 }
311 element.addEventListener('iron-overlay-' + mode, listener);
312 });
313 }
314
Dmitrii Filippov2b459762019-09-24 16:26:10 +0200315 function isFocusInsideElement(element) {
316 // In Polymer 2 focused element either <paper-input> or nested
317 // native input <input> element depending on the current focus
318 // in browser window.
319 // For example, the focus is changed if the developer console
320 // get a focus.
321 let activeElement = getActiveElement();
322 while (activeElement) {
323 if (activeElement === element) {
324 return true;
325 }
326 if (activeElement.parentElement) {
327 activeElement = activeElement.parentElement;
328 } else {
329 activeElement = activeElement.getRootNode().host;
330 }
331 }
332 return false;
333 }
334
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800335 function testConfirmationDialog(done, cc) {
Kasper Nilssonad908b12017-05-11 11:26:10 -0700336 const yesButton =
Logan Hanksa008aa92016-07-22 10:31:12 -0700337 element.$$('.reviewerConfirmationButtons gr-button:first-child');
Kasper Nilssonad908b12017-05-11 11:26:10 -0700338 const noButton =
Logan Hanksa008aa92016-07-22 10:31:12 -0700339 element.$$('.reviewerConfirmationButtons gr-button:last-child');
340
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800341 element._ccPendingConfirmation = null;
Logan Hanksa008aa92016-07-22 10:31:12 -0700342 element._reviewerPendingConfirmation = null;
343 flushAsynchronousOperations();
344 assert.isFalse(isVisible(element.$.reviewerConfirmationOverlay));
345
346 // Cause the confirmation dialog to display.
Kasper Nilssonad908b12017-05-11 11:26:10 -0700347 let observer = overlayObserver('opened');
348 const group = {
Logan Hanksa008aa92016-07-22 10:31:12 -0700349 id: 'id',
350 name: 'name',
Logan Hanksa008aa92016-07-22 10:31:12 -0700351 };
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800352 if (cc) {
353 element._ccPendingConfirmation = {
Kasper Nilssonad908b12017-05-11 11:26:10 -0700354 group,
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800355 count: 10,
356 };
357 } else {
358 element._reviewerPendingConfirmation = {
Kasper Nilssonad908b12017-05-11 11:26:10 -0700359 group,
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800360 count: 10,
361 };
362 }
363 flushAsynchronousOperations();
364
365 if (cc) {
366 assert.deepEqual(
367 element._ccPendingConfirmation,
368 element._pendingConfirmationDetails);
369 } else {
370 assert.deepEqual(
371 element._reviewerPendingConfirmation,
372 element._pendingConfirmationDetails);
373 }
Logan Hanksa008aa92016-07-22 10:31:12 -0700374
Kasper Nilssonad908b12017-05-11 11:26:10 -0700375 observer.then(() => {
Logan Hanksa008aa92016-07-22 10:31:12 -0700376 assert.isTrue(isVisible(element.$.reviewerConfirmationOverlay));
377 observer = overlayObserver('closed');
Kasper Nilssonad908b12017-05-11 11:26:10 -0700378 const expected = 'Group name has 10 members';
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800379 assert.notEqual(
380 element.$.reviewerConfirmationOverlay.innerText.indexOf(expected),
381 -1);
Logan Hanksa008aa92016-07-22 10:31:12 -0700382 MockInteractions.tap(noButton); // close the overlay
383 return observer;
Kasper Nilssonad908b12017-05-11 11:26:10 -0700384 }).then(() => {
Logan Hanksa008aa92016-07-22 10:31:12 -0700385 assert.isFalse(isVisible(element.$.reviewerConfirmationOverlay));
386
387 // We should be focused on account entry input.
Dmitrii Filippov2b459762019-09-24 16:26:10 +0200388 assert.isTrue(
389 isFocusInsideElement(element.$.reviewers.$.entry.$.input.$.input));
Logan Hanksa008aa92016-07-22 10:31:12 -0700390
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800391 // No reviewer/CC should have been added.
Tao Zhoub5a5a3c2019-11-04 17:40:19 +0100392 assert.equal(element.$.ccs.additions().length, 0);
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800393 assert.equal(element.$.reviewers.additions().length, 0);
Logan Hanksa008aa92016-07-22 10:31:12 -0700394
395 // Reopen confirmation dialog.
396 observer = overlayObserver('opened');
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800397 if (cc) {
398 element._ccPendingConfirmation = {
Kasper Nilssonad908b12017-05-11 11:26:10 -0700399 group,
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800400 count: 10,
401 };
402 } else {
403 element._reviewerPendingConfirmation = {
Kasper Nilssonad908b12017-05-11 11:26:10 -0700404 group,
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800405 count: 10,
406 };
407 }
Logan Hanksa008aa92016-07-22 10:31:12 -0700408 return observer;
Kasper Nilssonad908b12017-05-11 11:26:10 -0700409 }).then(() => {
Logan Hanksa008aa92016-07-22 10:31:12 -0700410 assert.isTrue(isVisible(element.$.reviewerConfirmationOverlay));
411 observer = overlayObserver('closed');
Becky Siegel8bb09342016-10-17 17:49:31 -0700412 MockInteractions.tap(yesButton); // Confirm the group.
Logan Hanksa008aa92016-07-22 10:31:12 -0700413 return observer;
Kasper Nilssonad908b12017-05-11 11:26:10 -0700414 }).then(() => {
Logan Hanksa008aa92016-07-22 10:31:12 -0700415 assert.isFalse(isVisible(element.$.reviewerConfirmationOverlay));
Kasper Nilssonad908b12017-05-11 11:26:10 -0700416 const additions = cc ?
David Ostrovsky84b1a3a2020-01-04 13:41:34 +0100417 element.$.ccs.additions() :
Thomas Draebing6ff72df2019-12-27 10:58:03 +0100418 element.$.reviewers.additions();
Logan Hanksa008aa92016-07-22 10:31:12 -0700419 assert.deepEqual(
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800420 additions,
Logan Hanksa008aa92016-07-22 10:31:12 -0700421 [
422 {
423 group: {
424 id: 'id',
425 name: 'name',
Logan Hanksa008aa92016-07-22 10:31:12 -0700426 confirmed: true,
427 _group: true,
428 _pendingAdd: true,
429 },
430 },
431 ]);
432
433 // We should be focused on account entry input.
Dmitrii Filippov2b459762019-09-24 16:26:10 +0200434 if (cc) {
435 assert.isTrue(
436 isFocusInsideElement(element.$.ccs.$.entry.$.input.$.input));
437 } else {
438 assert.isTrue(
439 isFocusInsideElement(element.$.reviewers.$.entry.$.input.$.input));
440 }
Logan Hanksa008aa92016-07-22 10:31:12 -0700441 }).then(done);
Kasper Nilssonad908b12017-05-11 11:26:10 -0700442 }
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800443
Kasper Nilssonad908b12017-05-11 11:26:10 -0700444 test('cc confirmation', done => {
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800445 testConfirmationDialog(done, true);
446 });
447
Kasper Nilssonad908b12017-05-11 11:26:10 -0700448 test('reviewer confirmation', done => {
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800449 testConfirmationDialog(done, false);
Logan Hanksa008aa92016-07-22 10:31:12 -0700450 });
Wyatt Allen4f4b3a72016-07-28 12:05:53 -0700451
Kasper Nilssonad908b12017-05-11 11:26:10 -0700452 test('_getStorageLocation', () => {
453 const actual = element._getStorageLocation();
Wyatt Allen4f4b3a72016-07-28 12:05:53 -0700454 assert.equal(actual.changeNum, changeNum);
Wyatt Allene05392a2017-06-01 16:23:51 -0700455 assert.equal(actual.patchNum, '@change');
Wyatt Allen4f4b3a72016-07-28 12:05:53 -0700456 assert.equal(actual.path, '@change');
457 });
458
Becky Siegel97994152017-12-19 14:31:26 -0800459 test('_reviewersMutated when account-text-change is fired from ccs', () => {
Becky Siegel97994152017-12-19 14:31:26 -0800460 flushAsynchronousOperations();
461 assert.isFalse(element._reviewersMutated);
Tao Zhoub5a5a3c2019-11-04 17:40:19 +0100462 assert.isTrue(element.$.ccs.allowAnyInput);
Becky Siegel97994152017-12-19 14:31:26 -0800463 assert.isFalse(element.$$('#reviewers').allowAnyInput);
Tao Zhoub5a5a3c2019-11-04 17:40:19 +0100464 element.$.ccs.dispatchEvent(new CustomEvent('account-text-changed',
Ole Rehmsenc82baba2019-05-16 14:43:01 +0200465 {bubbles: true, composed: true}));
Becky Siegel97994152017-12-19 14:31:26 -0800466 assert.isTrue(element._reviewersMutated);
467 });
468
Kasper Nilssonad908b12017-05-11 11:26:10 -0700469 test('gets draft from storage on open', () => {
470 const storedDraft = 'hello world';
Wyatt Allen4f4b3a72016-07-28 12:05:53 -0700471 getDraftCommentStub.returns({message: storedDraft});
472 element.open();
473 assert.isTrue(getDraftCommentStub.called);
474 assert.equal(element.draft, storedDraft);
475 });
476
Wyatt Allen20592ec2017-11-15 14:26:57 -0800477 test('gets draft from storage even when text is already present', () => {
478 const storedDraft = 'hello world';
479 getDraftCommentStub.returns({message: storedDraft});
480 element.draft = 'foo bar';
481 element.open();
482 assert.isTrue(getDraftCommentStub.called);
483 assert.equal(element.draft, storedDraft);
484 });
485
Kasper Nilssonad908b12017-05-11 11:26:10 -0700486 test('blank if no stored draft', () => {
Wyatt Allen4f4b3a72016-07-28 12:05:53 -0700487 getDraftCommentStub.returns(null);
Wyatt Allen20592ec2017-11-15 14:26:57 -0800488 element.draft = 'foo bar';
Wyatt Allen4f4b3a72016-07-28 12:05:53 -0700489 element.open();
490 assert.isTrue(getDraftCommentStub.called);
491 assert.equal(element.draft, '');
492 });
493
Wyatt Allena77e8512017-11-20 14:06:30 -0800494 test('does not check stored draft when quote is present', () => {
495 const storedDraft = 'hello world';
496 const quote = '> foo bar';
497 getDraftCommentStub.returns({message: storedDraft});
498 element.quote = quote;
499 element.open();
500 assert.isFalse(getDraftCommentStub.called);
501 assert.equal(element.draft, quote);
502 assert.isNotOk(element.quote);
503 });
504
Kasper Nilssonad908b12017-05-11 11:26:10 -0700505 test('updates stored draft on edits', () => {
506 const firstEdit = 'hello';
507 const location = element._getStorageLocation();
Wyatt Allen4f4b3a72016-07-28 12:05:53 -0700508
509 element.draft = firstEdit;
510 element.flushDebouncer('store');
511
512 assert.isTrue(setDraftCommentStub.calledWith(location, firstEdit));
513
514 element.draft = '';
515 element.flushDebouncer('store');
516
517 assert.isTrue(eraseDraftCommentStub.calledWith(location));
518 });
Logan Hanksf1063a22016-07-22 16:05:04 -0700519
Kasper Nilssonad908b12017-05-11 11:26:10 -0700520 test('400 converts to human-readable server-error', done => {
521 sandbox.stub(window, 'fetch', () => {
522 const text = '....{"reviewers":{"id1":{"error":"first error"}},' +
Logan Hanksf1063a22016-07-22 16:05:04 -0700523 '"ccs":{"id2":{"error":"second error"}}}';
Ben Rohlfs9bdbceb2019-07-03 17:06:52 +0200524 return Promise.resolve(cloneableResponse(400, text));
Logan Hanksf1063a22016-07-22 16:05:04 -0700525 });
526
Kasper Nilssonad908b12017-05-11 11:26:10 -0700527 element.addEventListener('server-error', event => {
Logan Hanksf1063a22016-07-22 16:05:04 -0700528 if (event.target !== element) {
529 return;
530 }
Kasper Nilssonad908b12017-05-11 11:26:10 -0700531 event.detail.response.text().then(body => {
Logan Hanksf1063a22016-07-22 16:05:04 -0700532 assert.equal(body, 'first error, second error');
Wyatt Allene013aad2017-05-18 16:49:04 -0700533 done();
Logan Hanksf1063a22016-07-22 16:05:04 -0700534 });
535 });
Andrew Bonventreefd0a532016-08-23 17:04:23 -0400536
537 // Async tick is needed because iron-selector content is distributed and
538 // distributed content requires an observer to be set up.
Wyatt Allene013aad2017-05-18 16:49:04 -0700539 flush(() => { element.send(); });
Logan Hanksa75fb052016-08-01 13:23:38 -0700540 });
Logan Hanksf1063a22016-07-22 16:05:04 -0700541
Ben Rohlfs9bdbceb2019-07-03 17:06:52 +0200542 test('non-json 400 is treated as a normal server-error', done => {
543 sandbox.stub(window, 'fetch', () => {
544 const text = 'Comment validation error!';
545 return Promise.resolve(cloneableResponse(400, text));
546 });
547
548 element.addEventListener('server-error', event => {
549 if (event.target !== element) {
550 return;
551 }
552 event.detail.response.text().then(body => {
553 assert.equal(body, 'Comment validation error!');
554 done();
555 });
556 });
557
558 // Async tick is needed because iron-selector content is distributed and
559 // distributed content requires an observer to be set up.
560 flush(() => { element.send(); });
561 });
562
Kasper Nilssonad908b12017-05-11 11:26:10 -0700563 test('filterReviewerSuggestion', () => {
564 const owner = makeAccount();
565 const reviewer1 = makeAccount();
566 const reviewer2 = makeGroup();
567 const cc1 = makeAccount();
568 const cc2 = makeGroup();
Kasper Nilssond5896712017-06-01 17:59:02 -0700569 let filter = element._filterReviewerSuggestionGenerator(false);
Logan Hanksa75fb052016-08-01 13:23:38 -0700570
571 element._owner = owner;
572 element._reviewers = [reviewer1, reviewer2];
573 element._ccs = [cc1, cc2];
574
Kasper Nilssond5896712017-06-01 17:59:02 -0700575 assert.isTrue(filter({account: makeAccount()}));
576 assert.isTrue(filter({group: makeGroup()}));
Logan Hanksa75fb052016-08-01 13:23:38 -0700577
578 // Owner should be excluded.
Kasper Nilssond5896712017-06-01 17:59:02 -0700579 assert.isFalse(filter({account: owner}));
Logan Hanksa75fb052016-08-01 13:23:38 -0700580
Kasper Nilssond5896712017-06-01 17:59:02 -0700581 // Existing and pending reviewers should be excluded when isCC = false.
582 assert.isFalse(filter({account: reviewer1}));
583 assert.isFalse(filter({group: reviewer2}));
Logan Hanksa75fb052016-08-01 13:23:38 -0700584
Kasper Nilssond5896712017-06-01 17:59:02 -0700585 filter = element._filterReviewerSuggestionGenerator(true);
586
587 // Existing and pending CCs should be excluded when isCC = true;.
588 assert.isFalse(filter({account: cc1}));
589 assert.isFalse(filter({group: cc2}));
Logan Hanksf1063a22016-07-22 16:05:04 -0700590 });
Logan Hanksfc57acc2016-08-05 13:54:50 -0700591
Becky Siegelb7b41452017-11-14 14:35:29 -0800592 test('_focusOn', () => {
Becky Siegelafa82552017-11-17 15:59:39 -0800593 sandbox.spy(element, '_chooseFocusTarget');
Becky Siegelb7b41452017-11-14 14:35:29 -0800594 flushAsynchronousOperations();
595 const textareaStub = sandbox.stub(element.$.textarea, 'async');
596 const reviewerEntryStub = sandbox.stub(element.$.reviewers.focusStart,
597 'async');
Tao Zhoub5a5a3c2019-11-04 17:40:19 +0100598 const ccStub = sandbox.stub(element.$.ccs.focusStart, 'async');
Becky Siegelb7b41452017-11-14 14:35:29 -0800599 element._focusOn();
600 assert.equal(element._chooseFocusTarget.callCount, 1);
Becky Siegelafa82552017-11-17 15:59:39 -0800601 assert.deepEqual(textareaStub.callCount, 1);
Becky Siegelb7b41452017-11-14 14:35:29 -0800602 assert.deepEqual(reviewerEntryStub.callCount, 0);
603 assert.deepEqual(ccStub.callCount, 0);
604
605 element._focusOn(element.FocusTarget.ANY);
606 assert.equal(element._chooseFocusTarget.callCount, 2);
Becky Siegelafa82552017-11-17 15:59:39 -0800607 assert.deepEqual(textareaStub.callCount, 2);
Becky Siegelb7b41452017-11-14 14:35:29 -0800608 assert.deepEqual(reviewerEntryStub.callCount, 0);
609 assert.deepEqual(ccStub.callCount, 0);
610
611 element._focusOn(element.FocusTarget.BODY);
612 assert.equal(element._chooseFocusTarget.callCount, 2);
Becky Siegelafa82552017-11-17 15:59:39 -0800613 assert.deepEqual(textareaStub.callCount, 3);
Becky Siegelb7b41452017-11-14 14:35:29 -0800614 assert.deepEqual(reviewerEntryStub.callCount, 0);
615 assert.deepEqual(ccStub.callCount, 0);
616
617 element._focusOn(element.FocusTarget.REVIEWERS);
618 assert.equal(element._chooseFocusTarget.callCount, 2);
Becky Siegelafa82552017-11-17 15:59:39 -0800619 assert.deepEqual(textareaStub.callCount, 3);
Becky Siegelb7b41452017-11-14 14:35:29 -0800620 assert.deepEqual(reviewerEntryStub.callCount, 1);
621 assert.deepEqual(ccStub.callCount, 0);
622
623 element._focusOn(element.FocusTarget.CCS);
624 assert.equal(element._chooseFocusTarget.callCount, 2);
Becky Siegelafa82552017-11-17 15:59:39 -0800625 assert.deepEqual(textareaStub.callCount, 3);
Becky Siegelb7b41452017-11-14 14:35:29 -0800626 assert.deepEqual(reviewerEntryStub.callCount, 1);
627 assert.deepEqual(ccStub.callCount, 1);
628 });
629
Kasper Nilssonad908b12017-05-11 11:26:10 -0700630 test('_chooseFocusTarget', () => {
Logan Hanksfc57acc2016-08-05 13:54:50 -0700631 element._account = null;
632 assert.strictEqual(
633 element._chooseFocusTarget(), element.FocusTarget.BODY);
634
635 element._account = {_account_id: 1};
636 assert.strictEqual(
637 element._chooseFocusTarget(), element.FocusTarget.BODY);
638
639 element.change.owner = {_account_id: 2};
640 assert.strictEqual(
641 element._chooseFocusTarget(), element.FocusTarget.BODY);
642
643 element.change.owner._account_id = 1;
644 element.change._reviewers = null;
645 assert.strictEqual(
646 element._chooseFocusTarget(), element.FocusTarget.REVIEWERS);
647
648 element._reviewers = [];
649 assert.strictEqual(
650 element._chooseFocusTarget(), element.FocusTarget.REVIEWERS);
651
652 element._reviewers.push({});
653 assert.strictEqual(
654 element._chooseFocusTarget(), element.FocusTarget.BODY);
655 });
Logan Hanksca3c4972016-09-14 16:25:56 -0700656
Kasper Nilssonad908b12017-05-11 11:26:10 -0700657 test('only send labels that have changed', done => {
658 flush(() => {
Logan Hanks02c781e2017-07-18 14:18:01 -0700659 stubSaveReview(review => {
Viktar Donich51bdb8d2017-07-14 11:39:24 -0700660 assert.deepEqual(review.labels, {
661 'Code-Review': 0,
662 'Verified': -1,
663 });
Kasper Nilsson4416acd2016-09-16 13:27:11 -0700664 });
Logan Hanksca3c4972016-09-14 16:25:56 -0700665
Kasper Nilssonad908b12017-05-11 11:26:10 -0700666 element.addEventListener('send', () => {
Kasper Nilsson4416acd2016-09-16 13:27:11 -0700667 done();
668 });
669 // Without wrapping this test in flush(), the below two calls to
670 // MockInteractions.tap() cause a race in some situations in shadow DOM.
671 // The send button can be tapped before the others, causing the test to
672 // fail.
Becky Siegela499e3d2017-04-06 16:57:13 -0700673
Becky Siegel61613932017-05-30 15:09:20 -0700674 element.$$('gr-label-scores').$$(
675 'gr-label-score-row[name="Verified"]').setSelectedValue(-1);
Kasper Nilsson4416acd2016-09-16 13:27:11 -0700676 MockInteractions.tap(element.$$('.send'));
Logan Hanksca3c4972016-09-14 16:25:56 -0700677 });
Logan Hanksca3c4972016-09-14 16:25:56 -0700678 });
Becky Siegel8bb09342016-10-17 17:49:31 -0700679
Kasper Nilssonad908b12017-05-11 11:26:10 -0700680 test('_processReviewerChange', () => {
681 const mockIndexSplices = function(toRemove) {
Kasper Nilssona8271552017-01-17 11:54:40 -0800682 return [{
683 removed: [toRemove],
684 }];
685 };
686
687 element._processReviewerChange(
688 mockIndexSplices(makeAccount()), 'REVIEWER');
689 assert.equal(element._reviewersPendingRemove.REVIEWER.length, 1);
690 });
691
Kasper Nilssonad908b12017-05-11 11:26:10 -0700692 test('_purgeReviewersPendingRemove', () => {
693 const removeStub = sandbox.stub(element, '_removeAccount');
694 const mock = function() {
Kasper Nilssona8271552017-01-17 11:54:40 -0800695 element._reviewersPendingRemove = {
696 test: [makeAccount()],
697 test2: [makeAccount(), makeAccount()],
698 };
699 };
Kasper Nilssonad908b12017-05-11 11:26:10 -0700700 const checkObjEmpty = function(obj) {
701 for (const prop in obj) {
Kasper Nilssona8271552017-01-17 11:54:40 -0800702 if (obj.hasOwnProperty(prop) && obj[prop].length) { return false; }
703 }
704 return true;
705 };
706 mock();
707 element._purgeReviewersPendingRemove(true); // Cancel
708 assert.isFalse(removeStub.called);
709 assert.isTrue(checkObjEmpty(element._reviewersPendingRemove));
710
711 mock();
712 element._purgeReviewersPendingRemove(false); // Submit
713 assert.isTrue(removeStub.called);
714 assert.isTrue(checkObjEmpty(element._reviewersPendingRemove));
715 });
716
Kasper Nilssonad908b12017-05-11 11:26:10 -0700717 test('_removeAccount', done => {
Kasper Nilssona8271552017-01-17 11:54:40 -0800718 sandbox.stub(element.$.restAPI, 'removeChangeReviewer')
719 .returns(Promise.resolve({ok: true}));
Kasper Nilssonad908b12017-05-11 11:26:10 -0700720 const arr = [makeAccount(), makeAccount()];
Kasper Nilssona8271552017-01-17 11:54:40 -0800721 element.change.reviewers = {
722 REVIEWER: arr.slice(),
723 };
724
Kasper Nilssonad908b12017-05-11 11:26:10 -0700725 element._removeAccount(arr[1], 'REVIEWER').then(() => {
Kasper Nilssona8271552017-01-17 11:54:40 -0800726 assert.equal(element.change.reviewers.REVIEWER.length, 1);
727 assert.deepEqual(element.change.reviewers.REVIEWER, arr.slice(0, 1));
728 done();
729 });
730 });
Logan Hanks64451c02017-03-22 13:39:40 -0700731
Kasper Nilssond5896712017-06-01 17:59:02 -0700732 test('moving from cc to reviewer', () => {
Kasper Nilssond5896712017-06-01 17:59:02 -0700733 element._reviewersPendingRemove = {
734 CC: [],
735 REVIEWER: [],
736 };
737 flushAsynchronousOperations();
738
739 const reviewer1 = makeAccount();
740 const reviewer2 = makeAccount();
741 const reviewer3 = makeAccount();
742 const cc1 = makeAccount();
743 const cc2 = makeAccount();
744 const cc3 = makeAccount();
745 const cc4 = makeAccount();
746 element._reviewers = [reviewer1, reviewer2, reviewer3];
747 element._ccs = [cc1, cc2, cc3, cc4];
748 element.push('_reviewers', cc1);
749 flushAsynchronousOperations();
750
751 assert.deepEqual(element._reviewers,
752 [reviewer1, reviewer2, reviewer3, cc1]);
753 assert.deepEqual(element._ccs, [cc2, cc3, cc4]);
754 assert.deepEqual(element._reviewersPendingRemove.CC, [cc1]);
755
756 element.push('_reviewers', cc4, cc3);
757 flushAsynchronousOperations();
758
759 assert.deepEqual(element._reviewers,
760 [reviewer1, reviewer2, reviewer3, cc1, cc4, cc3]);
761 assert.deepEqual(element._ccs, [cc2]);
762 assert.deepEqual(element._reviewersPendingRemove.CC, [cc1, cc4, cc3]);
763 });
764
Milutin Kristoficf6ccd412019-09-16 14:29:27 +0200765 test('moving from reviewer to cc', () => {
766 element._reviewersPendingRemove = {
767 CC: [],
768 REVIEWER: [],
769 };
770 flushAsynchronousOperations();
771
772 const reviewer1 = makeAccount();
773 const reviewer2 = makeAccount();
774 const reviewer3 = makeAccount();
775 const cc1 = makeAccount();
776 const cc2 = makeAccount();
777 const cc3 = makeAccount();
778 const cc4 = makeAccount();
779 element._reviewers = [reviewer1, reviewer2, reviewer3];
780 element._ccs = [cc1, cc2, cc3, cc4];
781 element.push('_ccs', reviewer1);
782 flushAsynchronousOperations();
783
784 assert.deepEqual(element._reviewers,
785 [reviewer2, reviewer3]);
786 assert.deepEqual(element._ccs, [cc1, cc2, cc3, cc4, reviewer1]);
787 assert.deepEqual(element._reviewersPendingRemove.REVIEWER, [reviewer1]);
788
789 element.push('_ccs', reviewer3, reviewer2);
790 flushAsynchronousOperations();
791
792 assert.deepEqual(element._reviewers, []);
793 assert.deepEqual(element._ccs,
794 [cc1, cc2, cc3, cc4, reviewer1, reviewer3, reviewer2]);
795 assert.deepEqual(element._reviewersPendingRemove.REVIEWER,
796 [reviewer1, reviewer3, reviewer2]);
797 });
798
Kasper Nilssonad908b12017-05-11 11:26:10 -0700799 test('migrate reviewers between states', done => {
Logan Hanks64451c02017-03-22 13:39:40 -0700800 element._reviewersPendingRemove = {
801 CC: [],
802 REVIEWER: [],
803 };
804 flushAsynchronousOperations();
Kasper Nilssonad908b12017-05-11 11:26:10 -0700805 const reviewers = element.$.reviewers;
Tao Zhoub5a5a3c2019-11-04 17:40:19 +0100806 const ccs = element.$.ccs;
Kasper Nilssonad908b12017-05-11 11:26:10 -0700807 const reviewer1 = makeAccount();
808 const reviewer2 = makeAccount();
809 const cc1 = makeAccount();
810 const cc2 = makeAccount();
Kasper Nilsson7591ceb2017-05-12 16:16:53 -0700811 const cc3 = makeAccount();
Logan Hanks64451c02017-03-22 13:39:40 -0700812 element._reviewers = [reviewer1, reviewer2];
Kasper Nilsson7591ceb2017-05-12 16:16:53 -0700813 element._ccs = [cc1, cc2, cc3];
Logan Hanks64451c02017-03-22 13:39:40 -0700814
Kasper Nilssonad908b12017-05-11 11:26:10 -0700815 const mutations = [];
Logan Hanks64451c02017-03-22 13:39:40 -0700816
Logan Hanks02c781e2017-07-18 14:18:01 -0700817 stubSaveReview(review => mutations.push(...review.reviewers));
Logan Hanks64451c02017-03-22 13:39:40 -0700818
Kasper Nilssonad908b12017-05-11 11:26:10 -0700819 sandbox.stub(element, '_removeAccount', (account, type) => {
820 mutations.push({state: 'REMOVED', account});
Logan Hanks64451c02017-03-22 13:39:40 -0700821 return Promise.resolve();
822 });
823
824 // Remove and add to other field.
825 reviewers.fire('remove', {account: reviewer1});
826 ccs.$.entry.fire('add', {value: {account: reviewer1}});
827 ccs.fire('remove', {account: cc1});
Kasper Nilsson7591ceb2017-05-12 16:16:53 -0700828 ccs.fire('remove', {account: cc3});
Logan Hanks64451c02017-03-22 13:39:40 -0700829 reviewers.$.entry.fire('add', {value: {account: cc1}});
830
831 // Add to other field without removing from former field.
832 // (Currently not possible in UI, but this is a good consistency check).
833 reviewers.$.entry.fire('add', {value: {account: cc2}});
834 ccs.$.entry.fire('add', {value: {account: reviewer2}});
Kasper Nilssonad908b12017-05-11 11:26:10 -0700835 const mapReviewer = function(reviewer, opt_state) {
836 const result = {reviewer: reviewer._account_id, confirmed: undefined};
Logan Hanks64451c02017-03-22 13:39:40 -0700837 if (opt_state) {
838 result.state = opt_state;
839 }
840 return result;
841 };
842
Kasper Nilsson7591ceb2017-05-12 16:16:53 -0700843 // Send and purge and verify moves, delete cc3.
Logan Hanks64451c02017-03-22 13:39:40 -0700844 element.send()
Kasper Nilsson7591ceb2017-05-12 16:16:53 -0700845 .then(keepReviewers =>
Thomas Draebing6ff72df2019-12-27 10:58:03 +0100846 element._purgeReviewersPendingRemove(false, keepReviewers))
Kasper Nilssonad908b12017-05-11 11:26:10 -0700847 .then(() => {
848 assert.deepEqual(
849 mutations, [
850 mapReviewer(cc1),
851 mapReviewer(cc2),
852 mapReviewer(reviewer1, 'CC'),
853 mapReviewer(reviewer2, 'CC'),
Kasper Nilsson7591ceb2017-05-12 16:16:53 -0700854 {account: cc3, state: 'REMOVED'},
Kasper Nilssonad908b12017-05-11 11:26:10 -0700855 ]);
856 done();
857 });
Logan Hanks64451c02017-03-22 13:39:40 -0700858 });
Kasper Nilssonff0745d2017-03-21 16:41:24 -0700859
Kasper Nilssonad908b12017-05-11 11:26:10 -0700860 test('emits cancel on esc key', () => {
861 const cancelHandler = sandbox.spy();
Kasper Nilssonff0745d2017-03-21 16:41:24 -0700862 element.addEventListener('cancel', cancelHandler);
863 MockInteractions.pressAndReleaseKeyOn(element, 27, null, 'esc');
864 flushAsynchronousOperations();
865
866 assert.isTrue(cancelHandler.called);
867 });
Logan Hanksdc65dde2017-04-27 11:45:23 +0200868
Mike Frysinger81b90bc2017-07-25 18:24:32 -0400869 test('should not send on enter key', () => {
Logan Hanks3e0917e2017-07-19 08:32:25 -0700870 stubSaveReview(() => undefined);
Mike Frysinger81b90bc2017-07-25 18:24:32 -0400871 element.addEventListener('send', () => assert.fail('wrongly called'));
872 MockInteractions.pressAndReleaseKeyOn(element, 13, null, 'enter');
873 flushAsynchronousOperations();
874 });
875
876 test('emit send on ctrl+enter key', done => {
Logan Hanks3e0917e2017-07-19 08:32:25 -0700877 stubSaveReview(() => undefined);
Mike Frysinger81b90bc2017-07-25 18:24:32 -0400878 element.addEventListener('send', () => done());
879 MockInteractions.pressAndReleaseKeyOn(element, 13, 'ctrl', 'enter');
880 flushAsynchronousOperations();
881 });
882
Kasper Nilssonad908b12017-05-11 11:26:10 -0700883 test('_computeMessagePlaceholder', () => {
Logan Hanksdc65dde2017-04-27 11:45:23 +0200884 assert.equal(
885 element._computeMessagePlaceholder(false),
886 'Say something nice...');
887 assert.equal(
888 element._computeMessagePlaceholder(true),
889 'Add a note for your reviewers...');
890 });
891
Kasper Nilssonad908b12017-05-11 11:26:10 -0700892 test('_computeSendButtonLabel', () => {
Logan Hanksdc65dde2017-04-27 11:45:23 +0200893 assert.equal(
894 element._computeSendButtonLabel(false),
895 'Send');
896 assert.equal(
897 element._computeSendButtonLabel(true),
898 'Start review');
899 });
Wyatt Allene013aad2017-05-18 16:49:04 -0700900
901 test('_handle400Error reviewrs and CCs', done => {
902 const error1 = 'error 1';
903 const error2 = 'error 2';
904 const error3 = 'error 3';
Ben Rohlfs9bdbceb2019-07-03 17:06:52 +0200905 const text = ')]}\'' + JSON.stringify({
906 reviewers: {
907 username1: {
908 input: 'user 1',
909 error: error1,
910 },
911 username2: {
912 input: 'user 2',
913 error: error2,
914 },
Kasper Nilsson5a93dbd2017-05-19 10:43:02 -0700915 },
Ben Rohlfs9bdbceb2019-07-03 17:06:52 +0200916 ccs: {
917 username3: {
918 input: 'user 3',
919 error: error3,
920 },
921 },
922 });
Wyatt Allene013aad2017-05-18 16:49:04 -0700923 element.addEventListener('server-error', e => {
924 e.detail.response.text().then(text => {
925 assert.equal(text, [error1, error2, error3].join(', '));
926 done();
Kasper Nilsson5a93dbd2017-05-19 10:43:02 -0700927 });
Wyatt Allene013aad2017-05-18 16:49:04 -0700928 });
Ben Rohlfs9bdbceb2019-07-03 17:06:52 +0200929 element._handle400Error(cloneableResponse(400, text));
Wyatt Allene013aad2017-05-18 16:49:04 -0700930 });
931
932 test('_handle400Error CCs only', done => {
933 const error1 = 'error 1';
Ben Rohlfs9bdbceb2019-07-03 17:06:52 +0200934 const text = ')]}\'' + JSON.stringify({
935 ccs: {
936 username1: {
937 input: 'user 1',
938 error: error1,
939 },
Kasper Nilsson5a93dbd2017-05-19 10:43:02 -0700940 },
Ben Rohlfs9bdbceb2019-07-03 17:06:52 +0200941 });
Wyatt Allene013aad2017-05-18 16:49:04 -0700942 element.addEventListener('server-error', e => {
943 e.detail.response.text().then(text => {
944 assert.equal(text, error1);
945 done();
Kasper Nilsson5a93dbd2017-05-19 10:43:02 -0700946 });
Wyatt Allene013aad2017-05-18 16:49:04 -0700947 });
Ben Rohlfs9bdbceb2019-07-03 17:06:52 +0200948 element._handle400Error(cloneableResponse(400, text));
Wyatt Allene013aad2017-05-18 16:49:04 -0700949 });
Logan Hanks68b082e2017-07-13 14:19:13 -0700950
Wyatt Allenf5d2d792017-08-14 11:53:19 -0700951 test('fires height change when the drafts load', done => {
952 // Flush DOM operations before binding to the autogrow event so we don't
953 // catch the events fired from the initial layout.
954 flush(() => {
955 const autoGrowHandler = sinon.stub();
956 element.addEventListener('autogrow', autoGrowHandler);
957 element.diffDrafts = {};
958 flush(() => {
959 assert.isTrue(autoGrowHandler.called);
960 done();
961 });
962 });
963 });
964
Logan Hanks02c781e2017-07-18 14:18:01 -0700965 suite('post review API', () => {
966 let startReviewStub;
Logan Hanks68b082e2017-07-13 14:19:13 -0700967
Logan Hanks02c781e2017-07-18 14:18:01 -0700968 setup(() => {
Logan Hanks3e0917e2017-07-19 08:32:25 -0700969 startReviewStub = sandbox.stub(element.$.restAPI, 'startReview', () => {
970 return Promise.resolve();
971 });
Logan Hanks02c781e2017-07-18 14:18:01 -0700972 });
973
974 test('ready property in review input on start review', () => {
975 stubSaveReview(review => {
976 assert.isTrue(review.ready);
977 return {ready: true};
978 });
979 return element.send(true, true).then(() => {
980 assert.isFalse(startReviewStub.called);
981 });
982 });
983
984 test('no ready property in review input on save review', () => {
985 stubSaveReview(review => {
986 assert.isUndefined(review.ready);
Logan Hanks02c781e2017-07-18 14:18:01 -0700987 });
988 return element.send(true, false).then(() => {
989 assert.isFalse(startReviewStub.called);
990 });
991 });
Logan Hanks68b082e2017-07-13 14:19:13 -0700992 });
993
Logan Hanks02c781e2017-07-18 14:18:01 -0700994 suite('start review and save buttons', () => {
995 let sendStub;
996
Logan Hanks68b082e2017-07-13 14:19:13 -0700997 setup(() => {
Logan Hanks02c781e2017-07-18 14:18:01 -0700998 sendStub = sandbox.stub(element, 'send', () => Promise.resolve());
Logan Hanks68b082e2017-07-13 14:19:13 -0700999 element.canBeStarted = true;
1000 // Flush to make both Start/Save buttons appear in DOM.
1001 flushAsynchronousOperations();
1002 });
1003
1004 test('start review sets ready', () => {
1005 MockInteractions.tap(element.$$('.send'));
1006 flushAsynchronousOperations();
Logan Hanks02c781e2017-07-18 14:18:01 -07001007 assert.isTrue(sendStub.calledWith(true, true));
Logan Hanks68b082e2017-07-13 14:19:13 -07001008 });
1009
1010 test('save review doesn\'t set ready', () => {
1011 MockInteractions.tap(element.$$('.save'));
1012 flushAsynchronousOperations();
Logan Hanks02c781e2017-07-18 14:18:01 -07001013 assert.isTrue(sendStub.calledWith(true, false));
Logan Hanks68b082e2017-07-13 14:19:13 -07001014 });
1015 });
Logan Hanks3f1a6ff2017-07-25 11:05:23 -07001016
Logan Hanks3e0917e2017-07-19 08:32:25 -07001017 test('buttons disabled until all API calls are resolved', () => {
1018 stubSaveReview(review => {
Tao Zhou94953202019-10-01 14:35:02 +02001019 return {ready: true};
Logan Hanks3e0917e2017-07-19 08:32:25 -07001020 });
1021 return element.send(true, true).then(() => {
1022 assert.isFalse(element.disabled);
1023 });
1024 });
1025
1026 suite('error handling', () => {
1027 const expectedDraft = 'draft';
1028 const expectedError = new Error('test');
1029
1030 setup(() => {
1031 element.draft = expectedDraft;
1032 });
1033
1034 function assertDialogOpenAndEnabled() {
1035 assert.strictEqual(expectedDraft, element.draft);
1036 assert.isFalse(element.disabled);
1037 }
1038
Logan Hanks3e0917e2017-07-19 08:32:25 -07001039 test('error occurs in _saveReview', () => {
1040 stubSaveReview(review => {
1041 throw expectedError;
1042 });
1043 return element.send(true, true).catch(err => {
1044 assert.strictEqual(expectedError, err);
1045 assertDialogOpenAndEnabled();
1046 });
1047 });
1048
Kasper Nilsson68735492017-08-01 16:39:48 -07001049 suite('pending diff drafts?', () => {
1050 test('yes', () => {
1051 const promise = mockPromise();
1052 const refreshHandler = sandbox.stub();
1053
1054 element.addEventListener('comment-refresh', refreshHandler);
1055 sandbox.stub(element.$.restAPI, 'hasPendingDiffDrafts').returns(true);
1056 element.$.restAPI._pendingRequests.sendDiffDraft = [promise];
1057 element.open();
1058
1059 assert.isFalse(refreshHandler.called);
1060 assert.isTrue(element._savingComments);
1061
1062 promise.resolve();
1063
1064 return element.$.restAPI.awaitPendingDiffDrafts().then(() => {
1065 assert.isTrue(refreshHandler.called);
1066 assert.isFalse(element._savingComments);
1067 });
1068 });
1069
1070 test('no', () => {
1071 sandbox.stub(element.$.restAPI, 'hasPendingDiffDrafts').returns(false);
1072 element.open();
1073 assert.notOk(element._savingComments);
1074 });
1075 });
Logan Hanks3e0917e2017-07-19 08:32:25 -07001076 });
Kasper Nilssonaa746bd2017-09-18 22:12:30 -07001077
1078 test('_computeSendButtonDisabled', () => {
1079 const fn = element._computeSendButtonDisabled.bind(element);
Tao Zhoud2fca292019-08-12 18:53:02 +02001080 assert.isFalse(fn(
1081 /* buttonLabel= */ 'Start review',
1082 /* drafts= */ {},
1083 /* text= */ '',
1084 /* reviewersMutated= */ false,
1085 /* labelsChanged= */ false,
1086 /* includeComments= */ false,
David Pursehouse029bf462019-12-14 15:21:57 +09001087 /* disabled= */ false
Tao Zhoud2fca292019-08-12 18:53:02 +02001088 ));
1089 assert.isTrue(fn(
1090 /* buttonLabel= */ 'Send',
1091 /* drafts= */ {},
1092 /* text= */ '',
1093 /* reviewersMutated= */ false,
1094 /* labelsChanged= */ false,
1095 /* includeComments= */ false,
David Pursehouse029bf462019-12-14 15:21:57 +09001096 /* disabled= */ false
Tao Zhoud2fca292019-08-12 18:53:02 +02001097 ));
Kasper Nilsson2eed3c32017-09-20 14:00:47 -07001098 // Mock nonempty comment draft array, with seding comments.
Tao Zhoud2fca292019-08-12 18:53:02 +02001099 assert.isFalse(fn(
1100 /* buttonLabel= */ 'Send',
1101 /* drafts= */ {file: ['draft']},
1102 /* text= */ '',
1103 /* reviewersMutated= */ false,
1104 /* labelsChanged= */ false,
1105 /* includeComments= */ true,
David Pursehouse029bf462019-12-14 15:21:57 +09001106 /* disabled= */ false
Tao Zhoud2fca292019-08-12 18:53:02 +02001107 ));
Kasper Nilsson2eed3c32017-09-20 14:00:47 -07001108 // Mock nonempty comment draft array, without seding comments.
Tao Zhoud2fca292019-08-12 18:53:02 +02001109 assert.isTrue(fn(
1110 /* buttonLabel= */ 'Send',
1111 /* drafts= */ {file: ['draft']},
1112 /* text= */ '',
1113 /* reviewersMutated= */ false,
1114 /* labelsChanged= */ false,
1115 /* includeComments= */ false,
David Pursehouse029bf462019-12-14 15:21:57 +09001116 /* disabled= */ false
Tao Zhoud2fca292019-08-12 18:53:02 +02001117 ));
Kasper Nilssonaa746bd2017-09-18 22:12:30 -07001118 // Mock nonempty change message.
Tao Zhoud2fca292019-08-12 18:53:02 +02001119 assert.isFalse(fn(
1120 /* buttonLabel= */ 'Send',
1121 /* drafts= */ {},
1122 /* text= */ 'test',
1123 /* reviewersMutated= */ false,
1124 /* labelsChanged= */ false,
1125 /* includeComments= */ false,
David Pursehouse029bf462019-12-14 15:21:57 +09001126 /* disabled= */ false
Tao Zhoud2fca292019-08-12 18:53:02 +02001127 ));
Kasper Nilssonaa746bd2017-09-18 22:12:30 -07001128 // Mock reviewers mutated.
Tao Zhoud2fca292019-08-12 18:53:02 +02001129 assert.isFalse(fn(
1130 /* buttonLabel= */ 'Send',
1131 /* drafts= */ {},
1132 /* text= */ '',
1133 /* reviewersMutated= */ true,
1134 /* labelsChanged= */ false,
1135 /* includeComments= */ false,
David Pursehouse029bf462019-12-14 15:21:57 +09001136 /* disabled= */ false
Tao Zhoud2fca292019-08-12 18:53:02 +02001137 ));
Kasper Nilssonaa746bd2017-09-18 22:12:30 -07001138 // Mock labels changed.
Tao Zhoud2fca292019-08-12 18:53:02 +02001139 assert.isFalse(fn(
1140 /* buttonLabel= */ 'Send',
1141 /* drafts= */ {},
1142 /* text= */ '',
1143 /* reviewersMutated= */ false,
1144 /* labelsChanged= */ true,
1145 /* includeComments= */ false,
David Pursehouse029bf462019-12-14 15:21:57 +09001146 /* disabled= */ false
Tao Zhoud2fca292019-08-12 18:53:02 +02001147 ));
Kasper Nilsson06864f02018-05-03 14:10:17 -07001148 // Whole dialog is disabled.
Tao Zhoud2fca292019-08-12 18:53:02 +02001149 assert.isTrue(fn(
1150 /* buttonLabel= */ 'Send',
1151 /* drafts= */ {},
1152 /* text= */ '',
1153 /* reviewersMutated= */ false,
1154 /* labelsChanged= */ true,
1155 /* includeComments= */ false,
David Pursehouse029bf462019-12-14 15:21:57 +09001156 /* disabled= */ true
Tao Zhoud2fca292019-08-12 18:53:02 +02001157 ));
Kasper Nilssonaa746bd2017-09-18 22:12:30 -07001158 });
Viktar Donich3ae72b32018-02-28 14:41:58 -08001159
Kasper Nilssonfb54dc62018-03-26 13:57:57 -07001160 test('_submit blocked when no mutations exist', () => {
1161 const sendStub = sandbox.stub(element, 'send').returns(Promise.resolve());
1162 // Stub the below function to avoid side effects from the send promise
1163 // resolving.
1164 sandbox.stub(element, '_purgeReviewersPendingRemove');
1165 element.diffDrafts = {};
1166 flushAsynchronousOperations();
1167
1168 MockInteractions.tap(element.$$('gr-button.send'));
1169 assert.isFalse(sendStub.called);
1170
Dmitrii Filippov0998cfa2019-11-12 10:47:06 +01001171 element.diffDrafts = {test: [{val: true}]};
Kasper Nilssonfb54dc62018-03-26 13:57:57 -07001172 flushAsynchronousOperations();
1173
1174 MockInteractions.tap(element.$$('gr-button.send'));
1175 assert.isTrue(sendStub.called);
1176 });
1177
1178 test('getFocusStops', () => {
1179 // Setting diffDrafts to an empty object causes _sendDisabled to be
1180 // computed to false.
1181 element.diffDrafts = {};
1182 assert.equal(element.getFocusStops().end, element.$.cancelButton);
Dmitrii Filippov0998cfa2019-11-12 10:47:06 +01001183 element.diffDrafts = {test: [{val: true}]};
Kasper Nilssonfb54dc62018-03-26 13:57:57 -07001184 assert.equal(element.getFocusStops().end, element.$.sendButton);
1185 });
1186
Viktar Donich3ae72b32018-02-28 14:41:58 -08001187 test('setPluginMessage', () => {
1188 element.setPluginMessage('foo');
1189 assert.equal(element.$.pluginMessage.textContent, 'foo');
1190 });
Andrew Bonventref8b026d2015-12-09 17:55:54 -05001191 });
Dmitrii Filippov2b459762019-09-24 16:26:10 +02001192</script>