blob: 5d9a2db4f4840e49e0f31c1629c54acd8c015754 [file] [log] [blame]
Andrew Bonventref8b026d2015-12-09 17:55:54 -05001<!DOCTYPE html>
2<!--
3Copyright (C) 2015 The Android Open Source Project
4
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
16-->
17
18<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 -050019<title>gr-reply-dialog</title>
Andrew Bonventref8b026d2015-12-09 17:55:54 -050020
Viktar Donich29e1ce52017-03-28 17:02:44 -070021<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
Andrew Bonventre78792e82016-03-04 17:48:22 -050022<script src="../../../bower_components/web-component-tester/browser.js"></script>
Mike Samuele07c4b22017-06-02 13:08:19 -040023<link rel="import" href="../../../test/common-test-setup.html"/>
Andrew Bonventre78792e82016-03-04 17:48:22 -050024<link rel="import" href="gr-reply-dialog.html">
Andrew Bonventref8b026d2015-12-09 17:55:54 -050025
Viktar Donich29e1ce52017-03-28 17:02:44 -070026<script>void(0);</script>
27
Andrew Bonventref8b026d2015-12-09 17:55:54 -050028<test-fixture id="basic">
29 <template>
Andrew Bonventre882043f2016-02-22 18:12:27 -050030 <gr-reply-dialog></gr-reply-dialog>
Andrew Bonventref8b026d2015-12-09 17:55:54 -050031 </template>
32</test-fixture>
33
34<script>
Kasper Nilssonad908b12017-05-11 11:26:10 -070035 suite('gr-reply-dialog tests', () => {
36 let element;
37 let changeNum;
38 let patchNum;
Wyatt Allen4f4b3a72016-07-28 12:05:53 -070039
Kasper Nilssonad908b12017-05-11 11:26:10 -070040 let sandbox;
41 let getDraftCommentStub;
42 let setDraftCommentStub;
43 let eraseDraftCommentStub;
Andrew Bonventref8b026d2015-12-09 17:55:54 -050044
Kasper Nilssonad908b12017-05-11 11:26:10 -070045 let lastId = 0;
46 const makeAccount = function() { return {_account_id: lastId++}; };
47 const makeGroup = function() { return {id: lastId++}; };
Kasper Nilssona8271552017-01-17 11:54:40 -080048
Kasper Nilssonad908b12017-05-11 11:26:10 -070049 setup(() => {
Wyatt Allen4f4b3a72016-07-28 12:05:53 -070050 sandbox = sinon.sandbox.create();
51
52 changeNum = 42;
53 patchNum = 1;
54
Andrew Bonventre5c09ba92016-05-02 17:31:23 -040055 stub('gr-rest-api-interface', {
Kasper Nilssonad908b12017-05-11 11:26:10 -070056 getConfig() { return Promise.resolve({}); },
57 getAccount() { return Promise.resolve({}); },
Andrew Bonventrecfacb802016-03-29 14:06:39 -040058 });
Wyatt Allen4f4b3a72016-07-28 12:05:53 -070059
Andrew Bonventref8b026d2015-12-09 17:55:54 -050060 element = fixture('basic');
Kasper Nilsson352b68f2016-10-11 11:51:35 -070061 element.change = {
62 _number: changeNum,
63 labels: {
Kasper Nilssonad908b12017-05-11 11:26:10 -070064 'Verified': {
Kasper Nilsson352b68f2016-10-11 11:51:35 -070065 values: {
66 '-1': 'Fails',
67 ' 0': 'No score',
68 '+1': 'Verified',
69 },
70 default_value: 0,
Andrew Bonventref8b026d2015-12-09 17:55:54 -050071 },
Kasper Nilsson352b68f2016-10-11 11:51:35 -070072 'Code-Review': {
73 values: {
74 '-2': 'Do not submit',
75 '-1': 'I would prefer that you didn\'t submit this',
76 ' 0': 'No score',
77 '+1': 'Looks good to me, but someone else must approve',
78 '+2': 'Looks good to me, approved',
79 },
80 default_value: 0,
81 },
Andrew Bonventref8b026d2015-12-09 17:55:54 -050082 },
Andrew Bonventref8b026d2015-12-09 17:55:54 -050083 };
Kasper Nilsson352b68f2016-10-11 11:51:35 -070084 element.patchNum = patchNum;
Andrew Bonventref8b026d2015-12-09 17:55:54 -050085 element.permittedLabels = {
86 'Code-Review': [
87 '-1',
88 ' 0',
Kasper Nilsson352b68f2016-10-11 11:51:35 -070089 '+1',
Andrew Bonventref8b026d2015-12-09 17:55:54 -050090 ],
Kasper Nilssonad908b12017-05-11 11:26:10 -070091 'Verified': [
Andrew Bonventref8b026d2015-12-09 17:55:54 -050092 '-1',
93 ' 0',
Kasper Nilsson352b68f2016-10-11 11:51:35 -070094 '+1',
95 ],
Andrew Bonventref8b026d2015-12-09 17:55:54 -050096 };
Logan Hanksa75fb052016-08-01 13:23:38 -070097 element.serverConfig = {};
Andrew Bonventref8b026d2015-12-09 17:55:54 -050098
Wyatt Allen4f4b3a72016-07-28 12:05:53 -070099 getDraftCommentStub = sandbox.stub(element.$.storage, 'getDraftComment');
100 setDraftCommentStub = sandbox.stub(element.$.storage, 'setDraftComment');
101 eraseDraftCommentStub = sandbox.stub(element.$.storage,
102 'eraseDraftComment');
103
Wyatt Allen6cf58752017-04-24 16:59:07 +0200104 sandbox.stub(element, 'fetchIsLatestKnown',
Kasper Nilssonad908b12017-05-11 11:26:10 -0700105 () => { return Promise.resolve(true); });
Wyatt Allen6cf58752017-04-24 16:59:07 +0200106
Andrew Bonventref8b026d2015-12-09 17:55:54 -0500107 // Allow the elements created by dom-repeat to be stamped.
108 flushAsynchronousOperations();
109 });
110
Kasper Nilssonad908b12017-05-11 11:26:10 -0700111 teardown(() => {
Wyatt Allen4f4b3a72016-07-28 12:05:53 -0700112 sandbox.restore();
113 });
114
Logan Hanks02c781e2017-07-18 14:18:01 -0700115 function stubSaveReview(jsonResponseProducer) {
116 return sandbox.stub(element, '_saveReview', review => {
117 const result = jsonResponseProducer(review);
118 const resultStr =
119 element.$.restAPI.JSON_PREFIX + JSON.stringify(result);
120 return Promise.resolve({
121 ok: true,
122 text() {
123 return Promise.resolve(resultStr);
124 },
125 });
126 });
127 }
128
Kasper Nilssonad908b12017-05-11 11:26:10 -0700129 test('default to publishing drafts with reply', done => {
Becky Siegel9efcd572017-03-23 08:28:03 -0700130 // Async tick is needed because iron-selector content is distributed and
131 // distributed content requires an observer to be set up.
132 // Note: Double flush seems to be needed in Safari. {@see Issue 4963}.
Kasper Nilssonad908b12017-05-11 11:26:10 -0700133 flush(() => {
134 flush(() => {
Becky Siegel9efcd572017-03-23 08:28:03 -0700135 element.draft = 'I wholeheartedly disapprove';
136
Logan Hanks02c781e2017-07-18 14:18:01 -0700137 stubSaveReview(review => {
Becky Siegel9efcd572017-03-23 08:28:03 -0700138 assert.deepEqual(review, {
139 drafts: 'PUBLISH_ALL_REVISIONS',
Viktar Donich51bdb8d2017-07-14 11:39:24 -0700140 labels: {
141 'Code-Review': 0,
142 'Verified': 0,
143 },
Becky Siegel9efcd572017-03-23 08:28:03 -0700144 message: 'I wholeheartedly disapprove',
145 reviewers: [],
146 });
147 assert.isFalse(element.$.commentList.hidden);
148 done();
Becky Siegel9efcd572017-03-23 08:28:03 -0700149 });
150
151 // This is needed on non-Blink engines most likely due to the ways in
152 // which the dom-repeat elements are stamped.
Kasper Nilssonad908b12017-05-11 11:26:10 -0700153 flush(() => {
Becky Siegel9efcd572017-03-23 08:28:03 -0700154 MockInteractions.tap(element.$$('.send'));
155 });
156 });
157 });
158 });
159
Kasper Nilssonad908b12017-05-11 11:26:10 -0700160 test('keep drafts with reply', done => {
Becky Siegel9efcd572017-03-23 08:28:03 -0700161 MockInteractions.tap(element.$$('#includeComments'));
162 assert.equal(element._includeComments, false);
163
164 // Async tick is needed because iron-selector content is distributed and
165 // distributed content requires an observer to be set up.
166 // Note: Double flush seems to be needed in Safari. {@see Issue 4963}.
Kasper Nilssonad908b12017-05-11 11:26:10 -0700167 flush(() => {
168 flush(() => {
Becky Siegel9efcd572017-03-23 08:28:03 -0700169 element.draft = 'I wholeheartedly disapprove';
170
Logan Hanks02c781e2017-07-18 14:18:01 -0700171 stubSaveReview(review => {
Becky Siegel9efcd572017-03-23 08:28:03 -0700172 assert.deepEqual(review, {
173 drafts: 'KEEP',
Viktar Donich51bdb8d2017-07-14 11:39:24 -0700174 labels: {
175 'Code-Review': 0,
176 'Verified': 0,
177 },
Becky Siegel9efcd572017-03-23 08:28:03 -0700178 message: 'I wholeheartedly disapprove',
179 reviewers: [],
180 });
181 assert.isTrue(element.$.commentList.hidden);
182 done();
Becky Siegel9efcd572017-03-23 08:28:03 -0700183 });
184
185 // This is needed on non-Blink engines most likely due to the ways in
186 // which the dom-repeat elements are stamped.
Kasper Nilssonad908b12017-05-11 11:26:10 -0700187 flush(() => {
Becky Siegel9efcd572017-03-23 08:28:03 -0700188 MockInteractions.tap(element.$$('.send'));
189 });
190 });
191 });
192 });
193
Kasper Nilssonad908b12017-05-11 11:26:10 -0700194 test('label picker', done => {
Becky Siegela499e3d2017-04-06 16:57:13 -0700195 element.draft = 'I wholeheartedly disapprove';
Logan Hanks02c781e2017-07-18 14:18:01 -0700196 stubSaveReview(review => {
Becky Siegela499e3d2017-04-06 16:57:13 -0700197 assert.deepEqual(review, {
198 drafts: 'PUBLISH_ALL_REVISIONS',
199 labels: {
200 'Code-Review': -1,
201 'Verified': -1,
202 },
203 message: 'I wholeheartedly disapprove',
204 reviewers: [],
Urs Wolferb6036942016-03-06 14:57:02 +0100205 });
Becky Siegela499e3d2017-04-06 16:57:13 -0700206 });
207
Kasper Nilssonad908b12017-05-11 11:26:10 -0700208 sandbox.stub(element.$.labelScores, 'getLabelValues', () => {
Becky Siegela499e3d2017-04-06 16:57:13 -0700209 return {
210 'Code-Review': -1,
211 'Verified': -1,
212 };
213 });
214
Kasper Nilssonad908b12017-05-11 11:26:10 -0700215 element.addEventListener('send', () => {
Becky Siegela499e3d2017-04-06 16:57:13 -0700216 assert.isFalse(element.disabled,
217 'Element should be enabled when done sending reply.');
218 assert.equal(element.draft.length, 0);
219 done();
220 });
221
222 // This is needed on non-Blink engines most likely due to the ways in
223 // which the dom-repeat elements are stamped.
Kasper Nilssonad908b12017-05-11 11:26:10 -0700224 flush(() => {
Becky Siegela499e3d2017-04-06 16:57:13 -0700225 MockInteractions.tap(element.$$('.send'));
226 assert.isTrue(element.disabled);
Urs Wolferb6036942016-03-06 14:57:02 +0100227 });
Andrew Bonventref8b026d2015-12-09 17:55:54 -0500228 });
Logan Hanksa008aa92016-07-22 10:31:12 -0700229
Viktar Donich7d23f222017-05-17 09:25:06 -0700230 test('getlabelValue returns value', done => {
231 flush(() => {
Becky Siegel61613932017-05-30 15:09:20 -0700232 element.$$('gr-label-scores').$$(`gr-label-score-row[name="Verified"]`)
233 .setSelectedValue(-1);
Viktar Donich7d23f222017-05-17 09:25:06 -0700234 assert.equal('-1', element.getLabelValue('Verified'));
235 done();
236 });
237 });
238
239 test('getlabelValue when no score is selected', done => {
240 flush(() => {
Becky Siegel61613932017-05-30 15:09:20 -0700241 element.$$('gr-label-scores').$$(`gr-label-score-row[name="Code-Review"]`)
242 .setSelectedValue(-1);
Viktar Donich51bdb8d2017-07-14 11:39:24 -0700243 assert.strictEqual(element.getLabelValue('Verified'), ' 0');
Viktar Donich7d23f222017-05-17 09:25:06 -0700244 done();
245 });
246 });
247
Kasper Nilssonad908b12017-05-11 11:26:10 -0700248 test('setlabelValue', () => {
Becky Siegel452a5c62017-05-09 09:59:55 -0700249 element._account = {_account_id: 1};
250 flushAsynchronousOperations();
Kasper Nilssonad908b12017-05-11 11:26:10 -0700251 const label = 'Verified';
252 const value = '+1';
Becky Siegel452a5c62017-05-09 09:59:55 -0700253 element.setLabelValue(label, value);
254 flushAsynchronousOperations();
Kasper Nilssonad908b12017-05-11 11:26:10 -0700255 const labels = element.$.labelScores.getLabelValues();
Viktar Donich51bdb8d2017-07-14 11:39:24 -0700256 assert.deepEqual(labels, {
257 'Code-Review': 0,
258 'Verified': 1,
259 });
Becky Siegel452a5c62017-05-09 09:59:55 -0700260 });
261
Logan Hanksa008aa92016-07-22 10:31:12 -0700262 function getActiveElement() {
263 return Polymer.IronOverlayManager.deepActiveElement;
264 }
265
266 function isVisible(el) {
267 assert.ok(el);
268 return getComputedStyle(el).getPropertyValue('display') != 'none';
269 }
270
271 function overlayObserver(mode) {
Kasper Nilssonad908b12017-05-11 11:26:10 -0700272 return new Promise(resolve => {
Logan Hanksa008aa92016-07-22 10:31:12 -0700273 function listener() {
274 element.removeEventListener('iron-overlay-' + mode, listener);
275 resolve();
276 }
277 element.addEventListener('iron-overlay-' + mode, listener);
278 });
279 }
280
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800281 function testConfirmationDialog(done, cc) {
Kasper Nilssonad908b12017-05-11 11:26:10 -0700282 const yesButton =
Logan Hanksa008aa92016-07-22 10:31:12 -0700283 element.$$('.reviewerConfirmationButtons gr-button:first-child');
Kasper Nilssonad908b12017-05-11 11:26:10 -0700284 const noButton =
Logan Hanksa008aa92016-07-22 10:31:12 -0700285 element.$$('.reviewerConfirmationButtons gr-button:last-child');
286
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800287 element.serverConfig = {note_db_enabled: true};
288 element._ccPendingConfirmation = null;
Logan Hanksa008aa92016-07-22 10:31:12 -0700289 element._reviewerPendingConfirmation = null;
290 flushAsynchronousOperations();
291 assert.isFalse(isVisible(element.$.reviewerConfirmationOverlay));
292
293 // Cause the confirmation dialog to display.
Kasper Nilssonad908b12017-05-11 11:26:10 -0700294 let observer = overlayObserver('opened');
295 const group = {
Logan Hanksa008aa92016-07-22 10:31:12 -0700296 id: 'id',
297 name: 'name',
Logan Hanksa008aa92016-07-22 10:31:12 -0700298 };
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800299 if (cc) {
300 element._ccPendingConfirmation = {
Kasper Nilssonad908b12017-05-11 11:26:10 -0700301 group,
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800302 count: 10,
303 };
304 } else {
305 element._reviewerPendingConfirmation = {
Kasper Nilssonad908b12017-05-11 11:26:10 -0700306 group,
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800307 count: 10,
308 };
309 }
310 flushAsynchronousOperations();
311
312 if (cc) {
313 assert.deepEqual(
314 element._ccPendingConfirmation,
315 element._pendingConfirmationDetails);
316 } else {
317 assert.deepEqual(
318 element._reviewerPendingConfirmation,
319 element._pendingConfirmationDetails);
320 }
Logan Hanksa008aa92016-07-22 10:31:12 -0700321
Kasper Nilssonad908b12017-05-11 11:26:10 -0700322 observer.then(() => {
Logan Hanksa008aa92016-07-22 10:31:12 -0700323 assert.isTrue(isVisible(element.$.reviewerConfirmationOverlay));
324 observer = overlayObserver('closed');
Kasper Nilssonad908b12017-05-11 11:26:10 -0700325 const expected = 'Group name has 10 members';
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800326 assert.notEqual(
327 element.$.reviewerConfirmationOverlay.innerText.indexOf(expected),
328 -1);
Logan Hanksa008aa92016-07-22 10:31:12 -0700329 MockInteractions.tap(noButton); // close the overlay
330 return observer;
Kasper Nilssonad908b12017-05-11 11:26:10 -0700331 }).then(() => {
Logan Hanksa008aa92016-07-22 10:31:12 -0700332 assert.isFalse(isVisible(element.$.reviewerConfirmationOverlay));
333
334 // We should be focused on account entry input.
335 assert.equal(getActiveElement().id, 'input');
336
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800337 // No reviewer/CC should have been added.
338 assert.equal(element.$$('#ccs').additions().length, 0);
339 assert.equal(element.$.reviewers.additions().length, 0);
Logan Hanksa008aa92016-07-22 10:31:12 -0700340
341 // Reopen confirmation dialog.
342 observer = overlayObserver('opened');
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800343 if (cc) {
344 element._ccPendingConfirmation = {
Kasper Nilssonad908b12017-05-11 11:26:10 -0700345 group,
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800346 count: 10,
347 };
348 } else {
349 element._reviewerPendingConfirmation = {
Kasper Nilssonad908b12017-05-11 11:26:10 -0700350 group,
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800351 count: 10,
352 };
353 }
Logan Hanksa008aa92016-07-22 10:31:12 -0700354 return observer;
Kasper Nilssonad908b12017-05-11 11:26:10 -0700355 }).then(() => {
Logan Hanksa008aa92016-07-22 10:31:12 -0700356 assert.isTrue(isVisible(element.$.reviewerConfirmationOverlay));
357 observer = overlayObserver('closed');
Becky Siegel8bb09342016-10-17 17:49:31 -0700358 MockInteractions.tap(yesButton); // Confirm the group.
Logan Hanksa008aa92016-07-22 10:31:12 -0700359 return observer;
Kasper Nilssonad908b12017-05-11 11:26:10 -0700360 }).then(() => {
Logan Hanksa008aa92016-07-22 10:31:12 -0700361 assert.isFalse(isVisible(element.$.reviewerConfirmationOverlay));
Kasper Nilssonad908b12017-05-11 11:26:10 -0700362 const additions = cc ?
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800363 element.$$('#ccs').additions() :
364 element.$.reviewers.additions();
Logan Hanksa008aa92016-07-22 10:31:12 -0700365 assert.deepEqual(
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800366 additions,
Logan Hanksa008aa92016-07-22 10:31:12 -0700367 [
368 {
369 group: {
370 id: 'id',
371 name: 'name',
Logan Hanksa008aa92016-07-22 10:31:12 -0700372 confirmed: true,
373 _group: true,
374 _pendingAdd: true,
375 },
376 },
377 ]);
378
379 // We should be focused on account entry input.
380 assert.equal(getActiveElement().id, 'input');
381 }).then(done);
Kasper Nilssonad908b12017-05-11 11:26:10 -0700382 }
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800383
Kasper Nilssonad908b12017-05-11 11:26:10 -0700384 test('cc confirmation', done => {
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800385 testConfirmationDialog(done, true);
386 });
387
Kasper Nilssonad908b12017-05-11 11:26:10 -0700388 test('reviewer confirmation', done => {
Logan Hanks43d4dbb2016-12-14 18:17:47 -0800389 testConfirmationDialog(done, false);
Logan Hanksa008aa92016-07-22 10:31:12 -0700390 });
Wyatt Allen4f4b3a72016-07-28 12:05:53 -0700391
Kasper Nilssonad908b12017-05-11 11:26:10 -0700392 test('_getStorageLocation', () => {
393 const actual = element._getStorageLocation();
Wyatt Allen4f4b3a72016-07-28 12:05:53 -0700394 assert.equal(actual.changeNum, changeNum);
Wyatt Allene05392a2017-06-01 16:23:51 -0700395 assert.equal(actual.patchNum, '@change');
Wyatt Allen4f4b3a72016-07-28 12:05:53 -0700396 assert.equal(actual.path, '@change');
397 });
398
Kasper Nilssonad908b12017-05-11 11:26:10 -0700399 test('gets draft from storage on open', () => {
400 const storedDraft = 'hello world';
Wyatt Allen4f4b3a72016-07-28 12:05:53 -0700401 getDraftCommentStub.returns({message: storedDraft});
402 element.open();
403 assert.isTrue(getDraftCommentStub.called);
404 assert.equal(element.draft, storedDraft);
405 });
406
Kasper Nilssonad908b12017-05-11 11:26:10 -0700407 test('blank if no stored draft', () => {
Wyatt Allen4f4b3a72016-07-28 12:05:53 -0700408 getDraftCommentStub.returns(null);
409 element.open();
410 assert.isTrue(getDraftCommentStub.called);
411 assert.equal(element.draft, '');
412 });
413
Kasper Nilssonad908b12017-05-11 11:26:10 -0700414 test('updates stored draft on edits', () => {
415 const firstEdit = 'hello';
416 const location = element._getStorageLocation();
Wyatt Allen4f4b3a72016-07-28 12:05:53 -0700417
418 element.draft = firstEdit;
419 element.flushDebouncer('store');
420
421 assert.isTrue(setDraftCommentStub.calledWith(location, firstEdit));
422
423 element.draft = '';
424 element.flushDebouncer('store');
425
426 assert.isTrue(eraseDraftCommentStub.calledWith(location));
427 });
Logan Hanksf1063a22016-07-22 16:05:04 -0700428
Kasper Nilssonad908b12017-05-11 11:26:10 -0700429 test('400 converts to human-readable server-error', done => {
430 sandbox.stub(window, 'fetch', () => {
431 const text = '....{"reviewers":{"id1":{"error":"first error"}},' +
Logan Hanksf1063a22016-07-22 16:05:04 -0700432 '"ccs":{"id2":{"error":"second error"}}}';
433 return Promise.resolve({
434 ok: false,
435 status: 400,
Kasper Nilssonad908b12017-05-11 11:26:10 -0700436 text() { return Promise.resolve(text); },
Logan Hanksf1063a22016-07-22 16:05:04 -0700437 });
438 });
439
Kasper Nilssonad908b12017-05-11 11:26:10 -0700440 element.addEventListener('server-error', event => {
Logan Hanksf1063a22016-07-22 16:05:04 -0700441 if (event.target !== element) {
442 return;
443 }
Kasper Nilssonad908b12017-05-11 11:26:10 -0700444 event.detail.response.text().then(body => {
Logan Hanksf1063a22016-07-22 16:05:04 -0700445 assert.equal(body, 'first error, second error');
Wyatt Allene013aad2017-05-18 16:49:04 -0700446 done();
Logan Hanksf1063a22016-07-22 16:05:04 -0700447 });
448 });
Andrew Bonventreefd0a532016-08-23 17:04:23 -0400449
450 // Async tick is needed because iron-selector content is distributed and
451 // distributed content requires an observer to be set up.
Wyatt Allene013aad2017-05-18 16:49:04 -0700452 flush(() => { element.send(); });
Logan Hanksa75fb052016-08-01 13:23:38 -0700453 });
Logan Hanksf1063a22016-07-22 16:05:04 -0700454
Kasper Nilssonad908b12017-05-11 11:26:10 -0700455 test('ccs are displayed if NoteDb is enabled', () => {
Logan Hanksa75fb052016-08-01 13:23:38 -0700456 function hasCc() {
457 flushAsynchronousOperations();
458 return !!element.$$('#ccs');
459 }
460
461 element.serverConfig = {};
462 assert.isFalse(hasCc());
463
464 element.serverConfig = {note_db_enabled: true};
465 assert.isTrue(hasCc());
466 });
467
Kasper Nilssonad908b12017-05-11 11:26:10 -0700468 test('filterReviewerSuggestion', () => {
469 const owner = makeAccount();
470 const reviewer1 = makeAccount();
471 const reviewer2 = makeGroup();
472 const cc1 = makeAccount();
473 const cc2 = makeGroup();
Kasper Nilssond5896712017-06-01 17:59:02 -0700474 let filter = element._filterReviewerSuggestionGenerator(false);
Logan Hanksa75fb052016-08-01 13:23:38 -0700475
476 element._owner = owner;
477 element._reviewers = [reviewer1, reviewer2];
478 element._ccs = [cc1, cc2];
479
Kasper Nilssond5896712017-06-01 17:59:02 -0700480 assert.isTrue(filter({account: makeAccount()}));
481 assert.isTrue(filter({group: makeGroup()}));
Logan Hanksa75fb052016-08-01 13:23:38 -0700482
483 // Owner should be excluded.
Kasper Nilssond5896712017-06-01 17:59:02 -0700484 assert.isFalse(filter({account: owner}));
Logan Hanksa75fb052016-08-01 13:23:38 -0700485
Kasper Nilssond5896712017-06-01 17:59:02 -0700486 // Existing and pending reviewers should be excluded when isCC = false.
487 assert.isFalse(filter({account: reviewer1}));
488 assert.isFalse(filter({group: reviewer2}));
Logan Hanksa75fb052016-08-01 13:23:38 -0700489
Kasper Nilssond5896712017-06-01 17:59:02 -0700490 filter = element._filterReviewerSuggestionGenerator(true);
491
492 // Existing and pending CCs should be excluded when isCC = true;.
493 assert.isFalse(filter({account: cc1}));
494 assert.isFalse(filter({group: cc2}));
Logan Hanksf1063a22016-07-22 16:05:04 -0700495 });
Logan Hanksfc57acc2016-08-05 13:54:50 -0700496
Kasper Nilssonad908b12017-05-11 11:26:10 -0700497 test('_chooseFocusTarget', () => {
Logan Hanksfc57acc2016-08-05 13:54:50 -0700498 element._account = null;
499 assert.strictEqual(
500 element._chooseFocusTarget(), element.FocusTarget.BODY);
501
502 element._account = {_account_id: 1};
503 assert.strictEqual(
504 element._chooseFocusTarget(), element.FocusTarget.BODY);
505
506 element.change.owner = {_account_id: 2};
507 assert.strictEqual(
508 element._chooseFocusTarget(), element.FocusTarget.BODY);
509
510 element.change.owner._account_id = 1;
511 element.change._reviewers = null;
512 assert.strictEqual(
513 element._chooseFocusTarget(), element.FocusTarget.REVIEWERS);
514
515 element._reviewers = [];
516 assert.strictEqual(
517 element._chooseFocusTarget(), element.FocusTarget.REVIEWERS);
518
519 element._reviewers.push({});
520 assert.strictEqual(
521 element._chooseFocusTarget(), element.FocusTarget.BODY);
522 });
Logan Hanksca3c4972016-09-14 16:25:56 -0700523
Kasper Nilssonad908b12017-05-11 11:26:10 -0700524 test('only send labels that have changed', done => {
525 flush(() => {
Logan Hanks02c781e2017-07-18 14:18:01 -0700526 stubSaveReview(review => {
Viktar Donich51bdb8d2017-07-14 11:39:24 -0700527 assert.deepEqual(review.labels, {
528 'Code-Review': 0,
529 'Verified': -1,
530 });
Kasper Nilsson4416acd2016-09-16 13:27:11 -0700531 });
Logan Hanksca3c4972016-09-14 16:25:56 -0700532
Kasper Nilssonad908b12017-05-11 11:26:10 -0700533 element.addEventListener('send', () => {
Kasper Nilsson4416acd2016-09-16 13:27:11 -0700534 done();
535 });
536 // Without wrapping this test in flush(), the below two calls to
537 // MockInteractions.tap() cause a race in some situations in shadow DOM.
538 // The send button can be tapped before the others, causing the test to
539 // fail.
Becky Siegela499e3d2017-04-06 16:57:13 -0700540
Becky Siegel61613932017-05-30 15:09:20 -0700541 element.$$('gr-label-scores').$$(
542 'gr-label-score-row[name="Verified"]').setSelectedValue(-1);
Kasper Nilsson4416acd2016-09-16 13:27:11 -0700543 MockInteractions.tap(element.$$('.send'));
Logan Hanksca3c4972016-09-14 16:25:56 -0700544 });
Logan Hanksca3c4972016-09-14 16:25:56 -0700545 });
Becky Siegel8bb09342016-10-17 17:49:31 -0700546
Kasper Nilssonad908b12017-05-11 11:26:10 -0700547 test('_processReviewerChange', () => {
548 const mockIndexSplices = function(toRemove) {
Kasper Nilssona8271552017-01-17 11:54:40 -0800549 return [{
550 removed: [toRemove],
551 }];
552 };
553
554 element._processReviewerChange(
555 mockIndexSplices(makeAccount()), 'REVIEWER');
556 assert.equal(element._reviewersPendingRemove.REVIEWER.length, 1);
557 });
558
Kasper Nilssonad908b12017-05-11 11:26:10 -0700559 test('_purgeReviewersPendingRemove', () => {
560 const removeStub = sandbox.stub(element, '_removeAccount');
561 const mock = function() {
Kasper Nilssona8271552017-01-17 11:54:40 -0800562 element._reviewersPendingRemove = {
563 test: [makeAccount()],
564 test2: [makeAccount(), makeAccount()],
565 };
566 };
Kasper Nilssonad908b12017-05-11 11:26:10 -0700567 const checkObjEmpty = function(obj) {
568 for (const prop in obj) {
Kasper Nilssona8271552017-01-17 11:54:40 -0800569 if (obj.hasOwnProperty(prop) && obj[prop].length) { return false; }
570 }
571 return true;
572 };
573 mock();
574 element._purgeReviewersPendingRemove(true); // Cancel
575 assert.isFalse(removeStub.called);
576 assert.isTrue(checkObjEmpty(element._reviewersPendingRemove));
577
578 mock();
579 element._purgeReviewersPendingRemove(false); // Submit
580 assert.isTrue(removeStub.called);
581 assert.isTrue(checkObjEmpty(element._reviewersPendingRemove));
582 });
583
Kasper Nilssonad908b12017-05-11 11:26:10 -0700584 test('_removeAccount', done => {
Kasper Nilssona8271552017-01-17 11:54:40 -0800585 sandbox.stub(element.$.restAPI, 'removeChangeReviewer')
586 .returns(Promise.resolve({ok: true}));
Kasper Nilssonad908b12017-05-11 11:26:10 -0700587 const arr = [makeAccount(), makeAccount()];
Kasper Nilssona8271552017-01-17 11:54:40 -0800588 element.change.reviewers = {
589 REVIEWER: arr.slice(),
590 };
591
Kasper Nilssonad908b12017-05-11 11:26:10 -0700592 element._removeAccount(arr[1], 'REVIEWER').then(() => {
Kasper Nilssona8271552017-01-17 11:54:40 -0800593 assert.equal(element.change.reviewers.REVIEWER.length, 1);
594 assert.deepEqual(element.change.reviewers.REVIEWER, arr.slice(0, 1));
595 done();
596 });
597 });
Logan Hanks64451c02017-03-22 13:39:40 -0700598
Kasper Nilssond5896712017-06-01 17:59:02 -0700599 test('moving from cc to reviewer', () => {
600 element.serverConfig = {note_db_enabled: true};
601 element._reviewersPendingRemove = {
602 CC: [],
603 REVIEWER: [],
604 };
605 flushAsynchronousOperations();
606
607 const reviewer1 = makeAccount();
608 const reviewer2 = makeAccount();
609 const reviewer3 = makeAccount();
610 const cc1 = makeAccount();
611 const cc2 = makeAccount();
612 const cc3 = makeAccount();
613 const cc4 = makeAccount();
614 element._reviewers = [reviewer1, reviewer2, reviewer3];
615 element._ccs = [cc1, cc2, cc3, cc4];
616 element.push('_reviewers', cc1);
617 flushAsynchronousOperations();
618
619 assert.deepEqual(element._reviewers,
620 [reviewer1, reviewer2, reviewer3, cc1]);
621 assert.deepEqual(element._ccs, [cc2, cc3, cc4]);
622 assert.deepEqual(element._reviewersPendingRemove.CC, [cc1]);
623
624 element.push('_reviewers', cc4, cc3);
625 flushAsynchronousOperations();
626
627 assert.deepEqual(element._reviewers,
628 [reviewer1, reviewer2, reviewer3, cc1, cc4, cc3]);
629 assert.deepEqual(element._ccs, [cc2]);
630 assert.deepEqual(element._reviewersPendingRemove.CC, [cc1, cc4, cc3]);
631 });
632
Kasper Nilssonad908b12017-05-11 11:26:10 -0700633 test('migrate reviewers between states', done => {
Logan Hanks64451c02017-03-22 13:39:40 -0700634 element.serverConfig = {note_db_enabled: true};
635 element._reviewersPendingRemove = {
636 CC: [],
637 REVIEWER: [],
638 };
639 flushAsynchronousOperations();
Kasper Nilssonad908b12017-05-11 11:26:10 -0700640 const reviewers = element.$.reviewers;
641 const ccs = element.$$('#ccs');
642 const reviewer1 = makeAccount();
643 const reviewer2 = makeAccount();
644 const cc1 = makeAccount();
645 const cc2 = makeAccount();
Kasper Nilsson7591ceb2017-05-12 16:16:53 -0700646 const cc3 = makeAccount();
Logan Hanks64451c02017-03-22 13:39:40 -0700647 element._reviewers = [reviewer1, reviewer2];
Kasper Nilsson7591ceb2017-05-12 16:16:53 -0700648 element._ccs = [cc1, cc2, cc3];
Logan Hanks64451c02017-03-22 13:39:40 -0700649
Kasper Nilssonad908b12017-05-11 11:26:10 -0700650 const mutations = [];
Logan Hanks64451c02017-03-22 13:39:40 -0700651
Logan Hanks02c781e2017-07-18 14:18:01 -0700652 stubSaveReview(review => mutations.push(...review.reviewers));
Logan Hanks64451c02017-03-22 13:39:40 -0700653
Kasper Nilssonad908b12017-05-11 11:26:10 -0700654 sandbox.stub(element, '_removeAccount', (account, type) => {
655 mutations.push({state: 'REMOVED', account});
Logan Hanks64451c02017-03-22 13:39:40 -0700656 return Promise.resolve();
657 });
658
659 // Remove and add to other field.
660 reviewers.fire('remove', {account: reviewer1});
661 ccs.$.entry.fire('add', {value: {account: reviewer1}});
662 ccs.fire('remove', {account: cc1});
Kasper Nilsson7591ceb2017-05-12 16:16:53 -0700663 ccs.fire('remove', {account: cc3});
Logan Hanks64451c02017-03-22 13:39:40 -0700664 reviewers.$.entry.fire('add', {value: {account: cc1}});
665
666 // Add to other field without removing from former field.
667 // (Currently not possible in UI, but this is a good consistency check).
668 reviewers.$.entry.fire('add', {value: {account: cc2}});
669 ccs.$.entry.fire('add', {value: {account: reviewer2}});
Kasper Nilssonad908b12017-05-11 11:26:10 -0700670 const mapReviewer = function(reviewer, opt_state) {
671 const result = {reviewer: reviewer._account_id, confirmed: undefined};
Logan Hanks64451c02017-03-22 13:39:40 -0700672 if (opt_state) {
673 result.state = opt_state;
674 }
675 return result;
676 };
677
Kasper Nilsson7591ceb2017-05-12 16:16:53 -0700678 // Send and purge and verify moves, delete cc3.
Logan Hanks64451c02017-03-22 13:39:40 -0700679 element.send()
Kasper Nilsson7591ceb2017-05-12 16:16:53 -0700680 .then(keepReviewers =>
681 element._purgeReviewersPendingRemove(false, keepReviewers))
Kasper Nilssonad908b12017-05-11 11:26:10 -0700682 .then(() => {
683 assert.deepEqual(
684 mutations, [
685 mapReviewer(cc1),
686 mapReviewer(cc2),
687 mapReviewer(reviewer1, 'CC'),
688 mapReviewer(reviewer2, 'CC'),
Kasper Nilsson7591ceb2017-05-12 16:16:53 -0700689 {account: cc3, state: 'REMOVED'},
Kasper Nilssonad908b12017-05-11 11:26:10 -0700690 ]);
691 done();
692 });
Logan Hanks64451c02017-03-22 13:39:40 -0700693 });
Kasper Nilssonff0745d2017-03-21 16:41:24 -0700694
Kasper Nilssonad908b12017-05-11 11:26:10 -0700695 test('emits cancel on esc key', () => {
696 const cancelHandler = sandbox.spy();
Kasper Nilssonff0745d2017-03-21 16:41:24 -0700697 element.addEventListener('cancel', cancelHandler);
698 MockInteractions.pressAndReleaseKeyOn(element, 27, null, 'esc');
699 flushAsynchronousOperations();
700
701 assert.isTrue(cancelHandler.called);
702 });
Logan Hanksdc65dde2017-04-27 11:45:23 +0200703
Mike Frysinger81b90bc2017-07-25 18:24:32 -0400704 test('should not send on enter key', () => {
705 element.addEventListener('send', () => assert.fail('wrongly called'));
706 MockInteractions.pressAndReleaseKeyOn(element, 13, null, 'enter');
707 flushAsynchronousOperations();
708 });
709
710 test('emit send on ctrl+enter key', done => {
711 element.addEventListener('send', () => done());
712 MockInteractions.pressAndReleaseKeyOn(element, 13, 'ctrl', 'enter');
713 flushAsynchronousOperations();
714 });
715
Kasper Nilssonad908b12017-05-11 11:26:10 -0700716 test('_computeMessagePlaceholder', () => {
Logan Hanksdc65dde2017-04-27 11:45:23 +0200717 assert.equal(
718 element._computeMessagePlaceholder(false),
719 'Say something nice...');
720 assert.equal(
721 element._computeMessagePlaceholder(true),
722 'Add a note for your reviewers...');
723 });
724
Kasper Nilssonad908b12017-05-11 11:26:10 -0700725 test('_computeSendButtonLabel', () => {
Logan Hanksdc65dde2017-04-27 11:45:23 +0200726 assert.equal(
727 element._computeSendButtonLabel(false),
728 'Send');
729 assert.equal(
730 element._computeSendButtonLabel(true),
731 'Start review');
732 });
Wyatt Allene013aad2017-05-18 16:49:04 -0700733
734 test('_handle400Error reviewrs and CCs', done => {
735 const error1 = 'error 1';
736 const error2 = 'error 2';
737 const error3 = 'error 3';
738 const response = {
739 status: 400,
740 text() {
741 return Promise.resolve(')]}\'' + JSON.stringify({
742 reviewers: {
743 username1: {
744 input: 'user 1',
745 error: error1,
746 },
747 username2: {
748 input: 'user 2',
749 error: error2,
750 },
751 },
752 ccs: {
753 username3: {
754 input: 'user 3',
755 error: error3,
Kasper Nilsson5a93dbd2017-05-19 10:43:02 -0700756 },
Wyatt Allene013aad2017-05-18 16:49:04 -0700757 },
758 }));
Kasper Nilsson5a93dbd2017-05-19 10:43:02 -0700759 },
Wyatt Allene013aad2017-05-18 16:49:04 -0700760 };
761 element.addEventListener('server-error', e => {
762 e.detail.response.text().then(text => {
763 assert.equal(text, [error1, error2, error3].join(', '));
764 done();
Kasper Nilsson5a93dbd2017-05-19 10:43:02 -0700765 });
Wyatt Allene013aad2017-05-18 16:49:04 -0700766 });
767 element._handle400Error(response);
768 });
769
770 test('_handle400Error CCs only', done => {
771 const error1 = 'error 1';
772 const response = {
773 status: 400,
774 text() {
775 return Promise.resolve(')]}\'' + JSON.stringify({
776 ccs: {
777 username1: {
778 input: 'user 1',
779 error: error1,
Kasper Nilsson5a93dbd2017-05-19 10:43:02 -0700780 },
Wyatt Allene013aad2017-05-18 16:49:04 -0700781 },
782 }));
Kasper Nilsson5a93dbd2017-05-19 10:43:02 -0700783 },
Wyatt Allene013aad2017-05-18 16:49:04 -0700784 };
785 element.addEventListener('server-error', e => {
786 e.detail.response.text().then(text => {
787 assert.equal(text, error1);
788 done();
Kasper Nilsson5a93dbd2017-05-19 10:43:02 -0700789 });
Wyatt Allene013aad2017-05-18 16:49:04 -0700790 });
791 element._handle400Error(response);
792 });
Logan Hanks68b082e2017-07-13 14:19:13 -0700793
Logan Hanks02c781e2017-07-18 14:18:01 -0700794 suite('post review API', () => {
795 let startReviewStub;
Logan Hanks68b082e2017-07-13 14:19:13 -0700796
Logan Hanks02c781e2017-07-18 14:18:01 -0700797 setup(() => {
798 startReviewStub = sandbox.stub(element.$.restAPI, 'startReview');
799 });
800
801 test('ready property in review input on start review', () => {
802 stubSaveReview(review => {
803 assert.isTrue(review.ready);
804 return {ready: true};
805 });
806 return element.send(true, true).then(() => {
807 assert.isFalse(startReviewStub.called);
808 });
809 });
810
811 test('no ready property in review input on save review', () => {
812 stubSaveReview(review => {
813 assert.isUndefined(review.ready);
814 return {};
815 });
816 return element.send(true, false).then(() => {
817 assert.isFalse(startReviewStub.called);
818 });
819 });
820
821 test('fall back to start review against old backend', () => {
822 stubSaveReview(review => {
823 return {}; // old backend won't set ready: true
824 });
825
826 return element.send(true, true).then(() => {
827 assert.isTrue(startReviewStub.called);
828 }).then(() => {
829 startReviewStub.reset();
830 return element.send(true, false);
831 }).then(() => {
832 assert.isFalse(startReviewStub.called);
833 });
834 });
Logan Hanks68b082e2017-07-13 14:19:13 -0700835 });
836
Logan Hanks02c781e2017-07-18 14:18:01 -0700837 suite('start review and save buttons', () => {
838 let sendStub;
839
Logan Hanks68b082e2017-07-13 14:19:13 -0700840 setup(() => {
Logan Hanks02c781e2017-07-18 14:18:01 -0700841 sendStub = sandbox.stub(element, 'send', () => Promise.resolve());
Logan Hanks68b082e2017-07-13 14:19:13 -0700842 element.canBeStarted = true;
843 // Flush to make both Start/Save buttons appear in DOM.
844 flushAsynchronousOperations();
845 });
846
847 test('start review sets ready', () => {
848 MockInteractions.tap(element.$$('.send'));
849 flushAsynchronousOperations();
Logan Hanks02c781e2017-07-18 14:18:01 -0700850 assert.isTrue(sendStub.calledWith(true, true));
Logan Hanks68b082e2017-07-13 14:19:13 -0700851 });
852
853 test('save review doesn\'t set ready', () => {
854 MockInteractions.tap(element.$$('.save'));
855 flushAsynchronousOperations();
Logan Hanks02c781e2017-07-18 14:18:01 -0700856 assert.isTrue(sendStub.calledWith(true, false));
Logan Hanks68b082e2017-07-13 14:19:13 -0700857 });
858 });
Logan Hanks3f1a6ff2017-07-25 11:05:23 -0700859
860 test('dummy message to force email on start review', () => {
861 stubSaveReview(review => {
862 assert.equal(review.message, element.START_REVIEW_MESSAGE);
863 return {ready: true};
864 });
865 return element.send(true, true);
866 });
Andrew Bonventref8b026d2015-12-09 17:55:54 -0500867 });
868</script>