From b0dd806289b026ba00bffe34c87dee292e8e19c6 Mon Sep 17 00:00:00 2001 From: Viktor Vorona Date: Thu, 15 Jun 2023 16:31:15 +0200 Subject: [PATCH] SONAR-18440 RTL Migration Project Management --- .../sonar-web/src/main/js/api/components.ts | 58 +-- .../js/api/mocks/PermissionsServiceMock.ts | 19 +- .../mocks/ProjectsManagementServiceMock.ts | 184 +++++++ .../main/js/api/mocks/SettingsServiceMock.ts | 2 +- .../sonar-web/src/main/js/api/permissions.ts | 8 - .../src/main/js/api/project-management.ts | 117 +++++ .../global/__tests__/GlobalNavMenu-test.tsx | 8 +- .../create/project/__tests__/Manual-it.tsx | 6 +- .../__tests__/ManualProjectCreate-test.tsx | 5 +- .../project/manual/ManualProjectCreate.tsx | 3 +- .../__tests__/PermissionTemplatesApp-it.tsx | 2 +- .../PermissionTemplatesApp-it.tsx.snap | 2 +- .../__tests__/PermissionsProject-it.tsx | 2 +- .../src/main/js/apps/projectDeletion/Form.tsx | 4 +- .../projectDeletion/__tests__/Form-test.tsx | 4 +- .../ChangeDefaultVisibilityForm.tsx | 8 +- .../projectsManagement/CreateProjectForm.tsx | 11 +- .../apps/projectsManagement/DeleteModal.tsx | 2 +- .../ProjectManagementApp.tsx | 9 +- .../js/apps/projectsManagement/ProjectRow.tsx | 2 +- .../projectsManagement/ProjectRowActions.tsx | 2 +- .../js/apps/projectsManagement/Projects.tsx | 2 +- .../projectsManagement/RestoreAccessModal.tsx | 4 +- .../js/apps/projectsManagement/Search.tsx | 4 +- .../__tests__/BulkApplyTemplateModal-test.tsx | 122 ----- .../ChangeDefaultVisibilityForm-test.tsx | 55 -- .../__tests__/CreateProjectForm-test.tsx | 82 --- .../__tests__/DeleteModal-test.tsx | 91 ---- .../__tests__/Header-test.tsx | 67 --- .../__tests__/ProjectManagementApp-it.tsx | 471 ++++++++++++++---- .../__tests__/ProjectManagementApp-test.tsx | 185 ------- .../__tests__/ProjectRow-test.tsx | 60 --- .../__tests__/ProjectRowActions-test.tsx | 133 ----- .../__tests__/Projects-test.tsx | 57 --- .../__tests__/Search-test.tsx | 160 ------ .../BulkApplyTemplateModal-test.tsx.snap | 447 ----------------- .../ChangeDefaultVisibilityForm-test.tsx.snap | 159 ------ .../__snapshots__/DeleteModal-test.tsx.snap | 171 ------- .../__snapshots__/Header-test.tsx.snap | 99 ---- .../__snapshots__/ProjectRow-test.tsx.snap | 279 ----------- .../ProjectRowActions-test.tsx.snap | 60 --- .../__snapshots__/Projects-test.tsx.snap | 69 --- .../__snapshots__/Search-test.tsx.snap | 315 ------------ .../js/apps/projectsManagement/routes.tsx | 2 +- .../src/main/js/helpers/mocks/projects.ts | 5 +- 45 files changed, 735 insertions(+), 2822 deletions(-) create mode 100644 server/sonar-web/src/main/js/api/mocks/ProjectsManagementServiceMock.ts create mode 100644 server/sonar-web/src/main/js/api/project-management.ts delete mode 100644 server/sonar-web/src/main/js/apps/projectsManagement/__tests__/BulkApplyTemplateModal-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ChangeDefaultVisibilityForm-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/projectsManagement/__tests__/CreateProjectForm-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/projectsManagement/__tests__/DeleteModal-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Header-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectManagementApp-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRow-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRowActions-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Projects-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Search-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/BulkApplyTemplateModal-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ChangeDefaultVisibilityForm-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/DeleteModal-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Header-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRow-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRowActions-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Projects-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Search-test.tsx.snap diff --git a/server/sonar-web/src/main/js/api/components.ts b/server/sonar-web/src/main/js/api/components.ts index 7c05e3ec95c..e57e665cfa5 100644 --- a/server/sonar-web/src/main/js/api/components.ts +++ b/server/sonar-web/src/main/js/api/components.ts @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { throwGlobalError } from '../helpers/error'; -import { getJSON, post, postJSON, RequestData } from '../helpers/request'; +import { getJSON, post, RequestData } from '../helpers/request'; import { BranchParameters } from '../types/branch-like'; import { ComponentQualifier, @@ -54,16 +54,6 @@ export interface ProjectBase { visibility: Visibility; } -export interface Project extends ProjectBase { - id: string; - lastAnalysisDate?: string; -} - -export interface SearchProjectsParameters extends BaseSearchProjectsParameters { - p?: number; - ps?: number; -} - export interface ComponentRaw { key: string; name: string; @@ -76,52 +66,6 @@ export interface ComponentRaw { needIssueSync?: boolean; } -export function getComponents(parameters: SearchProjectsParameters): Promise<{ - components: Project[]; - paging: Paging; -}> { - return getJSON('/api/projects/search', parameters); -} - -export function bulkDeleteProjects( - parameters: BaseSearchProjectsParameters -): Promise { - return post('/api/projects/bulk_delete', parameters).catch(throwGlobalError); -} - -export function deleteProject(project: string): Promise { - return post('/api/projects/delete', { project }).catch(throwGlobalError); -} - -export function deletePortfolio(portfolio: string): Promise { - return post('/api/views/delete', { key: portfolio }).catch(throwGlobalError); -} - -export function setupManualProjectCreation(data: { - name: string; - project: string; - mainBranch: string; - visibility?: Visibility; -}) { - return (newCodeDefinitionType?: string, newCodeDefinitionValue?: string) => - createProject({ - ...data, - newCodeDefinitionType, - newCodeDefinitionValue, - }); -} - -export function createProject(data: { - name: string; - project: string; - mainBranch: string; - visibility?: Visibility; - newCodeDefinitionType?: string; - newCodeDefinitionValue?: string; -}): Promise<{ project: ProjectBase }> { - return postJSON('/api/projects/create', data).catch(throwGlobalError); -} - export function searchProjectTags(data?: { ps?: number; q?: string }): Promise { return getJSON('/api/project_tags/search', data).catch(throwGlobalError); } diff --git a/server/sonar-web/src/main/js/api/mocks/PermissionsServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/PermissionsServiceMock.ts index 4d7b322fd66..9a99f4b19da 100644 --- a/server/sonar-web/src/main/js/api/mocks/PermissionsServiceMock.ts +++ b/server/sonar-web/src/main/js/api/mocks/PermissionsServiceMock.ts @@ -39,11 +39,11 @@ import { deletePermissionTemplate, getGlobalPermissionsGroups, getGlobalPermissionsUsers, - getPermissionsGroupsForComponent, - getPermissionsUsersForComponent, getPermissionTemplateGroups, - getPermissionTemplates, getPermissionTemplateUsers, + getPermissionTemplates, + getPermissionsGroupsForComponent, + getPermissionsUsersForComponent, grantPermissionToGroup, grantPermissionToUser, grantTemplatePermissionToGroup, @@ -64,7 +64,12 @@ const defaultUsers = [ mockPermissionUser({ login: 'gooduser1', name: 'John', - permissions: [Permissions.IssueAdmin, Permissions.SecurityHotspotAdmin, Permissions.Browse], + permissions: [ + Permissions.IssueAdmin, + Permissions.SecurityHotspotAdmin, + Permissions.Browse, + Permissions.Admin, + ], }), mockPermissionUser({ login: 'gooduser2', @@ -251,7 +256,11 @@ export default class PermissionsServiceMock { const users = q && q.length >= MIN_QUERY_LENGTH - ? this.#users.filter((user) => user.name.toLowerCase().includes(q.toLowerCase())) + ? this.#users.filter( + (user) => + user.name.toLowerCase().includes(q.toLowerCase()) || + user.login.toLowerCase().includes(q.toLowerCase()) + ) : this.#users; const usersChunked = chunk( diff --git a/server/sonar-web/src/main/js/api/mocks/ProjectsManagementServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/ProjectsManagementServiceMock.ts new file mode 100644 index 00000000000..29e2e0e515f --- /dev/null +++ b/server/sonar-web/src/main/js/api/mocks/ProjectsManagementServiceMock.ts @@ -0,0 +1,184 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { cloneDeep } from 'lodash'; +import { mockProject } from '../../helpers/mocks/projects'; +import { ComponentQualifier, Visibility } from '../../types/component'; +import { SettingsKey } from '../../types/settings'; +import { + Project, + bulkDeleteProjects, + changeProjectDefaultVisibility, + createProject, + deletePortfolio, + deleteProject, + getComponents, +} from '../project-management'; +import SettingsServiceMock from './SettingsServiceMock'; + +jest.mock('../project-management'); + +const defaultProject = [ + mockProject({ key: 'project1', name: 'Project 1' }), + mockProject({ key: 'project2', name: 'Project 2', visibility: Visibility.Private }), + mockProject({ key: 'project3', name: 'Project 3', lastAnalysisDate: undefined }), + mockProject({ key: 'projectProvisioned', name: 'Project 4' }), + mockProject({ key: 'portfolio1', name: 'Portfolio 1', qualifier: ComponentQualifier.Portfolio }), + mockProject({ + key: 'portfolio2', + name: 'Portfolio 2', + qualifier: ComponentQualifier.Portfolio, + visibility: Visibility.Private, + }), + mockProject({ + key: 'portfolio3', + name: 'Portfolio 3', + qualifier: ComponentQualifier.Portfolio, + lastAnalysisDate: undefined, + }), + mockProject({ + key: 'application1', + name: 'Application 1', + qualifier: ComponentQualifier.Application, + }), + mockProject({ + key: 'application2', + name: 'Application 2', + qualifier: ComponentQualifier.Application, + visibility: Visibility.Private, + }), + mockProject({ + key: 'application3', + name: 'Application 3', + qualifier: ComponentQualifier.Application, + lastAnalysisDate: undefined, + }), +]; + +export default class ProjectManagementServiceMock { + #projects: Project[]; + #settingsService: SettingsServiceMock; + + constructor(settingsService: SettingsServiceMock) { + this.#projects = cloneDeep(defaultProject); + this.#settingsService = settingsService; + jest.mocked(getComponents).mockImplementation(this.handleGetComponents); + jest.mocked(createProject).mockImplementation(this.handleCreateProject); + jest.mocked(bulkDeleteProjects).mockImplementation(this.handleBulkDeleteProjects); + jest.mocked(deleteProject).mockImplementation(this.handleDeleteProject); + jest.mocked(deletePortfolio).mockImplementation(this.handleDeletePortfolio); + jest + .mocked(changeProjectDefaultVisibility) + .mockImplementation(this.handleChangeProjectDefaultVisibility); + } + + setProjects = (projects: Project[]) => { + this.#projects = cloneDeep(projects); + }; + + handleGetComponents: typeof getComponents = (params) => { + const pageIndex = params.p || 1; + const pageSize = params.ps || 50; + const components = this.#projects.filter((item) => { + if (params.qualifiers && item.qualifier !== params.qualifiers) { + return false; + } + if (params.visibility && item.visibility !== params.visibility) { + return false; + } + if ( + params.analyzedBefore && + (!item.lastAnalysisDate || + new Date(item.lastAnalysisDate) > new Date(params.analyzedBefore)) + ) { + return false; + } + if (params.onProvisionedOnly && !item.key.includes('Provisioned')) { + return false; + } + if ( + params.q && + !item.key.toLowerCase().includes(params.q.toLowerCase()) && + !item.name.toLowerCase().includes(params.q.toLowerCase()) + ) { + return false; + } + return true; + }); + + return this.reply({ + components: components.slice((pageIndex - 1) * pageSize, pageSize * pageIndex), + paging: { pageIndex, pageSize, total: components.length }, + }); + }; + + handleCreateProject: typeof createProject = ({ project, name, visibility }) => { + this.#projects.unshift( + mockProject({ + key: project, + name, + visibility, + lastAnalysisDate: undefined, + }) + ); + return this.reply({ project: this.#projects[0] }); + }; + + handleBulkDeleteProjects: typeof bulkDeleteProjects = ({ projects }) => { + if (projects === undefined) { + return Promise.reject(); + } + + this.#projects = this.#projects.filter((item) => !projects.split(',').includes(item.key)); + return this.reply(); + }; + + handleDeleteProject: typeof deleteProject = (key) => { + const projectToDelete = this.#projects.find((item) => key === item.key); + if (projectToDelete?.qualifier !== ComponentQualifier.Project) { + return Promise.reject(); + } + this.#projects = this.#projects.filter((p) => p.key !== key); + return this.reply(); + }; + + handleDeletePortfolio: typeof deletePortfolio = (key) => { + const portfolioToDelete = this.#projects.find((item) => key === item.key); + if (portfolioToDelete?.qualifier !== ComponentQualifier.Portfolio) { + return Promise.reject(); + } + this.#projects = this.#projects.filter((p) => p.key !== key); + return this.reply(); + }; + + handleChangeProjectDefaultVisibility: typeof changeProjectDefaultVisibility = (visibility) => { + this.#settingsService.set(SettingsKey.DefaultProjectVisibility, visibility); + return this.reply(); + }; + + reset = () => { + this.#projects = cloneDeep(defaultProject); + }; + + reply(): Promise; + reply(response: T): Promise; + reply(response?: T): Promise { + return Promise.resolve(response ? cloneDeep(response) : undefined); + } +} diff --git a/server/sonar-web/src/main/js/api/mocks/SettingsServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/SettingsServiceMock.ts index d5ca5119c96..aefca24cfd0 100644 --- a/server/sonar-web/src/main/js/api/mocks/SettingsServiceMock.ts +++ b/server/sonar-web/src/main/js/api/mocks/SettingsServiceMock.ts @@ -134,7 +134,7 @@ export default class SettingsServiceMock { handleGetValue = (data: { key: string; component?: string } & BranchParameters) => { const setting = this.#settingValues.find((s) => s.key === data.key) as SettingValue; - return this.reply(setting); + return this.reply(setting ?? {}); }; handleGetValues = (data: { keys: string[]; component?: string } & BranchParameters) => { diff --git a/server/sonar-web/src/main/js/api/permissions.ts b/server/sonar-web/src/main/js/api/permissions.ts index dfde7efcc22..733fabbb9fc 100644 --- a/server/sonar-web/src/main/js/api/permissions.ts +++ b/server/sonar-web/src/main/js/api/permissions.ts @@ -227,11 +227,3 @@ export function changeProjectVisibility( ): Promise { return post('/api/projects/update_visibility', { project, visibility }).catch(throwGlobalError); } - -export function changeProjectDefaultVisibility( - projectVisibility: Visibility -): Promise { - return post('/api/projects/update_default_visibility', { projectVisibility }).catch( - throwGlobalError - ); -} diff --git a/server/sonar-web/src/main/js/api/project-management.ts b/server/sonar-web/src/main/js/api/project-management.ts new file mode 100644 index 00000000000..c560ff1e8c3 --- /dev/null +++ b/server/sonar-web/src/main/js/api/project-management.ts @@ -0,0 +1,117 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { throwGlobalError } from '../helpers/error'; +import { getJSON, post, postJSON } from '../helpers/request'; +import { ComponentQualifier, Visibility } from '../types/component'; +import { Paging } from '../types/types'; + +export interface BaseSearchProjectsParameters { + analyzedBefore?: string; + onProvisionedOnly?: boolean; + projects?: string; + q?: string; + qualifiers?: string; + visibility?: Visibility; +} + +export interface ProjectBase { + key: string; + name: string; + qualifier: + | ComponentQualifier.Application + | ComponentQualifier.Portfolio + | ComponentQualifier.Project; + visibility: Visibility; +} + +export interface Project extends ProjectBase { + lastAnalysisDate?: string; +} + +export interface SearchProjectsParameters extends BaseSearchProjectsParameters { + p?: number; + ps?: number; +} + +export interface ComponentRaw { + key: string; + name: string; + isFavorite?: boolean; + analysisDate?: string; + qualifier: ComponentQualifier; + tags: string[]; + visibility: Visibility; + leakPeriodDate?: string; + needIssueSync?: boolean; +} + +export function getComponents(parameters: SearchProjectsParameters): Promise<{ + components: Project[]; + paging: Paging; +}> { + return getJSON('/api/projects/search', parameters); +} + +export function bulkDeleteProjects( + parameters: BaseSearchProjectsParameters +): Promise { + return post('/api/projects/bulk_delete', parameters).catch(throwGlobalError); +} + +export function deleteProject(project: string): Promise { + return post('/api/projects/delete', { project }).catch(throwGlobalError); +} + +export function deletePortfolio(portfolio: string): Promise { + return post('/api/views/delete', { key: portfolio }).catch(throwGlobalError); +} + +export function createProject(data: { + name: string; + project: string; + mainBranch: string; + visibility?: Visibility; + newCodeDefinitionType?: string; + newCodeDefinitionValue?: string; +}): Promise<{ project: ProjectBase }> { + return postJSON('/api/projects/create', data).catch(throwGlobalError); +} + +export function setupManualProjectCreation(data: { + name: string; + project: string; + mainBranch: string; + visibility?: Visibility; +}) { + return (newCodeDefinitionType?: string, newCodeDefinitionValue?: string) => + createProject({ + ...data, + newCodeDefinitionType, + newCodeDefinitionValue, + }); +} + +export function changeProjectDefaultVisibility( + projectVisibility: Visibility +): Promise { + return post('/api/projects/update_default_visibility', { projectVisibility }).catch( + throwGlobalError + ); +} diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavMenu-test.tsx b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavMenu-test.tsx index d2592e50756..08dbe83c815 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavMenu-test.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavMenu-test.tsx @@ -29,10 +29,10 @@ it('should work with extensions', () => { qualifiers: ['TRK'], }); - const currentUser = { + const currentUser = mockCurrentUser({ isLoggedIn: false, dismissedNotices: {}, - }; + }); renderGlobalNavMenu({ appState, currentUser }); expect(screen.getByText('more')).toBeInTheDocument(); }); @@ -43,10 +43,10 @@ it('should show administration menu if the user has the rights', () => { globalPages: [], qualifiers: ['TRK'], }); - const currentUser = { + const currentUser = mockCurrentUser({ isLoggedIn: false, dismissedNotices: {}, - }; + }); renderGlobalNavMenu({ appState, currentUser }); expect(screen.getByText('layout.settings')).toBeInTheDocument(); diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/Manual-it.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/Manual-it.tsx index 7fc9af55881..dfa41c26dd8 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/Manual-it.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/Manual-it.tsx @@ -32,11 +32,13 @@ import CreateProjectPage, { CreateProjectPageProps } from '../CreateProjectPage' jest.mock('../../../../api/alm-settings'); jest.mock('../../../../api/newCodePeriod'); -jest.mock('../../../../api/components', () => ({ - ...jest.requireActual('../../../../api/components'), +jest.mock('../../../../api/project-management', () => ({ setupManualProjectCreation: jest .fn() .mockReturnValue(() => Promise.resolve({ project: mockProject() })), +})); +jest.mock('../../../../api/components', () => ({ + ...jest.requireActual('../../../../api/components'), doesComponentExists: jest .fn() .mockImplementation(({ component }) => Promise.resolve(component === 'exists')), diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/ManualProjectCreate-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/ManualProjectCreate-test.tsx index 1bf27b9b489..479b6df5c16 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/ManualProjectCreate-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/ManualProjectCreate-test.tsx @@ -29,8 +29,11 @@ const ui = { nextButton: byRole('button', { name: 'next' }), }; -jest.mock('../../../../api/components', () => ({ +jest.mock('../../../../api/project-management', () => ({ setupManualProjectCreation: jest.fn(), +})); + +jest.mock('../../../../api/components', () => ({ doesComponentExists: jest .fn() .mockImplementation(({ component }) => Promise.resolve(component === 'exists')), diff --git a/server/sonar-web/src/main/js/apps/create/project/manual/ManualProjectCreate.tsx b/server/sonar-web/src/main/js/apps/create/project/manual/ManualProjectCreate.tsx index ef344ed928d..c6b0bfc89df 100644 --- a/server/sonar-web/src/main/js/apps/create/project/manual/ManualProjectCreate.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/manual/ManualProjectCreate.tsx @@ -21,7 +21,8 @@ import classNames from 'classnames'; import { debounce, isEmpty } from 'lodash'; import * as React from 'react'; import { FormattedMessage } from 'react-intl'; -import { doesComponentExists, setupManualProjectCreation } from '../../../../api/components'; +import { doesComponentExists } from '../../../../api/components'; +import { setupManualProjectCreation } from '../../../../api/project-management'; import { getValue } from '../../../../api/settings'; import DocLink from '../../../../components/common/DocLink'; import ProjectKeyInput from '../../../../components/common/ProjectKeyInput'; diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/PermissionTemplatesApp-it.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/PermissionTemplatesApp-it.tsx index 412d3bd93ff..e3710f2ed83 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/PermissionTemplatesApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/PermissionTemplatesApp-it.tsx @@ -286,7 +286,7 @@ describe('filtering', () => { expect(screen.getAllByRole('row').length).toBe(12); await ui.toggleFilterByPermission(Permissions.Admin); - expect(screen.getAllByRole('row').length).toBe(2); + expect(screen.getAllByRole('row').length).toBe(3); await ui.toggleFilterByPermission(Permissions.Admin); expect(screen.getAllByRole('row').length).toBe(12); }); diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/PermissionTemplatesApp-it.tsx.snap b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/PermissionTemplatesApp-it.tsx.snap index e05b7037600..82f709663db 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/PermissionTemplatesApp-it.tsx.snap +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/PermissionTemplatesApp-it.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`rendering should render the list of templates: Permission Template 1: admin 1`] = `"0 user(s)1 group(s)"`; +exports[`rendering should render the list of templates: Permission Template 1: admin 1`] = `"1 user(s)1 group(s)"`; exports[`rendering should render the list of templates: Permission Template 1: codeviewer 1`] = `"0 user(s)0 group(s)"`; diff --git a/server/sonar-web/src/main/js/apps/permissions/project/components/__tests__/PermissionsProject-it.tsx b/server/sonar-web/src/main/js/apps/permissions/project/components/__tests__/PermissionsProject-it.tsx index 568fa687c7b..d76dad4e0ea 100644 --- a/server/sonar-web/src/main/js/apps/permissions/project/components/__tests__/PermissionsProject-it.tsx +++ b/server/sonar-web/src/main/js/apps/permissions/project/components/__tests__/PermissionsProject-it.tsx @@ -105,7 +105,7 @@ describe('filtering', () => { expect(screen.getAllByRole('row').length).toBe(11); await ui.toggleFilterByPermission(Permissions.Admin); - expect(screen.getAllByRole('row').length).toBe(2); + expect(screen.getAllByRole('row').length).toBe(3); await ui.toggleFilterByPermission(Permissions.Admin); expect(screen.getAllByRole('row').length).toBe(11); }); diff --git a/server/sonar-web/src/main/js/apps/projectDeletion/Form.tsx b/server/sonar-web/src/main/js/apps/projectDeletion/Form.tsx index 777510ac1e8..b9c387f9664 100644 --- a/server/sonar-web/src/main/js/apps/projectDeletion/Form.tsx +++ b/server/sonar-web/src/main/js/apps/projectDeletion/Form.tsx @@ -19,9 +19,9 @@ */ import * as React from 'react'; import { deleteApplication } from '../../api/application'; -import { deletePortfolio, deleteProject } from '../../api/components'; -import { Button } from '../../components/controls/buttons'; +import { deletePortfolio, deleteProject } from '../../api/project-management'; import ConfirmButton from '../../components/controls/ConfirmButton'; +import { Button } from '../../components/controls/buttons'; import { Router, withRouter } from '../../components/hoc/withRouter'; import { addGlobalSuccessMessage } from '../../helpers/globalMessages'; import { translate, translateWithParameters } from '../../helpers/l10n'; diff --git a/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/Form-test.tsx b/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/Form-test.tsx index 9e83d7e2c9f..393786ce3f0 100644 --- a/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/Form-test.tsx +++ b/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/Form-test.tsx @@ -20,11 +20,11 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { deleteApplication } from '../../../api/application'; -import { deletePortfolio, deleteProject } from '../../../api/components'; +import { deletePortfolio, deleteProject } from '../../../api/project-management'; import { mockRouter } from '../../../helpers/testMocks'; import { Form } from '../Form'; -jest.mock('../../../api/components', () => ({ +jest.mock('../../../api/project-management', () => ({ deleteProject: jest.fn().mockResolvedValue(undefined), deletePortfolio: jest.fn().mockResolvedValue(undefined), })); diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/ChangeDefaultVisibilityForm.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/ChangeDefaultVisibilityForm.tsx index 65da87ca632..242ef8f106f 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/ChangeDefaultVisibilityForm.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/ChangeDefaultVisibilityForm.tsx @@ -18,9 +18,9 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { Button, ResetButtonLink } from '../../components/controls/buttons'; import Modal from '../../components/controls/Modal'; import Radio from '../../components/controls/Radio'; +import { Button, ResetButtonLink } from '../../components/controls/buttons'; import { Alert } from '../../components/ui/Alert'; import { translate } from '../../helpers/l10n'; import { Visibility } from '../../types/component'; @@ -51,10 +51,12 @@ export default class ChangeDefaultVisibilityForm extends React.PureComponent +
-

{translate('settings.projects.change_visibility_form.header')}

+

{header}

diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/CreateProjectForm.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/CreateProjectForm.tsx index f8d669f835a..c7809034a3d 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/CreateProjectForm.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/CreateProjectForm.tsx @@ -19,12 +19,12 @@ */ import * as React from 'react'; import { FormattedMessage } from 'react-intl'; -import { createProject } from '../../api/components'; +import { createProject } from '../../api/project-management'; import { getValue } from '../../api/settings'; import Link from '../../components/common/Link'; import VisibilitySelector from '../../components/common/VisibilitySelector'; -import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons'; import Modal from '../../components/controls/Modal'; +import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons'; import { Alert } from '../../components/ui/Alert'; import MandatoryFieldMarker from '../../components/ui/MandatoryFieldMarker'; import MandatoryFieldsExplanation from '../../components/ui/MandatoryFieldsExplanation'; @@ -130,13 +130,14 @@ export default class CreateProjectForm extends React.PureComponent render() { const { defaultProjectVisibility } = this.props; const { createdProject } = this.state; + const header = translate('qualifiers.create.TRK'); return ( - + {createdProject ? (
-

{translate('qualifiers.create.TRK')}

+

{header}

@@ -168,7 +169,7 @@ export default class CreateProjectForm extends React.PureComponent ) : (
-

{translate('qualifiers.create.TRK')}

+

{header}

diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/DeleteModal.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/DeleteModal.tsx index 343747b4906..d272fa22505 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/DeleteModal.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/DeleteModal.tsx @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { bulkDeleteProjects } from '../../api/components'; +import { bulkDeleteProjects } from '../../api/project-management'; import Modal from '../../components/controls/Modal'; import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons'; import { Alert } from '../../components/ui/Alert'; diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectManagementApp.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectManagementApp.tsx index 730d7539134..780a6a16775 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectManagementApp.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectManagementApp.tsx @@ -20,8 +20,11 @@ import { debounce, uniq, without } from 'lodash'; import * as React from 'react'; import { Helmet } from 'react-helmet-async'; -import { Project, getComponents } from '../../api/components'; -import { changeProjectDefaultVisibility } from '../../api/permissions'; +import { + Project, + changeProjectDefaultVisibility, + getComponents, +} from '../../api/project-management'; import { getValue } from '../../api/settings'; import withCurrentUserContext from '../../app/components/current-user/withCurrentUserContext'; import ListFooter from '../../components/controls/ListFooter'; @@ -61,7 +64,7 @@ interface State { const DEBOUNCE_DELAY = 250; const PAGE_SIZE = 50; -export class ProjectManagementApp extends React.PureComponent { +class ProjectManagementApp extends React.PureComponent { mounted = false; constructor(props: Props) { diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRow.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRow.tsx index 8125bf3fbf9..026f6cac3cb 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRow.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRow.tsx @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { Project } from '../../api/components'; +import { Project } from '../../api/project-management'; import Link from '../../components/common/Link'; import PrivacyBadgeContainer from '../../components/common/PrivacyBadgeContainer'; import Checkbox from '../../components/controls/Checkbox'; diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx index 7fff9e79f9d..b41ac29b0b3 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx @@ -18,8 +18,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { Project } from '../../api/components'; import { getComponentNavigation } from '../../api/navigation'; +import { Project } from '../../api/project-management'; import ActionsDropdown, { ActionsDropdownItem } from '../../components/controls/ActionsDropdown'; import DeferredSpinner from '../../components/ui/DeferredSpinner'; import { translate, translateWithParameters } from '../../helpers/l10n'; diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/Projects.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/Projects.tsx index 9c9f8d2f634..447e1a64d3d 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/Projects.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/Projects.tsx @@ -19,7 +19,7 @@ */ import classNames from 'classnames'; import * as React from 'react'; -import { Project } from '../../api/components'; +import { Project } from '../../api/project-management'; import { translate } from '../../helpers/l10n'; import { LoggedInUser } from '../../types/users'; import ProjectRow from './ProjectRow'; diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/RestoreAccessModal.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/RestoreAccessModal.tsx index da6e250ea57..ee64532c43e 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/RestoreAccessModal.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/RestoreAccessModal.tsx @@ -19,10 +19,10 @@ */ import * as React from 'react'; import { FormattedMessage } from 'react-intl'; -import { Project } from '../../api/components'; import { grantPermissionToUser } from '../../api/permissions'; -import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons'; +import { Project } from '../../api/project-management'; import Modal from '../../components/controls/Modal'; +import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons'; import { translate } from '../../helpers/l10n'; import { LoggedInUser } from '../../types/users'; diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx index 0e5e903e1a6..30fd95ab1b5 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx @@ -20,7 +20,7 @@ import { sortBy } from 'lodash'; import * as React from 'react'; import { components, OptionProps, SingleValueProps } from 'react-select'; -import { Project } from '../../api/components'; +import { Project } from '../../api/project-management'; import withAppStateContext from '../../app/components/app-state/withAppStateContext'; import { Button } from '../../components/controls/buttons'; import Checkbox from '../../components/controls/Checkbox'; @@ -63,7 +63,7 @@ interface State { const QUALIFIERS_ORDER = ['TRK', 'VW', 'APP']; -export class Search extends React.PureComponent { +class Search extends React.PureComponent { state: State = { bulkApplyTemplateModal: false, deleteModal: false }; getQualifierOptions = () => { diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/BulkApplyTemplateModal-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/BulkApplyTemplateModal-test.tsx deleted file mode 100644 index 7be41f79ce5..00000000000 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/BulkApplyTemplateModal-test.tsx +++ /dev/null @@ -1,122 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { parseDate } from '../../../helpers/dates'; -import { click, waitAndUpdate } from '../../../helpers/testUtils'; -import BulkApplyTemplateModal, { Props } from '../BulkApplyTemplateModal'; - -jest.mock('../../../api/permissions', () => ({ - bulkApplyTemplate: jest.fn(() => Promise.resolve()), - getPermissionTemplates: jest.fn(() => Promise.resolve({ permissionTemplates: [] })), -})); - -const bulkApplyTemplate = require('../../../api/permissions').bulkApplyTemplate as jest.Mock; -const getPermissionTemplates = require('../../../api/permissions') - .getPermissionTemplates as jest.Mock; - -beforeEach(() => { - bulkApplyTemplate.mockClear(); - getPermissionTemplates.mockClear(); -}); - -it('fetches permission templates on component mount', () => { - shallow(render()); - expect(getPermissionTemplates).toHaveBeenCalled(); -}); - -it('bulk applies template to all results', async () => { - const wrapper = shallow(render()); - (wrapper.instance() as BulkApplyTemplateModal).mounted = true; - expect(wrapper).toMatchSnapshot(); - - wrapper.setState({ - loading: false, - permissionTemplate: 'foo', - permissionTemplates: [ - { id: 'foo', name: 'Foo' }, - { id: 'bar', name: 'Bar' }, - ], - }); - expect(wrapper).toMatchSnapshot(); - - click(wrapper.find('SubmitButton')); - expect(bulkApplyTemplate).toHaveBeenCalledWith({ - analyzedBefore: '2017-04-08T00:00:00+0000', - onProvisionedOnly: true, - q: 'bla', - qualifiers: 'TRK', - templateId: 'foo', - }); - expect(wrapper).toMatchSnapshot(); - - await waitAndUpdate(wrapper); - expect(wrapper).toMatchSnapshot(); -}); - -it('bulk applies template to selected results', async () => { - const wrapper = shallow(render({ qualifier: 'VW', selection: ['proj1', 'proj2'] })); - (wrapper.instance() as BulkApplyTemplateModal).mounted = true; - expect(wrapper).toMatchSnapshot(); - - wrapper.setState({ - loading: false, - permissionTemplate: 'foo', - permissionTemplates: [ - { id: 'foo', name: 'Foo' }, - { id: 'bar', name: 'Bar' }, - ], - }); - expect(wrapper).toMatchSnapshot(); - - click(wrapper.find('SubmitButton')); - expect(wrapper).toMatchSnapshot(); - await new Promise(setImmediate); - expect(bulkApplyTemplate).toHaveBeenCalledWith({ - projects: 'proj1,proj2', - qualifiers: 'VW', - templateId: 'foo', - }); - - wrapper.update(); - expect(wrapper).toMatchSnapshot(); -}); - -it('closes', () => { - const onClose = jest.fn(); - const wrapper = shallow(render({ onClose })); - click(wrapper.find('ResetButtonLink')); - expect(onClose).toHaveBeenCalled(); -}); - -function render(props?: { [P in keyof Props]?: Props[P] }) { - return ( - - ); -} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ChangeDefaultVisibilityForm-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ChangeDefaultVisibilityForm-test.tsx deleted file mode 100644 index e6763d02746..00000000000 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ChangeDefaultVisibilityForm-test.tsx +++ /dev/null @@ -1,55 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import Radio from '../../../components/controls/Radio'; -import { click } from '../../../helpers/testUtils'; -import { Visibility } from '../../../types/component'; -import ChangeDefaultVisibilityForm from '../ChangeDefaultVisibilityForm'; - -it('closes', () => { - const onClose = jest.fn(); - const wrapper = shallowRender({ onClose }); - click(wrapper.find('.js-modal-close')); - expect(onClose).toHaveBeenCalled(); -}); - -it('changes visibility', () => { - const onConfirm = jest.fn(); - const wrapper = shallowRender({ onConfirm }); - expect(wrapper).toMatchSnapshot(); - - wrapper.find(Radio).first().props().onCheck(Visibility.Private); - expect(wrapper).toMatchSnapshot(); - - click(wrapper.find('.js-confirm')); - expect(onConfirm).toHaveBeenCalledWith(Visibility.Private); -}); - -function shallowRender(props: Partial = {}) { - return shallow( - - ); -} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/CreateProjectForm-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/CreateProjectForm-test.tsx deleted file mode 100644 index 72643b0bcff..00000000000 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/CreateProjectForm-test.tsx +++ /dev/null @@ -1,82 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import * as React from 'react'; -import { createProject } from '../../../api/components'; -import CreateProjectForm from '../CreateProjectForm'; - -jest.mock('../../../api/components', () => ({ - createProject: jest.fn().mockResolvedValue({}), - doesComponentExists: jest - .fn() - .mockImplementation(({ component }) => Promise.resolve(component === 'exists')), -})); - -jest.mock('../../../api/settings', () => ({ - getValue: jest.fn().mockResolvedValue({ value: 'main' }), -})); - -beforeEach(() => { - jest.clearAllMocks(); -}); - -it('should render all inputs and create a project', async () => { - const user = userEvent.setup(); - renderCreateProjectForm(); - - await user.type( - screen.getByRole('textbox', { - name: 'onboarding.create_project.display_name field_required', - }), - 'ProjectName' - ); - - await user.type( - screen.getByRole('textbox', { - name: 'onboarding.create_project.project_key field_required', - }), - 'ProjectKey' - ); - - expect( - screen.getByRole('textbox', { - name: 'onboarding.create_project.main_branch_name field_required', - }) - ).toHaveValue('main'); - - await user.type( - screen.getByRole('textbox', { - name: 'onboarding.create_project.main_branch_name field_required', - }), - '{Control>}a{/Control}{Backspace}ProjectMainBranch' - ); - - await user.click(screen.getByRole('button', { name: 'create' })); - expect(createProject).toHaveBeenCalledWith({ - name: 'ProjectName', - project: 'ProjectKey', - mainBranch: 'ProjectMainBranch', - }); -}); - -function renderCreateProjectForm(props: Partial = {}) { - render(); -} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/DeleteModal-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/DeleteModal-test.tsx deleted file mode 100644 index 7e9accba7c1..00000000000 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/DeleteModal-test.tsx +++ /dev/null @@ -1,91 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -/* eslint-disable import/first */ -jest.mock('../../../api/components', () => ({ - bulkDeleteProjects: jest.fn(() => Promise.resolve()), -})); - -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { parseDate } from '../../../helpers/dates'; -import { click } from '../../../helpers/testUtils'; -import DeleteModal, { Props } from '../DeleteModal'; - -const bulkDeleteProjects = require('../../../api/components').bulkDeleteProjects as jest.Mock; - -beforeEach(() => { - bulkDeleteProjects.mockClear(); -}); - -it('deletes all projects', async () => { - const onConfirm = jest.fn(); - const wrapper = shallowRender({ onConfirm }); - (wrapper.instance() as DeleteModal).mounted = true; - expect(wrapper).toMatchSnapshot(); - - click(wrapper.find('SubmitButton')); - expect(wrapper).toMatchSnapshot(); - expect(bulkDeleteProjects).toHaveBeenCalledWith({ - analyzedBefore: '2017-04-08T00:00:00+0000', - onProvisionedOnly: undefined, - q: 'bla', - qualifiers: 'TRK', - }); - - await new Promise(setImmediate); - expect(onConfirm).toHaveBeenCalled(); -}); - -it('deletes selected projects', async () => { - const onConfirm = jest.fn(); - const wrapper = shallowRender({ onConfirm, selection: ['proj1', 'proj2'] }); - (wrapper.instance() as DeleteModal).mounted = true; - expect(wrapper).toMatchSnapshot(); - - click(wrapper.find('SubmitButton')); - expect(wrapper).toMatchSnapshot(); - expect(bulkDeleteProjects).toHaveBeenCalledWith({ projects: 'proj1,proj2' }); - - await new Promise(setImmediate); - expect(onConfirm).toHaveBeenCalled(); -}); - -it('closes', () => { - const onClose = jest.fn(); - const wrapper = shallowRender({ onClose }); - click(wrapper.find('ResetButtonLink')); - expect(onClose).toHaveBeenCalled(); -}); - -function shallowRender(props?: { [P in keyof Props]?: Props[P] }) { - return shallow( - - ); -} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Header-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Header-test.tsx deleted file mode 100644 index c3930a0a6fe..00000000000 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Header-test.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { click } from '../../../helpers/testUtils'; -import { Visibility } from '../../../types/component'; -import Header, { Props } from '../Header'; - -jest.mock('../../../helpers/system', () => ({ - getReactDomContainerSelector: jest.fn(() => '#content'), -})); - -it('renders', () => { - expect(shallowRender()).toMatchSnapshot('undefined visibility'); - expect(shallowRender({ defaultProjectVisibility: Visibility.Public })).toMatchSnapshot('default'); -}); - -it('creates project', () => { - const onProjectCreate = jest.fn(); - const wrapper = shallowRender({ onProjectCreate }); - click(wrapper.find('#create-project')); - expect(onProjectCreate).toHaveBeenCalledWith(); -}); - -it('changes default visibility', () => { - const onChangeDefaultProjectVisibility = jest.fn(); - const wrapper = shallowRender({ onChangeDefaultProjectVisibility }); - - click(wrapper.find('.js-change-visibility')); - - const modalWrapper = wrapper.find('ChangeDefaultVisibilityForm'); - expect(modalWrapper).toMatchSnapshot(); - modalWrapper.prop('onConfirm')(Visibility.Private); - expect(onChangeDefaultProjectVisibility).toHaveBeenCalledWith(Visibility.Private); - - modalWrapper.prop('onClose')(); - wrapper.update(); - expect(wrapper.find('ChangeDefaultVisibilityForm').exists()).toBe(false); -}); - -function shallowRender(props?: { [P in keyof Props]?: Props[P] }) { - return shallow( -
- ); -} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectManagementApp-it.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectManagementApp-it.tsx index 66aee4b45ac..1a8fac665f0 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectManagementApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectManagementApp-it.tsx @@ -17,139 +17,406 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { screen, within } from '@testing-library/react'; +import { screen, waitFor, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { getComponents, SearchProjectsParameters } from '../../../api/components'; +import selectEvent from 'react-select-event'; import PermissionsServiceMock from '../../../api/mocks/PermissionsServiceMock'; +import ProjectManagementServiceMock from '../../../api/mocks/ProjectsManagementServiceMock'; +import SettingsServiceMock from '../../../api/mocks/SettingsServiceMock'; +import { mockComponent } from '../../../helpers/mocks/component'; +import { mockProject } from '../../../helpers/mocks/projects'; +import { mockAppState, mockCurrentUser } from '../../../helpers/testMocks'; import { renderAppWithAdminContext } from '../../../helpers/testReactTestingUtils'; -import { ComponentQualifier, Visibility } from '../../../types/component'; +import { byPlaceholderText, byRole, byText } from '../../../helpers/testSelector'; +import { AppState } from '../../../types/appstate'; +import { ComponentQualifier } from '../../../types/component'; +import { Permissions } from '../../../types/permissions'; +import { GlobalSettingKeys } from '../../../types/settings'; +import { LoggedInUser } from '../../../types/users'; import routes from '../routes'; -jest.mock('../../../api/components', () => ({ - getComponents: jest.fn().mockResolvedValue({ - paging: { total: 0 }, - components: [], - }), -})); - -jest.mock('../../../api/settings', () => ({ - getValue: jest.fn().mockResolvedValue({ value: 'public' }), -})); - -const components = mockComponents(11); - -let serviceMock: PermissionsServiceMock; -beforeAll(() => { - serviceMock = new PermissionsServiceMock(); -}); - -afterEach(() => { - serviceMock.reset(); -}); +let login: string; -describe('Bulk Apply', () => { - (getComponents as jest.Mock).mockImplementation(getComponentsImplementation(10)); +const permissionsHandler = new PermissionsServiceMock(); +const settingsHandler = new SettingsServiceMock(); +const handler = new ProjectManagementServiceMock(settingsHandler); - it('should work as expected', async () => { - const user = userEvent.setup(); - - renderGlobalBackgroundTasksApp(); - - const bulkApplyButton = await screen.findByRole('button', { - name: 'permission_templates.bulk_apply_permission_template', +jest.mock('../../../api/navigation', () => ({ + getComponentNavigation: jest.fn().mockImplementation(async ({ component }) => { + const canBrowseProjectResponse = await permissionsHandler.handleGetPermissionUsersForComponent({ + projectKey: component, + q: login, + permission: Permissions.Browse, + }); + const showPermissionsResponse = await permissionsHandler.handleGetPermissionUsersForComponent({ + projectKey: component, + q: login, + permission: Permissions.Admin, }); - expect(bulkApplyButton).toBeDisabled(); - - const projects = getProjects(); + return Promise.resolve( + mockComponent({ + configuration: { + canBrowseProject: canBrowseProjectResponse.users.length > 0, + showPermissions: showPermissionsResponse.users.length > 0, + }, + }) + ); + }), +})); - expect(projects).toHaveLength(10); +const ui = { + row: byRole('row'), + firstProjectActions: byRole('button', { + name: 'projects_management.show_actions_for_x.Project 1', + }), + editPermissions: byRole('link', { name: 'edit_permissions' }), + applyPermissionTemplate: byRole('button', { name: 'projects_role.apply_template' }), + restoreAccess: byRole('button', { name: 'global_permissions.restore_access' }), + editPermissionsPage: byText('/project_roles?id=project1'), + + apply: byRole('button', { name: 'apply' }), + cancel: byRole('button', { name: 'cancel' }), + delete: byRole('button', { name: 'delete' }), + create: byRole('button', { name: 'create' }), + close: byRole('button', { name: 'close' }), + restore: byRole('button', { name: 'restore' }), + checkbox: byRole('checkbox'), + deleteProjects: byRole('button', { + name: /permission_templates.(select_to_delete|delete_selected)/, + }), + showMore: byRole('button', { name: 'show_more' }), + checkAll: byRole('checkbox', { name: 'check_all' }), + uncheckAll: byRole('checkbox', { name: 'uncheck_all' }), + bulkApplyButton: byRole('button', { + name: 'permission_templates.bulk_apply_permission_template', + }), + createProject: byRole('button', { + name: 'qualifiers.create.TRK', + }), - await user.click(screen.getByRole('button', { name: 'show_more' })); + visibilityFilter: byRole('combobox', { name: 'projects_management.filter_by_visibility' }), + qualifierFilter: byRole('combobox', { name: 'projects_management.filter_by_component' }), + analysisDateFilter: byPlaceholderText('last_analysis_before'), + provisionedFilter: byRole('checkbox', { + name: 'provisioning.only_provisioned provisioning.only_provisioned.tooltip', + }), + searchFilter: byRole('searchbox', { name: 'search.search_by_name_or_key' }), - expect(getProjects()).toHaveLength(11); + defaultVisibility: byText('settings.projects.default_visibility_of_new_projects'), - await user.click(screen.getByRole('checkbox', { name: 'check_all' })); + createDialog: byRole('dialog', { name: 'qualifiers.create.TRK' }), + displayNameInput: byRole('textbox', { + name: 'onboarding.create_project.display_name field_required', + }), + projectKeyInput: byRole('textbox', { + name: 'onboarding.create_project.project_key field_required', + }), + mainBranchNameInput: byRole('textbox', { + name: 'onboarding.create_project.main_branch_name field_required', + }), + privateVisibility: byRole('radio', { name: 'visibility.private' }), + successMsg: byText('projects_management.project_has_been_successfully_created'), - expect(bulkApplyButton).toBeEnabled(); + bulkApplyDialog: byRole('dialog', { + name: 'permission_templates.bulk_apply_permission_template', + }), + applyTemplateDialog: byRole('dialog', { + name: 'projects_role.apply_template_to_x.Project 1', + }), + selectTemplate: byRole('combobox', { name: 'template field_required' }), - await user.click(bulkApplyButton); + deleteDialog: byRole('dialog', { name: 'qualifiers.delete.TRK' }), - let modal = await screen.findByRole('dialog'); + changeDefaultVisibilityDialog: byRole('dialog', { + name: 'settings.projects.change_visibility_form.header', + }), + editDefaultVisibility: byRole('button', { + name: 'settings.projects.change_visibility_form.label', + }), + visibilityPublicRadio: byRole('radio', { + name: 'visibility.public visibility.public.description.short', + }), + submitDefaultVisibilityChange: byRole('button', { + name: 'settings.projects.change_visibility_form.submit', + }), - expect(modal).toBeInTheDocument(); - expect( - within(modal).getByText( - 'permission_templates.bulk_apply_permission_template.apply_to_selected.11' - ) - ).toBeInTheDocument(); + restoreAccessDialog: byRole('dialog', { + name: 'global_permissions.restore_access', + }), +}; - await user.click(within(modal).getByRole('button', { name: 'apply' })); +beforeAll(() => { + jest.useFakeTimers({ + advanceTimers: true, + now: new Date('2019-01-05T07:08:59Z'), + }); +}); - expect( - await screen.findByText('bulk apply permission template error message') - ).toBeInTheDocument(); - expect(await screen.findByRole('dialog')).toBeInTheDocument(); +afterEach(() => { + permissionsHandler.reset(); + settingsHandler.reset(); + handler.reset(); +}); - await user.click(within(modal).getByRole('button', { name: 'cancel' })); +it('should filter projects', async () => { + const user = userEvent.setup(); + renderProjectManagementApp(); + await waitFor(() => expect(ui.row.getAll()).toHaveLength(5)); + await selectEvent.select(ui.visibilityFilter.get(), 'visibility.public'); + expect(ui.row.getAll()).toHaveLength(4); + await user.click(ui.analysisDateFilter.get()); + await user.click(await screen.findByRole('gridcell', { name: '5' })); + expect(ui.row.getAll()).toHaveLength(3); + await user.click(ui.provisionedFilter.get()); + expect(ui.row.getAll()).toHaveLength(2); + expect(ui.row.getAll()[1]).toHaveTextContent('Project 4'); + await selectEvent.select(ui.qualifierFilter.get(), 'qualifiers.VW'); + expect(ui.provisionedFilter.query()).not.toBeInTheDocument(); + expect(ui.row.getAll()).toHaveLength(2); + expect(ui.row.getAll()[1]).toHaveTextContent('Portfolio 1'); + await selectEvent.select(ui.qualifierFilter.get(), 'qualifiers.APP'); + expect(ui.provisionedFilter.query()).not.toBeInTheDocument(); + expect(ui.row.getAll()).toHaveLength(2); + expect(ui.row.getAll()[1]).toHaveTextContent('Application 1'); +}); - const checkboxes = screen.getAllByRole('checkbox'); - await user.click(checkboxes[8]); - await user.click(checkboxes[9]); +it('should search by text', async () => { + const user = userEvent.setup(); + renderProjectManagementApp(); + await waitFor(() => expect(ui.row.getAll()).toHaveLength(5)); + await user.type(ui.searchFilter.get(), 'provision'); + expect(ui.row.getAll()).toHaveLength(2); + await selectEvent.select(ui.qualifierFilter.get(), 'qualifiers.VW'); + expect(ui.row.getAll()).toHaveLength(4); + expect(ui.searchFilter.get()).toHaveValue(''); + await user.type(ui.searchFilter.get(), 'Portfolio 2'); + expect(ui.row.getAll()).toHaveLength(2); + await selectEvent.select(ui.qualifierFilter.get(), 'qualifiers.APP'); + expect(ui.row.getAll()).toHaveLength(4); + expect(ui.searchFilter.get()).toHaveValue(''); + await user.type(ui.searchFilter.get(), 'Application 3'); + expect(ui.row.getAll()).toHaveLength(2); +}); - await user.click(bulkApplyButton); +it('should hide quilifier filter', () => { + renderProjectManagementApp({ qualifiers: [ComponentQualifier.Project] }); + expect(ui.qualifierFilter.query()).not.toBeInTheDocument(); +}); - modal = await screen.findByRole('dialog'); +it('should hide create Project button', () => { + renderProjectManagementApp(); + expect(ui.createProject.query()).not.toBeInTheDocument(); +}); - expect(modal).toBeInTheDocument(); - expect( - within(modal).getByText( - 'permission_templates.bulk_apply_permission_template.apply_to_selected.9' - ) - ).toBeInTheDocument(); +it('should delete projects, but not Portfolios or Applications', async () => { + const user = userEvent.setup(); + renderProjectManagementApp(); + expect(await ui.deleteProjects.find()).toBeDisabled(); + expect(ui.row.getAll()).toHaveLength(5); + await user.click(ui.checkbox.get(ui.row.getAll()[1])); + await user.click(ui.checkbox.get(ui.row.getAll()[2])); + expect(ui.deleteProjects.get()).toBeEnabled(); + await user.click(ui.deleteProjects.get()); + expect(ui.deleteDialog.get()).toBeInTheDocument(); + expect( + within(ui.deleteDialog.get()).getByText('projects_management.delete_selected_warning.2') + ).toBeInTheDocument(); + await user.click(ui.delete.get(ui.deleteDialog.get())); + expect(ui.row.getAll()).toHaveLength(3); +}); - await user.click(within(modal).getByRole('button', { name: 'apply' })); +it('should bulk apply permission templates to projects', async () => { + const user = userEvent.setup(); + handler.setProjects( + Array.from({ length: 11 }, (_, i) => mockProject({ key: i.toString(), name: `Test ${i}` })) + ); + renderProjectManagementApp(); + + expect(await ui.bulkApplyButton.find()).toBeDisabled(); + const projects = ui.row.getAll().slice(1); + expect(projects).toHaveLength(11); + await user.click(ui.checkAll.get()); + expect(ui.bulkApplyButton.get()).toBeEnabled(); + + await user.click(ui.bulkApplyButton.get()); + expect(await ui.bulkApplyDialog.find()).toBeInTheDocument(); + expect( + within(ui.bulkApplyDialog.get()).getByText( + 'permission_templates.bulk_apply_permission_template.apply_to_selected.11' + ) + ).toBeInTheDocument(); + + await user.click(ui.apply.get(ui.bulkApplyDialog.get())); + expect( + await screen.findByText('bulk apply permission template error message') + ).toBeInTheDocument(); + expect(ui.bulkApplyDialog.get()).toBeInTheDocument(); + + await user.click(ui.cancel.get(ui.bulkApplyDialog.get())); + + await user.click(ui.uncheckAll.get()); + await user.click(ui.checkbox.get(projects[8])); + await user.click(ui.checkbox.get(projects[9])); + await user.click(ui.checkbox.get(projects[10])); + await user.click(ui.checkbox.get(projects[9])); // uncheck one + await user.click(ui.bulkApplyButton.get()); + + expect(await ui.bulkApplyDialog.find()).toBeInTheDocument(); + expect( + within(ui.bulkApplyDialog.get()).getByText( + 'permission_templates.bulk_apply_permission_template.apply_to_selected.2' + ) + ).toBeInTheDocument(); + await selectEvent.select( + ui.selectTemplate.get(ui.bulkApplyDialog.get()), + 'Permission Template 2' + ); + await user.click(ui.apply.get(ui.bulkApplyDialog.get())); + + expect( + await within(ui.bulkApplyDialog.get()).findByText('projects_role.apply_template.success') + ).toBeInTheDocument(); +}); - modal = await screen.findByRole('dialog'); - expect( - await within(modal).findByText('projects_role.apply_template.success') - ).toBeInTheDocument(); - }); +it('should load more and change the filter without caching old pages', async () => { + const user = userEvent.setup(); + handler.setProjects([ + ...Array.from({ length: 60 }, (_, i) => + mockProject({ + key: ComponentQualifier.Project + i.toString(), + name: `Project ${i}`, + qualifier: ComponentQualifier.Project, + }) + ), + ...Array.from({ length: 60 }, (_, i) => + mockProject({ + key: ComponentQualifier.Portfolio + i.toString(), + name: `Portfolio ${i}`, + qualifier: ComponentQualifier.Portfolio, + }) + ), + ...Array.from({ length: 60 }, (_, i) => + mockProject({ + key: ComponentQualifier.Application + i.toString(), + name: `Application ${i}`, + qualifier: ComponentQualifier.Application, + }) + ), + ]); + renderProjectManagementApp(); + await waitFor(() => expect(ui.row.getAll()).toHaveLength(51)); + await user.click(ui.showMore.get()); + let rows = ui.row.getAll(); + expect(rows).toHaveLength(61); + expect(rows[1]).toHaveTextContent('Project 0'); + expect(rows[60]).toHaveTextContent('Project 59'); + await selectEvent.select(ui.qualifierFilter.get(), 'qualifiers.VW'); + rows = ui.row.getAll(); + expect(rows).toHaveLength(51); + expect(rows[1]).toHaveTextContent('Portfolio 0'); + await user.click(ui.showMore.get()); + rows = ui.row.getAll(); + expect(rows).toHaveLength(61); + expect(rows[1]).toHaveTextContent('Portfolio 0'); + expect(rows[60]).toHaveTextContent('Portfolio 59'); }); -function getProjects() { - // Remove the first row (header) - return screen.getAllByRole('row').slice(1); -} +it('should create project', async () => { + settingsHandler.set(GlobalSettingKeys.MainBranchName, 'main'); + const user = userEvent.setup(); + renderProjectManagementApp({}, { permissions: { global: [Permissions.ProjectCreation] } }); + await waitFor(() => expect(ui.row.getAll()).toHaveLength(5)); + await user.click(await ui.createProject.find()); + let dialog = ui.createDialog.get(); + expect(dialog).toBeInTheDocument(); + expect(ui.privateVisibility.get(dialog)).not.toBeChecked(); + await user.click(ui.privateVisibility.get(dialog)); + expect(ui.privateVisibility.get(dialog)).not.toBeChecked(); + await user.click(ui.cancel.get(dialog)); + + expect(await ui.defaultVisibility.find()).toBeInTheDocument(); + expect(ui.defaultVisibility.get()).toHaveTextContent('—'); + await user.click(ui.editDefaultVisibility.get()); + expect(await ui.changeDefaultVisibilityDialog.find()).toBeInTheDocument(); + await user.click(ui.visibilityPublicRadio.get(ui.changeDefaultVisibilityDialog.get())); + await user.click(ui.submitDefaultVisibilityChange.get(ui.changeDefaultVisibilityDialog.get())); + expect(ui.changeDefaultVisibilityDialog.query()).not.toBeInTheDocument(); + expect(ui.defaultVisibility.get()).toHaveTextContent('visibility.public'); + + await user.click(await ui.createProject.find()); + dialog = ui.createDialog.get(); + expect(dialog).toBeInTheDocument(); + await user.click(ui.privateVisibility.get(dialog)); + expect(ui.privateVisibility.get(dialog)).toBeChecked(); + await user.type(ui.displayNameInput.get(dialog), 'a Test'); + await user.type(ui.projectKeyInput.get(dialog), 'test'); + expect(ui.mainBranchNameInput.get(dialog)).toHaveValue('main'); + await user.click(ui.create.get(dialog)); + expect(ui.successMsg.get(dialog)).toBeInTheDocument(); + await user.click(ui.close.get(dialog)); + expect(ui.row.getAll()).toHaveLength(6); + expect(ui.row.getAll()[1]).toHaveTextContent('qualifier.TRKa Testvisibility.privatetest—'); +}); -function getComponentsImplementation(overridePageSize?: number) { - return (params: SearchProjectsParameters) => { - const pageSize = overridePageSize ?? params.ps ?? 50; - const startIndex = ((params.p ?? 1) - 1) * pageSize; // artifically bump the page size to 500 - return Promise.resolve({ - paging: { - total: 1001, - }, - components: components.slice(startIndex, startIndex + pageSize), - }); - }; -} +it('should edit permissions of single project', async () => { + const user = userEvent.setup(); + renderProjectManagementApp(); + await user.click(await ui.firstProjectActions.find()); + expect(ui.restoreAccess.query()).not.toBeInTheDocument(); + expect(ui.editPermissions.get()).toBeInTheDocument(); + await user.click(ui.editPermissions.get()); -function mockComponents(n: number) { - const results = []; + expect(await ui.editPermissionsPage.find()).toBeInTheDocument(); +}); - for (let i = 0; i < n; i++) { - results.push({ - key: `project-${i + 1}`, - name: `Project ${i + 1}`, - qualifier: ComponentQualifier.Project, - visibility: Visibility.Private, - }); - } +it('should apply template for single object', async () => { + const user = userEvent.setup(); + renderProjectManagementApp(); + await user.click(await ui.firstProjectActions.find()); + await user.click(ui.applyPermissionTemplate.get()); + + expect(ui.applyTemplateDialog.get()).toBeInTheDocument(); + await selectEvent.select( + ui.selectTemplate.get(ui.applyTemplateDialog.get()), + 'Permission Template 2' + ); + await user.click(ui.apply.get(ui.applyTemplateDialog.get())); + + expect( + await within(ui.applyTemplateDialog.get()).findByText('projects_role.apply_template.success') + ).toBeInTheDocument(); +}); - return results; -} +it('should restore access to admin', async () => { + const user = userEvent.setup(); + renderProjectManagementApp({}, { login: 'gooduser2' }); + await user.click(await ui.firstProjectActions.find()); + expect(await ui.restoreAccess.find()).toBeInTheDocument(); + expect(ui.editPermissions.query()).not.toBeInTheDocument(); + await user.click(ui.restoreAccess.get()); + expect(ui.restoreAccessDialog.get()).toBeInTheDocument(); + await user.click(ui.restore.get(ui.restoreAccessDialog.get())); + expect(ui.restoreAccessDialog.query()).not.toBeInTheDocument(); + await user.click(await ui.firstProjectActions.find()); + expect(ui.restoreAccess.query()).not.toBeInTheDocument(); + expect(ui.editPermissions.get()).toBeInTheDocument(); +}); -function renderGlobalBackgroundTasksApp() { - renderAppWithAdminContext('admin/projects_management', routes, {}); +function renderProjectManagementApp( + overrides: Partial = {}, + user: Partial = {} +) { + login = user?.login ?? 'gooduser1'; + renderAppWithAdminContext('admin/projects_management', routes, { + appState: mockAppState({ + qualifiers: [ + ComponentQualifier.Project, + ComponentQualifier.Application, + ComponentQualifier.Portfolio, + ], + ...overrides, + }), + currentUser: mockCurrentUser(user), + }); } diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectManagementApp-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectManagementApp-test.tsx deleted file mode 100644 index ee3828034e3..00000000000 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectManagementApp-test.tsx +++ /dev/null @@ -1,185 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { getComponents } from '../../../api/components'; -import { changeProjectDefaultVisibility } from '../../../api/permissions'; -import { getValue } from '../../../api/settings'; -import { mockLoggedInUser } from '../../../helpers/testMocks'; -import { waitAndUpdate } from '../../../helpers/testUtils'; -import { ComponentQualifier, Visibility } from '../../../types/component'; -import { ProjectManagementApp, Props } from '../ProjectManagementApp'; - -jest.mock('lodash', () => { - const lodash = jest.requireActual('lodash'); - lodash.debounce = - (fn: Function) => - (...args: any[]) => - fn(args); - return lodash; -}); - -jest.mock('../../../api/components', () => ({ - getComponents: jest.fn().mockResolvedValue({ paging: { total: 0 }, components: [] }), -})); - -jest.mock('../../../api/permissions', () => ({ - changeProjectDefaultVisibility: jest.fn().mockResolvedValue({}), -})); - -jest.mock('../../../api/settings', () => ({ - getValue: jest.fn().mockResolvedValue({ value: 'public' }), -})); - -const defaultSearchParameters = { - p: undefined, - ps: 50, - q: undefined, -}; - -beforeEach(() => { - jest.clearAllMocks(); -}); - -it('fetches all projects on mount', async () => { - const wrapper = shallowRender(); - await waitAndUpdate(wrapper); - expect(getComponents).toHaveBeenLastCalledWith({ - ...defaultSearchParameters, - qualifiers: ComponentQualifier.Project, - }); - expect(getValue).toHaveBeenCalled(); - expect(wrapper.state().defaultProjectVisibility).toBe(Visibility.Public); -}); - -it('selects provisioned', () => { - const wrapper = shallowRender(); - wrapper.find('withAppStateContext(Search)').prop('onProvisionedChanged')(true); - expect(getComponents).toHaveBeenLastCalledWith({ - ...defaultSearchParameters, - onProvisionedOnly: true, - qualifiers: 'TRK', - }); -}); - -it('changes qualifier and resets provisioned', () => { - const wrapper = shallowRender(); - wrapper.setState({ provisioned: true }); - wrapper.find('withAppStateContext(Search)').prop('onQualifierChanged')('VW'); - expect(getComponents).toHaveBeenLastCalledWith({ ...defaultSearchParameters, qualifiers: 'VW' }); -}); - -it('searches', () => { - const wrapper = shallowRender(); - wrapper.find('withAppStateContext(Search)').prop('onSearch')('foo'); - expect(getComponents).toHaveBeenLastCalledWith({ - ...defaultSearchParameters, - q: 'foo', - qualifiers: 'TRK', - }); -}); - -it('should handle date filtering', () => { - const wrapper = shallowRender(); - wrapper.find('withAppStateContext(Search)').prop('onDateChanged')( - '2019-11-14T06:55:02.663Z' - ); - expect(getComponents).toHaveBeenCalledWith({ - ...defaultSearchParameters, - qualifiers: 'TRK', - analyzedBefore: '2019-11-14', - }); -}); - -it('should handle default project visibility change', async () => { - const wrapper = shallowRender(); - - await waitAndUpdate(wrapper); - - expect(wrapper.state().defaultProjectVisibility).toBe(Visibility.Public); - wrapper.instance().handleDefaultProjectVisibilityChange(Visibility.Private); - - expect(changeProjectDefaultVisibility).toHaveBeenCalledWith(Visibility.Private); - await waitAndUpdate(wrapper); - expect(wrapper.state().defaultProjectVisibility).toBe(Visibility.Private); -}); - -it('loads more', () => { - const wrapper = shallowRender(); - wrapper.find('ListFooter').prop('loadMore')(); - expect(getComponents).toHaveBeenLastCalledWith({ - ...defaultSearchParameters, - p: 2, - qualifiers: 'TRK', - }); -}); - -it('selects and deselects projects', async () => { - (getComponents as jest.Mock).mockImplementation(() => - Promise.resolve({ paging: { total: 2 }, components: [{ key: 'foo' }, { key: 'bar' }] }) - ); - const wrapper = shallowRender(); - await new Promise(setImmediate); - - wrapper.find('Projects').prop('onProjectSelected')('foo'); - expect(wrapper.state('selection')).toEqual(['foo']); - - wrapper.find('Projects').prop('onProjectSelected')('bar'); - expect(wrapper.state('selection')).toEqual(['foo', 'bar']); - - // should not select already selected project - wrapper.find('Projects').prop('onProjectSelected')('bar'); - expect(wrapper.state('selection')).toEqual(['foo', 'bar']); - - wrapper.find('Projects').prop('onProjectDeselected')('foo'); - expect(wrapper.state('selection')).toEqual(['bar']); - - wrapper.find('withAppStateContext(Search)').prop('onAllDeselected')(); - expect(wrapper.state('selection')).toEqual([]); - - wrapper.find('withAppStateContext(Search)').prop('onAllSelected')(); - expect(wrapper.state('selection')).toEqual(['foo', 'bar']); -}); - -it('creates project', () => { - const wrapper = shallowRender(); - expect(wrapper.find('CreateProjectForm').exists()).toBe(false); - - wrapper.find('Header').prop('onProjectCreate')(); - wrapper.update(); - expect(wrapper.find('CreateProjectForm').exists()).toBe(true); - - wrapper.find('CreateProjectForm').prop('onProjectCreated')(); - wrapper.update(); - expect((getComponents as jest.Mock).mock.calls).toHaveLength(2); - - wrapper.find('CreateProjectForm').prop('onClose')(); - wrapper.update(); - expect(wrapper.find('CreateProjectForm').exists()).toBe(false); -}); - -function shallowRender(props?: { [P in keyof Props]?: Props[P] }) { - return shallow( - - ); -} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRow-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRow-test.tsx deleted file mode 100644 index e1a9026b7d5..00000000000 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRow-test.tsx +++ /dev/null @@ -1,60 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { ComponentQualifier, Visibility } from '../../../types/component'; -import ProjectRow from '../ProjectRow'; - -const project = { - key: 'project', - name: 'Project', - qualifier: ComponentQualifier.Project, - visibility: Visibility.Private, -}; - -it('renders', () => { - expect(shallowRender()).toMatchSnapshot(); - expect( - shallowRender({ project: { ...project, lastAnalysisDate: '2017-04-08T00:00:00.000Z' } }) - ).toMatchSnapshot('with lastAnalysisDate'); - expect( - shallowRender({ project: { ...project, qualifier: ComponentQualifier.Portfolio } }) - ).toMatchSnapshot('portfolio'); -}); - -it('checks project', () => { - const onProjectCheck = jest.fn(); - const wrapper = shallowRender({ onProjectCheck }); - wrapper.find('Checkbox').prop('onCheck')(false); - expect(onProjectCheck).toHaveBeenCalledWith(project, false); -}); - -function shallowRender(props?: any) { - return shallow( - - ); -} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRowActions-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRowActions-test.tsx deleted file mode 100644 index 819bd7ae73d..00000000000 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRowActions-test.tsx +++ /dev/null @@ -1,133 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { getComponentNavigation } from '../../../api/navigation'; -import { mockLoggedInUser } from '../../../helpers/testMocks'; -import { click, waitAndUpdate } from '../../../helpers/testUtils'; -import { ComponentQualifier, Visibility } from '../../../types/component'; -import ProjectRowActions, { Props } from '../ProjectRowActions'; - -jest.mock('../../../api/navigation', () => ({ - getComponentNavigation: jest.fn().mockResolvedValue({}), -})); - -beforeEach(() => { - jest.clearAllMocks(); -}); - -it('renders correctly', () => { - const wrapper = shallowRender(); - expect(wrapper).toMatchSnapshot(); -}); - -describe('restore access', () => { - beforeAll(() => { - (getComponentNavigation as jest.Mock).mockResolvedValue({ - configuration: { - canBrowseProject: false, - showPermissions: false, - }, - }); - }); - - it('shows the restore access action', async () => { - const wrapper = shallowRender(); - wrapper.instance().handleDropdownOpen(); - await waitAndUpdate(wrapper); - - expect(getComponentNavigation).toHaveBeenCalledWith({ component: 'foo' }); - expect(wrapper.find('.js-restore-access').exists()).toBe(true); - }); - - it('shows the restore access modal', async () => { - const wrapper = shallowRender(); - wrapper.instance().handleDropdownOpen(); - await waitAndUpdate(wrapper); - - click(wrapper.find('.js-restore-access')); - expect(wrapper.find('RestoreAccessModal')).toMatchSnapshot(); - - wrapper.instance().handleRestoreAccessDone(); - await waitAndUpdate(wrapper); - expect(wrapper.find('.js-restore-access').exists()).toBe(false); - expect(wrapper.find('RestoreAccessModal').exists()).toBe(false); - }); - - it('also shows the restore access when browse permission is missing', async () => { - (getComponentNavigation as jest.Mock).mockResolvedValueOnce({ - configuration: { canBrowseProject: false, showPermissions: true }, - }); - - const wrapper = shallowRender(); - wrapper.instance().handleDropdownOpen(); - await waitAndUpdate(wrapper); - - expect(getComponentNavigation).toHaveBeenCalledWith({ component: 'foo' }); - expect(wrapper.find('.js-restore-access').exists()).toBe(true); - }); -}); - -describe('permissions', () => { - beforeAll(() => { - (getComponentNavigation as jest.Mock).mockResolvedValue({ - configuration: { - canBrowseProject: true, - showPermissions: true, - }, - }); - }); - - it('shows the update permissions action', async () => { - const wrapper = shallowRender(); - wrapper.instance().handleDropdownOpen(); - await waitAndUpdate(wrapper); - expect(wrapper.find('.js-edit-permissions').exists()).toBe(true); - }); - - it('shows the apply permission template modal', async () => { - const wrapper = shallowRender(); - wrapper.instance().handleDropdownOpen(); - await waitAndUpdate(wrapper); - - click(wrapper.find('.js-apply-template')); - expect(wrapper.find('ApplyTemplate')).toMatchSnapshot(); - - wrapper.instance().handleApplyTemplateClose(); - await waitAndUpdate(wrapper); - expect(wrapper.find('ApplyTemplate').exists()).toBe(false); - }); -}); - -function shallowRender(props: Partial = {}) { - return shallow( - - ); -} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Projects-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Projects-test.tsx deleted file mode 100644 index aa3c008d100..00000000000 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Projects-test.tsx +++ /dev/null @@ -1,57 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { ComponentQualifier, Visibility } from '../../../types/component'; -import Projects from '../Projects'; - -const projects = [ - { key: 'a', name: 'A', qualifier: ComponentQualifier.Project, visibility: Visibility.Public }, - { key: 'b', name: 'B', qualifier: ComponentQualifier.Project, visibility: Visibility.Public }, -]; -const selection = ['a']; - -it('renders list of projects', () => { - expect(shallowRender({ projects, selection })).toMatchSnapshot(); -}); - -it('selects and deselects project', () => { - const onProjectDeselected = jest.fn(); - const onProjectSelected = jest.fn(); - const wrapper = shallowRender({ onProjectDeselected, onProjectSelected, projects }); - - wrapper.find('ProjectRow').first().prop('onProjectCheck')(projects[0], true); - expect(onProjectSelected).toHaveBeenCalledWith('a'); - - wrapper.find('ProjectRow').first().prop('onProjectCheck')(projects[0], false); - expect(onProjectDeselected).toHaveBeenCalledWith('a'); -}); - -function shallowRender(props?: any) { - return shallow( - - ); -} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Search-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Search-test.tsx deleted file mode 100644 index d9de391edb9..00000000000 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Search-test.tsx +++ /dev/null @@ -1,160 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { mockReactSelectOptionProps } from '../../../helpers/mocks/react-select'; -import { mockAppState } from '../../../helpers/testMocks'; -import { click } from '../../../helpers/testUtils'; -import { Props, Search } from '../Search'; - -it('renders', () => { - expect(shallowRender()).toMatchSnapshot(); -}); - -it('disables the delete and bulk apply buttons unless a project is selected', () => { - const wrapper = shallowRender(); - expect(wrapper.find('Button.js-delete').prop('disabled')).toBe(true); - expect(wrapper.find('Button.js-bulk-apply-permission-template').prop('disabled')).toBe(true); - - wrapper.setProps({ selection: ['foo'] }); - expect(wrapper.find('Button.js-delete').prop('disabled')).toBe(false); - expect(wrapper.find('Button.js-bulk-apply-permission-template').prop('disabled')).toBe(false); -}); - -it('render qualifiers filter', () => { - expect( - shallowRender({ appState: mockAppState({ qualifiers: ['TRK', 'VW', 'APP'] }) }) - ).toMatchSnapshot(); -}); - -it('updates qualifier', () => { - const onQualifierChanged = jest.fn(); - const wrapper = shallowRender({ - onQualifierChanged, - appState: mockAppState({ qualifiers: ['TRK', 'VW', 'APP'] }), - }); - wrapper.find('Select[name="projects-qualifier"]').simulate('change', { - value: 'VW', - }); - expect(onQualifierChanged).toHaveBeenCalledWith('VW'); -}); - -it('renders optionrenderer and singlevaluerenderer', () => { - const wrapper = shallowRender({ - appState: mockAppState({ qualifiers: ['TRK', 'VW', 'APP'] }), - }); - const OptionRendererer = wrapper.instance().optionRenderer; - const SingleValueRendererer = wrapper.instance().singleValueRenderer; - expect( - shallow() - ).toMatchSnapshot('option renderer'); - expect( - shallow( - - ) - ).toMatchSnapshot('single value renderer'); -}); - -it('selects provisioned', () => { - const onProvisionedChanged = jest.fn(); - const wrapper = shallowRender({ onProvisionedChanged }); - wrapper.find('Checkbox[id="projects-provisioned"]').prop('onCheck')(true); - expect(onProvisionedChanged).toHaveBeenCalledWith(true); -}); - -it('does not render provisioned filter for portfolios', () => { - const wrapper = shallowRender(); - expect(wrapper.find('Checkbox[id="projects-provisioned"]').exists()).toBe(true); - wrapper.setProps({ qualifiers: 'VW' }); - expect(wrapper.find('Checkbox[id="projects-provisioned"]').exists()).toBe(false); -}); - -it('updates analysis date', () => { - const onDateChanged = jest.fn(); - const wrapper = shallowRender({ onDateChanged }); - - wrapper.find('DateInput').prop('onChange')('2017-04-08T00:00:00.000Z'); - expect(onDateChanged).toHaveBeenCalledWith('2017-04-08T00:00:00.000Z'); - - wrapper.find('DateInput').prop('onChange')(undefined); - expect(onDateChanged).toHaveBeenCalledWith(undefined); -}); - -it('searches', () => { - const onSearch = jest.fn(); - const wrapper = shallowRender({ onSearch }); - wrapper.find('SearchBox').prop('onChange')('foo'); - expect(onSearch).toHaveBeenCalledWith('foo'); -}); - -it('checks all or none projects', () => { - const onAllDeselected = jest.fn(); - const onAllSelected = jest.fn(); - const wrapper = shallowRender({ onAllDeselected, onAllSelected }); - - wrapper.find('Checkbox[id="projects-selection"]').prop('onCheck')(true); - expect(onAllSelected).toHaveBeenCalled(); - - wrapper.find('Checkbox[id="projects-selection"]').prop('onCheck')(false); - expect(onAllDeselected).toHaveBeenCalled(); -}); - -it('deletes projects', () => { - const onDeleteProjects = jest.fn(); - const wrapper = shallowRender({ onDeleteProjects, selection: ['foo', 'bar'] }); - click(wrapper.find('.js-delete')); - expect(wrapper.find('DeleteModal')).toMatchSnapshot(); - wrapper.find('DeleteModal').prop('onConfirm')(); - expect(onDeleteProjects).toHaveBeenCalled(); -}); - -it('bulk applies permission template', () => { - const wrapper = shallowRender({}); - click(wrapper.find('.js-bulk-apply-permission-template')); - expect(wrapper.find('BulkApplyTemplateModal')).toMatchSnapshot(); - wrapper.find('BulkApplyTemplateModal').prop('onClose')(); - wrapper.update(); - expect(wrapper.find('BulkApplyTemplateModal').exists()).toBe(false); -}); - -function shallowRender(props?: { [P in keyof Props]?: Props[P] }) { - return shallow( - - ); -} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/BulkApplyTemplateModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/BulkApplyTemplateModal-test.tsx.snap deleted file mode 100644 index 2e0bb0a492a..00000000000 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/BulkApplyTemplateModal-test.tsx.snap +++ /dev/null @@ -1,447 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`bulk applies template to all results 1`] = ` - -
-

- permission_templates.bulk_apply_permission_template -

-
-
- -
-
- - cancel - -
-
-`; - -exports[`bulk applies template to all results 2`] = ` - -
-

- permission_templates.bulk_apply_permission_template -

-
-
- - - permission_templates.bulk_apply_permission_template.apply_to_all.17 - -
- - -
-
-
- - - apply - - - cancel - -
-
-`; - -exports[`bulk applies template to all results 4`] = ` - -
-

- permission_templates.bulk_apply_permission_template -

-
-
- - projects_role.apply_template.success - -
-
- - close - -
-
-`; - -exports[`bulk applies template to selected results 1`] = ` - -
-

- permission_templates.bulk_apply_permission_template -

-
-
- -
-
- - cancel - -
-
-`; - -exports[`bulk applies template to selected results 2`] = ` - -
-

- permission_templates.bulk_apply_permission_template -

-
-
- - - permission_templates.bulk_apply_permission_template.apply_to_selected.2 - -
- - -
-
-
- - - apply - - - cancel - -
-
-`; - -exports[`bulk applies template to selected results 4`] = ` - -
-

- permission_templates.bulk_apply_permission_template -

-
-
- - projects_role.apply_template.success - -
-
- - close - -
-
-`; diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ChangeDefaultVisibilityForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ChangeDefaultVisibilityForm-test.tsx.snap deleted file mode 100644 index a669b4a499d..00000000000 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ChangeDefaultVisibilityForm-test.tsx.snap +++ /dev/null @@ -1,159 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`changes visibility 1`] = ` - -
-

- settings.projects.change_visibility_form.header -

-
-
-
- -
- visibility.public -

- visibility.public.description.short -

-
-
-
-
- -
- visibility.private -

- visibility.private.description.short -

-
-
-
- - settings.projects.change_visibility_form.warning - -
-
- - - cancel - -
-
-`; - -exports[`changes visibility 2`] = ` - -
-

- settings.projects.change_visibility_form.header -

-
-
-
- -
- visibility.public -

- visibility.public.description.short -

-
-
-
-
- -
- visibility.private -

- visibility.private.description.short -

-
-
-
- - settings.projects.change_visibility_form.warning - -
-
- - - cancel - -
-
-`; diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/DeleteModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/DeleteModal-test.tsx.snap deleted file mode 100644 index 662bae226c1..00000000000 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/DeleteModal-test.tsx.snap +++ /dev/null @@ -1,171 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`deletes all projects 1`] = ` - -
-

- qualifiers.delete.TRK -

-
-
- - projects_management.delete_all_warning.17 - - qualifiers.delete_confirm.TRK -
-
- - delete - - - cancel - -
-
-`; - -exports[`deletes all projects 2`] = ` - -
-

- qualifiers.delete.TRK -

-
-
- - projects_management.delete_all_warning.17 - - qualifiers.delete_confirm.TRK -
-
- - - delete - - - cancel - -
-
-`; - -exports[`deletes selected projects 1`] = ` - -
-

- qualifiers.delete.TRK -

-
-
- - projects_management.delete_selected_warning.2 - - qualifiers.delete_confirm.TRK -
-
- - delete - - - cancel - -
-
-`; - -exports[`deletes selected projects 2`] = ` - -
-

- qualifiers.delete.TRK -

-
-
- - projects_management.delete_selected_warning.2 - - qualifiers.delete_confirm.TRK -
-
- - - delete - - - cancel - -
-
-`; diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Header-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Header-test.tsx.snap deleted file mode 100644 index 839270b15bc..00000000000 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Header-test.tsx.snap +++ /dev/null @@ -1,99 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`changes default visibility 1`] = ` - -`; - -exports[`renders: default 1`] = ` -
-

- projects_management -

-
- - - settings.projects.default_visibility_of_new_projects - - - visibility.public - - - - - -
-

- projects_management.page.description -

-
-`; - -exports[`renders: undefined visibility 1`] = ` -
-

- projects_management -

-
- - - settings.projects.default_visibility_of_new_projects - - - — - - - - - -
-

- projects_management.page.description -

-
-`; diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRow-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRow-test.tsx.snap deleted file mode 100644 index e59f26b8049..00000000000 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRow-test.tsx.snap +++ /dev/null @@ -1,279 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders 1`] = ` - - - - - - - - - - Project - - - - - - - - - - - project - - - - - - — - - - - - - -`; - -exports[`renders: portfolio 1`] = ` - - - - - - - - - - Project - - - - - - - - - - - project - - - - - - — - - - - - - -`; - -exports[`renders: with lastAnalysisDate 1`] = ` - - - - - - - - - - Project - - - - - - - - - - - project - - - - - - - - - - -`; diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRowActions-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRowActions-test.tsx.snap deleted file mode 100644 index f892badf19b..00000000000 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRowActions-test.tsx.snap +++ /dev/null @@ -1,60 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`permissions shows the apply permission template modal 1`] = ` - -`; - -exports[`renders correctly 1`] = ` - - - - projects_role.apply_template - - - -`; - -exports[`restore access shows the restore access modal 1`] = ` - -`; diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Projects-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Projects-test.tsx.snap deleted file mode 100644 index 31f831ad1d8..00000000000 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Projects-test.tsx.snap +++ /dev/null @@ -1,69 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders list of projects 1`] = ` -
- - - - - - - - - - - - -
- - name - - - key - - last_analysis - -
-
-`; diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Search-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Search-test.tsx.snap deleted file mode 100644 index e505f08023c..00000000000 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Search-test.tsx.snap +++ /dev/null @@ -1,315 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`bulk applies permission template 1`] = ` - -`; - -exports[`deletes projects 1`] = ` - -`; - -exports[`render qualifiers filter 1`] = ` -
-
-
- -
- -
- - - provisioning.only_provisioned - - - -
-
- -
-
- - -
-
-
-`; - -exports[`renders 1`] = ` -
-
-
- -
- -