sonarQubeLastConnectionDate: '2023-06-27T17:08:59+0200',
sonarLintLastConnectionDate: '2023-05-27T17:08:59+0200',
email: 'alice.merveille@wonderland.com',
- // groups: ['group1', 'group2', 'group3', 'group4'],
- groupsCount: 4,
+ groupsCount: 2,
}),
mockRestUser({
managed: false,
handleAddUserToGroup: typeof addUserToGroup = ({ name }) => {
this.groups = this.groups.map((g) => (g.name === name ? { ...g, selected: true } : g));
+ this.users.find((u) => u.login === 'alice.merveille')!.groupsCount++;
return this.reply({});
};
}
return g;
});
+ this.users.find((u) => u.login === 'alice.merveille')!.groupsCount--;
return isDefault
? Promise.reject({
errors: [{ msg: 'Cannot remove Default group' }],
expect(ui.dialogCreateUser.query()).not.toBeInTheDocument();
});
- it("should be able to see user's group", async () => {
- const user = userEvent.setup();
- renderUsersApp();
-
- await act(async () =>
- expect(await within(await ui.aliceRow.find()).findByText('group1')).toBeInTheDocument()
- );
- expect(within(ui.aliceRow.get()).queryByText('group4')).not.toBeInTheDocument();
- expect(within(ui.aliceRow.get()).getByText('more_x.2')).toBeInTheDocument();
- await user.click(within(ui.aliceRow.get()).getByText('more_x.2'));
- expect(within(ui.aliceRow.get()).queryByText('more_x.2')).not.toBeInTheDocument();
- expect(await within(ui.aliceRow.get()).findByText('group4')).toBeInTheDocument();
- expect(ui.bobUpdateGroupButton.get()).toBeInTheDocument();
- });
-
it('should render all users', async () => {
renderUsersApp();
it('should be able to edit the groups of a user', async () => {
const user = userEvent.setup();
renderUsersApp();
+ expect(await within(await ui.aliceRow.find()).findByText('2')).toBeInTheDocument();
await act(async () => user.click(await ui.aliceUpdateGroupButton.find()));
expect(await ui.dialogGroups.find()).toBeInTheDocument();
expect(ui.getGroups()).toHaveLength(2);
- await user.click(await ui.allFilter.find());
+ await act(async () => user.click(await ui.allFilter.find()));
expect(ui.getGroups()).toHaveLength(3);
- await user.click(ui.unselectedFilter.get());
+ await act(() => user.click(ui.unselectedFilter.get()));
expect(ui.reloadButton.query()).not.toBeInTheDocument();
- await user.click(ui.getGroups()[0]);
+ await act(() => user.click(ui.getGroups()[0]));
expect(await ui.reloadButton.find()).toBeInTheDocument();
- await user.click(ui.selectedFilter.get());
+ await act(() => user.click(ui.selectedFilter.get()));
expect(ui.getGroups()).toHaveLength(3);
- expect(ui.reloadButton.query()).not.toBeInTheDocument();
- await user.click(ui.getGroups()[1]);
+
+ await act(() => user.click(ui.doneButton.get()));
+ expect(ui.dialogGroups.query()).not.toBeInTheDocument();
+ expect(await within(await ui.aliceRow.find()).findByText('3')).toBeInTheDocument();
+
+ await act(async () => user.click(await ui.aliceUpdateGroupButton.find()));
+
+ await user.click(ui.selectedFilter.get());
+
+ await act(() => user.click(ui.getGroups()[1]));
expect(await ui.reloadButton.find()).toBeInTheDocument();
- await user.click(ui.reloadButton.get());
+ await act(() => user.click(ui.reloadButton.get()));
expect(ui.getGroups()).toHaveLength(2);
- await user.type(within(ui.dialogGroups.get()).getByRole('searchbox'), '3');
+ await act(() => user.type(within(ui.dialogGroups.get()).getByRole('searchbox'), '3'));
expect(ui.getGroups()).toHaveLength(1);
await act(() => user.click(ui.doneButton.get()));
expect(ui.dialogGroups.query()).not.toBeInTheDocument();
+ expect(await within(await ui.aliceRow.find()).findByText('2')).toBeInTheDocument();
});
it('should update user', async () => {
+++ /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 * as React from 'react';
-import { RestUser } from '../../../api/users';
-import { ButtonIcon, ButtonLink } from '../../../components/controls/buttons';
-import BulletListIcon from '../../../components/icons/BulletListIcon';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
-import GroupsForm from './GroupsForm';
-
-interface Props {
- groups: string[];
- user: RestUser<'admin'>;
- manageProvider: string | undefined;
-}
-
-const GROUPS_LIMIT = 3;
-
-export default function UserGroups(props: Props) {
- const { groups, user, manageProvider } = props;
-
- const [openForm, setOpenForm] = React.useState(false);
- const [showMore, setShowMore] = React.useState(false);
-
- const limit = groups.length > GROUPS_LIMIT ? GROUPS_LIMIT - 1 : GROUPS_LIMIT;
- return (
- <ul>
- {groups.slice(0, limit).map((group) => (
- <li className="little-spacer-bottom" key={group}>
- {group}
- </li>
- ))}
- {groups.length > GROUPS_LIMIT &&
- showMore &&
- groups.slice(limit).map((group) => (
- <li className="little-spacer-bottom" key={group}>
- {group}
- </li>
- ))}
- <li className="little-spacer-bottom">
- {groups.length > GROUPS_LIMIT && !showMore && (
- <ButtonLink
- className="js-user-more-groups spacer-right"
- onClick={() => setShowMore(!showMore)}
- >
- {translateWithParameters('more_x', groups.length - limit)}
- </ButtonLink>
- )}
- {manageProvider === undefined && (
- <ButtonIcon
- aria-label={translateWithParameters('users.update_users_groups', user.login)}
- className="js-user-groups button-small"
- onClick={() => setOpenForm(true)}
- tooltip={translate('users.update_groups')}
- >
- <BulletListIcon />
- </ButtonIcon>
- )}
- </li>
- {openForm && <GroupsForm onClose={() => setOpenForm(false)} user={user} />}
- </ul>
- );
-}
import BulletListIcon from '../../../components/icons/BulletListIcon';
import DateFromNow from '../../../components/intl/DateFromNow';
import LegacyAvatar from '../../../components/ui/LegacyAvatar';
-import { translateWithParameters } from '../../../helpers/l10n';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
import { IdentityProvider } from '../../../types/types';
+import GroupsForm from './GroupsForm';
import TokensFormModal from './TokensFormModal';
import UserActions from './UserActions';
import UserListItemIdentity from './UserListItemIdentity';
name,
login,
managed,
+ groupsCount,
tokensCount,
avatar,
sonarQubeLastConnectionDate,
} = user;
const [openTokenForm, setOpenTokenForm] = React.useState(false);
+ const [openGroupForm, setOpenGroupForm] = React.useState(false);
return (
<tr>
<DateFromNow date={sonarLintLastConnectionDate ?? ''} hourPrecision />
</td>
<td className="thin nowrap text-middle">
- {user.groupsCount}
- {/* <UserGroups groups={user.groupsCount} manageProvider={manageProvider} user={user} /> */}
+ {groupsCount}
+ {manageProvider === undefined && (
+ <ButtonIcon
+ aria-label={translateWithParameters('users.update_users_groups', user.login)}
+ className="js-user-groups spacer-left button-small"
+ onClick={() => setOpenGroupForm(true)}
+ tooltip={translate('users.update_groups')}
+ >
+ <BulletListIcon />
+ </ButtonIcon>
+ )}
</td>
<td className="thin nowrap text-middle">
{tokensCount}
)}
{openTokenForm && <TokensFormModal onClose={() => setOpenTokenForm(false)} user={user} />}
+ {openGroupForm && <GroupsForm onClose={() => setOpenGroupForm(false)} user={user} />}
</tr>
);
}