mockPermission,
mockPermissionGroup,
mockPermissionTemplate,
+ mockPermissionTemplateGroup,
mockPermissionUser,
- mockTemplateGroup,
- mockTemplateUser,
} from '../../helpers/mocks/permissions';
+import { PERMISSIONS_ORDER_FOR_PROJECT_TEMPLATE } from '../../helpers/permissions';
import { ComponentQualifier, Visibility } from '../../types/component';
import { Permissions } from '../../types/permissions';
import { Permission, PermissionGroup, PermissionTemplate, PermissionUser } from '../../types/types';
applyTemplateToProject,
bulkApplyTemplate,
changeProjectVisibility,
+ createPermissionTemplate,
+ deletePermissionTemplate,
getGlobalPermissionsGroups,
getGlobalPermissionsUsers,
getPermissionsGroupsForComponent,
revokePermissionFromUser,
revokeTemplatePermissionFromGroup,
revokeTemplatePermissionFromUser,
+ setDefaultPermissionTemplate,
+ updatePermissionTemplate,
} from '../permissions';
const MAX_PROJECTS_TO_APPLY_PERMISSION_TEMPLATE = 10;
-const defaultPermissionTemplates: PermissionTemplate[] = [
- mockPermissionTemplate(),
- mockPermissionTemplate({
- id: 'template2',
- name: 'Permission Template 2',
- }),
-];
-
-const templateUsers = [
- mockTemplateUser(),
- mockTemplateUser({
+const defaultUsers = [
+ mockPermissionUser(),
+ mockPermissionUser({
login: 'gooduser1',
name: 'John',
- permissions: ['issueadmin', 'securityhotspotadmin', 'user'],
+ permissions: [Permissions.IssueAdmin, Permissions.SecurityHotspotAdmin, Permissions.Browse],
}),
- mockTemplateUser({
+ mockPermissionUser({
login: 'gooduser2',
name: 'Alexa',
- permissions: ['issueadmin', 'user'],
+ permissions: [Permissions.IssueAdmin, Permissions.Browse],
}),
- mockTemplateUser({
+ mockPermissionUser({
name: 'Siri',
login: 'gooduser3',
}),
- mockTemplateUser({
+ mockPermissionUser({
login: 'gooduser4',
name: 'Cool',
- permissions: ['user'],
+ permissions: [Permissions.Browse],
}),
- mockTemplateUser({
+ mockPermissionUser({
name: 'White',
login: 'baduser1',
}),
- mockTemplateUser({
+ mockPermissionUser({
name: 'Green',
login: 'baduser2',
}),
];
-const templateGroups = [
- mockTemplateGroup(),
- mockTemplateGroup({ id: 'admins', name: 'admins', permissions: [] }),
-];
-
-const defaultUsers = [mockPermissionUser()];
-
const defaultGroups = [
mockPermissionGroup({ name: 'sonar-users', permissions: [Permissions.Browse] }),
mockPermissionGroup({
mockPermissionGroup({ name: 'sonar-losers', permissions: [] }),
];
+const defaultTemplates: PermissionTemplate[] = [
+ mockPermissionTemplate({
+ id: 'template1',
+ name: 'Permission Template 1',
+ description: 'This is permission template 1',
+ defaultFor: [
+ ComponentQualifier.Project,
+ ComponentQualifier.Application,
+ ComponentQualifier.Portfolio,
+ ],
+ permissions: PERMISSIONS_ORDER_FOR_PROJECT_TEMPLATE.map((key) =>
+ mockPermissionTemplateGroup({
+ key,
+ groupsCount: defaultGroups.filter((g) => g.permissions.includes(key)).length,
+ usersCount: defaultUsers.filter((g) => g.permissions.includes(key)).length,
+ withProjectCreator: false,
+ })
+ ),
+ }),
+ mockPermissionTemplate({
+ id: 'template2',
+ name: 'Permission Template 2',
+ permissions: PERMISSIONS_ORDER_FOR_PROJECT_TEMPLATE.map((key) =>
+ mockPermissionTemplateGroup({
+ key,
+ groupsCount: 0,
+ usersCount: 0,
+ withProjectCreator: [Permissions.Browse, Permissions.CodeViewer].includes(key),
+ })
+ ),
+ }),
+];
+
const PAGE_SIZE = 5;
const MIN_QUERY_LENGTH = 3;
const DEFAULT_PAGE = 1;
export default class PermissionsServiceMock {
#permissionTemplates: PermissionTemplate[] = [];
#permissions: Permission[];
- #defaultTemplates: Array<{ templateId: string; qualifier: string }>;
+ #defaultTemplates: Array<{ templateId: string; qualifier: string }> = [];
#groups: PermissionGroup[];
#users: PermissionUser[];
#isAllowedToChangePermissions = true;
constructor() {
- this.#permissionTemplates = cloneDeep(defaultPermissionTemplates);
- this.#defaultTemplates = [
- ComponentQualifier.Project,
- ComponentQualifier.Application,
- ComponentQualifier.Portfolio,
- ].map((qualifier) => ({ templateId: this.#permissionTemplates[0].id, qualifier }));
- this.#permissions = [
- Permissions.Admin,
- Permissions.CodeViewer,
- Permissions.IssueAdmin,
- Permissions.SecurityHotspotAdmin,
- Permissions.Scan,
- Permissions.Browse,
- ].map((key) => mockPermission({ key, name: key }));
+ this.#permissionTemplates = cloneDeep(defaultTemplates);
+ this.#permissions = PERMISSIONS_ORDER_FOR_PROJECT_TEMPLATE.map((key) =>
+ mockPermission({ key, name: key })
+ );
this.#groups = cloneDeep(defaultGroups);
this.#users = cloneDeep(defaultUsers);
+ this.updateDefaults();
jest.mocked(getPermissionTemplates).mockImplementation(this.handleGetPermissionTemplates);
jest.mocked(bulkApplyTemplate).mockImplementation(this.handleBulkApplyTemplate);
jest.mocked(revokeTemplatePermissionFromGroup).mockImplementation(this.handlePermissionChange);
jest.mocked(grantTemplatePermissionToUser).mockImplementation(this.handlePermissionChange);
jest.mocked(revokeTemplatePermissionFromUser).mockImplementation(this.handlePermissionChange);
+ jest.mocked(createPermissionTemplate).mockImplementation(this.handleCreatePermissionTemplate);
+ jest.mocked(updatePermissionTemplate).mockImplementation(this.handleUpdatePermissionTemplate);
+ jest.mocked(deletePermissionTemplate).mockImplementation(this.handleDeletePermissionTemplate);
+ jest
+ .mocked(setDefaultPermissionTemplate)
+ .mockImplementation(this.handleSetDefaultPermissionTemplate);
jest.mocked(changeProjectVisibility).mockImplementation(this.handleChangeProjectVisibility);
jest.mocked(getGlobalPermissionsUsers).mockImplementation(this.handleGetPermissionUsers);
jest.mocked(getGlobalPermissionsGroups).mockImplementation(this.handleGetPermissionGroups);
return Promise.resolve();
};
- handleGetPermissionTemplateUsers = (data: { q?: string | null; p?: number; ps?: number }) => {
- const { ps = PAGE_SIZE, p = DEFAULT_PAGE, q } = data;
-
- const users =
- q && q.length >= MIN_QUERY_LENGTH
- ? templateUsers.filter((user) =>
- [user.login, user.name].some((key) => key.toLowerCase().includes(q.toLowerCase()))
- )
- : templateUsers;
-
- const usersChunks = chunk(users, ps);
-
- return this.reply({
- paging: { pageSize: ps, total: users.length, pageIndex: p },
- users: usersChunks[p - 1] ?? [],
- });
+ handleGetPermissionTemplateUsers = (data: {
+ templateId: string;
+ q?: string;
+ permission?: string;
+ p?: number;
+ ps?: number;
+ }) => {
+ return this.handleGetPermissionUsers(data);
};
handleGetPermissionTemplateGroups = (data: {
templateId: string;
- q?: string | null;
+ q?: string;
permission?: string;
p?: number;
ps?: number;
}) => {
- const { ps = PAGE_SIZE, p = DEFAULT_PAGE, q } = data;
-
- const groups =
- q && q.length >= MIN_QUERY_LENGTH
- ? templateGroups.filter((group) => group.name.toLowerCase().includes(q.toLowerCase()))
- : templateGroups;
-
- const groupsChunks = chunk(groups, ps);
-
- return this.reply({
- paging: { pageSize: ps, total: groups.length, pageIndex: p },
- groups: groupsChunks[p - 1] ?? [],
- });
+ return this.handleGetPermissionGroups(data);
};
handleChangeProjectVisibility = (_project: string, _visibility: Visibility) => {
return this.#isAllowedToChangePermissions ? Promise.resolve() : Promise.reject();
};
+ handleCreatePermissionTemplate = (data: {
+ name: string;
+ description?: string;
+ projectKeyPattern?: string;
+ }) => {
+ const newTemplate = mockPermissionTemplate({
+ id: `template-${this.#permissionTemplates.length + 1}`,
+ ...data,
+ });
+ this.#permissionTemplates.push(newTemplate);
+ return this.reply({ permissionTemplate: newTemplate });
+ };
+
+ handleUpdatePermissionTemplate = (data: {
+ id: string;
+ description?: string;
+ name?: string;
+ projectKeyPattern?: string;
+ }) => {
+ const { id } = data;
+ const template = this.#permissionTemplates.find((t) => t.id === id);
+ if (template === undefined) {
+ throw new Error(`Couldn't find template with id ${id}`);
+ }
+ Object.assign(template, data);
+
+ return this.reply(undefined);
+ };
+
+ handleDeletePermissionTemplate = (data: { templateId?: string; templateName?: string }) => {
+ const { templateId } = data;
+ this.#permissionTemplates = this.#permissionTemplates.filter((t) => t.id !== templateId);
+ return this.reply(undefined);
+ };
+
+ handleSetDefaultPermissionTemplate = (templateId: string, qualifier: ComponentQualifier) => {
+ this.#permissionTemplates = this.#permissionTemplates.map((t) => ({
+ ...t,
+ defaultFor: t.defaultFor.filter((q) => q !== qualifier),
+ }));
+
+ const template = this.#permissionTemplates.find((t) => t.id === templateId);
+ if (template === undefined) {
+ throw new Error(`Couldn't find template with id ${templateId}`);
+ }
+ template.defaultFor = uniq([...template.defaultFor, qualifier]);
+
+ this.updateDefaults();
+
+ return this.reply(undefined);
+ };
+
setIsAllowedToChangePermissions = (val: boolean) => {
this.#isAllowedToChangePermissions = val;
};
this.#users = users;
};
+ getTemplates = () => {
+ return this.#permissionTemplates;
+ };
+
+ updateDefaults = () => {
+ this.#defaultTemplates = [
+ ComponentQualifier.Project,
+ ComponentQualifier.Application,
+ ComponentQualifier.Portfolio,
+ ].map((qualifier) => ({
+ templateId:
+ this.#permissionTemplates.find((t) => t.defaultFor.includes(qualifier))?.id ??
+ this.#permissionTemplates[0].id,
+ qualifier,
+ }));
+ };
+
reset = () => {
- this.#permissionTemplates = cloneDeep(defaultPermissionTemplates);
+ this.#permissionTemplates = cloneDeep(defaultTemplates);
this.#groups = cloneDeep(defaultGroups);
this.#users = cloneDeep(defaultUsers);
this.setIsAllowedToChangePermissions(true);
return getJSON(url);
}
-export function createPermissionTemplate(data: RequestData) {
+export function createPermissionTemplate(data: {
+ name: string;
+ description?: string;
+ projectKeyPattern?: string;
+}): Promise<{ permissionTemplate: Omit<PermissionTemplate, 'defaultFor'> }> {
return postJSON('/api/permissions/create_template', data);
}
-export function updatePermissionTemplate(data: RequestData): Promise<void> {
+export function updatePermissionTemplate(data: {
+ id: string;
+ description?: string;
+ name?: string;
+ projectKeyPattern?: string;
+}): Promise<void> {
return post('/api/permissions/update_template', data);
}
-export function deletePermissionTemplate(data: RequestData) {
+export function deletePermissionTemplate(data: { templateId?: string; templateName?: string }) {
return post('/api/permissions/delete_template', data).catch(throwGlobalError);
}
export function getPermissionTemplateUsers(data: {
templateId: string;
- q?: string | null;
+ q?: string;
permission?: string;
p?: number;
ps?: number;
export function getPermissionTemplateGroups(data: {
templateId: string;
- q?: string | null;
+ q?: string;
permission?: string;
p?: number;
ps?: number;
import ActionsDropdown, { ActionsDropdownItem } from '../../../components/controls/ActionsDropdown';
import { Router, withRouter } from '../../../components/hoc/withRouter';
import QualifierIcon from '../../../components/icons/QualifierIcon';
-import { translate } from '../../../helpers/l10n';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
import { queryToSearch } from '../../../helpers/urls';
import { PermissionTemplate } from '../../../types/types';
import { PERMISSION_TEMPLATES_PATH } from '../utils';
updateModal: boolean;
}
-export class ActionsCell extends React.PureComponent<Props, State> {
+class ActionsCell extends React.PureComponent<Props, State> {
mounted = false;
state: State = { deleteForm: false, updateModal: false };
return (
<>
- <ActionsDropdown>
+ <ActionsDropdown
+ label={translateWithParameters('permission_templates.show_actions_for_x', t.name)}
+ >
{this.renderSetDefaultsControl()}
{!this.props.fromDetails && (
<SimpleModal header={header} onClose={onClose} onSubmit={onSubmit}>
{({ onCloseClick, onFormSubmit, submitting }) => (
<form onSubmit={onFormSubmit}>
- <header className="modal-head">
+ <div className="modal-head">
<h2>{header}</h2>
- </header>
+ </div>
<div className="modal-body">
{translateWithParameters(
)}
</div>
- <footer className="modal-foot">
+ <div className="modal-foot">
<DeferredSpinner className="spacer-right" loading={submitting} />
<SubmitButton className="button-red" disabled={submitting}>
{translate('delete')}
<ResetButtonLink disabled={submitting} onClick={onCloseClick}>
{translate('cancel')}
</ResetButtonLink>
- </footer>
+ </div>
</form>
)}
</SimpleModal>
>
{({ onCloseClick, onFormSubmit, submitting }) => (
<form id="permission-template-form" onSubmit={onFormSubmit}>
- <header className="modal-head">
+ <div className="modal-head">
<h2>{this.props.header}</h2>
- </header>
+ </div>
<div className="modal-body">
<MandatoryFieldsExplanation className="modal-field" />
</div>
</div>
- <footer className="modal-foot">
+ <div className="modal-foot">
<DeferredSpinner className="spacer-right" loading={submitting} />
<SubmitButton disabled={submitting} id="permission-template-submit">
{this.props.confirmButtonText}
>
{translate('cancel')}
</ResetButtonLink>
- </footer>
+ </div>
</form>
)}
</SimpleModal>
import { createPermissionTemplate } from '../../../api/permissions';
import { Button } from '../../../components/controls/buttons';
import { Router, withRouter } from '../../../components/hoc/withRouter';
+import DeferredSpinner from '../../../components/ui/DeferredSpinner';
import { translate } from '../../../helpers/l10n';
import { PERMISSION_TEMPLATES_PATH } from '../utils';
import Form from './Form';
<header className="page-header" id="project-permissions-header">
<h1 className="page-title">{translate('permission_templates.page')}</h1>
- {!this.props.ready && <i className="spinner" />}
+ <DeferredSpinner loading={!this.props.ready} />
<div className="page-actions">
<Button onClick={this.handleCreateClick}>{translate('create')}</Button>
<Header ready={props.ready} refresh={props.refresh} />
- <List
- permissionTemplates={props.permissionTemplates}
- permissions={props.permissions}
- refresh={props.refresh}
- topQualifiers={props.topQualifiers}
- />
+ <main>
+ <List
+ permissionTemplates={props.permissionTemplates}
+ permissions={props.permissions}
+ refresh={props.refresh}
+ topQualifiers={props.topQualifiers}
+ />
+ </main>
</div>
);
}
permissionTemplates: PermissionTemplate[];
}
-export class PermissionTemplatesApp extends React.PureComponent<Props, State> {
+class PermissionTemplatesApp extends React.PureComponent<Props, State> {
mounted = false;
state: State = {
ready: false,
componentDidMount() {
this.mounted = true;
- this.requestPermissions();
+ this.handleRefresh();
}
componentWillUnmount() {
this.mounted = false;
}
- requestPermissions = async () => {
+ handleRefresh = async () => {
const { permissions, defaultTemplates, permissionTemplates } = await getPermissionTemplates();
-
if (this.mounted) {
const sortedPerm = sortPermissions(permissions);
const permissionTemplatesMerged = mergeDefaultsToTemplates(
};
renderTemplate(id: string) {
- if (!this.state.ready) {
+ const { permissionTemplates, ready } = this.state;
+ if (!ready) {
return null;
}
- const template = this.state.permissionTemplates.find((t) => t.id === id);
+ const template = permissionTemplates.find((t) => t.id === id);
if (!template) {
return null;
}
return (
<Template
- refresh={this.requestPermissions}
+ refresh={this.handleRefresh}
template={template}
topQualifiers={this.props.appState.qualifiers}
/>
);
}
- renderHome() {
- return (
- <Home
- permissionTemplates={this.state.permissionTemplates}
- permissions={this.state.permissions}
- ready={this.state.ready}
- refresh={this.requestPermissions}
- topQualifiers={this.props.appState.qualifiers}
- />
- );
- }
-
render() {
- const { id } = this.props.location.query;
+ const { appState, location } = this.props;
+ const { id } = location.query;
+ const { permissionTemplates, permissions, ready } = this.state;
return (
- <main>
+ <>
<Suggestions suggestions="permission_templates" />
<Helmet defer={false} title={translate('permission_templates.page')} />
- {id && this.renderTemplate(id)}
- {!id && this.renderHome()}
- </main>
+ {id === undefined ? (
+ <Home
+ permissionTemplates={permissionTemplates}
+ permissions={permissions}
+ ready={ready}
+ refresh={this.handleRefresh}
+ topQualifiers={appState.qualifiers}
+ />
+ ) : (
+ this.renderTemplate(id)
+ )}
+ </>
);
}
}
filter !== 'groups'
? api.getPermissionTemplateUsers({
templateId: template.id,
- q: query || null,
+ q: query || undefined,
permission: selectedPermission,
p: usersPage,
})
filter !== 'users'
? api.getPermissionTemplateGroups({
templateId: template.id,
- q: query || null,
+ q: query || undefined,
permission: selectedPermission,
p: groupsPage,
})
template={template}
topQualifiers={topQualifiers}
/>
-
- <TemplateDetails template={template} />
-
- <AllHoldersList
- filter={filter}
- onGrantPermissionToGroup={this.grantPermissionToGroup}
- onGrantPermissionToUser={this.grantPermissionToUser}
- groups={groups}
- groupsPaging={groupsPaging}
- loading={loading}
- onFilter={this.handleFilter}
- onLoadMore={this.onLoadMore}
- onQuery={this.handleSearch}
- query={query}
- onRevokePermissionFromGroup={this.revokePermissionFromGroup}
- onRevokePermissionFromUser={this.revokePermissionFromUser}
- users={allUsers}
- usersPaging={usersPagingWithCreator}
- permissions={permissions}
- selectedPermission={selectedPermission}
- onSelectPermission={this.handleSelectPermission}
- />
+ <main>
+ <TemplateDetails template={template} />
+
+ <AllHoldersList
+ filter={filter}
+ onGrantPermissionToGroup={this.grantPermissionToGroup}
+ onGrantPermissionToUser={this.grantPermissionToUser}
+ groups={groups}
+ groupsPaging={groupsPaging}
+ loading={loading}
+ onFilter={this.handleFilter}
+ onLoadMore={this.onLoadMore}
+ onQuery={this.handleSearch}
+ query={query}
+ onRevokePermissionFromGroup={this.revokePermissionFromGroup}
+ onRevokePermissionFromUser={this.revokePermissionFromUser}
+ users={allUsers}
+ usersPaging={usersPagingWithCreator}
+ permissions={permissions}
+ selectedPermission={selectedPermission}
+ onSelectPermission={this.handleSelectPermission}
+ />
+ </main>
</div>
);
}
*/
import * as React from 'react';
import Link from '../../../components/common/Link';
+import DeferredSpinner from '../../../components/ui/DeferredSpinner';
import { translate } from '../../../helpers/l10n';
import { PermissionTemplate } from '../../../types/types';
import { PERMISSION_TEMPLATES_PATH } from '../utils';
}
export default function TemplateHeader(props: Props) {
- const { template } = props;
+ const { template, loading } = props;
return (
<header className="page-header" id="project-permissions-header">
<div className="note spacer-bottom">
<h1 className="page-title">{template.name}</h1>
- {props.loading && <i className="spinner" />}
+ <DeferredSpinner loading={loading} />
<div className="pull-right">
<ActionsCell
+++ /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 { mockRouter } from '../../../../helpers/testMocks';
-import { ActionsCell } from '../ActionsCell';
-
-const SAMPLE = {
- createdAt: '2018-01-01',
- id: 'id',
- name: 'name',
- permissions: [],
- defaultFor: [],
-};
-
-function renderActionsCell(props?: Partial<ActionsCell['props']>) {
- return shallow(
- <ActionsCell
- permissionTemplate={SAMPLE}
- refresh={() => true}
- router={mockRouter()}
- topQualifiers={['TRK', 'VW']}
- {...props}
- />
- );
-}
-
-it('should set default', () => {
- const setDefault = renderActionsCell().find('.js-set-default');
- expect(setDefault.length).toBe(2);
- expect(setDefault.at(0).prop('data-qualifier')).toBe('TRK');
- expect(setDefault.at(1).prop('data-qualifier')).toBe('VW');
-});
-
-it('should not set default', () => {
- const permissionTemplate = { ...SAMPLE, defaultFor: ['TRK', 'VW'] };
- const setDefault = renderActionsCell({ permissionTemplate }).find('.js-set-default');
- expect(setDefault.length).toBe(0);
-});
+++ /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 { PermissionTemplate } from '../../../../types/types';
-import Defaults from '../Defaults';
-
-const SAMPLE: PermissionTemplate = {
- createdAt: '2018-01-01',
- defaultFor: [],
- id: 'id',
- name: 'name',
- permissions: [],
-};
-
-it('should render one qualifier', () => {
- const sample = { ...SAMPLE, defaultFor: ['DEV'] };
- const output = shallow(<Defaults template={sample} />);
- expect(output).toMatchSnapshot();
-});
-
-it('should render several qualifiers', () => {
- const sample = { ...SAMPLE, defaultFor: ['TRK', 'VW'] };
- const output = shallow(<Defaults template={sample} />);
- expect(output).toMatchSnapshot();
-});
+++ /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 Form from '../Form';
-
-it('render correctly', () => {
- expect(
- shallow(
- <Form confirmButtonText="confirm" header="title" onClose={jest.fn()} onSubmit={jest.fn()} />
- )
- ).toMatchSnapshot();
-});
+++ /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 ListItem from '../ListItem';
-
-it('render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-function shallowRender() {
- return shallow(
- <ListItem
- key="1"
- refresh={async () => {}}
- template={{
- id: '1',
- createdAt: '2020-01-01',
- name: 'test',
- defaultFor: [],
- permissions: [],
- }}
- topQualifiers={[]}
- />
- );
-}
+++ /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 NameCell from '../NameCell';
-
-it('render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-function shallowRender() {
- return shallow(
- <NameCell
- template={{
- id: '1',
- createdAt: '2020-01-01',
- name: 'test',
- defaultFor: ['user'],
- permissions: [],
- }}
- />
- );
-}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { act, screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
-import React from 'react';
-import { byRole } from 'testing-library-selector';
+import { UserEvent } from '@testing-library/user-event/dist/types/setup/setup';
+import { uniq } from 'lodash';
+import { byLabelText, byRole } from 'testing-library-selector';
import PermissionsServiceMock from '../../../../api/mocks/PermissionsServiceMock';
+import { mockPermissionGroup, mockPermissionUser } from '../../../../helpers/mocks/permissions';
+import { PERMISSIONS_ORDER_FOR_PROJECT_TEMPLATE } from '../../../../helpers/permissions';
import { mockAppState } from '../../../../helpers/testMocks';
-import { renderApp } from '../../../../helpers/testReactTestingUtils';
+import { renderAppWithAdminContext } from '../../../../helpers/testReactTestingUtils';
import { ComponentQualifier } from '../../../../types/component';
import { Permissions } from '../../../../types/permissions';
-import PermissionTemplatesApp from '../PermissionTemplatesApp';
+import { PermissionGroup, PermissionUser } from '../../../../types/types';
+import routes from '../../routes';
const serviceMock = new PermissionsServiceMock();
serviceMock.reset();
});
-const ui = {
- templateLink1: byRole('link', { name: 'Permission Template 1' }),
- permissionCheckbox: (target: string, permission: Permissions) =>
- byRole('checkbox', {
- name: `permission.assign_x_to_y.projects_role.${permission}.${target}`,
- }),
- showMoreButton: byRole('button', { name: 'show_more' }),
-};
+describe('rendering', () => {
+ it('should render the list of templates', async () => {
+ const user = userEvent.setup();
+ const ui = getPageObject(user);
+ renderPermissionTemplatesApp();
+ await ui.appLoaded();
-it('grants/revokes permission from users or groups', async () => {
- const user = userEvent.setup();
- renderPermissionTemplatesApp();
+ // Lists all templates.
+ expect(ui.templateLink('Permission Template 1').get()).toBeInTheDocument();
+ expect(ui.templateLink('Permission Template 2').get()).toBeInTheDocument();
+
+ // Shows all permission table headers.
+ PERMISSIONS_ORDER_FOR_PROJECT_TEMPLATE.forEach((permission, i) => {
+ expect(ui.getTableHeaderHelpTooltip(i + 1)).toHaveTextContent(
+ `projects_role.${permission}.desc`
+ );
+ });
+
+ // Shows warning for browse and code viewer permissions.
+ [Permissions.Browse, Permissions.CodeViewer].forEach((_permission, i) => {
+ expect(ui.getTableHeaderHelpTooltip(i + 1)).toHaveTextContent(
+ 'projects_role.public_projects_warning'
+ );
+ });
+
+ // Check summaries.
+ // Note: because of the intricacies of these table cells, and the verbosity
+ // this would introduce in this test, I went ahead and relied on snapshots.
+ // The snapshots only focus on the text content, so any updates in styling
+ // or DOM structure should not alter the snapshots.
+ const row1 = within(screen.getByRole('row', { name: /Permission Template 1/ }));
+ PERMISSIONS_ORDER_FOR_PROJECT_TEMPLATE.forEach((permission, i) => {
+ expect(row1.getAllByRole('cell').at(i + 1)?.textContent).toMatchSnapshot(
+ `Permission Template 1: ${permission}`
+ );
+ });
+ const row2 = within(screen.getByRole('row', { name: /Permission Template 2/ }));
+ PERMISSIONS_ORDER_FOR_PROJECT_TEMPLATE.forEach((permission, i) => {
+ expect(row2.getAllByRole('cell').at(i + 1)?.textContent).toMatchSnapshot(
+ `Permission Template 2: ${permission}`
+ );
+ });
+ });
+
+ it('should render the correct template', async () => {
+ const user = userEvent.setup();
+ const ui = getPageObject(user);
+ renderPermissionTemplatesApp();
+ await ui.appLoaded();
+
+ await ui.openTemplateDetails('Permission Template 1');
+ await ui.appLoaded();
+
+ expect(screen.getByText('This is permission template 1')).toBeInTheDocument();
+ PERMISSIONS_ORDER_FOR_PROJECT_TEMPLATE.forEach((permission, i) => {
+ expect(ui.permissionCheckbox('johndoe', permission).get()).toBeInTheDocument();
+ expect(ui.getTableHeaderHelpTooltip(i)).toHaveTextContent(`projects_role.${permission}.desc`);
+ });
+ });
+});
+
+describe('CRUD', () => {
+ it('should allow the creation of new templates', async () => {
+ const user = userEvent.setup();
+ const ui = getPageObject(user);
+ renderPermissionTemplatesApp();
+ await ui.appLoaded();
+
+ await act(async () => {
+ await ui.createNewTemplate('New Permission Template', 'New template description');
+ });
+ await ui.appLoaded();
+
+ expect(screen.getByRole('heading', { name: 'New Permission Template' })).toBeInTheDocument();
+ expect(screen.getByText('New template description')).toBeInTheDocument();
+ });
+
+ it('should allow the create modal to be opened and closed', async () => {
+ const user = userEvent.setup();
+ const ui = getPageObject(user);
+ renderPermissionTemplatesApp();
+ await ui.appLoaded();
+
+ await ui.openCreateModal();
+ await ui.closeModal();
+
+ expect(ui.modal.query()).not.toBeInTheDocument();
+ });
+
+ it('should allow template details to be updated from the list', async () => {
+ const user = userEvent.setup();
+ const ui = getPageObject(user);
+ renderPermissionTemplatesApp();
+ await ui.appLoaded();
+
+ await ui.updateTemplate(
+ 'Permission Template 2',
+ 'Updated name',
+ 'Updated description',
+ '/new pattern/'
+ );
- await user.click(await ui.templateLink1.find());
+ expect(ui.templateLink('Updated name').get()).toBeInTheDocument();
+ expect(screen.getByText('Updated description')).toBeInTheDocument();
+ expect(screen.getByText('/new pattern/')).toBeInTheDocument();
+ });
+
+ it('should allow template details to be updated from the template page directly', async () => {
+ const user = userEvent.setup();
+ const ui = getPageObject(user);
+ renderPermissionTemplatesApp();
+ await ui.appLoaded();
+
+ await ui.openTemplateDetails('Permission Template 2');
+ await ui.appLoaded();
+
+ await ui.updateTemplate(
+ 'Permission Template 2',
+ 'Updated name',
+ 'Updated description',
+ '/new pattern/'
+ );
+
+ expect(screen.getByText('Updated name')).toBeInTheDocument();
+ expect(screen.getByText('Updated description')).toBeInTheDocument();
+ expect(screen.getByText('/new pattern/')).toBeInTheDocument();
+ });
- // User
- expect(ui.permissionCheckbox('Admin Admin', Permissions.Browse).get()).not.toBeChecked();
- await user.click(ui.permissionCheckbox('Admin Admin', Permissions.Browse).get());
- expect(ui.permissionCheckbox('Admin Admin', Permissions.Browse).get()).toBeChecked();
+ it('should allow the update modal to be opened and closed', async () => {
+ const user = userEvent.setup();
+ const ui = getPageObject(user);
+ renderPermissionTemplatesApp();
+ await ui.appLoaded();
- expect(ui.permissionCheckbox('Admin Admin', Permissions.Admin).get()).toBeChecked();
- await user.click(ui.permissionCheckbox('Admin Admin', Permissions.Admin).get());
- expect(ui.permissionCheckbox('Admin Admin', Permissions.Admin).get()).not.toBeChecked();
+ await ui.openUpdateModal('Permission Template 2');
+ await ui.closeModal();
- // Group
- expect(ui.permissionCheckbox('Anyone', Permissions.Browse).get()).not.toBeChecked();
- await user.click(ui.permissionCheckbox('Anyone', Permissions.Browse).get());
- expect(ui.permissionCheckbox('Anyone', Permissions.Browse).get()).toBeChecked();
+ expect(ui.modal.query()).not.toBeInTheDocument();
+ });
+
+ it('should allow templates to be deleted from the list', async () => {
+ const user = userEvent.setup();
+ const ui = getPageObject(user);
+ renderPermissionTemplatesApp();
+ await ui.appLoaded();
+
+ await act(async () => {
+ await ui.deleteTemplate('Permission Template 2');
+ });
+ await ui.appLoaded();
+
+ expect(ui.templateLink('Permission Template 1').get()).toBeInTheDocument();
+ expect(ui.templateLink('Permission Template 2').query()).not.toBeInTheDocument();
+ });
- expect(ui.permissionCheckbox('Anyone', Permissions.CodeViewer).get()).toBeChecked();
- await user.click(ui.permissionCheckbox('Anyone', Permissions.CodeViewer).get());
- expect(ui.permissionCheckbox('Anyone', Permissions.CodeViewer).get()).not.toBeChecked();
+ it('should allow templates to be deleted from the template page directly', async () => {
+ const user = userEvent.setup();
+ const ui = getPageObject(user);
+ renderPermissionTemplatesApp();
+ await ui.appLoaded();
- // Handles error on permission change
- serviceMock.setIsAllowedToChangePermissions(false);
- await user.click(ui.permissionCheckbox('Admin Admin', Permissions.Browse).get());
- expect(ui.permissionCheckbox('Admin Admin', Permissions.Browse).get()).toBeChecked();
+ await ui.openTemplateDetails('Permission Template 2');
+ await ui.appLoaded();
- await user.click(ui.permissionCheckbox('Anyone', Permissions.CodeViewer).get());
- expect(ui.permissionCheckbox('Anyone', Permissions.CodeViewer).get()).not.toBeChecked();
+ await act(async () => {
+ await ui.deleteTemplate('Permission Template 2');
+ });
+ await ui.appLoaded();
- await user.click(ui.permissionCheckbox('Admin Admin', Permissions.Admin).get());
- expect(ui.permissionCheckbox('Admin Admin', Permissions.Admin).get()).not.toBeChecked();
+ expect(ui.templateLink('Permission Template 1').get()).toBeInTheDocument();
+ expect(ui.templateLink('Permission Template 2').query()).not.toBeInTheDocument();
+ });
+
+ it('should allow the delete modal to be opened and closed', async () => {
+ const user = userEvent.setup();
+ const ui = getPageObject(user);
+ renderPermissionTemplatesApp();
+ await ui.appLoaded();
+
+ await ui.openDeleteModal('Permission Template 2');
+ await ui.closeModal();
+
+ expect(ui.modal.query()).not.toBeInTheDocument();
+ });
+
+ it('should not allow a default template to be deleted', async () => {
+ const user = userEvent.setup();
+ const ui = getPageObject(user);
+ renderPermissionTemplatesApp();
+ await ui.appLoaded();
+
+ await user.click(ui.cogMenuBtn('Permission Template 1').get());
+
+ expect(ui.deleteBtn.query()).not.toBeInTheDocument();
+ });
});
-it('loads more items on Show More', async () => {
+describe('filtering', () => {
+ it('should allow to filter permission holders', async () => {
+ const user = userEvent.setup();
+ const ui = getPageObject(user);
+ renderPermissionTemplatesApp();
+ await ui.appLoaded();
+
+ await ui.openTemplateDetails('Permission Template 1');
+ await ui.appLoaded();
+
+ expect(screen.getByText('sonar-users')).toBeInTheDocument();
+ expect(screen.getByText('johndoe')).toBeInTheDocument();
+
+ await ui.showOnlyUsers();
+ expect(screen.queryByText('sonar-users')).not.toBeInTheDocument();
+ expect(screen.getByText('johndoe')).toBeInTheDocument();
+
+ await ui.showOnlyGroups();
+ expect(screen.getByText('sonar-users')).toBeInTheDocument();
+ expect(screen.queryByText('johndoe')).not.toBeInTheDocument();
+
+ await ui.showAll();
+ expect(screen.getByText('sonar-users')).toBeInTheDocument();
+ expect(screen.getByText('johndoe')).toBeInTheDocument();
+
+ await ui.searchFor('sonar-adm');
+ expect(screen.getByText('sonar-admins')).toBeInTheDocument();
+ expect(screen.queryByText('sonar-users')).not.toBeInTheDocument();
+ expect(screen.queryByText('johndoe')).not.toBeInTheDocument();
+
+ await ui.clearSearch();
+ expect(screen.getByText('sonar-users')).toBeInTheDocument();
+ expect(screen.getByText('johndoe')).toBeInTheDocument();
+ });
+
+ it('should allow to show only permission holders with a specific permission', async () => {
+ const user = userEvent.setup();
+ const ui = getPageObject(user);
+ renderPermissionTemplatesApp();
+ await ui.appLoaded();
+
+ await ui.openTemplateDetails('Permission Template 1');
+ await ui.appLoaded();
+
+ expect(screen.getAllByRole('row').length).toBe(12);
+ await ui.toggleFilterByPermission(Permissions.Admin);
+ expect(screen.getAllByRole('row').length).toBe(2);
+ await ui.toggleFilterByPermission(Permissions.Admin);
+ expect(screen.getAllByRole('row').length).toBe(12);
+ });
+});
+
+describe('assigning/revoking permissions', () => {
+ it('should add and remove permissions to/from a group', async () => {
+ const user = userEvent.setup();
+ const ui = getPageObject(user);
+ renderPermissionTemplatesApp();
+ await ui.appLoaded();
+
+ await ui.openTemplateDetails('Permission Template 1');
+ await ui.appLoaded();
+
+ expect(ui.permissionCheckbox('sonar-users', Permissions.Admin).get()).not.toBeChecked();
+
+ await ui.togglePermission('sonar-users', Permissions.Admin);
+ await ui.appLoaded();
+ expect(ui.permissionCheckbox('sonar-users', Permissions.Admin).get()).toBeChecked();
+
+ await ui.togglePermission('sonar-users', Permissions.Admin);
+ await ui.appLoaded();
+ expect(ui.permissionCheckbox('sonar-users', Permissions.Admin).get()).not.toBeChecked();
+ });
+
+ it('should add and remove permissions to/from a user', async () => {
+ const user = userEvent.setup();
+ const ui = getPageObject(user);
+ renderPermissionTemplatesApp();
+ await ui.appLoaded();
+
+ await ui.openTemplateDetails('Permission Template 1');
+ await ui.appLoaded();
+
+ expect(ui.permissionCheckbox('johndoe', Permissions.Scan).get()).not.toBeChecked();
+
+ await ui.togglePermission('johndoe', Permissions.Scan);
+ await ui.appLoaded();
+ expect(ui.permissionCheckbox('johndoe', Permissions.Scan).get()).toBeChecked();
+
+ await ui.togglePermission('johndoe', Permissions.Scan);
+ await ui.appLoaded();
+ expect(ui.permissionCheckbox('johndoe', Permissions.Scan).get()).not.toBeChecked();
+ });
+
+ it('should handle errors correctly', async () => {
+ serviceMock.setIsAllowedToChangePermissions(false);
+ const user = userEvent.setup();
+ const ui = getPageObject(user);
+ renderPermissionTemplatesApp();
+ await ui.appLoaded();
+
+ await ui.openTemplateDetails('Permission Template 1');
+ await ui.appLoaded();
+
+ expect(ui.permissionCheckbox('johndoe', Permissions.Scan).get()).not.toBeChecked();
+ await ui.togglePermission('johndoe', Permissions.Scan);
+ await ui.appLoaded();
+ expect(ui.permissionCheckbox('johndoe', Permissions.Scan).get()).not.toBeChecked();
+ });
+});
+
+it('should correctly handle pagination', async () => {
+ const groups: PermissionGroup[] = [];
+ const users: PermissionUser[] = [];
+ Array.from(Array(20).keys()).forEach((i) => {
+ groups.push(mockPermissionGroup({ name: `Group ${i}` }));
+ users.push(mockPermissionUser({ login: `user-${i}` }));
+ });
+ serviceMock.setGroups(groups);
+ serviceMock.setUsers(users);
+
const user = userEvent.setup();
+ const ui = getPageObject(user);
renderPermissionTemplatesApp();
+ await ui.appLoaded();
- await user.click(await ui.templateLink1.find());
+ await ui.openTemplateDetails('Permission Template 1');
+ await ui.appLoaded();
- expect(ui.permissionCheckbox('White', Permissions.Browse).query()).not.toBeInTheDocument();
- await user.click(ui.showMoreButton.get());
- expect(ui.permissionCheckbox('White', Permissions.Browse).get()).toBeInTheDocument();
+ expect(screen.getAllByRole('row').length).toBe(14);
+ await ui.clickLoadMore();
+ expect(screen.getAllByRole('row').length).toBe(24);
});
-function renderPermissionTemplatesApp() {
- renderApp('admin/permission_templates', <PermissionTemplatesApp />, {
- appState: mockAppState({ qualifiers: [ComponentQualifier.Project] }),
+it.each([ComponentQualifier.Project, ComponentQualifier.Application, ComponentQualifier.Portfolio])(
+ 'should correctly be assignable by default to %s',
+ async (qualifier) => {
+ const user = userEvent.setup();
+ const ui = getPageObject(user);
+ renderPermissionTemplatesApp(uniq([ComponentQualifier.Project, qualifier]));
+ await ui.appLoaded();
+
+ await ui.setTemplateAsDefaultFor('Permission Template 2', qualifier);
+
+ const row1 = within(screen.getByRole('row', { name: /Permission Template 1/ }));
+ const row2 = within(screen.getByRole('row', { name: /Permission Template 2/ }));
+ const regex = new RegExp(`permission_template\\.default_for\\.(.*)qualifiers.${qualifier}`);
+ expect(row2.getByText(regex)).toBeInTheDocument();
+ expect(row1.queryByText(regex)).not.toBeInTheDocument();
+ }
+);
+
+function getPageObject(user: UserEvent) {
+ const ui = {
+ loading: byLabelText('loading'),
+ templateLink: (name: string) => byRole('link', { name }),
+ permissionCheckbox: (target: string, permission: Permissions) =>
+ byRole('checkbox', {
+ name: `permission.assign_x_to_y.projects_role.${permission}.${target}`,
+ }),
+ tableHeaderFilter: (permission: Permissions) =>
+ byRole('link', { name: `projects_role.${permission}` }),
+ onlyUsersBtn: byRole('button', { name: 'users.page' }),
+ onlyGroupsBtn: byRole('button', { name: 'user_groups.page' }),
+ showAllBtn: byRole('button', { name: 'all' }),
+ searchInput: byRole('searchbox', { name: 'search.search_for_users_or_groups' }),
+ loadMoreBtn: byRole('button', { name: 'show_more' }),
+ createNewTemplateBtn: byRole('button', { name: 'create' }),
+ modal: byRole('dialog'),
+ cogMenuBtn: (name: string) =>
+ byRole('button', { name: `permission_templates.show_actions_for_x.${name}` }),
+ deleteBtn: byRole('button', { name: 'delete' }),
+ updateDetailsBtn: byRole('button', { name: 'update_details' }),
+ setDefaultBtn: (qualifier: ComponentQualifier) =>
+ byRole('button', {
+ name:
+ qualifier === ComponentQualifier.Project
+ ? 'permission_templates.set_default'
+ : `permission_templates.set_default_for qualifier.${qualifier} qualifiers.${qualifier}`,
+ }),
+ };
+
+ return {
+ ...ui,
+ async appLoaded() {
+ await waitFor(() => {
+ expect(ui.loading.query()).not.toBeInTheDocument();
+ });
+ },
+ async openTemplateDetails(name: string) {
+ await user.click(ui.templateLink(name).get());
+ },
+ async toggleFilterByPermission(permission: Permissions) {
+ await user.click(ui.tableHeaderFilter(permission).get());
+ },
+ async showOnlyUsers() {
+ await user.click(ui.onlyUsersBtn.get());
+ },
+ async showOnlyGroups() {
+ await user.click(ui.onlyGroupsBtn.get());
+ },
+ async showAll() {
+ await user.click(ui.showAllBtn.get());
+ },
+ async searchFor(name: string) {
+ await user.type(ui.searchInput.get(), name);
+ },
+ async clearSearch() {
+ await user.clear(ui.searchInput.get());
+ },
+ async clickLoadMore() {
+ await user.click(ui.loadMoreBtn.get());
+ },
+ async togglePermission(target: string, permission: Permissions) {
+ await user.click(ui.permissionCheckbox(target, permission).get());
+ },
+ async openCreateModal() {
+ await user.click(ui.createNewTemplateBtn.get());
+ },
+ async createNewTemplate(name: string, description: string, pattern?: string) {
+ await user.click(ui.createNewTemplateBtn.get());
+ const modal = within(ui.modal.get());
+ await user.type(modal.getByRole('textbox', { name: /name/ }), name);
+ await user.type(modal.getByRole('textbox', { name: 'description' }), description);
+ if (pattern) {
+ await user.type(
+ modal.getByRole('textbox', { name: 'permission_template.key_pattern' }),
+ pattern
+ );
+ }
+ await user.click(modal.getByRole('button', { name: 'create' }));
+ },
+ async openDeleteModal(name: string) {
+ await user.click(ui.cogMenuBtn(name).get());
+ await user.click(ui.deleteBtn.get());
+ },
+ async deleteTemplate(name: string) {
+ await user.click(ui.cogMenuBtn(name).get());
+ await user.click(ui.deleteBtn.get());
+ const modal = within(ui.modal.get());
+ await user.click(modal.getByRole('button', { name: 'delete' }));
+ },
+ async openUpdateModal(name: string) {
+ await user.click(ui.cogMenuBtn(name).get());
+ await user.click(ui.updateDetailsBtn.get());
+ },
+ async updateTemplate(
+ name: string,
+ newName: string,
+ newDescription: string,
+ newPattern: string
+ ) {
+ await user.click(ui.cogMenuBtn(name).get());
+ await user.click(ui.updateDetailsBtn.get());
+
+ const modal = within(ui.modal.get());
+ const nameInput = modal.getByRole('textbox', { name: /name/ });
+ const descriptionInput = modal.getByRole('textbox', { name: 'description' });
+ const patternInput = modal.getByRole('textbox', { name: 'permission_template.key_pattern' });
+
+ await user.clear(nameInput);
+ await user.type(nameInput, newName);
+ await user.clear(descriptionInput);
+ await user.type(descriptionInput, newDescription);
+ await user.clear(patternInput);
+ await user.type(patternInput, newPattern);
+
+ await user.click(modal.getByRole('button', { name: 'update_verb' }));
+ },
+ async closeModal() {
+ const modal = within(ui.modal.get());
+ await user.click(modal.getByRole('button', { name: 'cancel' }));
+ },
+ async setTemplateAsDefaultFor(name: string, qualifier: ComponentQualifier) {
+ await user.click(ui.cogMenuBtn(name).get());
+ await user.click(ui.setDefaultBtn(qualifier).get());
+ },
+ getTableHeaderHelpTooltip(i: number) {
+ const th = byRole('columnheader').getAll().at(i);
+ if (th === undefined) {
+ throw new Error(`Couldn't locate the <th> at index ${i}`);
+ }
+ within(th).getByTestId('help-tooltip-activator').focus();
+ return screen.getByRole('tooltip');
+ },
+ };
+}
+
+function renderPermissionTemplatesApp(qualifiers = [ComponentQualifier.Project]) {
+ renderAppWithAdminContext('admin/permission_templates', routes, {
+ appState: mockAppState({ qualifiers }),
});
}
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render one qualifier 1`] = `
-<div>
- <span
- className="badge spacer-right"
- >
- permission_template.default_for.qualifiers.DEV
- </span>
-</div>
-`;
-
-exports[`should render several qualifiers 1`] = `
-<div>
- <span
- className="badge spacer-right"
- >
- permission_template.default_for.qualifiers.TRK, qualifiers.VW
- </span>
-</div>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`render correctly 1`] = `
-<SimpleModal
- header="title"
- onClose={[MockFunction]}
- onSubmit={[Function]}
- size="small"
->
- <Component />
-</SimpleModal>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`render correctly 1`] = `
-<tr
- data-id="1"
- data-name="test"
->
- <NameCell
- template={
- {
- "createdAt": "2020-01-01",
- "defaultFor": [],
- "id": "1",
- "name": "test",
- "permissions": [],
- }
- }
- />
- <td
- className="nowrap thin text-right text-top little-padded-left little-padded-right"
- >
- <withRouter(ActionsCell)
- permissionTemplate={
- {
- "createdAt": "2020-01-01",
- "defaultFor": [],
- "id": "1",
- "name": "test",
- "permissions": [],
- }
- }
- refresh={[Function]}
- topQualifiers={[]}
- />
- </td>
-</tr>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`render correctly 1`] = `
-<td
- className="little-padded-left little-padded-right"
->
- <ForwardRef(Link)
- to={
- {
- "pathname": "/admin/permission_templates",
- "search": "?id=1",
- }
- }
- >
- <strong
- className="js-name"
- >
- test
- </strong>
- </ForwardRef(Link)>
- <div
- className="spacer-top js-defaults"
- >
- <Defaults
- template={
- {
- "createdAt": "2020-01-01",
- "defaultFor": [
- "user",
- ],
- "id": "1",
- "name": "test",
- "permissions": [],
- }
- }
- />
- </div>
-</td>
-`;
--- /dev/null
+// 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: codeviewer 1`] = `"0 user(s)0 group(s)"`;
+
+exports[`rendering should render the list of templates: Permission Template 1: issueadmin 1`] = `"2 user(s)0 group(s)"`;
+
+exports[`rendering should render the list of templates: Permission Template 1: scan 1`] = `"0 user(s)0 group(s)"`;
+
+exports[`rendering should render the list of templates: Permission Template 1: securityhotspotadmin 1`] = `"1 user(s)0 group(s)"`;
+
+exports[`rendering should render the list of templates: Permission Template 1: user 1`] = `"3 user(s)2 group(s)"`;
+
+exports[`rendering should render the list of templates: Permission Template 2: admin 1`] = `"0 user(s)0 group(s)"`;
+
+exports[`rendering should render the list of templates: Permission Template 2: codeviewer 1`] = `"permission_templates.project_creatorspermission_templates.project_creators.explanation0 user(s)0 group(s)"`;
+
+exports[`rendering should render the list of templates: Permission Template 2: issueadmin 1`] = `"0 user(s)0 group(s)"`;
+
+exports[`rendering should render the list of templates: Permission Template 2: scan 1`] = `"0 user(s)0 group(s)"`;
+
+exports[`rendering should render the list of templates: Permission Template 2: securityhotspotadmin 1`] = `"0 user(s)0 group(s)"`;
+
+exports[`rendering should render the list of templates: Permission Template 2: user 1`] = `"permission_templates.project_creatorspermission_templates.project_creators.explanation0 user(s)0 group(s)"`;
renderPermissionsProjectApp();
await ui.appLoaded();
- expect(screen.getAllByRole('row').length).toBe(7);
+ expect(screen.getAllByRole('row').length).toBe(11);
await ui.toggleFilterByPermission(Permissions.Admin);
expect(screen.getAllByRole('row').length).toBe(2);
await ui.toggleFilterByPermission(Permissions.Admin);
- expect(screen.getAllByRole('row').length).toBe(7);
+ expect(screen.getAllByRole('row').length).toBe(11);
});
});
...override,
};
}
-
-export function mockTemplateUser(override: Partial<PermissionUser> = {}) {
- return {
- login: 'admin',
- name: 'Admin Admin',
- permissions: ['admin', 'codeviewer'],
- ...override,
- };
-}
-
-export function mockTemplateGroup(override: Partial<PermissionGroup> = {}) {
- return {
- id: 'Anyone',
- name: 'Anyone',
- description: 'everyone',
- permissions: ['admin', 'codeviewer'],
- ...override,
- };
-}
permission_templates.bulk_apply_permission_template.apply_to_all=You're about to apply the selected permission template to {0} item(s).
permission_templates.select_to_delete=You must select at least one item
permission_templates.delete_selected=Delete all selected items
+permission_templates.show_actions_for_x=Show actions for template {0}
#------------------------------------------------------------------------------