blob: 31edd4cf392a1248489e311f1f3261a96a029279 [file] [log] [blame]
Copybara854996b2021-09-07 19:36:02 +00001// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import {assert} from 'chai';
6import sinon from 'sinon';
7import {MrIssuePage} from './mr-issue-page.js';
8import {store, resetState} from 'reducers/base.js';
9import * as issueV0 from 'reducers/issueV0.js';
10import {prpcClient} from 'prpc-client-instance.js';
11
12let element;
13let loadingElement;
14let fetchErrorElement;
15let deletedElement;
16let movedElement;
17let issueElement;
18
19function populateElementReferences() {
20 loadingElement = element.querySelector('#loading');
21 fetchErrorElement = element.querySelector('#fetch-error');
22 deletedElement = element.querySelector('#deleted');
23 movedElement = element.querySelector('#moved');
24 issueElement = element.querySelector('#issue');
25}
26
27describe('mr-issue-page', () => {
28 beforeEach(() => {
29 store.dispatch(resetState());
30 element = document.createElement('mr-issue-page');
31 document.body.appendChild(element);
32 sinon.stub(prpcClient, 'call');
33 // TODO(ehmaldonado): Remove once the old autocomplete code is deprecated.
34 window.TKR_populateAutocomplete = () => {};
35 });
36
37 afterEach(() => {
38 document.body.removeChild(element);
39 prpcClient.call.restore();
40 // TODO(ehmaldonado): Remove once the old autocomplete code is deprecated.
41 window.TKR_populateAutocomplete = undefined;
42 });
43
44 it('initializes', () => {
45 assert.instanceOf(element, MrIssuePage);
46 });
47
48 describe('_pageTitle', () => {
49 it('displays loading when no issue', () => {
50 assert.equal(element._pageTitle({}, {}), 'Loading issue...');
51 });
52
53 it('display issue ID when available', () => {
54 assert.equal(element._pageTitle({projectName: 'test', localId: 1}, {}),
55 '1 - Loading issue...');
56 });
57
58 it('display deleted issues', () => {
59 assert.equal(element._pageTitle({projectName: 'test', localId: 1},
60 {projectName: 'test', localId: 1, isDeleted: true},
61 ), '1 - Deleted issue');
62 });
63
64 it('displays loaded issue', () => {
65 assert.equal(element._pageTitle({projectName: 'test', localId: 2},
66 {projectName: 'test', localId: 2, summary: 'test'}), '2 - test');
67 });
68 });
69
70 it('issue not loaded yet', async () => {
71 // Prevent unrelated Redux changes from affecting this test.
72 // TODO(zhangtiff): Find a more canonical way to test components
73 // in and out of Redux.
74 sinon.stub(store, 'dispatch');
75
76 element.fetchingIssue = true;
77
78 await element.updateComplete;
79 populateElementReferences();
80
81 assert.isNotNull(loadingElement);
82 assert.isNull(fetchErrorElement);
83 assert.isNull(deletedElement);
84 assert.isNull(issueElement);
85
86 store.dispatch.restore();
87 });
88
89 it('no loading on future issue fetches', async () => {
90 element.issue = {localId: 222};
91 element.fetchingIssue = true;
92
93 await element.updateComplete;
94 populateElementReferences();
95
96 assert.isNull(loadingElement);
97 assert.isNull(fetchErrorElement);
98 assert.isNull(deletedElement);
99 assert.isNotNull(issueElement);
100 });
101
102 it('fetch error', async () => {
103 element.fetchingIssue = false;
104 element.fetchIssueError = 'error';
105
106 await element.updateComplete;
107 populateElementReferences();
108
109 assert.isNull(loadingElement);
110 assert.isNotNull(fetchErrorElement);
111 assert.isNull(deletedElement);
112 assert.isNull(issueElement);
113 });
114
115 it('deleted issue', async () => {
116 element.fetchingIssue = false;
117 element.issue = {isDeleted: true};
118
119 await element.updateComplete;
120 populateElementReferences();
121
122 assert.isNull(loadingElement);
123 assert.isNull(fetchErrorElement);
124 assert.isNotNull(deletedElement);
125 assert.isNull(issueElement);
126 });
127
128 it('normal issue', async () => {
129 element.fetchingIssue = false;
130 element.issue = {localId: 111};
131
132 await element.updateComplete;
133 populateElementReferences();
134
135 assert.isNull(loadingElement);
136 assert.isNull(fetchErrorElement);
137 assert.isNull(deletedElement);
138 assert.isNotNull(issueElement);
139 });
140
141 it('code font pref toggles attribute', async () => {
142 await element.updateComplete;
143
144 assert.isFalse(element.hasAttribute('codeFont'));
145
146 element.prefs = new Map([['code_font', true]]);
147 await element.updateComplete;
148
149 assert.isTrue(element.hasAttribute('codeFont'));
150
151 element.prefs = new Map([['code_font', false]]);
152 await element.updateComplete;
153
154 assert.isFalse(element.hasAttribute('codeFont'));
155 });
156
157 it('undeleting issue only shown if you have permissions', async () => {
158 sinon.stub(store, 'dispatch');
159
160 element.issue = {isDeleted: true};
161
162 await element.updateComplete;
163 populateElementReferences();
164
165 assert.isNotNull(deletedElement);
166
167 let button = element.querySelector('.undelete');
168 assert.isNull(button);
169
170 element.issuePermissions = ['deleteissue'];
171 await element.updateComplete;
172
173 button = element.querySelector('.undelete');
174 assert.isNotNull(button);
175
176 store.dispatch.restore();
177 });
178
179 it('undeleting issue updates page with issue', async () => {
180 const issueRef = {localId: 111, projectName: 'test'};
181 const deletedIssuePromise = Promise.resolve({
182 issue: {isDeleted: true},
183 });
184 const issuePromise = Promise.resolve({
185 issue: {localId: 111, projectName: 'test'},
186 });
187 const deletePromise = Promise.resolve({});
188
189 sinon.spy(element, '_undeleteIssue');
190
191 prpcClient.call.withArgs('monorail.Issues', 'GetIssue', {issueRef})
192 .onFirstCall().returns(deletedIssuePromise)
193 .onSecondCall().returns(issuePromise);
194 prpcClient.call.withArgs('monorail.Issues', 'DeleteIssue',
195 {delete: false, issueRef}).returns(deletePromise);
196
197 store.dispatch(issueV0.viewIssue(issueRef));
198 store.dispatch(issueV0.fetchIssuePageData(issueRef));
199
200 await deletedIssuePromise;
201 await element.updateComplete;
202
203 populateElementReferences();
204
205 assert.deepEqual(element.issue,
206 {isDeleted: true, localId: 111, projectName: 'test'});
207 assert.isNull(issueElement);
208 assert.isNotNull(deletedElement);
209
210 // Make undelete button visible. This must be after deletedIssuePromise
211 // resolves since issuePermissions are cleared by Redux after that promise.
212 element.issuePermissions = ['deleteissue'];
213 await element.updateComplete;
214
215 const button = element.querySelector('.undelete');
216 button.click();
217
218 sinon.assert.calledWith(prpcClient.call, 'monorail.Issues', 'GetIssue',
219 {issueRef});
220 sinon.assert.calledWith(prpcClient.call, 'monorail.Issues', 'DeleteIssue',
221 {delete: false, issueRef});
222
223 await deletePromise;
224 await issuePromise;
225 await element.updateComplete;
226
227 assert.isTrue(element._undeleteIssue.calledOnce);
228
229 assert.deepEqual(element.issue, {localId: 111, projectName: 'test'});
230
231 await element.updateComplete;
232
233 populateElementReferences();
234 assert.isNotNull(issueElement);
235
236 element._undeleteIssue.restore();
237 });
238
239 it('issue has moved', async () => {
240 element.fetchingIssue = false;
241 element.issue = {movedToRef: {projectName: 'hello', localId: 10}};
242
243 await element.updateComplete;
244 populateElementReferences();
245
246 assert.isNull(issueElement);
247 assert.isNull(deletedElement);
248 assert.isNotNull(movedElement);
249
250 const link = movedElement.querySelector('.new-location');
251 assert.equal(link.getAttribute('href'), '/p/hello/issues/detail?id=10');
252 });
253
254 it('moving to a restricted issue', async () => {
255 element.fetchingIssue = false;
256 element.issue = {localId: 111};
257
258 await element.updateComplete;
259
260 element.issue = {localId: 222};
261 element.fetchIssueError = 'error';
262
263 await element.updateComplete;
264 populateElementReferences();
265
266 assert.isNull(loadingElement);
267 assert.isNotNull(fetchErrorElement);
268 assert.isNull(deletedElement);
269 assert.isNull(movedElement);
270 assert.isNull(issueElement);
271 });
272});