From 94971abd9c789fb1acbc961e972b7d5483a31f3a Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Mon, 1 Oct 2018 12:01:53 +0200 Subject: [PATCH] functions and tests --- .../app/features/users/UsersListPage.test.tsx | 51 ++ public/app/features/users/UsersListPage.tsx | 32 +- public/app/features/users/UsersTable.test.tsx | 33 ++ public/app/features/users/UsersTable.tsx | 80 ++-- .../app/features/users/__mocks__/userMocks.ts | 31 ++ .../__snapshots__/UsersListPage.test.tsx.snap | 29 ++ .../__snapshots__/UsersTable.test.tsx.snap | 448 ++++++++++++++++++ public/app/features/users/state/actions.ts | 14 + 8 files changed, 677 insertions(+), 41 deletions(-) create mode 100644 public/app/features/users/UsersListPage.test.tsx create mode 100644 public/app/features/users/UsersTable.test.tsx create mode 100644 public/app/features/users/__mocks__/userMocks.ts create mode 100644 public/app/features/users/__snapshots__/UsersListPage.test.tsx.snap create mode 100644 public/app/features/users/__snapshots__/UsersTable.test.tsx.snap diff --git a/public/app/features/users/UsersListPage.test.tsx b/public/app/features/users/UsersListPage.test.tsx new file mode 100644 index 00000000000..8ba8ec4b06c --- /dev/null +++ b/public/app/features/users/UsersListPage.test.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import { UsersListPage, Props } from './UsersListPage'; +import { NavModel, User } from 'app/types'; +import { getMockUser } from './__mocks__/userMocks'; +import appEvents from '../../core/app_events'; + +jest.mock('../../core/app_events', () => ({ + emit: jest.fn(), +})); + +const setup = (propOverrides?: object) => { + const props: Props = { + navModel: {} as NavModel, + users: [] as User[], + searchQuery: '', + loadUsers: jest.fn(), + updateUser: jest.fn(), + removeUser: jest.fn(), + setUsersSearchQuery: jest.fn(), + }; + + Object.assign(props, propOverrides); + + const wrapper = shallow(); + const instance = wrapper.instance() as UsersListPage; + + return { + wrapper, + instance, + }; +}; + +describe('Render', () => { + it('should render component', () => { + const { wrapper } = setup(); + + expect(wrapper).toMatchSnapshot(); + }); +}); + +describe('Functions', () => { + it('should emit show remove user modal', () => { + const { instance } = setup(); + const mockUser = getMockUser(); + + instance.onRemoveUser(mockUser); + + expect(appEvents.emit).toHaveBeenCalled(); + }); +}); diff --git a/public/app/features/users/UsersListPage.tsx b/public/app/features/users/UsersListPage.tsx index 4b935845259..88c290e80ff 100644 --- a/public/app/features/users/UsersListPage.tsx +++ b/public/app/features/users/UsersListPage.tsx @@ -5,7 +5,8 @@ import OrgActionBar from 'app/core/components/OrgActionBar/OrgActionBar'; import PageHeader from 'app/core/components/PageHeader/PageHeader'; import UsersTable from 'app/features/users/UsersTable'; import { NavModel, User } from 'app/types'; -import { loadUsers, setUsersSearchQuery } from './state/actions'; +import appEvents from 'app/core/app_events'; +import { loadUsers, setUsersSearchQuery, updateUser, removeUser } from './state/actions'; import { getNavModel } from '../../core/selectors/navModel'; import { getUsers, getUsersSearchQuery } from './state/selectors'; @@ -15,6 +16,8 @@ export interface Props { searchQuery: string; loadUsers: typeof loadUsers; setUsersSearchQuery: typeof setUsersSearchQuery; + updateUser: typeof updateUser; + removeUser: typeof removeUser; } export class UsersListPage extends PureComponent { @@ -25,6 +28,25 @@ export class UsersListPage extends PureComponent { async fetchUsers() { return await this.props.loadUsers(); } + + onRoleChange = (role, user) => { + const updatedUser = { ...user, role: role }; + + this.props.updateUser(updatedUser); + }; + + onRemoveUser = user => { + appEvents.emit('confirm-modal', { + title: 'Delete', + text: 'Are you sure you want to delete user ' + user.login + '?', + yesText: 'Delete', + icon: 'fa-warning', + onConfirm: () => { + this.props.removeUser(user.userId); + }, + }); + }; + render() { const { navModel, searchQuery, setUsersSearchQuery, users } = this.props; @@ -43,7 +65,11 @@ export class UsersListPage extends PureComponent { setSearchQuery={setUsersSearchQuery} linkButton={linkButton} /> - + this.onRoleChange(role, user)} + onRemoveUser={user => this.onRemoveUser(user)} + /> ); @@ -61,6 +87,8 @@ function mapStateToProps(state) { const mapDispatchToProps = { loadUsers, setUsersSearchQuery, + updateUser, + removeUser, }; export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(UsersListPage)); diff --git a/public/app/features/users/UsersTable.test.tsx b/public/app/features/users/UsersTable.test.tsx new file mode 100644 index 00000000000..8cbfb0b4e6f --- /dev/null +++ b/public/app/features/users/UsersTable.test.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import UsersTable, { Props } from './UsersTable'; +import { User } from 'app/types'; +import { getMockUsers } from './__mocks__/userMocks'; + +const setup = (propOverrides?: object) => { + const props: Props = { + users: [] as User[], + onRoleChange: jest.fn(), + onRemoveUser: jest.fn(), + }; + + Object.assign(props, propOverrides); + + return shallow(); +}; + +describe('Render', () => { + it('should render component', () => { + const wrapper = setup(); + + expect(wrapper).toMatchSnapshot(); + }); + + it('should render users table', () => { + const wrapper = setup({ + users: getMockUsers(5), + }); + + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/public/app/features/users/UsersTable.tsx b/public/app/features/users/UsersTable.tsx index 38ff720472f..e9b5edd7acb 100644 --- a/public/app/features/users/UsersTable.tsx +++ b/public/app/features/users/UsersTable.tsx @@ -3,15 +3,15 @@ import { User } from 'app/types'; export interface Props { users: User[]; - onRoleChange: (value: string) => {}; + onRoleChange: (role: string, user: User) => void; + onRemoveUser: (user: User) => void; } const UsersTable: SFC = props => { - const { users } = props; + const { users, onRoleChange, onRemoveUser } = props; return (
- Le Table @@ -23,42 +23,44 @@ const UsersTable: SFC = props => { - {users.map((user, index) => { - return ( - - - - - - - - - ); - })} + + {users.map((user, index) => { + return ( + + + + + + + + + ); + })} +
- - {user.login} - {user.email} - {user.lastSeenAtAge} -
- -
-
-
props.removeUser(user)} className="btn btn-danger btn-mini"> - -
-
+ + {user.login} + {user.email} + {user.lastSeenAtAge} +
+ +
+
+
onRemoveUser(user)} className="btn btn-danger btn-mini"> + +
+
); diff --git a/public/app/features/users/__mocks__/userMocks.ts b/public/app/features/users/__mocks__/userMocks.ts new file mode 100644 index 00000000000..ef7789458d0 --- /dev/null +++ b/public/app/features/users/__mocks__/userMocks.ts @@ -0,0 +1,31 @@ +export const getMockUsers = (amount: number) => { + const users = []; + + for (let i = 0; i <= amount; i++) { + users.push({ + avatarUrl: 'url/to/avatar', + email: `user-${i}@test.com`, + lastSeenAt: '2018-10-01', + lastSeenAtAge: '', + login: `user-${i}`, + orgId: 1, + role: 'Admin', + userId: i, + }); + } + + return users; +}; + +export const getMockUser = () => { + return { + avatarUrl: 'url/to/avatar', + email: `user@test.com`, + lastSeenAt: '2018-10-01', + lastSeenAtAge: '', + login: `user`, + orgId: 1, + role: 'Admin', + userId: 2, + }; +}; diff --git a/public/app/features/users/__snapshots__/UsersListPage.test.tsx.snap b/public/app/features/users/__snapshots__/UsersListPage.test.tsx.snap new file mode 100644 index 00000000000..689e7bd007b --- /dev/null +++ b/public/app/features/users/__snapshots__/UsersListPage.test.tsx.snap @@ -0,0 +1,29 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Render should render component 1`] = ` +
+ +
+ + +
+
+`; diff --git a/public/app/features/users/__snapshots__/UsersTable.test.tsx.snap b/public/app/features/users/__snapshots__/UsersTable.test.tsx.snap new file mode 100644 index 00000000000..9dace6a730f --- /dev/null +++ b/public/app/features/users/__snapshots__/UsersTable.test.tsx.snap @@ -0,0 +1,448 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Render should render component 1`] = ` +
+ + + + + + + + + + +
+ + Login + + Email + + Seen + + Role + +
+
+`; + +exports[`Render should render users table 1`] = ` +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Login + + Email + + Seen + + Role + +
+ + + user-0 + + + user-0@test.com + + + +
+ +
+
+
+ +
+
+ + + user-1 + + + user-1@test.com + + + +
+ +
+
+
+ +
+
+ + + user-2 + + + user-2@test.com + + + +
+ +
+
+
+ +
+
+ + + user-3 + + + user-3@test.com + + + +
+ +
+
+
+ +
+
+ + + user-4 + + + user-4@test.com + + + +
+ +
+
+
+ +
+
+ + + user-5 + + + user-5@test.com + + + +
+ +
+
+
+ +
+
+
+`; diff --git a/public/app/features/users/state/actions.ts b/public/app/features/users/state/actions.ts index 0bda6b0c58a..2c35fb06e7a 100644 --- a/public/app/features/users/state/actions.ts +++ b/public/app/features/users/state/actions.ts @@ -38,3 +38,17 @@ export function loadUsers(): ThunkResult { dispatch(usersLoaded(users)); }; } + +export function updateUser(user: User): ThunkResult { + return async dispatch => { + await getBackendSrv().patch(`/api/org/users/${user.userId}`, user); + dispatch(loadUsers()); + }; +} + +export function removeUser(userId: number): ThunkResult { + return async dispatch => { + await getBackendSrv().delete(`/api/org/users/${userId}`); + dispatch(loadUsers()); + }; +}