Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User edit create toast #2613

Merged
merged 5 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions assets/js/pages/Users/CreateUserPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { createUser } from '@lib/api/users';
import UserForm from './UserForm';

const ERROR_GETTING_ABILITIES = 'Error getting user abilities';
const SUCCESS_USER_CREATION = 'User created successfully';
const UNEXPECTED_ERROR_MESSAGE = 'Unexpected error occurred, refresh the page';
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any ideas for a better toast message?


export const fetchAbilities = (setAbilities) => {
listAbilities()
Expand All @@ -33,17 +35,17 @@ function CreateUserPage() {
setSaving(true);
createUser(payload)
.then(() => {
toast.success(SUCCESS_USER_CREATION);
navigate('/users');
})
.catch(
({
response: {
data: { errors },
},
}) => {
.catch((error) => {
if (error.response) {
const { errors } = error.response.data;
setErrors(errors);
return;
}
)
toast.error(UNEXPECTED_ERROR_MESSAGE);
})
.finally(() => {
setSaving(false);
});
Expand Down
33 changes: 32 additions & 1 deletion assets/js/pages/Users/CreateUserPage.test.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';

import { render, screen } from '@testing-library/react';
import { toast } from 'react-hot-toast';
import 'intersection-observer';
import '@testing-library/jest-dom';

Expand All @@ -21,6 +22,13 @@ const ABILITIES_URL = `/api/v1/abilities`;
const USERS_URL = '/api/v1/users';
const axiosMock = new MockAdapter(networkClient);

jest.mock('react-hot-toast', () => ({
toast: {
success: jest.fn(),
error: jest.fn(),
},
}));

describe('CreateUserPage', () => {
beforeEach(() => {
axiosMock.reset();
Expand Down Expand Up @@ -51,7 +59,8 @@ describe('CreateUserPage', () => {
expect(navigate).toHaveBeenCalledWith('/users');
});

it('should save a new user and redirect to users view', async () => {
it('should save a new user, redirect to users view and render success toast', async () => {
const toastMessage = 'User created successfully';
const user = userEvent.setup();
const navigate = jest.fn();
jest.spyOn(router, 'useNavigate').mockImplementation(() => navigate);
Expand Down Expand Up @@ -82,6 +91,7 @@ describe('CreateUserPage', () => {
await user.click(screen.getByRole('button', { name: 'Create' }));

expect(navigate).toHaveBeenCalledWith('/users');
expect(toast.success).toHaveBeenCalledWith(toastMessage);
});

it('should display validation errors', async () => {
Expand Down Expand Up @@ -113,4 +123,25 @@ describe('CreateUserPage', () => {

await screen.findByText('Error validating fullname');
});

it('should render toast with an error message when creating a user failed because of a network error', async () => {
const toastMessage = 'Unexpected error occurred, refresh the page';
const user = userEvent.setup();

const { fullname, email, username } = userFactory.build();
const password = faker.internet.password();

axiosMock.onPost(USERS_URL).networkError();

render(<CreateUserPage />);

await user.type(screen.getByPlaceholderText('Enter full name'), fullname);
await user.type(screen.getByPlaceholderText('Enter email address'), email);
await user.type(screen.getByPlaceholderText('Enter username'), username);
await user.type(screen.getByPlaceholderText('Enter password'), password);
await user.type(screen.getByPlaceholderText('Re-enter password'), password);

await user.click(screen.getByRole('button', { name: 'Create' }));
expect(toast.error).toHaveBeenCalledWith(toastMessage);
});
});
27 changes: 17 additions & 10 deletions assets/js/pages/Users/EditUserPage.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { toast } from 'react-hot-toast';

import BackButton from '@common/BackButton';
import Banner from '@common/Banners/Banner';
Expand All @@ -11,6 +12,9 @@ import { editUser, getUser } from '@lib/api/users';
import { fetchAbilities } from './CreateUserPage';
import UserForm from './UserForm';

const SUCCESS_EDIT_MESSAGE = 'User edited successfully';
const UNEXPECTED_ERROR_MESSAGE = 'Unexpected error occurred, refresh the page';

function EditUserPage() {
const { userID } = useParams();
const navigate = useNavigate();
Expand All @@ -28,7 +32,9 @@ function EditUserPage() {
setUserVersion(etag);
setUser(user);
})
.catch(() => {})
.catch(() => {
toast.error(UNEXPECTED_ERROR_MESSAGE);
})
.finally(() => {
setLoading(false);
});
Expand All @@ -42,22 +48,23 @@ function EditUserPage() {
setSaving(true);
editUser(userID, payload, userVersion)
.then(() => {
toast.success(SUCCESS_EDIT_MESSAGE);
navigate('/users');
})
.catch(
({
response: {
status,
data: { errors },
},
}) => {
.catch((error) => {
if (error.response) {
const { data, status } = error.response;
if (status === 412) {
setUpdatedByOther(true);
return;
}
setErrors(errors);
setErrors(data.errors);

return;
}
)
toast.error(UNEXPECTED_ERROR_MESSAGE);
})

.finally(() => {
setSaving(false);
});
Expand Down
31 changes: 31 additions & 0 deletions assets/js/pages/Users/EditUserPage.test.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';

import { screen } from '@testing-library/react';
import { toast } from 'react-hot-toast';
import 'intersection-observer';
import '@testing-library/jest-dom';

Expand All @@ -23,8 +24,16 @@ import EditUserPage from './EditUserPage';

const ABILITIES_URL = `/api/v1/abilities`;
const USERS_URL = '/api/v1/users/';
const TOAST_ERROR_MESSAGE = 'Unexpected error occurred, refresh the page';
const axiosMock = new MockAdapter(networkClient);

jest.mock('react-hot-toast', () => ({
toast: {
success: jest.fn(),
error: jest.fn(),
},
}));

describe('EditUserPage', () => {
beforeEach(() => {
axiosMock.reset();
Expand Down Expand Up @@ -79,9 +88,11 @@ describe('EditUserPage', () => {
});

await screen.findByText('Not found');
expect(toast.error).toHaveBeenCalledWith(TOAST_ERROR_MESSAGE);
});

it('should edit a user and redirect to users view', async () => {
const toastMessage = 'User edited successfully';
const user = userEvent.setup();
const navigate = jest.fn();
jest.spyOn(router, 'useNavigate').mockImplementation(() => navigate);
Expand Down Expand Up @@ -112,6 +123,7 @@ describe('EditUserPage', () => {
await user.click(screen.getByRole('button', { name: 'Edit' }));

expect(navigate).toHaveBeenCalledWith('/users');
expect(toast.success).toHaveBeenCalledWith(toastMessage);
});

it('should display validation errors', async () => {
Expand Down Expand Up @@ -190,4 +202,23 @@ describe('EditUserPage', () => {
await user.hover(editButton);
expect(await screen.findByText(toolTipText)).toBeVisible();
});

it('should render toast with an error message when editing a user failed because of a network error', async () => {
const user = userEvent.setup();
const userData = userFactory.build();

axiosMock
.onGet(USERS_URL.concat(userData.id))
.reply(200, userData)
.onPatch(USERS_URL.concat(userData.id))
.networkError();

renderWithRouterMatch(<EditUserPage />, {
path: '/users/:userID/edit',
route: `/users/${userData.id}/edit`,
});
await screen.findByText('Edit User');
await user.click(screen.getByRole('button', { name: 'Edit' }));
expect(toast.error).toHaveBeenCalledWith(TOAST_ERROR_MESSAGE);
});
});
Loading