]> source.dussan.org Git - sonarqube.git/blob
0e8d81a0d0fca2861d9130df6c8a9e9d8c33b5d9
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2023 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20
21 import { act, screen, waitFor } from '@testing-library/react';
22 import userEvent from '@testing-library/user-event';
23 import { UserEvent } from '@testing-library/user-event/dist/types/setup/setup';
24 import * as React from 'react';
25 import selectEvent from 'react-select-event';
26 import { byLabelText, byRole, byText } from 'testing-library-selector';
27 import PermissionsServiceMock from '../../../../../api/mocks/PermissionsServiceMock';
28 import { mockComponent } from '../../../../../helpers/mocks/component';
29 import { renderApp } from '../../../../../helpers/testReactTestingUtils';
30 import { ComponentQualifier, Visibility } from '../../../../../types/component';
31 import { Permissions } from '../../../../../types/permissions';
32 import { Component } from '../../../../../types/types';
33 import { PERMISSIONS_ORDER_FOR_PROJECT_TEMPLATE, PERMISSIONS_ORDER_FOR_VIEW } from '../../../utils';
34 import { PermissionsProjectApp } from '../PermissionsProjectApp';
35
36 let serviceMock: PermissionsServiceMock;
37 beforeAll(() => {
38   serviceMock = new PermissionsServiceMock();
39 });
40
41 afterEach(() => {
42   serviceMock.reset();
43 });
44
45 describe('rendering', () => {
46   it.each([
47     [ComponentQualifier.Project, 'roles.page.description2', PERMISSIONS_ORDER_FOR_PROJECT_TEMPLATE],
48     [ComponentQualifier.Portfolio, 'roles.page.description_portfolio', PERMISSIONS_ORDER_FOR_VIEW],
49     [
50       ComponentQualifier.Application,
51       'roles.page.description_application',
52       PERMISSIONS_ORDER_FOR_VIEW,
53     ],
54   ])('should render correctly for %s', async (qualifier, description, permissions) => {
55     const user = userEvent.setup();
56     const ui = getPageObject(user);
57     renderPermissionsProjectApp({ qualifier, visibility: Visibility.Private });
58     await ui.appLoaded();
59
60     expect(screen.getByText(description)).toBeInTheDocument();
61     permissions.forEach((permission) => {
62       expect(ui.permissionCheckbox('johndoe', permission).get()).toBeInTheDocument();
63     });
64   });
65 });
66
67 describe('filtering', () => {
68   it('should allow to filter permission holders', async () => {
69     const user = userEvent.setup();
70     const ui = getPageObject(user);
71     renderPermissionsProjectApp();
72     await ui.appLoaded();
73
74     expect(screen.getByText('sonar-users')).toBeInTheDocument();
75     expect(screen.getByText('johndoe')).toBeInTheDocument();
76
77     await ui.showOnlyUsers();
78     expect(screen.queryByText('sonar-users')).not.toBeInTheDocument();
79     expect(screen.getByText('johndoe')).toBeInTheDocument();
80
81     await ui.showOnlyGroups();
82     expect(screen.getByText('sonar-users')).toBeInTheDocument();
83     expect(screen.queryByText('johndoe')).not.toBeInTheDocument();
84
85     await ui.showAll();
86     expect(screen.getByText('sonar-users')).toBeInTheDocument();
87     expect(screen.getByText('johndoe')).toBeInTheDocument();
88
89     await ui.searchFor('sonar-adm');
90     expect(screen.getByText('sonar-admins')).toBeInTheDocument();
91     expect(screen.queryByText('sonar-users')).not.toBeInTheDocument();
92     expect(screen.queryByText('johndoe')).not.toBeInTheDocument();
93
94     await ui.clearSearch();
95     expect(screen.getByText('sonar-users')).toBeInTheDocument();
96     expect(screen.getByText('johndoe')).toBeInTheDocument();
97   });
98
99   it('should allow to show only permission holders with a specific permission', async () => {
100     const user = userEvent.setup();
101     const ui = getPageObject(user);
102     renderPermissionsProjectApp();
103     await ui.appLoaded();
104
105     expect(screen.getAllByRole('row').length).toBe(7);
106     await ui.toggleFilterByPermission(Permissions.Admin);
107     expect(screen.getAllByRole('row').length).toBe(2);
108   });
109 });
110
111 describe('assigning/revoking permissions', () => {
112   it('should allow to apply a permission template', async () => {
113     const user = userEvent.setup();
114     const ui = getPageObject(user);
115     renderPermissionsProjectApp();
116     await ui.appLoaded();
117
118     await ui.openTemplateModal();
119     expect(ui.confirmApplyTemplateBtn.get()).toBeDisabled();
120     await ui.chooseTemplate('Permission Template 2');
121     expect(ui.templateSuccessfullyApplied.get()).toBeInTheDocument();
122     await ui.closeTemplateModal();
123     expect(ui.templateSuccessfullyApplied.query()).not.toBeInTheDocument();
124   });
125
126   it('should allow to turn a public project private (and vice-versa)', async () => {
127     const user = userEvent.setup();
128     const ui = getPageObject(user);
129     renderPermissionsProjectApp();
130     await ui.appLoaded();
131
132     expect(ui.visibilityRadio(Visibility.Public).get()).toBeChecked();
133     expect(
134       ui.permissionCheckbox('sonar-users', Permissions.Browse).query()
135     ).not.toBeInTheDocument();
136     await act(async () => {
137       await ui.turnProjectPrivate();
138     });
139     expect(ui.visibilityRadio(Visibility.Private).get()).toBeChecked();
140     expect(ui.permissionCheckbox('sonar-users', Permissions.Browse).get()).toBeInTheDocument();
141
142     await ui.turnProjectPublic();
143     expect(ui.makePublicDisclaimer.get()).toBeInTheDocument();
144     await act(async () => {
145       await ui.confirmTurnProjectPublic();
146     });
147     expect(ui.visibilityRadio(Visibility.Public).get()).toBeChecked();
148   });
149
150   it('should add and remove permissions to/from a group', async () => {
151     const user = userEvent.setup();
152     const ui = getPageObject(user);
153     renderPermissionsProjectApp();
154     await ui.appLoaded();
155
156     expect(ui.permissionCheckbox('sonar-users', Permissions.Admin).get()).not.toBeChecked();
157
158     await ui.togglePermission('sonar-users', Permissions.Admin);
159     await ui.appLoaded();
160     expect(ui.permissionCheckbox('sonar-users', Permissions.Admin).get()).toBeChecked();
161
162     await ui.togglePermission('sonar-users', Permissions.Admin);
163     await ui.appLoaded();
164     expect(ui.permissionCheckbox('sonar-users', Permissions.Admin).get()).not.toBeChecked();
165   });
166
167   it('should add and remove permissions to/from a user', async () => {
168     const user = userEvent.setup();
169     const ui = getPageObject(user);
170     renderPermissionsProjectApp();
171     await ui.appLoaded();
172
173     expect(ui.permissionCheckbox('johndoe', Permissions.Scan).get()).not.toBeChecked();
174
175     await ui.togglePermission('johndoe', Permissions.Scan);
176     await ui.appLoaded();
177     expect(ui.permissionCheckbox('johndoe', Permissions.Scan).get()).toBeChecked();
178
179     await ui.togglePermission('johndoe', Permissions.Scan);
180     await ui.appLoaded();
181     expect(ui.permissionCheckbox('johndoe', Permissions.Scan).get()).not.toBeChecked();
182   });
183 });
184
185 function getPageObject(user: UserEvent) {
186   const ui = {
187     loading: byLabelText('loading'),
188     permissionCheckbox: (target: string, permission: Permissions) =>
189       byRole('checkbox', {
190         name: `permission.assign_x_to_y.projects_role.${permission}.${target}`,
191       }),
192     visibilityRadio: (visibility: Visibility) =>
193       byRole('radio', { name: `visibility.${visibility}` }),
194     makePublicDisclaimer: byText(
195       'projects_role.are_you_sure_to_turn_project_to_public.warning.TRK'
196     ),
197     confirmPublicBtn: byRole('button', { name: 'projects_role.turn_project_to_public.TRK' }),
198     openModalBtn: byRole('button', { name: 'projects_role.apply_template' }),
199     closeModalBtn: byRole('button', { name: 'close' }),
200     templateSelect: byRole('combobox', { name: /template/ }),
201     templateSuccessfullyApplied: byText('projects_role.apply_template.success'),
202     confirmApplyTemplateBtn: byRole('button', { name: 'apply' }),
203     tableHeaderFilter: (permission: Permissions) =>
204       byRole('link', { name: `projects_role.${permission}` }),
205     onlyUsersBtn: byRole('button', { name: 'users.page' }),
206     onlyGroupsBtn: byRole('button', { name: 'user_groups.page' }),
207     showAllBtn: byRole('button', { name: 'all' }),
208     searchInput: byRole('searchbox', { name: 'search.search_for_users_or_groups' }),
209   };
210
211   return {
212     ...ui,
213     async appLoaded() {
214       await waitFor(() => {
215         expect(ui.loading.query()).not.toBeInTheDocument();
216       });
217     },
218     async togglePermission(target: string, permission: Permissions) {
219       await user.click(ui.permissionCheckbox(target, permission).get());
220     },
221     async turnProjectPrivate() {
222       await user.click(ui.visibilityRadio(Visibility.Private).get());
223     },
224     async turnProjectPublic() {
225       await user.click(ui.visibilityRadio(Visibility.Public).get());
226     },
227     async confirmTurnProjectPublic() {
228       await user.click(ui.confirmPublicBtn.get());
229     },
230     async openTemplateModal() {
231       await user.click(ui.openModalBtn.get());
232     },
233     async closeTemplateModal() {
234       await user.click(ui.closeModalBtn.get());
235     },
236     async chooseTemplate(name: string) {
237       await selectEvent.select(ui.templateSelect.get(), [name]);
238       await user.click(ui.confirmApplyTemplateBtn.get());
239     },
240     async toggleFilterByPermission(permission: Permissions) {
241       await user.click(ui.tableHeaderFilter(permission).get());
242     },
243     async showOnlyUsers() {
244       await user.click(ui.onlyUsersBtn.get());
245     },
246     async showOnlyGroups() {
247       await user.click(ui.onlyGroupsBtn.get());
248     },
249     async showAll() {
250       await user.click(ui.showAllBtn.get());
251     },
252     async searchFor(name: string) {
253       await user.type(ui.searchInput.get(), name);
254     },
255     async clearSearch() {
256       await user.clear(ui.searchInput.get());
257     },
258   };
259 }
260
261 function renderPermissionsProjectApp(override?: Partial<Component>) {
262   function App({ component }: { component: Component }) {
263     const [realComponent, setRealComponent] = React.useState(component);
264     return (
265       <PermissionsProjectApp
266         component={realComponent}
267         onComponentChange={(changes: Partial<Component>) => {
268           setRealComponent({ ...realComponent, ...changes });
269         }}
270       />
271     );
272   }
273
274   return renderApp(
275     '/',
276     <App
277       component={mockComponent({
278         visibility: Visibility.Public,
279         configuration: {
280           canUpdateProjectVisibilityToPrivate: true,
281           canApplyPermissionTemplate: true,
282         },
283         ...override,
284       })}
285     />
286   );
287 }