aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/projectsManagement
diff options
context:
space:
mode:
authorViktor Vorona <viktor.vorona@sonarsource.com>2023-06-15 16:31:15 +0200
committersonartech <sonartech@sonarsource.com>2023-06-15 20:03:02 +0000
commitb0dd806289b026ba00bffe34c87dee292e8e19c6 (patch)
tree8b6be788feaa4fa6d3f8028ae937788f419b678a /server/sonar-web/src/main/js/apps/projectsManagement
parenta458479636767ad21136c071f038f1b5321ea991 (diff)
downloadsonarqube-b0dd806289b026ba00bffe34c87dee292e8e19c6.tar.gz
sonarqube-b0dd806289b026ba00bffe34c87dee292e8e19c6.zip
SONAR-18440 RTL Migration Project Management
Diffstat (limited to 'server/sonar-web/src/main/js/apps/projectsManagement')
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/ChangeDefaultVisibilityForm.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/CreateProjectForm.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/DeleteModal.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/ProjectManagementApp.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/ProjectRow.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/Projects.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/RestoreAccessModal.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/BulkApplyTemplateModal-test.tsx122
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ChangeDefaultVisibilityForm-test.tsx55
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/CreateProjectForm-test.tsx82
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/DeleteModal-test.tsx91
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Header-test.tsx67
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectManagementApp-it.tsx471
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectManagementApp-test.tsx185
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRow-test.tsx60
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRowActions-test.tsx133
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Projects-test.tsx57
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Search-test.tsx160
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/BulkApplyTemplateModal-test.tsx.snap447
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ChangeDefaultVisibilityForm-test.tsx.snap159
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/DeleteModal-test.tsx.snap171
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Header-test.tsx.snap99
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRow-test.tsx.snap279
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRowActions-test.tsx.snap60
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Projects-test.tsx.snap69
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Search-test.tsx.snap315
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/routes.tsx2
29 files changed, 395 insertions, 2733 deletions
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<Pro
};
render() {
+ const header = translate('settings.projects.change_visibility_form.header');
+
return (
- <Modal contentLabel="modal form" onRequestClose={this.props.onClose}>
+ <Modal contentLabel={header} onRequestClose={this.props.onClose}>
<header className="modal-head">
- <h2>{translate('settings.projects.change_visibility_form.header')}</h2>
+ <h2>{header}</h2>
</header>
<div className="modal-body">
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<Props, State>
render() {
const { defaultProjectVisibility } = this.props;
const { createdProject } = this.state;
+ const header = translate('qualifiers.create.TRK');
return (
- <Modal contentLabel="modal form" onRequestClose={this.props.onClose}>
+ <Modal contentLabel={header} onRequestClose={this.props.onClose}>
{createdProject ? (
<div>
<header className="modal-head">
- <h2>{translate('qualifiers.create.TRK')}</h2>
+ <h2>{header}</h2>
</header>
<div className="modal-body">
@@ -168,7 +169,7 @@ export default class CreateProjectForm extends React.PureComponent<Props, State>
) : (
<form id="create-project-form" onSubmit={this.handleFormSubmit}>
<header className="modal-head">
- <h2>{translate('qualifiers.create.TRK')}</h2>
+ <h2>{header}</h2>
</header>
<div className="modal-body">
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<Props, State> {
+class ProjectManagementApp extends React.PureComponent<Props, State> {
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<Props, State> {
+class Search extends React.PureComponent<Props, State> {
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<any>;
-const getPermissionTemplates = require('../../../api/permissions')
- .getPermissionTemplates as jest.Mock<any>;
-
-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 (
- <BulkApplyTemplateModal
- analyzedBefore={parseDate('2017-04-08T00:00:00.000Z')}
- onClose={jest.fn()}
- provisioned
- qualifier="TRK"
- query="bla"
- selection={[]}
- total={17}
- {...props}
- />
- );
-}
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<ChangeDefaultVisibilityForm['props']> = {}) {
- return shallow(
- <ChangeDefaultVisibilityForm
- defaultVisibility={Visibility.Public}
- onClose={jest.fn()}
- onConfirm={jest.fn()}
- {...props}
- />
- );
-}
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<CreateProjectForm['props']> = {}) {
- render(<CreateProjectForm onClose={jest.fn()} onProjectCreated={jest.fn()} {...props} />);
-}
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<any>;
-
-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(
- <DeleteModal
- analyzedBefore={parseDate('2017-04-08T00:00:00.000Z')}
- onClose={jest.fn()}
- onConfirm={jest.fn()}
- provisioned={false}
- qualifier="TRK"
- query="bla"
- selection={[]}
- total={17}
- {...props}
- />
- );
-}
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<Function>('onConfirm')(Visibility.Private);
- expect(onChangeDefaultProjectVisibility).toHaveBeenCalledWith(Visibility.Private);
-
- modalWrapper.prop<Function>('onClose')();
- wrapper.update();
- expect(wrapper.find('ChangeDefaultVisibilityForm').exists()).toBe(false);
-});
-
-function shallowRender(props?: { [P in keyof Props]?: Props[P] }) {
- return shallow(
- <Header
- hasProvisionPermission
- onChangeDefaultProjectVisibility={jest.fn()}
- onProjectCreate={jest.fn()}
- {...props}
- />
- );
-}
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<AppState> = {},
+ user: Partial<LoggedInUser> = {}
+) {
+ 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<Function>('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<Function>('onQualifierChanged')('VW');
- expect(getComponents).toHaveBeenLastCalledWith({ ...defaultSearchParameters, qualifiers: 'VW' });
-});
-
-it('searches', () => {
- const wrapper = shallowRender();
- wrapper.find('withAppStateContext(Search)').prop<Function>('onSearch')('foo');
- expect(getComponents).toHaveBeenLastCalledWith({
- ...defaultSearchParameters,
- q: 'foo',
- qualifiers: 'TRK',
- });
-});
-
-it('should handle date filtering', () => {
- const wrapper = shallowRender();
- wrapper.find('withAppStateContext(Search)').prop<Function>('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<Function>('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<Function>('onProjectSelected')('foo');
- expect(wrapper.state('selection')).toEqual(['foo']);
-
- wrapper.find('Projects').prop<Function>('onProjectSelected')('bar');
- expect(wrapper.state('selection')).toEqual(['foo', 'bar']);
-
- // should not select already selected project
- wrapper.find('Projects').prop<Function>('onProjectSelected')('bar');
- expect(wrapper.state('selection')).toEqual(['foo', 'bar']);
-
- wrapper.find('Projects').prop<Function>('onProjectDeselected')('foo');
- expect(wrapper.state('selection')).toEqual(['bar']);
-
- wrapper.find('withAppStateContext(Search)').prop<Function>('onAllDeselected')();
- expect(wrapper.state('selection')).toEqual([]);
-
- wrapper.find('withAppStateContext(Search)').prop<Function>('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<Function>('onProjectCreate')();
- wrapper.update();
- expect(wrapper.find('CreateProjectForm').exists()).toBe(true);
-
- wrapper.find('CreateProjectForm').prop<Function>('onProjectCreated')();
- wrapper.update();
- expect((getComponents as jest.Mock).mock.calls).toHaveLength(2);
-
- wrapper.find('CreateProjectForm').prop<Function>('onClose')();
- wrapper.update();
- expect(wrapper.find('CreateProjectForm').exists()).toBe(false);
-});
-
-function shallowRender(props?: { [P in keyof Props]?: Props[P] }) {
- return shallow<ProjectManagementApp>(
- <ProjectManagementApp
- currentUser={mockLoggedInUser({ login: 'foo', permissions: { global: ['provisioning'] } })}
- {...props}
- />
- );
-}
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<Function>('onCheck')(false);
- expect(onProjectCheck).toHaveBeenCalledWith(project, false);
-});
-
-function shallowRender(props?: any) {
- return shallow(
- <ProjectRow
- currentUser={{ login: 'foo' }}
- onApplyTemplate={jest.fn()}
- onProjectCheck={jest.fn()}
- project={project}
- selected
- {...props}
- />
- );
-}
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<Props> = {}) {
- return shallow<ProjectRowActions>(
- <ProjectRowActions
- currentUser={mockLoggedInUser()}
- project={{
- id: 'foo',
- key: 'foo',
- name: 'Foo',
- qualifier: ComponentQualifier.Project,
- visibility: Visibility.Private,
- }}
- {...props}
- />
- );
-}
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<Function>('onProjectCheck')(projects[0], true);
- expect(onProjectSelected).toHaveBeenCalledWith('a');
-
- wrapper.find('ProjectRow').first().prop<Function>('onProjectCheck')(projects[0], false);
- expect(onProjectDeselected).toHaveBeenCalledWith('a');
-});
-
-function shallowRender(props?: any) {
- return shallow(
- <Projects
- currentUser={{ login: 'foo' }}
- onProjectDeselected={jest.fn()}
- onProjectSelected={jest.fn()}
- selection={[]}
- {...props}
- />
- );
-}
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(<OptionRendererer {...mockReactSelectOptionProps({ label: 'Val', value: 'val' })} />)
- ).toMatchSnapshot('option renderer');
- expect(
- shallow(
- <SingleValueRendererer {...mockReactSelectOptionProps({ label: 'Val', value: 'val' })} />
- )
- ).toMatchSnapshot('single value renderer');
-});
-
-it('selects provisioned', () => {
- const onProvisionedChanged = jest.fn();
- const wrapper = shallowRender({ onProvisionedChanged });
- wrapper.find('Checkbox[id="projects-provisioned"]').prop<Function>('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<Function>('onChange')('2017-04-08T00:00:00.000Z');
- expect(onDateChanged).toHaveBeenCalledWith('2017-04-08T00:00:00.000Z');
-
- wrapper.find('DateInput').prop<Function>('onChange')(undefined);
- expect(onDateChanged).toHaveBeenCalledWith(undefined);
-});
-
-it('searches', () => {
- const onSearch = jest.fn();
- const wrapper = shallowRender({ onSearch });
- wrapper.find('SearchBox').prop<Function>('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<Function>('onCheck')(true);
- expect(onAllSelected).toHaveBeenCalled();
-
- wrapper.find('Checkbox[id="projects-selection"]').prop<Function>('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<Function>('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<Function>('onClose')();
- wrapper.update();
- expect(wrapper.find('BulkApplyTemplateModal').exists()).toBe(false);
-});
-
-function shallowRender(props?: { [P in keyof Props]?: Props[P] }) {
- return shallow<Search>(
- <Search
- analyzedBefore={undefined}
- onAllDeselected={jest.fn()}
- onAllSelected={jest.fn()}
- onDateChanged={jest.fn()}
- onDeleteProjects={jest.fn()}
- onProvisionedChanged={jest.fn()}
- onQualifierChanged={jest.fn()}
- onSearch={jest.fn()}
- onVisibilityChanged={jest.fn()}
- projects={[]}
- provisioned={false}
- qualifiers="TRK"
- query=""
- ready
- selection={[]}
- appState={mockAppState({ qualifiers: ['TRK'] })}
- total={17}
- {...props}
- />
- );
-}
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`] = `
-<Modal
- contentLabel="permission_templates.bulk_apply_permission_template"
- onRequestClose={[MockFunction]}
- size="small"
->
- <header
- className="modal-head"
- >
- <h2>
- permission_templates.bulk_apply_permission_template
- </h2>
- </header>
- <div
- className="modal-body"
- >
- <i
- className="spinner"
- />
- </div>
- <footer
- className="modal-foot"
- >
- <ResetButtonLink
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </footer>
-</Modal>
-`;
-
-exports[`bulk applies template to all results 2`] = `
-<Modal
- contentLabel="permission_templates.bulk_apply_permission_template"
- onRequestClose={[MockFunction]}
- size="small"
->
- <header
- className="modal-head"
- >
- <h2>
- permission_templates.bulk_apply_permission_template
- </h2>
- </header>
- <div
- className="modal-body"
- >
- <MandatoryFieldsExplanation
- className="spacer-bottom"
- />
- <Alert
- variant="warning"
- >
- permission_templates.bulk_apply_permission_template.apply_to_all.17
- </Alert>
- <div
- className="modal-field"
- >
- <label
- htmlFor="bulk-apply-template-input"
- >
- template
- <MandatoryFieldMarker />
- </label>
- <Select
- id="bulk-apply-template"
- inputId="bulk-apply-template-input"
- isDisabled={false}
- onChange={[Function]}
- options={
- [
- {
- "label": "Foo",
- "value": "foo",
- },
- {
- "label": "Bar",
- "value": "bar",
- },
- ]
- }
- value={
- {
- "label": "Foo",
- "value": "foo",
- }
- }
- />
- </div>
- </div>
- <footer
- className="modal-foot"
- >
- <SubmitButton
- disabled={false}
- onClick={[Function]}
- >
- apply
- </SubmitButton>
- <ResetButtonLink
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </footer>
-</Modal>
-`;
-
-exports[`bulk applies template to all results 3`] = `
-<Modal
- contentLabel="permission_templates.bulk_apply_permission_template"
- onRequestClose={[MockFunction]}
- size="small"
->
- <header
- className="modal-head"
- >
- <h2>
- permission_templates.bulk_apply_permission_template
- </h2>
- </header>
- <div
- className="modal-body"
- >
- <MandatoryFieldsExplanation
- className="spacer-bottom"
- />
- <Alert
- variant="warning"
- >
- permission_templates.bulk_apply_permission_template.apply_to_all.17
- </Alert>
- <div
- className="modal-field"
- >
- <label
- htmlFor="bulk-apply-template-input"
- >
- template
- <MandatoryFieldMarker />
- </label>
- <Select
- id="bulk-apply-template"
- inputId="bulk-apply-template-input"
- isDisabled={true}
- onChange={[Function]}
- options={
- [
- {
- "label": "Foo",
- "value": "foo",
- },
- {
- "label": "Bar",
- "value": "bar",
- },
- ]
- }
- value={
- {
- "label": "Foo",
- "value": "foo",
- }
- }
- />
- </div>
- </div>
- <footer
- className="modal-foot"
- >
- <i
- className="spinner spacer-right"
- />
- <SubmitButton
- disabled={true}
- onClick={[Function]}
- >
- apply
- </SubmitButton>
- <ResetButtonLink
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </footer>
-</Modal>
-`;
-
-exports[`bulk applies template to all results 4`] = `
-<Modal
- contentLabel="permission_templates.bulk_apply_permission_template"
- onRequestClose={[MockFunction]}
- size="small"
->
- <header
- className="modal-head"
- >
- <h2>
- permission_templates.bulk_apply_permission_template
- </h2>
- </header>
- <div
- className="modal-body"
- >
- <Alert
- variant="success"
- >
- projects_role.apply_template.success
- </Alert>
- </div>
- <footer
- className="modal-foot"
- >
- <ResetButtonLink
- onClick={[MockFunction]}
- >
- close
- </ResetButtonLink>
- </footer>
-</Modal>
-`;
-
-exports[`bulk applies template to selected results 1`] = `
-<Modal
- contentLabel="permission_templates.bulk_apply_permission_template"
- onRequestClose={[MockFunction]}
- size="small"
->
- <header
- className="modal-head"
- >
- <h2>
- permission_templates.bulk_apply_permission_template
- </h2>
- </header>
- <div
- className="modal-body"
- >
- <i
- className="spinner"
- />
- </div>
- <footer
- className="modal-foot"
- >
- <ResetButtonLink
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </footer>
-</Modal>
-`;
-
-exports[`bulk applies template to selected results 2`] = `
-<Modal
- contentLabel="permission_templates.bulk_apply_permission_template"
- onRequestClose={[MockFunction]}
- size="small"
->
- <header
- className="modal-head"
- >
- <h2>
- permission_templates.bulk_apply_permission_template
- </h2>
- </header>
- <div
- className="modal-body"
- >
- <MandatoryFieldsExplanation
- className="spacer-bottom"
- />
- <Alert
- variant="warning"
- >
- permission_templates.bulk_apply_permission_template.apply_to_selected.2
- </Alert>
- <div
- className="modal-field"
- >
- <label
- htmlFor="bulk-apply-template-input"
- >
- template
- <MandatoryFieldMarker />
- </label>
- <Select
- id="bulk-apply-template"
- inputId="bulk-apply-template-input"
- isDisabled={false}
- onChange={[Function]}
- options={
- [
- {
- "label": "Foo",
- "value": "foo",
- },
- {
- "label": "Bar",
- "value": "bar",
- },
- ]
- }
- value={
- {
- "label": "Foo",
- "value": "foo",
- }
- }
- />
- </div>
- </div>
- <footer
- className="modal-foot"
- >
- <SubmitButton
- disabled={false}
- onClick={[Function]}
- >
- apply
- </SubmitButton>
- <ResetButtonLink
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </footer>
-</Modal>
-`;
-
-exports[`bulk applies template to selected results 3`] = `
-<Modal
- contentLabel="permission_templates.bulk_apply_permission_template"
- onRequestClose={[MockFunction]}
- size="small"
->
- <header
- className="modal-head"
- >
- <h2>
- permission_templates.bulk_apply_permission_template
- </h2>
- </header>
- <div
- className="modal-body"
- >
- <MandatoryFieldsExplanation
- className="spacer-bottom"
- />
- <Alert
- variant="warning"
- >
- permission_templates.bulk_apply_permission_template.apply_to_selected.2
- </Alert>
- <div
- className="modal-field"
- >
- <label
- htmlFor="bulk-apply-template-input"
- >
- template
- <MandatoryFieldMarker />
- </label>
- <Select
- id="bulk-apply-template"
- inputId="bulk-apply-template-input"
- isDisabled={true}
- onChange={[Function]}
- options={
- [
- {
- "label": "Foo",
- "value": "foo",
- },
- {
- "label": "Bar",
- "value": "bar",
- },
- ]
- }
- value={
- {
- "label": "Foo",
- "value": "foo",
- }
- }
- />
- </div>
- </div>
- <footer
- className="modal-foot"
- >
- <i
- className="spinner spacer-right"
- />
- <SubmitButton
- disabled={true}
- onClick={[Function]}
- >
- apply
- </SubmitButton>
- <ResetButtonLink
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </footer>
-</Modal>
-`;
-
-exports[`bulk applies template to selected results 4`] = `
-<Modal
- contentLabel="permission_templates.bulk_apply_permission_template"
- onRequestClose={[MockFunction]}
- size="small"
->
- <header
- className="modal-head"
- >
- <h2>
- permission_templates.bulk_apply_permission_template
- </h2>
- </header>
- <div
- className="modal-body"
- >
- <Alert
- variant="success"
- >
- projects_role.apply_template.success
- </Alert>
- </div>
- <footer
- className="modal-foot"
- >
- <ResetButtonLink
- onClick={[MockFunction]}
- >
- close
- </ResetButtonLink>
- </footer>
-</Modal>
-`;
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`] = `
-<Modal
- contentLabel="modal form"
- onRequestClose={[MockFunction]}
->
- <header
- className="modal-head"
- >
- <h2>
- settings.projects.change_visibility_form.header
- </h2>
- </header>
- <div
- className="modal-body"
- >
- <div
- className="big-spacer-bottom"
- key="public"
- >
- <Radio
- checked={true}
- onCheck={[Function]}
- value="public"
- >
- <div>
- visibility.public
- <p
- className="text-muted spacer-top"
- >
- visibility.public.description.short
- </p>
- </div>
- </Radio>
- </div>
- <div
- className="big-spacer-bottom"
- key="private"
- >
- <Radio
- checked={false}
- onCheck={[Function]}
- value="private"
- >
- <div>
- visibility.private
- <p
- className="text-muted spacer-top"
- >
- visibility.private.description.short
- </p>
- </div>
- </Radio>
- </div>
- <Alert
- variant="warning"
- >
- settings.projects.change_visibility_form.warning
- </Alert>
- </div>
- <footer
- className="modal-foot"
- >
- <Button
- className="js-confirm"
- onClick={[Function]}
- type="submit"
- >
- settings.projects.change_visibility_form.submit
- </Button>
- <ResetButtonLink
- className="js-modal-close"
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </footer>
-</Modal>
-`;
-
-exports[`changes visibility 2`] = `
-<Modal
- contentLabel="modal form"
- onRequestClose={[MockFunction]}
->
- <header
- className="modal-head"
- >
- <h2>
- settings.projects.change_visibility_form.header
- </h2>
- </header>
- <div
- className="modal-body"
- >
- <div
- className="big-spacer-bottom"
- key="public"
- >
- <Radio
- checked={false}
- onCheck={[Function]}
- value="public"
- >
- <div>
- visibility.public
- <p
- className="text-muted spacer-top"
- >
- visibility.public.description.short
- </p>
- </div>
- </Radio>
- </div>
- <div
- className="big-spacer-bottom"
- key="private"
- >
- <Radio
- checked={true}
- onCheck={[Function]}
- value="private"
- >
- <div>
- visibility.private
- <p
- className="text-muted spacer-top"
- >
- visibility.private.description.short
- </p>
- </div>
- </Radio>
- </div>
- <Alert
- variant="warning"
- >
- settings.projects.change_visibility_form.warning
- </Alert>
- </div>
- <footer
- className="modal-foot"
- >
- <Button
- className="js-confirm"
- onClick={[Function]}
- type="submit"
- >
- settings.projects.change_visibility_form.submit
- </Button>
- <ResetButtonLink
- className="js-modal-close"
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </footer>
-</Modal>
-`;
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`] = `
-<Modal
- contentLabel="qualifiers.delete.TRK"
- onRequestClose={[MockFunction]}
->
- <header
- className="modal-head"
- >
- <h2>
- qualifiers.delete.TRK
- </h2>
- </header>
- <div
- className="modal-body"
- >
- <Alert
- variant="warning"
- >
- projects_management.delete_all_warning.17
- </Alert>
- qualifiers.delete_confirm.TRK
- </div>
- <footer
- className="modal-foot"
- >
- <SubmitButton
- className="button-red"
- disabled={false}
- onClick={[Function]}
- >
- delete
- </SubmitButton>
- <ResetButtonLink
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </footer>
-</Modal>
-`;
-
-exports[`deletes all projects 2`] = `
-<Modal
- contentLabel="qualifiers.delete.TRK"
- onRequestClose={[MockFunction]}
->
- <header
- className="modal-head"
- >
- <h2>
- qualifiers.delete.TRK
- </h2>
- </header>
- <div
- className="modal-body"
- >
- <Alert
- variant="warning"
- >
- projects_management.delete_all_warning.17
- </Alert>
- qualifiers.delete_confirm.TRK
- </div>
- <footer
- className="modal-foot"
- >
- <i
- className="spinner spacer-right"
- />
- <SubmitButton
- className="button-red"
- disabled={true}
- onClick={[Function]}
- >
- delete
- </SubmitButton>
- <ResetButtonLink
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </footer>
-</Modal>
-`;
-
-exports[`deletes selected projects 1`] = `
-<Modal
- contentLabel="qualifiers.delete.TRK"
- onRequestClose={[MockFunction]}
->
- <header
- className="modal-head"
- >
- <h2>
- qualifiers.delete.TRK
- </h2>
- </header>
- <div
- className="modal-body"
- >
- <Alert
- variant="warning"
- >
- projects_management.delete_selected_warning.2
- </Alert>
- qualifiers.delete_confirm.TRK
- </div>
- <footer
- className="modal-foot"
- >
- <SubmitButton
- className="button-red"
- disabled={false}
- onClick={[Function]}
- >
- delete
- </SubmitButton>
- <ResetButtonLink
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </footer>
-</Modal>
-`;
-
-exports[`deletes selected projects 2`] = `
-<Modal
- contentLabel="qualifiers.delete.TRK"
- onRequestClose={[MockFunction]}
->
- <header
- className="modal-head"
- >
- <h2>
- qualifiers.delete.TRK
- </h2>
- </header>
- <div
- className="modal-body"
- >
- <Alert
- variant="warning"
- >
- projects_management.delete_selected_warning.2
- </Alert>
- qualifiers.delete_confirm.TRK
- </div>
- <footer
- className="modal-foot"
- >
- <i
- className="spinner spacer-right"
- />
- <SubmitButton
- className="button-red"
- disabled={true}
- onClick={[Function]}
- >
- delete
- </SubmitButton>
- <ResetButtonLink
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </footer>
-</Modal>
-`;
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`] = `
-<ChangeDefaultVisibilityForm
- defaultVisibility="public"
- onClose={[Function]}
- onConfirm={[MockFunction]}
-/>
-`;
-
-exports[`renders: default 1`] = `
-<header
- className="page-header"
->
- <h1
- className="page-title"
- >
- projects_management
- </h1>
- <div
- className="page-actions"
- >
- <span
- className="big-spacer-right"
- >
- <span
- className="text-middle"
- >
- settings.projects.default_visibility_of_new_projects
-
- <strong>
- visibility.public
- </strong>
- </span>
- <EditButton
- aria-label="settings.projects.change_visibility_form.label"
- className="js-change-visibility spacer-left button-small"
- onClick={[Function]}
- />
- </span>
- <Button
- id="create-project"
- onClick={[MockFunction]}
- >
- qualifiers.create.TRK
- </Button>
- </div>
- <p
- className="page-description"
- >
- projects_management.page.description
- </p>
-</header>
-`;
-
-exports[`renders: undefined visibility 1`] = `
-<header
- className="page-header"
->
- <h1
- className="page-title"
- >
- projects_management
- </h1>
- <div
- className="page-actions"
- >
- <span
- className="big-spacer-right"
- >
- <span
- className="text-middle"
- >
- settings.projects.default_visibility_of_new_projects
-
- <strong>
- —
- </strong>
- </span>
- <EditButton
- aria-label="settings.projects.change_visibility_form.label"
- className="js-change-visibility spacer-left button-small"
- onClick={[Function]}
- />
- </span>
- <Button
- id="create-project"
- onClick={[MockFunction]}
- >
- qualifiers.create.TRK
- </Button>
- </div>
- <p
- className="page-description"
- >
- projects_management.page.description
- </p>
-</header>
-`;
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`] = `
-<tr
- data-project-key="project"
->
- <td
- className="thin"
- >
- <Checkbox
- checked={true}
- label="projects_management.select_project.Project"
- onCheck={[Function]}
- thirdState={false}
- />
- </td>
- <td
- className="nowrap hide-overflow project-row-text-cell"
- >
- <ForwardRef(Link)
- className="link-no-underline"
- to={
- {
- "pathname": "/dashboard",
- "search": "?id=project",
- }
- }
- >
- <QualifierIcon
- className="little-spacer-right"
- qualifier="TRK"
- />
- <Tooltip
- overlay="Project"
- placement="left"
- >
- <span>
- Project
- </span>
- </Tooltip>
- </ForwardRef(Link)>
- </td>
- <td
- className="thin nowrap"
- >
- <PrivacyBadgeContainer
- qualifier="TRK"
- visibility="private"
- />
- </td>
- <td
- className="nowrap hide-overflow project-row-text-cell"
- >
- <Tooltip
- overlay="project"
- placement="left"
- >
- <span
- className="note"
- >
- project
- </span>
- </Tooltip>
- </td>
- <td
- className="thin nowrap text-right"
- >
- <span
- className="note"
- >
- —
- </span>
- </td>
- <td
- className="thin nowrap"
- >
- <ProjectRowActions
- currentUser={
- {
- "login": "foo",
- }
- }
- project={
- {
- "key": "project",
- "name": "Project",
- "qualifier": "TRK",
- "visibility": "private",
- }
- }
- />
- </td>
-</tr>
-`;
-
-exports[`renders: portfolio 1`] = `
-<tr
- data-project-key="project"
->
- <td
- className="thin"
- >
- <Checkbox
- checked={true}
- label="projects_management.select_project.Project"
- onCheck={[Function]}
- thirdState={false}
- />
- </td>
- <td
- className="nowrap hide-overflow project-row-text-cell"
- >
- <ForwardRef(Link)
- className="link-no-underline"
- to={
- {
- "pathname": "/portfolio",
- "search": "?id=project",
- }
- }
- >
- <QualifierIcon
- className="little-spacer-right"
- qualifier="VW"
- />
- <Tooltip
- overlay="Project"
- placement="left"
- >
- <span>
- Project
- </span>
- </Tooltip>
- </ForwardRef(Link)>
- </td>
- <td
- className="thin nowrap"
- >
- <PrivacyBadgeContainer
- qualifier="VW"
- visibility="private"
- />
- </td>
- <td
- className="nowrap hide-overflow project-row-text-cell"
- >
- <Tooltip
- overlay="project"
- placement="left"
- >
- <span
- className="note"
- >
- project
- </span>
- </Tooltip>
- </td>
- <td
- className="thin nowrap text-right"
- >
- <span
- className="note"
- >
- —
- </span>
- </td>
- <td
- className="thin nowrap"
- >
- <ProjectRowActions
- currentUser={
- {
- "login": "foo",
- }
- }
- project={
- {
- "key": "project",
- "name": "Project",
- "qualifier": "VW",
- "visibility": "private",
- }
- }
- />
- </td>
-</tr>
-`;
-
-exports[`renders: with lastAnalysisDate 1`] = `
-<tr
- data-project-key="project"
->
- <td
- className="thin"
- >
- <Checkbox
- checked={true}
- label="projects_management.select_project.Project"
- onCheck={[Function]}
- thirdState={false}
- />
- </td>
- <td
- className="nowrap hide-overflow project-row-text-cell"
- >
- <ForwardRef(Link)
- className="link-no-underline"
- to={
- {
- "pathname": "/dashboard",
- "search": "?id=project",
- }
- }
- >
- <QualifierIcon
- className="little-spacer-right"
- qualifier="TRK"
- />
- <Tooltip
- overlay="Project"
- placement="left"
- >
- <span>
- Project
- </span>
- </Tooltip>
- </ForwardRef(Link)>
- </td>
- <td
- className="thin nowrap"
- >
- <PrivacyBadgeContainer
- qualifier="TRK"
- visibility="private"
- />
- </td>
- <td
- className="nowrap hide-overflow project-row-text-cell"
- >
- <Tooltip
- overlay="project"
- placement="left"
- >
- <span
- className="note"
- >
- project
- </span>
- </Tooltip>
- </td>
- <td
- className="thin nowrap text-right"
- >
- <DateFormatter
- date="2017-04-08T00:00:00.000Z"
- />
- </td>
- <td
- className="thin nowrap"
- >
- <ProjectRowActions
- currentUser={
- {
- "login": "foo",
- }
- }
- project={
- {
- "key": "project",
- "lastAnalysisDate": "2017-04-08T00:00:00.000Z",
- "name": "Project",
- "qualifier": "TRK",
- "visibility": "private",
- }
- }
- />
- </td>
-</tr>
-`;
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`] = `
-<ApplyTemplate
- onClose={[Function]}
- project={
- {
- "id": "foo",
- "key": "foo",
- "name": "Foo",
- "qualifier": "TRK",
- "visibility": "private",
- }
- }
-/>
-`;
-
-exports[`renders correctly 1`] = `
-<Fragment>
- <ActionsDropdown
- label="projects_management.show_actions_for_x.Foo"
- onOpen={[Function]}
- >
- <ActionsDropdownItem
- className="js-apply-template"
- onClick={[Function]}
- >
- projects_role.apply_template
- </ActionsDropdownItem>
- </ActionsDropdown>
-</Fragment>
-`;
-
-exports[`restore access shows the restore access modal 1`] = `
-<RestoreAccessModal
- currentUser={
- {
- "dismissedNotices": {
- "educationPrinciples": false,
- },
- "groups": [],
- "isLoggedIn": true,
- "login": "luke",
- "name": "Skywalker",
- "scmAccounts": [],
- }
- }
- onClose={[Function]}
- onRestoreAccess={[Function]}
- project={
- {
- "id": "foo",
- "key": "foo",
- "name": "Foo",
- "qualifier": "TRK",
- "visibility": "private",
- }
- }
-/>
-`;
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`] = `
-<div
- className="boxed-group boxed-group-inner"
->
- <table
- className="data zebra new-loading"
- id="projects-management-page-projects"
- >
- <thead>
- <tr>
- <th />
- <th>
- name
- </th>
- <th />
- <th>
- key
- </th>
- <th
- className="thin nowrap text-right"
- >
- last_analysis
- </th>
- <th />
- </tr>
- </thead>
- <tbody>
- <ProjectRow
- currentUser={
- {
- "login": "foo",
- }
- }
- key="a"
- onProjectCheck={[Function]}
- project={
- {
- "key": "a",
- "name": "A",
- "qualifier": "TRK",
- "visibility": "public",
- }
- }
- selected={true}
- />
- <ProjectRow
- currentUser={
- {
- "login": "foo",
- }
- }
- key="b"
- onProjectCheck={[Function]}
- project={
- {
- "key": "b",
- "name": "B",
- "qualifier": "TRK",
- "visibility": "public",
- }
- }
- selected={false}
- />
- </tbody>
- </table>
-</div>
-`;
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`] = `
-<BulkApplyTemplateModal
- onClose={[Function]}
- provisioned={false}
- qualifier="TRK"
- query=""
- selection={[]}
- total={17}
-/>
-`;
-
-exports[`deletes projects 1`] = `
-<DeleteModal
- onClose={[Function]}
- onConfirm={[Function]}
- provisioned={false}
- qualifier="TRK"
- query=""
- selection={
- [
- "foo",
- "bar",
- ]
- }
- total={17}
-/>
-`;
-
-exports[`render qualifiers filter 1`] = `
-<div
- className="big-spacer-bottom"
->
- <div
- className="projects-management-search"
- >
- <div>
- <Checkbox
- checked={false}
- id="projects-selection"
- onCheck={[Function]}
- thirdState={false}
- title="check_all"
- />
- </div>
- <Select
- aria-label="projects_management.filter_by_component"
- className="input-medium it__project-qualifier-select"
- components={
- {
- "Option": [Function],
- "SingleValue": [Function],
- }
- }
- isDisabled={false}
- isSearchable={false}
- name="projects-qualifier"
- onChange={[Function]}
- options={
- [
- {
- "label": "qualifiers.TRK",
- "value": "TRK",
- },
- {
- "label": "qualifiers.VW",
- "value": "VW",
- },
- {
- "label": "qualifiers.APP",
- "value": "APP",
- },
- ]
- }
- value={
- {
- "label": "qualifiers.TRK",
- "value": "TRK",
- }
- }
- />
- <DateInput
- inputClassName="input-medium"
- name="analyzed-before"
- onChange={[MockFunction]}
- placeholder="last_analysis_before"
- />
- <Select
- aria-label="projects_management.filter_by_visibility"
- className="input-small"
- isDisabled={false}
- isSearchable={false}
- name="projects-visibility"
- onChange={[Function]}
- options={
- [
- {
- "label": "visibility.both",
- "value": "all",
- },
- {
- "label": "visibility.public",
- "value": "public",
- },
- {
- "label": "visibility.private",
- "value": "private",
- },
- ]
- }
- value={
- {
- "label": "visibility.both",
- "value": "all",
- }
- }
- />
- <div>
- <Checkbox
- checked={false}
- className="link-checkbox-control"
- id="projects-provisioned"
- onCheck={[MockFunction]}
- thirdState={false}
- >
- <span
- className="text-middle little-spacer-left"
- >
- provisioning.only_provisioned
- </span>
- <HelpTooltip
- className="spacer-left"
- overlay="provisioning.only_provisioned.tooltip"
- />
- </Checkbox>
- </div>
- <div
- className="flex-grow"
- >
- <SearchBox
- minLength={3}
- onChange={[MockFunction]}
- placeholder="search.search_by_name_or_key"
- value=""
- />
- </div>
- <div
- className="bulk-actions"
- >
- <Button
- className="js-bulk-apply-permission-template"
- disabled={true}
- onClick={[Function]}
- >
- permission_templates.bulk_apply_permission_template
- </Button>
- <Button
- className="js-delete spacer-left button-red"
- disabled={true}
- onClick={[Function]}
- title="permission_templates.select_to_delete"
- >
- delete
- </Button>
- </div>
- </div>
-</div>
-`;
-
-exports[`renders 1`] = `
-<div
- className="big-spacer-bottom"
->
- <div
- className="projects-management-search"
- >
- <div>
- <Checkbox
- checked={false}
- id="projects-selection"
- onCheck={[Function]}
- thirdState={false}
- title="check_all"
- />
- </div>
- <DateInput
- inputClassName="input-medium"
- name="analyzed-before"
- onChange={[MockFunction]}
- placeholder="last_analysis_before"
- />
- <Select
- aria-label="projects_management.filter_by_visibility"
- className="input-small"
- isDisabled={false}
- isSearchable={false}
- name="projects-visibility"
- onChange={[Function]}
- options={
- [
- {
- "label": "visibility.both",
- "value": "all",
- },
- {
- "label": "visibility.public",
- "value": "public",
- },
- {
- "label": "visibility.private",
- "value": "private",
- },
- ]
- }
- value={
- {
- "label": "visibility.both",
- "value": "all",
- }
- }
- />
- <div>
- <Checkbox
- checked={false}
- className="link-checkbox-control"
- id="projects-provisioned"
- onCheck={[MockFunction]}
- thirdState={false}
- >
- <span
- className="text-middle little-spacer-left"
- >
- provisioning.only_provisioned
- </span>
- <HelpTooltip
- className="spacer-left"
- overlay="provisioning.only_provisioned.tooltip"
- />
- </Checkbox>
- </div>
- <div
- className="flex-grow"
- >
- <SearchBox
- minLength={3}
- onChange={[MockFunction]}
- placeholder="search.search_by_name_or_key"
- value=""
- />
- </div>
- <div
- className="bulk-actions"
- >
- <Button
- className="js-bulk-apply-permission-template"
- disabled={true}
- onClick={[Function]}
- >
- permission_templates.bulk_apply_permission_template
- </Button>
- <Button
- className="js-delete spacer-left button-red"
- disabled={true}
- onClick={[Function]}
- title="permission_templates.select_to_delete"
- >
- delete
- </Button>
- </div>
- </div>
-</div>
-`;
-
-exports[`renders optionrenderer and singlevaluerenderer: option renderer 1`] = `
-<Option
- data={
- {
- "label": "Val",
- "value": "val",
- }
- }
->
- <div
- className="display-flex-center"
- >
- <QualifierIcon
- className="little-spacer-right"
- qualifier="val"
- />
- Val
- </div>
-</Option>
-`;
-
-exports[`renders optionrenderer and singlevaluerenderer: single value renderer 1`] = `
-<SingleValue
- data={
- {
- "label": "Val",
- "value": "val",
- }
- }
->
- <div
- className="display-flex-center"
- >
- <QualifierIcon
- className="little-spacer-right"
- qualifier="val"
- />
- Val
- </div>
-</SingleValue>
-`;
diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/routes.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/routes.tsx
index 9672b0ae9bb..9fa5482ffe4 100644
--- a/server/sonar-web/src/main/js/apps/projectsManagement/routes.tsx
+++ b/server/sonar-web/src/main/js/apps/projectsManagement/routes.tsx
@@ -21,6 +21,6 @@ import React from 'react';
import { Route } from 'react-router-dom';
import ProjectManagementApp from './ProjectManagementApp';
-export const routes = () => <Route path="projects_management" element={<ProjectManagementApp />} />;
+const routes = () => <Route path="projects_management" element={<ProjectManagementApp />} />;
export default routes;