diff options
author | guillaume-peoch-sonarsource <guillaume.peoch@sonarsource.com> | 2023-03-09 10:10:18 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-03-22 20:04:07 +0000 |
commit | c96fc17ae41f274d56cd329af1373a94352d74bf (patch) | |
tree | 709b273f35559b424a5d18230d9725e638df8dc8 /server/sonar-web/src/main/js/apps | |
parent | 2879fc6789ada0ebfe449f47572a89ba7a83e9ed (diff) | |
download | sonarqube-c96fc17ae41f274d56cd329af1373a94352d74bf.tar.gz sonarqube-c96fc17ae41f274d56cd329af1373a94352d74bf.zip |
SONAR-18689 Add Filters for all, local and managed users on user list
Diffstat (limited to 'server/sonar-web/src/main/js/apps')
17 files changed, 200 insertions, 319 deletions
diff --git a/server/sonar-web/src/main/js/apps/users/Search.tsx b/server/sonar-web/src/main/js/apps/users/Search.tsx deleted file mode 100644 index f7f0b5e9a69..00000000000 --- a/server/sonar-web/src/main/js/apps/users/Search.tsx +++ /dev/null @@ -1,49 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import * as React from 'react'; -import SearchBox from '../../components/controls/SearchBox'; -import { translate } from '../../helpers/l10n'; -import { Query } from './utils'; - -interface Props { - query: Query; - updateQuery: (newQuery: Partial<Query>) => void; -} - -export default class Search extends React.PureComponent<Props> { - handleSearch = (search: string) => { - this.props.updateQuery({ search }); - }; - - render() { - const { query } = this.props; - - return ( - <div className="panel panel-vertical bordered-bottom spacer-bottom" id="users-search"> - <SearchBox - minLength={2} - onChange={this.handleSearch} - placeholder={translate('search.search_by_login_or_name')} - value={query.search} - /> - </div> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/users/UsersApp.tsx b/server/sonar-web/src/main/js/apps/users/UsersApp.tsx index 768b2c8f7e6..3eed4f2f5e4 100644 --- a/server/sonar-web/src/main/js/apps/users/UsersApp.tsx +++ b/server/sonar-web/src/main/js/apps/users/UsersApp.tsx @@ -22,14 +22,15 @@ import { Helmet } from 'react-helmet-async'; import { getSystemInfo } from '../../api/system'; import { getIdentityProviders, searchUsers } from '../../api/users'; import withCurrentUserContext from '../../app/components/current-user/withCurrentUserContext'; +import ButtonToggle from '../../components/controls/ButtonToggle'; import ListFooter from '../../components/controls/ListFooter'; +import SearchBox from '../../components/controls/SearchBox'; import Suggestions from '../../components/embed-docs-modal/Suggestions'; import { Location, Router, withRouter } from '../../components/hoc/withRouter'; import { translate } from '../../helpers/l10n'; import { IdentityProvider, Paging, SysInfoCluster } from '../../types/types'; import { CurrentUser, User } from '../../types/users'; import Header from './Header'; -import Search from './Search'; import UsersList from './UsersList'; import { parseQuery, Query, serializeQuery } from './utils'; @@ -59,7 +60,10 @@ export class UsersApp extends React.PureComponent<Props, State> { } componentDidUpdate(prevProps: Props) { - if (prevProps.location.query.search !== this.props.location.query.search) { + if ( + prevProps.location.query.search !== this.props.location.query.search || + prevProps.location.query.managed !== this.props.location.query.managed + ) { this.fetchUsers(); } } @@ -91,9 +95,12 @@ export class UsersApp extends React.PureComponent<Props, State> { }); fetchUsers = () => { - const { location } = this.props; + const { search, managed } = parseQuery(this.props.location.query); this.setState({ loading: true }); - searchUsers({ q: parseQuery(location.query).search }).then(({ paging, users }) => { + searchUsers({ + q: search, + managed, + }).then(({ paging, users }) => { if (this.mounted) { this.setState({ loading: false, paging, users }); } @@ -103,10 +110,12 @@ export class UsersApp extends React.PureComponent<Props, State> { fetchMoreUsers = () => { const { paging } = this.state; if (paging) { + const { search, managed } = parseQuery(this.props.location.query); this.setState({ loading: true }); searchUsers({ p: paging.pageIndex + 1, - q: parseQuery(this.props.location.query).search, + q: search, + managed, }).then(({ paging, users }) => { if (this.mounted) { this.setState((state) => ({ loading: false, users: [...state.users, ...users], paging })); @@ -127,20 +136,48 @@ export class UsersApp extends React.PureComponent<Props, State> { }; render() { - const query = parseQuery(this.props.location.query); + const { search, managed } = parseQuery(this.props.location.query); const { loading, paging, users, manageProvider } = this.state; + // What if we have ONLY managed users? Should we not display the filter toggle? return ( <main className="page page-limited" id="users-page"> <Suggestions suggestions="users" /> <Helmet defer={false} title={translate('users.page')} /> <Header loading={loading} onUpdateUsers={this.fetchUsers} manageProvider={manageProvider} /> - <Search query={query} updateQuery={this.updateQuery} /> + <div className="display-flex-justify-start big-spacer-bottom big-spacer-top"> + {manageProvider !== undefined && ( + <div className="big-spacer-right"> + <ButtonToggle + value={managed === undefined ? 'all' : managed} + options={[ + { label: translate('all'), value: 'all' }, + { label: translate('users.managed'), value: true }, + { label: translate('users.local'), value: false }, + ]} + onCheck={(filterOption) => { + if (filterOption === 'all') { + this.updateQuery({ managed: undefined }); + } else { + this.updateQuery({ managed: filterOption as boolean }); + } + }} + /> + </div> + )} + <SearchBox + id="users-search" + onChange={(search: string) => this.updateQuery({ search })} + placeholder={translate('search.search_by_login_or_name')} + value={search} + /> + </div> <UsersList currentUser={this.props.currentUser} identityProviders={this.state.identityProviders} onUpdateUsers={this.fetchUsers} updateTokensCount={this.updateTokensCount} users={users} + manageProvider={manageProvider} /> {paging !== undefined && ( <ListFooter diff --git a/server/sonar-web/src/main/js/apps/users/UsersList.tsx b/server/sonar-web/src/main/js/apps/users/UsersList.tsx index affc9381c9b..7dbb05134aa 100644 --- a/server/sonar-web/src/main/js/apps/users/UsersList.tsx +++ b/server/sonar-web/src/main/js/apps/users/UsersList.tsx @@ -29,6 +29,7 @@ interface Props { onUpdateUsers: () => void; updateTokensCount: (login: string, tokensCount: number) => void; users: User[]; + manageProvider: string | undefined; } export default function UsersList({ @@ -37,6 +38,7 @@ export default function UsersList({ onUpdateUsers, updateTokensCount, users, + manageProvider, }: Props) { return ( <div className="boxed-group boxed-group-inner"> @@ -63,6 +65,7 @@ export default function UsersList({ onUpdateUsers={onUpdateUsers} updateTokensCount={updateTokensCount} user={user} + manageProvider={manageProvider} /> ))} </tbody> diff --git a/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-it.tsx b/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-it.tsx index 3917a11296e..1532ad653c0 100644 --- a/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-it.tsx @@ -18,6 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import userEvent from '@testing-library/user-event'; import * as React from 'react'; import { byRole, byText } from 'testing-library-selector'; import UsersServiceMock from '../../../api/mocks/UsersServiceMock'; @@ -33,24 +34,82 @@ const ui = { createUserButton: byRole('button', { name: 'users.create_user' }), infoManageMode: byText(/users\.page\.managed_description/), description: byText('users.page.description'), + allFilter: byRole('button', { name: 'all' }), + managedFilter: byRole('button', { name: 'users.managed' }), + localFilter: byRole('button', { name: 'users.local' }), + aliceRow: byRole('row', { name: 'AM Alice Merveille alice.merveille never' }), + aliceRowWithLocalBadge: byRole('row', { + name: 'AM Alice Merveille alice.merveille users.local never', + }), + bobRow: byRole('row', { name: 'BM Bob Marley bob.marley never' }), }; -it('should render list of user in non manage mode', async () => { - handler.setIsManaged(false); - renderUsersApp(); +describe('in non managed mode', () => { + beforeEach(() => { + handler.setIsManaged(false); + }); - expect(await ui.description.find()).toBeInTheDocument(); - expect(ui.createUserButton.get()).toBeEnabled(); + it('should allow the creation of user', async () => { + renderUsersApp(); + + expect(await ui.description.find()).toBeInTheDocument(); + expect(ui.createUserButton.get()).toBeEnabled(); + }); + + it('should render all users', async () => { + renderUsersApp(); + + expect(ui.aliceRowWithLocalBadge.query()).not.toBeInTheDocument(); + expect(await ui.aliceRow.find()).toBeInTheDocument(); + expect(await ui.bobRow.find()).toBeInTheDocument(); + }); }); -it('should render list of user in manage mode', async () => { - handler.setIsManaged(true); - renderUsersApp(); +describe('in manage mode', () => { + beforeEach(() => { + handler.setIsManaged(true); + }); + + it('should not be able to create a user"', async () => { + renderUsersApp(); + expect(await ui.createUserButton.get()).toBeDisabled(); + expect(await ui.infoManageMode.find()).toBeInTheDocument(); + }); + + it('should render list of all users', async () => { + renderUsersApp(); + + expect(await ui.allFilter.find()).toBeInTheDocument(); + + expect(ui.aliceRowWithLocalBadge.get()).toBeInTheDocument(); + expect(ui.bobRow.get()).toBeInTheDocument(); + }); + + it('should render list of managed users', async () => { + const user = userEvent.setup(); + renderUsersApp(); + + // The click downs't work without this line + expect(await ui.managedFilter.find()).toBeInTheDocument(); + await user.click(await ui.managedFilter.get()); + + expect(ui.aliceRowWithLocalBadge.query()).not.toBeInTheDocument(); + expect(ui.bobRow.get()).toBeInTheDocument(); + }); + + it('should render list of local users', async () => { + const user = userEvent.setup(); + renderUsersApp(); + + // The click downs't work without this line + expect(await ui.localFilter.find()).toBeInTheDocument(); + await user.click(await ui.localFilter.get()); - expect(await ui.infoManageMode.find()).toBeInTheDocument(); - expect(ui.createUserButton.get()).toBeDisabled(); + expect(ui.aliceRowWithLocalBadge.get()).toBeInTheDocument(); + expect(ui.bobRow.query()).not.toBeInTheDocument(); + }); }); function renderUsersApp() { - renderApp('admin/users', <UsersApp />); + return renderApp('admin/users', <UsersApp />); } diff --git a/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-test.tsx b/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-test.tsx deleted file mode 100644 index e9cab0de5d9..00000000000 --- a/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-test.tsx +++ /dev/null @@ -1,84 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { Location } from '../../../components/hoc/withRouter'; -import { mockRouter } from '../../../helpers/testMocks'; -import { waitAndUpdate } from '../../../helpers/testUtils'; -import { UsersApp } from '../UsersApp'; - -jest.mock('../../../api/users', () => ({ - getIdentityProviders: jest.fn(() => - Promise.resolve({ - identityProviders: [ - { - backgroundColor: 'blue', - iconPath: 'icon/path', - key: 'foo', - name: 'Foo Provider', - }, - ], - }) - ), - searchUsers: jest.fn(() => - Promise.resolve({ - paging: { - pageIndex: 1, - pageSize: 1, - total: 2, - }, - users: [ - { - login: 'luke', - name: 'Luke', - active: true, - scmAccounts: [], - local: false, - }, - ], - }) - ), -})); - -const getIdentityProviders = require('../../../api/users').getIdentityProviders as jest.Mock<any>; -const searchUsers = require('../../../api/users').searchUsers as jest.Mock<any>; - -const currentUser = { isLoggedIn: true, login: 'luke', dismissedNotices: {} }; -const location = { pathname: '', query: {} } as Location; - -beforeEach(() => { - getIdentityProviders.mockClear(); - searchUsers.mockClear(); -}); - -it('should render correctly', async () => { - const wrapper = getWrapper(); - expect(wrapper).toMatchSnapshot(); - expect(getIdentityProviders).toHaveBeenCalled(); - expect(searchUsers).toHaveBeenCalled(); - await waitAndUpdate(wrapper); - expect(wrapper).toMatchSnapshot(); -}); - -function getWrapper(props: Partial<UsersApp['props']> = {}) { - return shallow( - <UsersApp currentUser={currentUser} location={location} router={mockRouter()} {...props} /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/users/__tests__/UsersList-test.tsx b/server/sonar-web/src/main/js/apps/users/__tests__/UsersList-test.tsx index 8d67e0b7894..51b3d06e945 100644 --- a/server/sonar-web/src/main/js/apps/users/__tests__/UsersList-test.tsx +++ b/server/sonar-web/src/main/js/apps/users/__tests__/UsersList-test.tsx @@ -28,6 +28,7 @@ const users = [ active: true, scmAccounts: [], local: false, + managed: false, }, { login: 'obi', @@ -35,6 +36,7 @@ const users = [ active: true, scmAccounts: [], local: false, + managed: false, }, ]; @@ -57,6 +59,7 @@ function getWrapper(props = {}) { onUpdateUsers={jest.fn()} updateTokensCount={jest.fn()} users={users} + manageProvider={undefined} {...props} /> ); diff --git a/server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/UsersApp-test.tsx.snap b/server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/UsersApp-test.tsx.snap deleted file mode 100644 index 41245b66512..00000000000 --- a/server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/UsersApp-test.tsx.snap +++ /dev/null @@ -1,110 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<main - className="page page-limited" - id="users-page" -> - <Suggestions - suggestions="users" - /> - <Helmet - defer={false} - encodeSpecialCharacters={true} - prioritizeSeoTags={false} - title="users.page" - /> - <Header - loading={true} - onUpdateUsers={[Function]} - /> - <Search - query={ - { - "search": "", - } - } - updateQuery={[Function]} - /> - <UsersList - currentUser={ - { - "dismissedNotices": {}, - "isLoggedIn": true, - "login": "luke", - } - } - identityProviders={[]} - onUpdateUsers={[Function]} - updateTokensCount={[Function]} - users={[]} - /> -</main> -`; - -exports[`should render correctly 2`] = ` -<main - className="page page-limited" - id="users-page" -> - <Suggestions - suggestions="users" - /> - <Helmet - defer={false} - encodeSpecialCharacters={true} - prioritizeSeoTags={false} - title="users.page" - /> - <Header - loading={false} - onUpdateUsers={[Function]} - /> - <Search - query={ - { - "search": "", - } - } - updateQuery={[Function]} - /> - <UsersList - currentUser={ - { - "dismissedNotices": {}, - "isLoggedIn": true, - "login": "luke", - } - } - identityProviders={ - [ - { - "backgroundColor": "blue", - "iconPath": "icon/path", - "key": "foo", - "name": "Foo Provider", - }, - ] - } - onUpdateUsers={[Function]} - updateTokensCount={[Function]} - users={ - [ - { - "active": true, - "local": false, - "login": "luke", - "name": "Luke", - "scmAccounts": [], - }, - ] - } - /> - <ListFooter - count={1} - loadMore={[Function]} - ready={true} - total={2} - /> -</main> -`; diff --git a/server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/UsersList-test.tsx.snap b/server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/UsersList-test.tsx.snap index 175936df424..64566ee533b 100644 --- a/server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/UsersList-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/UsersList-test.tsx.snap @@ -52,6 +52,7 @@ exports[`should render correctly 1`] = ` "active": true, "local": false, "login": "luke", + "managed": false, "name": "Luke", "scmAccounts": [], } @@ -67,6 +68,7 @@ exports[`should render correctly 1`] = ` "active": true, "local": false, "login": "obi", + "managed": false, "name": "One", "scmAccounts": [], } diff --git a/server/sonar-web/src/main/js/apps/users/components/UserListItem.tsx b/server/sonar-web/src/main/js/apps/users/components/UserListItem.tsx index 04a03dee287..4ec202012c5 100644 --- a/server/sonar-web/src/main/js/apps/users/components/UserListItem.tsx +++ b/server/sonar-web/src/main/js/apps/users/components/UserListItem.tsx @@ -31,67 +31,66 @@ import UserGroups from './UserGroups'; import UserListItemIdentity from './UserListItemIdentity'; import UserScmAccounts from './UserScmAccounts'; -interface Props { +export interface UserListItemProps { identityProvider?: IdentityProvider; isCurrentUser: boolean; onUpdateUsers: () => void; updateTokensCount: (login: string, tokensCount: number) => void; user: User; + manageProvider: string | undefined; } -interface State { - openTokenForm: boolean; -} - -export default class UserListItem extends React.PureComponent<Props, State> { - state: State = { openTokenForm: false }; - - handleOpenTokensForm = () => this.setState({ openTokenForm: true }); - handleCloseTokensForm = () => this.setState({ openTokenForm: false }); +export default function UserListItem(props: UserListItemProps) { + const [openTokenForm, setOpenTokenForm] = React.useState(false); - render() { - const { identityProvider, onUpdateUsers, user } = this.props; + const { + identityProvider, + onUpdateUsers, + user, + manageProvider, + isCurrentUser, + updateTokensCount, + } = props; - return ( - <tr> - <td className="thin nowrap text-middle"> - <Avatar hash={user.avatar} name={user.name} size={36} /> - </td> - <UserListItemIdentity identityProvider={identityProvider} user={user} /> - <td className="thin nowrap text-middle"> - <UserScmAccounts scmAccounts={user.scmAccounts || []} /> - </td> - <td className="thin nowrap text-middle"> - <DateFromNow date={user.lastConnectionDate} hourPrecision={true} /> - </td> - <td className="thin nowrap text-middle"> - <UserGroups groups={user.groups || []} onUpdateUsers={onUpdateUsers} user={user} /> - </td> - <td className="thin nowrap text-middle"> - {user.tokensCount} - <ButtonIcon - className="js-user-tokens spacer-left button-small" - onClick={this.handleOpenTokensForm} - tooltip={translate('users.update_tokens')} - > - <BulletListIcon /> - </ButtonIcon> - </td> - <td className="thin nowrap text-right text-middle"> - <UserActions - isCurrentUser={this.props.isCurrentUser} - onUpdateUsers={onUpdateUsers} - user={user} - /> - </td> - {this.state.openTokenForm && ( - <TokensFormModal - onClose={this.handleCloseTokensForm} - updateTokensCount={this.props.updateTokensCount} - user={user} - /> - )} - </tr> - ); - } + return ( + <tr> + <td className="thin nowrap text-middle"> + <Avatar hash={user.avatar} name={user.name} size={36} /> + </td> + <UserListItemIdentity + identityProvider={identityProvider} + user={user} + manageProvider={manageProvider} + /> + <td className="thin nowrap text-middle"> + <UserScmAccounts scmAccounts={user.scmAccounts || []} /> + </td> + <td className="thin nowrap text-middle"> + <DateFromNow date={user.lastConnectionDate} hourPrecision={true} /> + </td> + <td className="thin nowrap text-middle"> + <UserGroups groups={user.groups || []} onUpdateUsers={onUpdateUsers} user={user} /> + </td> + <td className="thin nowrap text-middle"> + {user.tokensCount} + <ButtonIcon + className="js-user-tokens spacer-left button-small" + onClick={() => setOpenTokenForm(true)} + tooltip={translate('users.update_tokens')} + > + <BulletListIcon /> + </ButtonIcon> + </td> + <td className="thin nowrap text-right text-middle"> + <UserActions isCurrentUser={isCurrentUser} onUpdateUsers={onUpdateUsers} user={user} /> + </td> + {openTokenForm && ( + <TokensFormModal + onClose={() => setOpenTokenForm(false)} + updateTokensCount={updateTokensCount} + user={user} + /> + )} + </tr> + ); } diff --git a/server/sonar-web/src/main/js/apps/users/components/UserListItemIdentity.tsx b/server/sonar-web/src/main/js/apps/users/components/UserListItemIdentity.tsx index 6d4d6872ce8..f73eb688689 100644 --- a/server/sonar-web/src/main/js/apps/users/components/UserListItemIdentity.tsx +++ b/server/sonar-web/src/main/js/apps/users/components/UserListItemIdentity.tsx @@ -21,6 +21,7 @@ import { getTextColor } from 'design-system'; import * as React from 'react'; import { colors } from '../../../app/theme'; +import { translate } from '../../../helpers/l10n'; import { getBaseUrl } from '../../../helpers/system'; import { IdentityProvider } from '../../../types/types'; import { User } from '../../../types/users'; @@ -28,9 +29,10 @@ import { User } from '../../../types/users'; export interface Props { identityProvider?: IdentityProvider; user: User; + manageProvider?: string; } -export default function UserListItemIdentity({ identityProvider, user }: Props) { +export default function UserListItemIdentity({ identityProvider, user, manageProvider }: Props) { return ( <td className="text-middle"> <div> @@ -41,11 +43,14 @@ export default function UserListItemIdentity({ identityProvider, user }: Props) {!user.local && user.externalProvider !== 'sonarqube' && ( <ExternalProvider identityProvider={identityProvider} user={user} /> )} + {user.managed === false && manageProvider !== undefined && ( + <span className="badge">{translate('users.local')}</span> + )} </td> ); } -export function ExternalProvider({ identityProvider, user }: Props) { +export function ExternalProvider({ identityProvider, user }: Omit<Props, 'manageProvider'>) { if (!identityProvider) { return ( <div className="js-user-identity-provider little-spacer-top"> diff --git a/server/sonar-web/src/main/js/apps/users/components/__tests__/UserActions-test.tsx b/server/sonar-web/src/main/js/apps/users/components/__tests__/UserActions-test.tsx index ab9a64daa91..e4925c08c9a 100644 --- a/server/sonar-web/src/main/js/apps/users/components/__tests__/UserActions-test.tsx +++ b/server/sonar-web/src/main/js/apps/users/components/__tests__/UserActions-test.tsx @@ -28,6 +28,7 @@ const user = { active: true, scmAccounts: [], local: false, + managed: false, }; it('should render correctly', () => { diff --git a/server/sonar-web/src/main/js/apps/users/components/__tests__/UserGroups-test.tsx b/server/sonar-web/src/main/js/apps/users/components/__tests__/UserGroups-test.tsx index 5683e8ef5c2..47ea651dbe4 100644 --- a/server/sonar-web/src/main/js/apps/users/components/__tests__/UserGroups-test.tsx +++ b/server/sonar-web/src/main/js/apps/users/components/__tests__/UserGroups-test.tsx @@ -28,6 +28,7 @@ const user = { active: true, scmAccounts: [], local: false, + managed: false, }; const groups = ['foo', 'bar', 'baz', 'plop']; diff --git a/server/sonar-web/src/main/js/apps/users/components/__tests__/UserListItem-test.tsx b/server/sonar-web/src/main/js/apps/users/components/__tests__/UserListItem-test.tsx index a3c0c1eb34d..cfd69e9e579 100644 --- a/server/sonar-web/src/main/js/apps/users/components/__tests__/UserListItem-test.tsx +++ b/server/sonar-web/src/main/js/apps/users/components/__tests__/UserListItem-test.tsx @@ -21,7 +21,7 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { click } from '../../../../helpers/testUtils'; import { User } from '../../../../types/users'; -import UserListItem from '../UserListItem'; +import UserListItem, { UserListItemProps } from '../UserListItem'; jest.mock('../../../../components/intl/DateFromNow'); jest.mock('../../../../components/intl/DateTimeFormatter'); @@ -33,6 +33,7 @@ const user: User = { login: 'obi', name: 'One', scmAccounts: [], + managed: false, }; it('should render correctly', () => { @@ -49,13 +50,14 @@ it('should open the correct forms', () => { expect(wrapper.find('TokensFormModal').exists()).toBe(true); }); -function shallowRender(props: Partial<UserListItem['props']> = {}) { +function shallowRender(props: Partial<UserListItemProps> = {}) { return shallow( <UserListItem isCurrentUser={false} onUpdateUsers={jest.fn()} updateTokensCount={jest.fn()} user={user} + manageProvider={undefined} {...props} /> ); diff --git a/server/sonar-web/src/main/js/apps/users/components/__tests__/UserListItemIdentity-test.tsx b/server/sonar-web/src/main/js/apps/users/components/__tests__/UserListItemIdentity-test.tsx index 661feb1fc1e..a362fd780d4 100644 --- a/server/sonar-web/src/main/js/apps/users/components/__tests__/UserListItemIdentity-test.tsx +++ b/server/sonar-web/src/main/js/apps/users/components/__tests__/UserListItemIdentity-test.tsx @@ -44,6 +44,7 @@ describe('#UserListItemIdentity', () => { login: 'obi', name: 'One', scmAccounts: [], + managed: false, }} {...props} /> @@ -78,6 +79,7 @@ describe('#ExternalProvider', () => { login: 'obi', name: 'One', scmAccounts: [], + managed: false, }} {...props} /> diff --git a/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UserListItem-test.tsx.snap b/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UserListItem-test.tsx.snap index a4e8b65bfc3..5970d8c3871 100644 --- a/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UserListItem-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UserListItem-test.tsx.snap @@ -17,6 +17,7 @@ exports[`should render correctly 1`] = ` "lastConnectionDate": "2019-01-18T15:06:33+0100", "local": false, "login": "obi", + "managed": false, "name": "One", "scmAccounts": [], } @@ -49,6 +50,7 @@ exports[`should render correctly 1`] = ` "lastConnectionDate": "2019-01-18T15:06:33+0100", "local": false, "login": "obi", + "managed": false, "name": "One", "scmAccounts": [], } @@ -78,6 +80,7 @@ exports[`should render correctly 1`] = ` "lastConnectionDate": "2019-01-18T15:06:33+0100", "local": false, "login": "obi", + "managed": false, "name": "One", "scmAccounts": [], } @@ -104,6 +107,7 @@ exports[`should render correctly without last connection date 1`] = ` "lastConnectionDate": "2019-01-18T15:06:33+0100", "local": false, "login": "obi", + "managed": false, "name": "One", "scmAccounts": [], } @@ -136,6 +140,7 @@ exports[`should render correctly without last connection date 1`] = ` "lastConnectionDate": "2019-01-18T15:06:33+0100", "local": false, "login": "obi", + "managed": false, "name": "One", "scmAccounts": [], } @@ -165,6 +170,7 @@ exports[`should render correctly without last connection date 1`] = ` "lastConnectionDate": "2019-01-18T15:06:33+0100", "local": false, "login": "obi", + "managed": false, "name": "One", "scmAccounts": [], } diff --git a/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UserListItemIdentity-test.tsx.snap b/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UserListItemIdentity-test.tsx.snap index eccdb8b8147..9fd139b23b9 100644 --- a/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UserListItemIdentity-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UserListItemIdentity-test.tsx.snap @@ -73,6 +73,7 @@ exports[`#UserListItemIdentity should render correctly 1`] = ` "lastConnectionDate": "2019-01-18T15:06:33+0100", "local": false, "login": "obi", + "managed": false, "name": "One", "scmAccounts": [], } diff --git a/server/sonar-web/src/main/js/apps/users/utils.ts b/server/sonar-web/src/main/js/apps/users/utils.ts index 9af11a617b8..c3ceb0cc5b4 100644 --- a/server/sonar-web/src/main/js/apps/users/utils.ts +++ b/server/sonar-web/src/main/js/apps/users/utils.ts @@ -23,11 +23,13 @@ import { RawQuery } from '../../types/types'; export interface Query { search: string; + managed?: boolean; } export const parseQuery = memoize( (urlQuery: RawQuery): Query => ({ search: parseAsString(urlQuery['search']), + managed: urlQuery['managed'] !== undefined ? urlQuery['managed'] === 'true' : undefined, }) ); @@ -35,5 +37,6 @@ export const serializeQuery = memoize( (query: Query): RawQuery => cleanQuery({ search: query.search ? serializeString(query.search) : undefined, + managed: query.managed, }) ); |