* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { throwGlobalError } from '../helpers/error';
-import { getJSON, post, postJSON, RequestData } from '../helpers/request';
+import { getJSON, post, RequestData } from '../helpers/request';
import { BranchParameters } from '../types/branch-like';
import {
ComponentQualifier,
visibility: Visibility;
}
-export interface Project extends ProjectBase {
- id: string;
- lastAnalysisDate?: string;
-}
-
-export interface SearchProjectsParameters extends BaseSearchProjectsParameters {
- p?: number;
- ps?: number;
-}
-
export interface ComponentRaw {
key: string;
name: string;
needIssueSync?: boolean;
}
-export function getComponents(parameters: SearchProjectsParameters): Promise<{
- components: Project[];
- paging: Paging;
-}> {
- return getJSON('/api/projects/search', parameters);
-}
-
-export function bulkDeleteProjects(
- parameters: BaseSearchProjectsParameters
-): Promise<void | Response> {
- return post('/api/projects/bulk_delete', parameters).catch(throwGlobalError);
-}
-
-export function deleteProject(project: string): Promise<void | Response> {
- return post('/api/projects/delete', { project }).catch(throwGlobalError);
-}
-
-export function deletePortfolio(portfolio: string): Promise<void | Response> {
- return post('/api/views/delete', { key: portfolio }).catch(throwGlobalError);
-}
-
-export function setupManualProjectCreation(data: {
- name: string;
- project: string;
- mainBranch: string;
- visibility?: Visibility;
-}) {
- return (newCodeDefinitionType?: string, newCodeDefinitionValue?: string) =>
- createProject({
- ...data,
- newCodeDefinitionType,
- newCodeDefinitionValue,
- });
-}
-
-export function createProject(data: {
- name: string;
- project: string;
- mainBranch: string;
- visibility?: Visibility;
- newCodeDefinitionType?: string;
- newCodeDefinitionValue?: string;
-}): Promise<{ project: ProjectBase }> {
- return postJSON('/api/projects/create', data).catch(throwGlobalError);
-}
-
export function searchProjectTags(data?: { ps?: number; q?: string }): Promise<any> {
return getJSON('/api/project_tags/search', data).catch(throwGlobalError);
}
deletePermissionTemplate,
getGlobalPermissionsGroups,
getGlobalPermissionsUsers,
- getPermissionsGroupsForComponent,
- getPermissionsUsersForComponent,
getPermissionTemplateGroups,
- getPermissionTemplates,
getPermissionTemplateUsers,
+ getPermissionTemplates,
+ getPermissionsGroupsForComponent,
+ getPermissionsUsersForComponent,
grantPermissionToGroup,
grantPermissionToUser,
grantTemplatePermissionToGroup,
mockPermissionUser({
login: 'gooduser1',
name: 'John',
- permissions: [Permissions.IssueAdmin, Permissions.SecurityHotspotAdmin, Permissions.Browse],
+ permissions: [
+ Permissions.IssueAdmin,
+ Permissions.SecurityHotspotAdmin,
+ Permissions.Browse,
+ Permissions.Admin,
+ ],
}),
mockPermissionUser({
login: 'gooduser2',
const users =
q && q.length >= MIN_QUERY_LENGTH
- ? this.#users.filter((user) => user.name.toLowerCase().includes(q.toLowerCase()))
+ ? this.#users.filter(
+ (user) =>
+ user.name.toLowerCase().includes(q.toLowerCase()) ||
+ user.login.toLowerCase().includes(q.toLowerCase())
+ )
: this.#users;
const usersChunked = chunk(
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { cloneDeep } from 'lodash';
+import { mockProject } from '../../helpers/mocks/projects';
+import { ComponentQualifier, Visibility } from '../../types/component';
+import { SettingsKey } from '../../types/settings';
+import {
+ Project,
+ bulkDeleteProjects,
+ changeProjectDefaultVisibility,
+ createProject,
+ deletePortfolio,
+ deleteProject,
+ getComponents,
+} from '../project-management';
+import SettingsServiceMock from './SettingsServiceMock';
+
+jest.mock('../project-management');
+
+const defaultProject = [
+ mockProject({ key: 'project1', name: 'Project 1' }),
+ mockProject({ key: 'project2', name: 'Project 2', visibility: Visibility.Private }),
+ mockProject({ key: 'project3', name: 'Project 3', lastAnalysisDate: undefined }),
+ mockProject({ key: 'projectProvisioned', name: 'Project 4' }),
+ mockProject({ key: 'portfolio1', name: 'Portfolio 1', qualifier: ComponentQualifier.Portfolio }),
+ mockProject({
+ key: 'portfolio2',
+ name: 'Portfolio 2',
+ qualifier: ComponentQualifier.Portfolio,
+ visibility: Visibility.Private,
+ }),
+ mockProject({
+ key: 'portfolio3',
+ name: 'Portfolio 3',
+ qualifier: ComponentQualifier.Portfolio,
+ lastAnalysisDate: undefined,
+ }),
+ mockProject({
+ key: 'application1',
+ name: 'Application 1',
+ qualifier: ComponentQualifier.Application,
+ }),
+ mockProject({
+ key: 'application2',
+ name: 'Application 2',
+ qualifier: ComponentQualifier.Application,
+ visibility: Visibility.Private,
+ }),
+ mockProject({
+ key: 'application3',
+ name: 'Application 3',
+ qualifier: ComponentQualifier.Application,
+ lastAnalysisDate: undefined,
+ }),
+];
+
+export default class ProjectManagementServiceMock {
+ #projects: Project[];
+ #settingsService: SettingsServiceMock;
+
+ constructor(settingsService: SettingsServiceMock) {
+ this.#projects = cloneDeep(defaultProject);
+ this.#settingsService = settingsService;
+ jest.mocked(getComponents).mockImplementation(this.handleGetComponents);
+ jest.mocked(createProject).mockImplementation(this.handleCreateProject);
+ jest.mocked(bulkDeleteProjects).mockImplementation(this.handleBulkDeleteProjects);
+ jest.mocked(deleteProject).mockImplementation(this.handleDeleteProject);
+ jest.mocked(deletePortfolio).mockImplementation(this.handleDeletePortfolio);
+ jest
+ .mocked(changeProjectDefaultVisibility)
+ .mockImplementation(this.handleChangeProjectDefaultVisibility);
+ }
+
+ setProjects = (projects: Project[]) => {
+ this.#projects = cloneDeep(projects);
+ };
+
+ handleGetComponents: typeof getComponents = (params) => {
+ const pageIndex = params.p || 1;
+ const pageSize = params.ps || 50;
+ const components = this.#projects.filter((item) => {
+ if (params.qualifiers && item.qualifier !== params.qualifiers) {
+ return false;
+ }
+ if (params.visibility && item.visibility !== params.visibility) {
+ return false;
+ }
+ if (
+ params.analyzedBefore &&
+ (!item.lastAnalysisDate ||
+ new Date(item.lastAnalysisDate) > new Date(params.analyzedBefore))
+ ) {
+ return false;
+ }
+ if (params.onProvisionedOnly && !item.key.includes('Provisioned')) {
+ return false;
+ }
+ if (
+ params.q &&
+ !item.key.toLowerCase().includes(params.q.toLowerCase()) &&
+ !item.name.toLowerCase().includes(params.q.toLowerCase())
+ ) {
+ return false;
+ }
+ return true;
+ });
+
+ return this.reply({
+ components: components.slice((pageIndex - 1) * pageSize, pageSize * pageIndex),
+ paging: { pageIndex, pageSize, total: components.length },
+ });
+ };
+
+ handleCreateProject: typeof createProject = ({ project, name, visibility }) => {
+ this.#projects.unshift(
+ mockProject({
+ key: project,
+ name,
+ visibility,
+ lastAnalysisDate: undefined,
+ })
+ );
+ return this.reply({ project: this.#projects[0] });
+ };
+
+ handleBulkDeleteProjects: typeof bulkDeleteProjects = ({ projects }) => {
+ if (projects === undefined) {
+ return Promise.reject();
+ }
+
+ this.#projects = this.#projects.filter((item) => !projects.split(',').includes(item.key));
+ return this.reply();
+ };
+
+ handleDeleteProject: typeof deleteProject = (key) => {
+ const projectToDelete = this.#projects.find((item) => key === item.key);
+ if (projectToDelete?.qualifier !== ComponentQualifier.Project) {
+ return Promise.reject();
+ }
+ this.#projects = this.#projects.filter((p) => p.key !== key);
+ return this.reply();
+ };
+
+ handleDeletePortfolio: typeof deletePortfolio = (key) => {
+ const portfolioToDelete = this.#projects.find((item) => key === item.key);
+ if (portfolioToDelete?.qualifier !== ComponentQualifier.Portfolio) {
+ return Promise.reject();
+ }
+ this.#projects = this.#projects.filter((p) => p.key !== key);
+ return this.reply();
+ };
+
+ handleChangeProjectDefaultVisibility: typeof changeProjectDefaultVisibility = (visibility) => {
+ this.#settingsService.set(SettingsKey.DefaultProjectVisibility, visibility);
+ return this.reply();
+ };
+
+ reset = () => {
+ this.#projects = cloneDeep(defaultProject);
+ };
+
+ reply<T>(): Promise<void>;
+ reply<T>(response: T): Promise<T>;
+ reply<T>(response?: T): Promise<T | void> {
+ return Promise.resolve(response ? cloneDeep(response) : undefined);
+ }
+}
handleGetValue = (data: { key: string; component?: string } & BranchParameters) => {
const setting = this.#settingValues.find((s) => s.key === data.key) as SettingValue;
- return this.reply(setting);
+ return this.reply(setting ?? {});
};
handleGetValues = (data: { keys: string[]; component?: string } & BranchParameters) => {
): Promise<void | Response> {
return post('/api/projects/update_visibility', { project, visibility }).catch(throwGlobalError);
}
-
-export function changeProjectDefaultVisibility(
- projectVisibility: Visibility
-): Promise<void | Response> {
- return post('/api/projects/update_default_visibility', { projectVisibility }).catch(
- throwGlobalError
- );
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { throwGlobalError } from '../helpers/error';
+import { getJSON, post, postJSON } from '../helpers/request';
+import { ComponentQualifier, Visibility } from '../types/component';
+import { Paging } from '../types/types';
+
+export interface BaseSearchProjectsParameters {
+ analyzedBefore?: string;
+ onProvisionedOnly?: boolean;
+ projects?: string;
+ q?: string;
+ qualifiers?: string;
+ visibility?: Visibility;
+}
+
+export interface ProjectBase {
+ key: string;
+ name: string;
+ qualifier:
+ | ComponentQualifier.Application
+ | ComponentQualifier.Portfolio
+ | ComponentQualifier.Project;
+ visibility: Visibility;
+}
+
+export interface Project extends ProjectBase {
+ lastAnalysisDate?: string;
+}
+
+export interface SearchProjectsParameters extends BaseSearchProjectsParameters {
+ p?: number;
+ ps?: number;
+}
+
+export interface ComponentRaw {
+ key: string;
+ name: string;
+ isFavorite?: boolean;
+ analysisDate?: string;
+ qualifier: ComponentQualifier;
+ tags: string[];
+ visibility: Visibility;
+ leakPeriodDate?: string;
+ needIssueSync?: boolean;
+}
+
+export function getComponents(parameters: SearchProjectsParameters): Promise<{
+ components: Project[];
+ paging: Paging;
+}> {
+ return getJSON('/api/projects/search', parameters);
+}
+
+export function bulkDeleteProjects(
+ parameters: BaseSearchProjectsParameters
+): Promise<void | Response> {
+ return post('/api/projects/bulk_delete', parameters).catch(throwGlobalError);
+}
+
+export function deleteProject(project: string): Promise<void | Response> {
+ return post('/api/projects/delete', { project }).catch(throwGlobalError);
+}
+
+export function deletePortfolio(portfolio: string): Promise<void | Response> {
+ return post('/api/views/delete', { key: portfolio }).catch(throwGlobalError);
+}
+
+export function createProject(data: {
+ name: string;
+ project: string;
+ mainBranch: string;
+ visibility?: Visibility;
+ newCodeDefinitionType?: string;
+ newCodeDefinitionValue?: string;
+}): Promise<{ project: ProjectBase }> {
+ return postJSON('/api/projects/create', data).catch(throwGlobalError);
+}
+
+export function setupManualProjectCreation(data: {
+ name: string;
+ project: string;
+ mainBranch: string;
+ visibility?: Visibility;
+}) {
+ return (newCodeDefinitionType?: string, newCodeDefinitionValue?: string) =>
+ createProject({
+ ...data,
+ newCodeDefinitionType,
+ newCodeDefinitionValue,
+ });
+}
+
+export function changeProjectDefaultVisibility(
+ projectVisibility: Visibility
+): Promise<void | Response> {
+ return post('/api/projects/update_default_visibility', { projectVisibility }).catch(
+ throwGlobalError
+ );
+}
qualifiers: ['TRK'],
});
- const currentUser = {
+ const currentUser = mockCurrentUser({
isLoggedIn: false,
dismissedNotices: {},
- };
+ });
renderGlobalNavMenu({ appState, currentUser });
expect(screen.getByText('more')).toBeInTheDocument();
});
globalPages: [],
qualifiers: ['TRK'],
});
- const currentUser = {
+ const currentUser = mockCurrentUser({
isLoggedIn: false,
dismissedNotices: {},
- };
+ });
renderGlobalNavMenu({ appState, currentUser });
expect(screen.getByText('layout.settings')).toBeInTheDocument();
jest.mock('../../../../api/alm-settings');
jest.mock('../../../../api/newCodePeriod');
-jest.mock('../../../../api/components', () => ({
- ...jest.requireActual('../../../../api/components'),
+jest.mock('../../../../api/project-management', () => ({
setupManualProjectCreation: jest
.fn()
.mockReturnValue(() => Promise.resolve({ project: mockProject() })),
+}));
+jest.mock('../../../../api/components', () => ({
+ ...jest.requireActual('../../../../api/components'),
doesComponentExists: jest
.fn()
.mockImplementation(({ component }) => Promise.resolve(component === 'exists')),
nextButton: byRole('button', { name: 'next' }),
};
-jest.mock('../../../../api/components', () => ({
+jest.mock('../../../../api/project-management', () => ({
setupManualProjectCreation: jest.fn(),
+}));
+
+jest.mock('../../../../api/components', () => ({
doesComponentExists: jest
.fn()
.mockImplementation(({ component }) => Promise.resolve(component === 'exists')),
import { debounce, isEmpty } from 'lodash';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
-import { doesComponentExists, setupManualProjectCreation } from '../../../../api/components';
+import { doesComponentExists } from '../../../../api/components';
+import { setupManualProjectCreation } from '../../../../api/project-management';
import { getValue } from '../../../../api/settings';
import DocLink from '../../../../components/common/DocLink';
import ProjectKeyInput from '../../../../components/common/ProjectKeyInput';
expect(screen.getAllByRole('row').length).toBe(12);
await ui.toggleFilterByPermission(Permissions.Admin);
- expect(screen.getAllByRole('row').length).toBe(2);
+ expect(screen.getAllByRole('row').length).toBe(3);
await ui.toggleFilterByPermission(Permissions.Admin);
expect(screen.getAllByRole('row').length).toBe(12);
});
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`rendering should render the list of templates: Permission Template 1: admin 1`] = `"0 user(s)1 group(s)"`;
+exports[`rendering should render the list of templates: Permission Template 1: admin 1`] = `"1 user(s)1 group(s)"`;
exports[`rendering should render the list of templates: Permission Template 1: codeviewer 1`] = `"0 user(s)0 group(s)"`;
expect(screen.getAllByRole('row').length).toBe(11);
await ui.toggleFilterByPermission(Permissions.Admin);
- expect(screen.getAllByRole('row').length).toBe(2);
+ expect(screen.getAllByRole('row').length).toBe(3);
await ui.toggleFilterByPermission(Permissions.Admin);
expect(screen.getAllByRole('row').length).toBe(11);
});
*/
import * as React from 'react';
import { deleteApplication } from '../../api/application';
-import { deletePortfolio, deleteProject } from '../../api/components';
-import { Button } from '../../components/controls/buttons';
+import { deletePortfolio, deleteProject } from '../../api/project-management';
import ConfirmButton from '../../components/controls/ConfirmButton';
+import { Button } from '../../components/controls/buttons';
import { Router, withRouter } from '../../components/hoc/withRouter';
import { addGlobalSuccessMessage } from '../../helpers/globalMessages';
import { translate, translateWithParameters } from '../../helpers/l10n';
import { shallow } from 'enzyme';
import * as React from 'react';
import { deleteApplication } from '../../../api/application';
-import { deletePortfolio, deleteProject } from '../../../api/components';
+import { deletePortfolio, deleteProject } from '../../../api/project-management';
import { mockRouter } from '../../../helpers/testMocks';
import { Form } from '../Form';
-jest.mock('../../../api/components', () => ({
+jest.mock('../../../api/project-management', () => ({
deleteProject: jest.fn().mockResolvedValue(undefined),
deletePortfolio: jest.fn().mockResolvedValue(undefined),
}));
* 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';
};
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">
*/
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';
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">
) : (
<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">
* 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';
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';
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) {
* 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';
* 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';
*/
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';
*/
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';
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';
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 = () => {
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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} />);
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
* 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),
+ });
}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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",
- }
- }
-/>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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>
-`;
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;
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { Project } from '../../apps/projects/types';
+import { Project } from '../../api/project-management';
import { ComponentQualifier, Visibility } from '../../types/component';
export function mockProject(overrides: Partial<Project> = {}): Project {
return {
key: 'foo',
name: 'Foo',
- measures: {},
qualifier: ComponentQualifier.Project,
- tags: [],
visibility: Visibility.Public,
+ lastAnalysisDate: '2019-01-04T09:51:48Z',
...overrides,
};
}