blob: 81b9bded3395e1c76f168f213fc2e18752bf9fb0 [file] [log] [blame]
Andrew Bonventre09c8c242016-02-23 17:28:50 -05001<!DOCTYPE html>
2<!--
Dave Borowitz8cdc76b2018-03-26 10:04:27 -04003@license
Andrew Bonventre09c8c242016-02-23 17:28:50 -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">
Tao Zhoud90e7f42020-04-29 17:07:14 +020020<meta charset="utf-8">
Andrew Bonventre09c8c242016-02-23 17:28:50 -050021<title>gr-change-metadata</title>
Tao Zhou8ef16f72019-11-18 14:14:36 -080022
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010023<script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
Andrew Bonventre09c8c242016-02-23 17:28:50 -050024
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010025<script src="/node_modules/@webcomponents/webcomponentsjs/webcomponents-lite.js"></script>
26<script src="/components/wct-browser-legacy/browser.js"></script>
Viktar Donich29e1ce52017-03-28 17:02:44 -070027
Andrew Bonventre09c8c242016-02-23 17:28:50 -050028<test-fixture id="basic">
29 <template>
30 <gr-change-metadata></gr-change-metadata>
31 </template>
32</test-fixture>
33
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010034<script type="module">
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010035import '../../../test/common-test-setup.js';
36import '../../core/gr-router/gr-router.js';
37import './gr-change-metadata.js';
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +020038import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
Dmitrii Filippov35aea692020-04-07 12:14:11 +020039import {pluginLoader} from '../../shared/gr-js-api-interface/gr-plugin-loader.js';
Dmitrii Filippov5f25dc02020-04-07 18:49:00 +020040import {_testOnly_initGerritPluginApi} from '../../shared/gr-js-api-interface/gr-gerrit.js';
Dmitrii Filippov35aea692020-04-07 12:14:11 +020041
Dmitrii Filippov5f25dc02020-04-07 18:49:00 +020042const pluginApi = _testOnly_initGerritPluginApi();
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +020043
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010044suite('gr-change-metadata tests', () => {
45 let element;
46 let sandbox;
47
48 setup(() => {
49 sandbox = sinon.sandbox.create();
50 stub('gr-endpoint-decorator', {
51 _import: sandbox.stub().returns(Promise.resolve()),
52 });
53 stub('gr-rest-api-interface', {
54 getConfig() { return Promise.resolve({}); },
55 getLoggedIn() { return Promise.resolve(false); },
56 });
57
58 element = fixture('basic');
59 });
60
61 teardown(() => {
62 sandbox.restore();
63 });
64
65 test('computed fields', () => {
66 assert.isFalse(element._computeHideStrategy({status: 'NEW'}));
67 assert.isTrue(element._computeHideStrategy({status: 'MERGED'}));
68 assert.isTrue(element._computeHideStrategy({status: 'ABANDONED'}));
69 assert.equal(element._computeStrategy({submit_type: 'CHERRY_PICK'}),
70 'Cherry Pick');
71 assert.equal(element._computeStrategy({submit_type: 'REBASE_ALWAYS'}),
72 'Rebase Always');
73 });
74
75 test('computed fields requirements', () => {
76 assert.isFalse(element._computeShowRequirements({status: 'MERGED'}));
77 assert.isFalse(element._computeShowRequirements({status: 'ABANDONED'}));
78
79 // No labels and no requirements: submit status is useless
80 assert.isFalse(element._computeShowRequirements({
81 status: 'NEW',
82 labels: {},
83 }));
84
85 // Work in Progress: submit status should be present
86 assert.isTrue(element._computeShowRequirements({
87 status: 'NEW',
88 labels: {},
89 work_in_progress: true,
90 }));
91
92 // We have at least one reason to display Submit Status
93 assert.isTrue(element._computeShowRequirements({
94 status: 'NEW',
95 labels: {
96 Verified: {
97 approved: false,
98 },
99 },
100 requirements: [],
101 }));
102 assert.isTrue(element._computeShowRequirements({
103 status: 'NEW',
104 labels: {},
105 requirements: [{
106 fallback_text: 'Resolve all comments',
107 status: 'OK',
108 }],
109 }));
110 });
111
112 test('show strategy for open change', () => {
113 element.change = {status: 'NEW', submit_type: 'CHERRY_PICK', labels: {}};
114 flushAsynchronousOperations();
115 const strategy = element.shadowRoot
116 .querySelector('.strategy');
117 assert.ok(strategy);
118 assert.isFalse(strategy.hasAttribute('hidden'));
119 assert.equal(strategy.children[1].innerHTML, 'Cherry Pick');
120 });
121
122 test('hide strategy for closed change', () => {
123 element.change = {status: 'MERGED', labels: {}};
124 flushAsynchronousOperations();
125 assert.isTrue(element.shadowRoot
126 .querySelector('.strategy').hasAttribute('hidden'));
127 });
128
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +0200129 test('weblinks use GerritNav interface', () => {
130 const weblinksStub = sandbox.stub(GerritNav, '_generateWeblinks')
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100131 .returns([{name: 'stubb', url: '#s'}]);
132 element.commitInfo = {};
133 element.serverConfig = {};
134 flushAsynchronousOperations();
135 const webLinks = element.$.webLinks;
136 assert.isTrue(weblinksStub.called);
137 assert.isFalse(webLinks.hasAttribute('hidden'));
138 assert.equal(element._computeWebLinks(element.commitInfo).length, 1);
139 });
140
141 test('weblinks hidden when no weblinks', () => {
142 element.commitInfo = {};
143 element.serverConfig = {};
144 flushAsynchronousOperations();
145 const webLinks = element.$.webLinks;
146 assert.isTrue(webLinks.hasAttribute('hidden'));
147 });
148
149 test('weblinks hidden when only gitiles weblink', () => {
150 element.commitInfo = {web_links: [{name: 'gitiles', url: '#'}]};
151 element.serverConfig = {};
152 flushAsynchronousOperations();
153 const webLinks = element.$.webLinks;
154 assert.isTrue(webLinks.hasAttribute('hidden'));
155 assert.equal(element._computeWebLinks(element.commitInfo), null);
156 });
157
158 test('weblinks hidden when sole weblink is set as primary', () => {
159 const browser = 'browser';
160 element.commitInfo = {web_links: [{name: browser, url: '#'}]};
161 element.serverConfig = {
162 gerrit: {
163 primary_weblink_name: browser,
164 },
165 };
166 flushAsynchronousOperations();
167 const webLinks = element.$.webLinks;
168 assert.isTrue(webLinks.hasAttribute('hidden'));
169 });
170
171 test('weblinks are visible when other weblinks', () => {
172 const router = document.createElement('gr-router');
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +0200173 sandbox.stub(GerritNav, '_generateWeblinks',
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100174 router._generateWeblinks.bind(router));
175
176 element.commitInfo = {web_links: [{name: 'test', url: '#'}]};
177 flushAsynchronousOperations();
178 const webLinks = element.$.webLinks;
179 assert.isFalse(webLinks.hasAttribute('hidden'));
180 assert.equal(element._computeWebLinks(element.commitInfo).length, 1);
181 // With two non-gitiles weblinks, there are two returned.
182 element.commitInfo = {
183 web_links: [{name: 'test', url: '#'}, {name: 'test2', url: '#'}]};
184 assert.equal(element._computeWebLinks(element.commitInfo).length, 2);
185 });
186
187 test('weblinks are visible when gitiles and other weblinks', () => {
188 const router = document.createElement('gr-router');
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +0200189 sandbox.stub(GerritNav, '_generateWeblinks',
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100190 router._generateWeblinks.bind(router));
191
192 element.commitInfo = {
193 web_links: [{name: 'test', url: '#'}, {name: 'gitiles', url: '#'}]};
194 flushAsynchronousOperations();
195 const webLinks = element.$.webLinks;
196 assert.isFalse(webLinks.hasAttribute('hidden'));
197 // Only the non-gitiles weblink is returned.
198 assert.equal(element._computeWebLinks(element.commitInfo).length, 1);
199 });
200
201 suite('_getNonOwnerRole', () => {
202 let change;
Andrew Bonventre09c8c242016-02-23 17:28:50 -0500203
Kasper Nilsson5514e0b2017-05-16 12:45:21 -0700204 setup(() => {
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100205 change = {
David Ostrovskyfc2599f2018-11-13 06:38:45 -0800206 owner: {
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100207 email: 'abc@def',
David Ostrovskyfc2599f2018-11-13 06:38:45 -0800208 _account_id: 1019328,
209 },
210 revisions: {
211 rev1: {
212 _number: 1,
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100213 uploader: {
214 email: 'ghi@def',
215 _account_id: 1011123,
216 },
217 commit: {
218 author: {email: 'jkl@def'},
219 committer: {email: 'ghi@def'},
David Ostrovskyfc2599f2018-11-13 06:38:45 -0800220 },
221 },
222 },
223 current_revision: 'rev1',
David Ostrovskyfc2599f2018-11-13 06:38:45 -0800224 };
David Ostrovskyfc2599f2018-11-13 06:38:45 -0800225 });
226
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100227 suite('role=uploader', () => {
228 test('_getNonOwnerRole for uploader', () => {
229 assert.deepEqual(
230 element._getNonOwnerRole(change, element._CHANGE_ROLE.UPLOADER),
231 {email: 'ghi@def', _account_id: 1011123});
Becky Siegel1f0ca992017-01-20 15:41:52 -0800232 });
233
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100234 test('_getNonOwnerRole that it does not return uploader', () => {
235 // Set the uploader email to be the same as the owner.
236 change.revisions.rev1.uploader._account_id = 1019328;
237 assert.isNull(element._getNonOwnerRole(change,
238 element._CHANGE_ROLE.UPLOADER));
Becky Siegel1f0ca992017-01-20 15:41:52 -0800239 });
240
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100241 test('_getNonOwnerRole null for uploader with no current rev', () => {
242 delete change.current_revision;
243 assert.isNull(element._getNonOwnerRole(change,
244 element._CHANGE_ROLE.UPLOADER));
Becky Siegel1f0ca992017-01-20 15:41:52 -0800245 });
246
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100247 test('_computeShowRoleClass show uploader', () => {
248 assert.equal(element._computeShowRoleClass(
249 change, element._CHANGE_ROLE.UPLOADER), '');
250 });
251
252 test('_computeShowRoleClass hide uploader', () => {
253 // Set the uploader email to be the same as the owner.
254 change.revisions.rev1.uploader._account_id = 1019328;
255 assert.equal(element._computeShowRoleClass(change,
256 element._CHANGE_ROLE.UPLOADER), 'hideDisplay');
Becky Siegel1f0ca992017-01-20 15:41:52 -0800257 });
258 });
259
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100260 suite('role=committer', () => {
261 test('_getNonOwnerRole for committer', () => {
262 assert.deepEqual(
263 element._getNonOwnerRole(change, element._CHANGE_ROLE.COMMITTER),
264 {email: 'ghi@def'});
Paladox none3785e532017-07-23 18:37:44 +0000265 });
266
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100267 test('_getNonOwnerRole that it does not return committer', () => {
268 // Set the committer email to be the same as the owner.
269 change.revisions.rev1.commit.committer.email = 'abc@def';
270 assert.isNull(element._getNonOwnerRole(change,
271 element._CHANGE_ROLE.COMMITTER));
Paladox none3785e532017-07-23 18:37:44 +0000272 });
273
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100274 test('_getNonOwnerRole null for committer with no current rev', () => {
275 delete change.current_revision;
276 assert.isNull(element._getNonOwnerRole(change,
277 element._CHANGE_ROLE.COMMITTER));
Paladox none3785e532017-07-23 18:37:44 +0000278 });
279
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100280 test('_getNonOwnerRole null for committer with no commit', () => {
281 delete change.revisions.rev1.commit;
282 assert.isNull(element._getNonOwnerRole(change,
283 element._CHANGE_ROLE.COMMITTER));
284 });
285
286 test('_getNonOwnerRole null for committer with no committer', () => {
287 delete change.revisions.rev1.commit.committer;
288 assert.isNull(element._getNonOwnerRole(change,
289 element._CHANGE_ROLE.COMMITTER));
Paladox none3785e532017-07-23 18:37:44 +0000290 });
291 });
292
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100293 suite('role=author', () => {
294 test('_getNonOwnerRole for author', () => {
295 assert.deepEqual(
296 element._getNonOwnerRole(change, element._CHANGE_ROLE.AUTHOR),
297 {email: 'jkl@def'});
Kasper Nilssonf0f57402016-09-28 14:56:06 -0700298 });
299
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100300 test('_getNonOwnerRole that it does not return author', () => {
301 // Set the author email to be the same as the owner.
302 change.revisions.rev1.commit.author.email = 'abc@def';
303 assert.isNull(element._getNonOwnerRole(change,
304 element._CHANGE_ROLE.AUTHOR));
Kasper Nilssonf0f57402016-09-28 14:56:06 -0700305 });
Wyatt Allen2f2a3a52016-10-10 14:04:20 -0700306
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100307 test('_getNonOwnerRole null for author with no current rev', () => {
308 delete change.current_revision;
309 assert.isNull(element._getNonOwnerRole(change,
310 element._CHANGE_ROLE.AUTHOR));
Wyatt Allen2f2a3a52016-10-10 14:04:20 -0700311 });
Kasper Nilsson42b1af42016-11-18 13:49:05 -0800312
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100313 test('_getNonOwnerRole null for author with no commit', () => {
314 delete change.revisions.rev1.commit;
315 assert.isNull(element._getNonOwnerRole(change,
316 element._CHANGE_ROLE.AUTHOR));
Kasper Nilsson42b1af42016-11-18 13:49:05 -0800317 });
Kasper Nilsson9bbd47c2017-01-09 10:14:36 -0800318
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100319 test('_getNonOwnerRole null for author with no author', () => {
320 delete change.revisions.rev1.commit.author;
321 assert.isNull(element._getNonOwnerRole(change,
322 element._CHANGE_ROLE.AUTHOR));
Viktar Donich0bb43e42017-11-17 11:16:21 -0800323 });
324 });
Andrew Bonventre09c8c242016-02-23 17:28:50 -0500325 });
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100326
327 test('Push Certificate Validation test BAD', () => {
328 const serverConfig = {
329 receive: {
330 enable_signed_push: true,
331 },
332 };
333 const change = {
334 change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca',
335 owner: {
336 _account_id: 1019328,
337 },
338 revisions: {
339 rev1: {
340 _number: 1,
341 push_certificate: {
342 key: {
343 status: 'BAD',
344 problems: [
345 'No public keys found for key ID E5E20E52',
346 ],
347 },
348 },
349 },
350 },
351 current_revision: 'rev1',
352 status: 'NEW',
353 labels: {},
354 mergeable: true,
355 };
356 const result =
357 element._computePushCertificateValidation(serverConfig, change);
358 assert.equal(result.message,
359 'Push certificate is invalid:\n' +
360 'No public keys found for key ID E5E20E52');
361 assert.equal(result.icon, 'gr-icons:close');
362 assert.equal(result.class, 'invalid');
363 });
364
365 test('Push Certificate Validation test TRUSTED', () => {
366 const serverConfig = {
367 receive: {
368 enable_signed_push: true,
369 },
370 };
371 const change = {
372 change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca',
373 owner: {
374 _account_id: 1019328,
375 },
376 revisions: {
377 rev1: {
378 _number: 1,
379 push_certificate: {
380 key: {
381 status: 'TRUSTED',
382 },
383 },
384 },
385 },
386 current_revision: 'rev1',
387 status: 'NEW',
388 labels: {},
389 mergeable: true,
390 };
391 const result =
392 element._computePushCertificateValidation(serverConfig, change);
393 assert.equal(result.message,
394 'Push certificate is valid and key is trusted');
395 assert.equal(result.icon, 'gr-icons:check');
396 assert.equal(result.class, 'trusted');
397 });
398
399 test('Push Certificate Validation is missing test', () => {
400 const serverConfig = {
401 receive: {
402 enable_signed_push: true,
403 },
404 };
405 const change = {
406 change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca',
407 owner: {
408 _account_id: 1019328,
409 },
410 revisions: {
411 rev1: {
412 _number: 1,
413 },
414 },
415 current_revision: 'rev1',
416 status: 'NEW',
417 labels: {},
418 mergeable: true,
419 };
420 const result =
421 element._computePushCertificateValidation(serverConfig, change);
422 assert.equal(result.message,
423 'This patch set was created without a push certificate');
424 assert.equal(result.icon, 'gr-icons:help');
425 assert.equal(result.class, 'help');
426 });
427
428 test('_computeParents', () => {
David Pursehousef7d7df42020-05-21 14:28:11 +0900429 const revision = {commit: {parents: [{commit: '123', subject: 'abc'}]}};
430 assert.isUndefined(element._computeParents({}));
431 assert.equal(element._computeParents(revision), revision.commit.parents);
432 });
433
434 test('_currentParents', () => {
435 element.revision = {
436 commit: {parents: [{commit: '123', subject: 'abc'}]},
437 };
438 assert.equal(element._currentParents[0].commit, '123');
439 element.revision = {
440 commit: {parents: [{commit: '12345', subject: 'abc'}]},
441 };
442 assert.equal(element._currentParents[0].commit, '12345');
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100443 });
444
445 test('_computeParentsLabel', () => {
446 const parent = {commit: 'abc123', subject: 'My parent commit'};
447 assert.equal(element._computeParentsLabel([parent]), 'Parent');
448 assert.equal(element._computeParentsLabel([parent, parent]),
449 'Parents');
450 });
451
452 test('_computeParentListClass', () => {
453 const parent = {commit: 'abc123', subject: 'My parent commit'};
454 assert.equal(element._computeParentListClass([parent], true),
455 'parentList nonMerge current');
456 assert.equal(element._computeParentListClass([parent], false),
457 'parentList nonMerge notCurrent');
458 assert.equal(element._computeParentListClass([parent, parent], false),
459 'parentList merge notCurrent');
460 assert.equal(element._computeParentListClass([parent, parent], true),
461 'parentList merge current');
462 });
463
464 test('_showAddTopic', () => {
465 assert.isTrue(element._showAddTopic(null, false));
466 assert.isTrue(element._showAddTopic({base: {topic: null}}, false));
467 assert.isFalse(element._showAddTopic({base: {topic: null}}, true));
468 assert.isFalse(element._showAddTopic({base: {topic: 'foo'}}, true));
469 assert.isFalse(element._showAddTopic({base: {topic: 'foo'}}, false));
470 });
471
472 test('_showTopicChip', () => {
473 assert.isFalse(element._showTopicChip(null, false));
474 assert.isFalse(element._showTopicChip({base: {topic: null}}, false));
475 assert.isFalse(element._showTopicChip({base: {topic: null}}, true));
476 assert.isFalse(element._showTopicChip({base: {topic: 'foo'}}, true));
477 assert.isTrue(element._showTopicChip({base: {topic: 'foo'}}, false));
478 });
479
480 test('_showCherryPickOf', () => {
481 assert.isFalse(element._showCherryPickOf(null));
482 assert.isFalse(element._showCherryPickOf({
483 base: {
484 cherry_pick_of_change: null,
485 cherry_pick_of_patch_set: null,
486 },
487 }));
488 assert.isTrue(element._showCherryPickOf({
489 base: {
490 cherry_pick_of_change: 123,
491 cherry_pick_of_patch_set: 1,
492 },
493 }));
494 });
495
496 suite('Topic removal', () => {
497 let change;
498 setup(() => {
499 change = {
500 _number: 'the number',
501 actions: {
502 topic: {enabled: false},
503 },
504 change_id: 'the id',
505 topic: 'the topic',
506 status: 'NEW',
507 submit_type: 'CHERRY_PICK',
508 labels: {
509 test: {
510 all: [{_account_id: 1, name: 'bojack', value: 1}],
511 default_value: 0,
512 values: [],
513 },
514 },
515 removable_reviewers: [],
516 };
517 });
518
519 test('_computeTopicReadOnly', () => {
520 let mutable = false;
521 assert.isTrue(element._computeTopicReadOnly(mutable, change));
522 mutable = true;
523 assert.isTrue(element._computeTopicReadOnly(mutable, change));
524 change.actions.topic.enabled = true;
525 assert.isFalse(element._computeTopicReadOnly(mutable, change));
526 mutable = false;
527 assert.isTrue(element._computeTopicReadOnly(mutable, change));
528 });
529
530 test('topic read only hides delete button', () => {
531 element.account = {};
532 element.change = change;
533 flushAsynchronousOperations();
534 const button = element.shadowRoot
535 .querySelector('gr-linked-chip').shadowRoot
536 .querySelector('gr-button');
537 assert.isTrue(button.hasAttribute('hidden'));
538 });
539
540 test('topic not read only does not hide delete button', () => {
541 element.account = {test: true};
542 change.actions.topic.enabled = true;
543 element.change = change;
544 flushAsynchronousOperations();
545 const button = element.shadowRoot
546 .querySelector('gr-linked-chip').shadowRoot
547 .querySelector('gr-button');
548 assert.isFalse(button.hasAttribute('hidden'));
549 });
550 });
551
552 suite('Hashtag removal', () => {
553 let change;
554 setup(() => {
555 change = {
556 _number: 'the number',
557 actions: {
558 hashtags: {enabled: false},
559 },
560 change_id: 'the id',
561 hashtags: ['test-hashtag'],
562 status: 'NEW',
563 submit_type: 'CHERRY_PICK',
564 labels: {
565 test: {
566 all: [{_account_id: 1, name: 'bojack', value: 1}],
567 default_value: 0,
568 values: [],
569 },
570 },
571 removable_reviewers: [],
572 };
573 });
574
575 test('_computeHashtagReadOnly', () => {
576 flushAsynchronousOperations();
577 let mutable = false;
578 assert.isTrue(element._computeHashtagReadOnly(mutable, change));
579 mutable = true;
580 assert.isTrue(element._computeHashtagReadOnly(mutable, change));
581 change.actions.hashtags.enabled = true;
582 assert.isFalse(element._computeHashtagReadOnly(mutable, change));
583 mutable = false;
584 assert.isTrue(element._computeHashtagReadOnly(mutable, change));
585 });
586
587 test('hashtag read only hides delete button', () => {
588 flushAsynchronousOperations();
589 element.account = {};
590 element.change = change;
591 flushAsynchronousOperations();
592 const button = element.shadowRoot
593 .querySelector('gr-linked-chip').shadowRoot
594 .querySelector('gr-button');
595 assert.isTrue(button.hasAttribute('hidden'));
596 });
597
598 test('hashtag not read only does not hide delete button', () => {
599 flushAsynchronousOperations();
600 element.account = {test: true};
601 change.actions.hashtags.enabled = true;
602 element.change = change;
603 flushAsynchronousOperations();
604 const button = element.shadowRoot
605 .querySelector('gr-linked-chip').shadowRoot
606 .querySelector('gr-button');
607 assert.isFalse(button.hasAttribute('hidden'));
608 });
609 });
610
611 suite('remove reviewer votes', () => {
612 setup(() => {
613 sandbox.stub(element, '_computeTopicReadOnly').returns(true);
614 element.change = {
615 _number: 42,
616 change_id: 'the id',
617 actions: [],
618 topic: 'the topic',
619 status: 'NEW',
620 submit_type: 'CHERRY_PICK',
621 labels: {
622 test: {
623 all: [{_account_id: 1, name: 'bojack', value: 1}],
624 default_value: 0,
625 values: [],
626 },
627 },
628 removable_reviewers: [],
629 };
630 flushAsynchronousOperations();
631 });
632
633 suite('assignee field', () => {
634 const dummyAccount = {
635 _account_id: 1,
636 name: 'bojack',
637 };
638 const change = {
639 actions: {
640 assignee: {enabled: false},
641 },
642 assignee: dummyAccount,
643 };
644 let deleteStub;
645 let setStub;
646
647 setup(() => {
648 deleteStub = sandbox.stub(element.$.restAPI, 'deleteAssignee');
649 setStub = sandbox.stub(element.$.restAPI, 'setAssignee');
Dmitrii Filippov8b416b02020-03-18 15:55:22 +0100650 element.serverConfig = {
651 change: {
652 enable_assignee: true,
653 },
654 };
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100655 });
656
657 test('changing change recomputes _assignee', () => {
658 assert.isFalse(!!element._assignee.length);
659 const change = element.change;
660 change.assignee = dummyAccount;
661 element._changeChanged(change);
662 assert.deepEqual(element._assignee[0], dummyAccount);
663 });
664
665 test('modifying _assignee calls API', () => {
666 assert.isFalse(!!element._assignee.length);
667 element.set('_assignee', [dummyAccount]);
668 assert.isTrue(setStub.calledOnce);
669 assert.deepEqual(element.change.assignee, dummyAccount);
670 element.set('_assignee', [dummyAccount]);
671 assert.isTrue(setStub.calledOnce);
672 element.set('_assignee', []);
673 assert.isTrue(deleteStub.calledOnce);
674 assert.equal(element.change.assignee, undefined);
675 element.set('_assignee', []);
676 assert.isTrue(deleteStub.calledOnce);
677 });
678
679 test('_computeAssigneeReadOnly', () => {
680 let mutable = false;
681 assert.isTrue(element._computeAssigneeReadOnly(mutable, change));
682 mutable = true;
683 assert.isTrue(element._computeAssigneeReadOnly(mutable, change));
684 change.actions.assignee.enabled = true;
685 assert.isFalse(element._computeAssigneeReadOnly(mutable, change));
686 mutable = false;
687 assert.isTrue(element._computeAssigneeReadOnly(mutable, change));
688 });
689 });
690
691 test('changing topic', () => {
692 const newTopic = 'the new topic';
693 sandbox.stub(element.$.restAPI, 'setChangeTopic').returns(
694 Promise.resolve(newTopic));
695 element._handleTopicChanged({}, newTopic);
696 const topicChangedSpy = sandbox.spy();
697 element.addEventListener('topic-changed', topicChangedSpy);
698 assert.isTrue(element.$.restAPI.setChangeTopic.calledWith(
699 42, newTopic));
700 return element.$.restAPI.setChangeTopic.lastCall.returnValue
701 .then(() => {
702 assert.equal(element.change.topic, newTopic);
703 assert.isTrue(topicChangedSpy.called);
704 });
705 });
706
707 test('topic removal', () => {
708 sandbox.stub(element.$.restAPI, 'setChangeTopic').returns(
709 Promise.resolve());
710 const chip = element.shadowRoot
711 .querySelector('gr-linked-chip');
712 const remove = chip.$.remove;
713 const topicChangedSpy = sandbox.spy();
714 element.addEventListener('topic-changed', topicChangedSpy);
715 MockInteractions.tap(remove);
716 assert.isTrue(chip.disabled);
717 assert.isTrue(element.$.restAPI.setChangeTopic.calledWith(
718 42, null));
719 return element.$.restAPI.setChangeTopic.lastCall.returnValue
720 .then(() => {
721 assert.isFalse(chip.disabled);
722 assert.equal(element.change.topic, '');
723 assert.isTrue(topicChangedSpy.called);
724 });
725 });
726
727 test('changing hashtag', () => {
728 flushAsynchronousOperations();
729 element._newHashtag = 'new hashtag';
730 const newHashtag = ['new hashtag'];
731 sandbox.stub(element.$.restAPI, 'setChangeHashtag').returns(
732 Promise.resolve(newHashtag));
733 element._handleHashtagChanged({}, 'new hashtag');
734 assert.isTrue(element.$.restAPI.setChangeHashtag.calledWith(
735 42, {add: ['new hashtag']}));
736 return element.$.restAPI.setChangeHashtag.lastCall.returnValue
737 .then(() => {
738 assert.equal(element.change.hashtags, newHashtag);
739 });
740 });
741 });
742
743 test('editTopic', () => {
744 element.account = {test: true};
745 element.change = {actions: {topic: {enabled: true}}};
746 flushAsynchronousOperations();
747
748 const label = element.shadowRoot
749 .querySelector('.topicEditableLabel');
750 assert.ok(label);
751 sandbox.stub(label, 'open');
752 element.editTopic();
753 flushAsynchronousOperations();
754
755 assert.isTrue(label.open.called);
756 });
757
758 suite('plugin endpoints', () => {
759 test('endpoint params', done => {
760 element.change = {labels: {}};
761 element.revision = {};
762 let hookEl;
763 let plugin;
Dmitrii Filippov5f25dc02020-04-07 18:49:00 +0200764 pluginApi.install(
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100765 p => {
766 plugin = p;
767 plugin.hook('change-metadata-item').getLastAttached()
768 .then(el => hookEl = el);
769 },
770 '0.1',
771 'http://some/plugins/url.html');
Dmitrii Filippov35aea692020-04-07 12:14:11 +0200772 pluginLoader.loadPlugins([]);
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100773 flush(() => {
774 assert.strictEqual(hookEl.plugin, plugin);
775 assert.strictEqual(hookEl.change, element.change);
776 assert.strictEqual(hookEl.revision, element.revision);
777 done();
778 });
779 });
780 });
781});
Andrew Bonventre09c8c242016-02-23 17:28:50 -0500782</script>