blob: 1a9a1e4f064115dafbba1d43de5809ae0dac4e9b [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 {prpcClient} from 'prpc-client-instance.js';
8import {stateUpdated} from 'reducers/base.js';
9import {users} from 'reducers/users.js';
10import {stars} from 'reducers/stars.js';
11import {MrProjectsPage} from './mr-projects-page.js';
12
13let element;
14
15describe('mr-projects-page', () => {
16 beforeEach(() => {
17 element = document.createElement('mr-projects-page');
18 document.body.appendChild(element);
19
20 sinon.stub(element, 'stateChanged');
21 });
22
23 afterEach(() => {
24 document.body.removeChild(element);
25 });
26
27 it('initializes', () => {
28 assert.instanceOf(element, MrProjectsPage);
29 });
30
31 it('renders loading', async () => {
32 element._isFetchingProjects = true;
33
34 await element.updateComplete;
35
36 assert.equal(element.shadowRoot.textContent.trim(), 'Loading...');
37 });
38
39 it('renders projects when refetching projects', async () => {
40 element._isFetchingProjects = true;
41 element._projects = [
42 {name: 'projects/chromium', displayName: 'chromium',
43 summary: 'Best project ever'},
44 ];
45
46 await element.updateComplete;
47
48 const headers = element.shadowRoot.querySelectorAll('h2');
49
50 assert.equal(headers.length, 1);
51 assert.equal(headers[0].textContent.trim(), 'All projects');
52
53 const projects = element.shadowRoot.querySelectorAll(
54 '.all-projects > .project');
55 assert.equal(projects.length, 1);
56
57 assert.include(projects[0].querySelector('h3').textContent, 'chromium');
58 assert.include(projects[0].textContent, 'Best project ever');
59 });
60
61 it('renders all projects when no user projects', async () => {
62 element._isFetchingProjects = false;
63 element._projects = [
64 {name: 'projects/chromium', displayName: 'chromium',
65 summary: 'Best project ever'},
66 {name: 'projects/infra', displayName: 'infra',
67 summary: 'Make it work'},
68 ];
69
70 await element.updateComplete;
71
72 const headers = element.shadowRoot.querySelectorAll('h2');
73
74 assert.equal(headers.length, 1);
75 assert.equal(headers[0].textContent.trim(), 'All projects');
76
77 const projects = element.shadowRoot.querySelectorAll(
78 '.all-projects > .project');
79 assert.equal(projects.length, 2);
80
81 assert.include(projects[0].querySelector('h3').textContent, 'chromium');
82 assert.include(projects[0].textContent, 'Best project ever');
83
84 assert.include(projects[1].querySelector('h3').textContent, 'infra');
85 assert.include(projects[1].textContent, 'Make it work');
86 });
87
88 it('renders no projects found', async () => {
89 element._isFetchingProjects = false;
90 sinon.stub(element, 'myProjects').get(() => []);
91 sinon.stub(element, 'otherProjects').get(() => []);
92
93 await element.updateComplete;
94
95 assert.equal(element.shadowRoot.textContent.trim(), 'No projects found.');
96 });
97
98 describe('project grouping', () => {
99 beforeEach(() => {
100 element._projects = [
101 {name: 'projects/chromium', displayName: 'chromium',
102 summary: 'Best project ever'},
103 {name: 'projects/infra', displayName: 'infra',
104 summary: 'Make it work'},
105 {name: 'projects/test', displayName: 'test',
106 summary: 'Hmm'},
107 {name: 'projects/a-project', displayName: 'a-project',
108 summary: 'I am Monkeyrail'},
109 ];
110 element._roleByProjectName = {
111 'projects/chromium': 'Owner',
112 'projects/infra': 'Committer',
113 };
114 element._isFetchingProjects = false;
115 });
116
117 it('myProjects filters out non-member projects', () => {
118 assert.deepEqual(element.myProjects, [
119 {name: 'projects/chromium', displayName: 'chromium',
120 summary: 'Best project ever'},
121 {name: 'projects/infra', displayName: 'infra',
122 summary: 'Make it work'},
123 ]);
124 });
125
126 it('otherProjects filters out member projects', () => {
127 assert.deepEqual(element.otherProjects, [
128 {name: 'projects/test', displayName: 'test',
129 summary: 'Hmm'},
130 {name: 'projects/a-project', displayName: 'a-project',
131 summary: 'I am Monkeyrail'},
132 ]);
133 });
134
135 it('renders user projects', async () => {
136 await element.updateComplete;
137
138 const projects = element.shadowRoot.querySelectorAll(
139 '.my-projects > .project');
140
141 assert.equal(projects.length, 2);
142 assert.include(projects[0].querySelector('h3').textContent, 'chromium');
143 assert.include(projects[0].textContent, 'Best project ever');
144 assert.include(projects[0].querySelector('.subtitle').textContent,
145 'Owner');
146
147 assert.include(projects[1].querySelector('h3').textContent, 'infra');
148 assert.include(projects[1].textContent, 'Make it work');
149 assert.include(projects[1].querySelector('.subtitle').textContent,
150 'Committer');
151 });
152
153 it('renders other projects', async () => {
154 await element.updateComplete;
155
156 const projects = element.shadowRoot.querySelectorAll(
157 '.other-projects > .project');
158
159 assert.equal(projects.length, 2);
160 assert.include(projects[0].querySelector('h3').textContent, 'test');
161 assert.include(projects[0].textContent, 'Hmm');
162
163 assert.include(projects[1].querySelector('h3').textContent, 'a-project');
164 assert.include(projects[1].textContent, 'I am Monkeyrail');
165 });
166 });
167});
168
169describe('mr-projects-page (connected)', () => {
170 beforeEach(() => {
171 sinon.stub(prpcClient, 'call');
172 sinon.spy(users, 'gatherProjectMemberships');
173 sinon.spy(stars, 'listProjects');
174
175 element = document.createElement('mr-projects-page');
176 });
177
178 afterEach(() => {
179 if (document.body.contains(element)) {
180 document.body.removeChild(element);
181 }
182
183 prpcClient.call.restore();
184 users.gatherProjectMemberships.restore();
185 stars.listProjects.restore();
186 });
187
188 it('fetches projects when connected', async () => {
189 const promise = Promise.resolve({
190 projects: [{name: 'projects/proj', displayName: 'proj',
191 summary: 'test'}],
192 });
193 prpcClient.call.returns(promise);
194
195 assert.isFalse(element._isFetchingProjects);
196 sinon.assert.notCalled(prpcClient.call);
197
198 // Trigger connectedCallback().
199 document.body.appendChild(element);
200 await stateUpdated, element.updateComplete;
201
202 sinon.assert.calledWith(prpcClient.call, 'monorail.v3.Projects',
203 'ListProjects', {});
204
205 assert.isFalse(element._isFetchingProjects);
206 assert.deepEqual(element._projects,
207 [{name: 'projects/proj', displayName: 'proj',
208 summary: 'test'}]);
209 });
210
211 it('does not gather projects when user is logged out', async () => {
212 document.body.appendChild(element);
213 element._currentUser = '';
214
215 await element.updateComplete;
216
217 sinon.assert.notCalled(users.gatherProjectMemberships);
218 });
219
220 it('gathers user projects when user is logged in', async () => {
221 document.body.appendChild(element);
222 element._currentUser = 'users/1234';
223
224 await element.updateComplete;
225
226 sinon.assert.calledOnce(users.gatherProjectMemberships);
227 sinon.assert.calledWith(users.gatherProjectMemberships, 'users/1234');
228 });
229
230 it('does not fetch stars user is logged out', async () => {
231 document.body.appendChild(element);
232 element._currentUser = '';
233
234 await element.updateComplete;
235
236 sinon.assert.notCalled(stars.listProjects);
237 });
238
239 it('fetches stars when user is logged in', async () => {
240 document.body.appendChild(element);
241 element._currentUser = 'users/1234';
242
243 await element.updateComplete;
244
245 sinon.assert.calledOnce(stars.listProjects);
246 sinon.assert.calledWith(stars.listProjects, 'users/1234');
247 });
248});