diff --git a/client/modules/User/components/AccountForm.unit.test.jsx b/client/modules/User/components/AccountForm.unit.test.jsx new file mode 100644 index 000000000..653dce524 --- /dev/null +++ b/client/modules/User/components/AccountForm.unit.test.jsx @@ -0,0 +1,122 @@ +import React from 'react'; +import thunk from 'redux-thunk'; +import configureStore from 'redux-mock-store'; +import { + reduxRender, + screen, + fireEvent, + act, + waitFor +} from '../../../test-utils'; +import AccountForm from './AccountForm'; +import { initialTestState } from '../../../testData/testReduxStore'; +import * as actions from '../actions'; + +const mockStore = configureStore([thunk]); +const store = mockStore(initialTestState); + +jest.mock('../actions', () => ({ + ...jest.requireActual('../actions'), + updateSettings: jest.fn().mockReturnValue( + (dispatch) => + new Promise((resolve) => { + setTimeout(() => { + dispatch({ type: 'UPDATE_SETTINGS', payload: {} }); + resolve(); + }, 100); + }) + ) +})); + +const subject = () => { + reduxRender(, { + store + }); +}; + +describe('', () => { + it('renders form fields with initial values', () => { + subject(); + const emailElement = screen.getByRole('textbox', { + name: /email/i + }); + expect(emailElement).toBeInTheDocument(); + expect(emailElement).toHaveValue('happydog@example.com'); + + const userNameElement = screen.getByRole('textbox', { + name: /username/i + }); + expect(userNameElement).toBeInTheDocument(); + expect(userNameElement).toHaveValue('happydog'); + + const currentPasswordElement = screen.getByLabelText(/current password/i); + expect(currentPasswordElement).toBeInTheDocument(); + expect(currentPasswordElement).toHaveValue(''); + + const newPasswordElement = screen.getByLabelText(/new password/i); + expect(newPasswordElement).toBeInTheDocument(); + expect(newPasswordElement).toHaveValue(''); + }); + + it('handles form submission and calls updateSettings', async () => { + subject(); + + const saveAllSettingsButton = screen.getByRole('button', { + name: /save all settings/i + }); + + const currentPasswordElement = screen.getByLabelText(/current password/i); + const newPasswordElement = screen.getByLabelText(/new password/i); + + fireEvent.change(currentPasswordElement, { + target: { + value: 'currentPassword' + } + }); + + fireEvent.change(newPasswordElement, { + target: { + value: 'newPassword' + } + }); + + await act(async () => { + fireEvent.click(saveAllSettingsButton); + }); + + await waitFor(() => { + expect(actions.updateSettings).toHaveBeenCalledTimes(1); + }); + }); + + it('Save all setting button should get disabled while submitting and enable when not submitting', async () => { + subject(); + + const saveAllSettingsButton = screen.getByRole('button', { + name: /save all settings/i + }); + + const currentPasswordElement = screen.getByLabelText(/current password/i); + const newPasswordElement = screen.getByLabelText(/new password/i); + + fireEvent.change(currentPasswordElement, { + target: { + value: 'currentPassword' + } + }); + + fireEvent.change(newPasswordElement, { + target: { + value: 'newPassword' + } + }); + expect(saveAllSettingsButton).not.toHaveAttribute('disabled'); + + await act(async () => { + fireEvent.click(saveAllSettingsButton); + await waitFor(() => { + expect(saveAllSettingsButton).toHaveAttribute('disabled'); + }); + }); + }); +}); diff --git a/client/modules/User/components/LoginForm.unit.test.jsx b/client/modules/User/components/LoginForm.unit.test.jsx new file mode 100644 index 000000000..28fb9383c --- /dev/null +++ b/client/modules/User/components/LoginForm.unit.test.jsx @@ -0,0 +1,86 @@ +import React from 'react'; +import thunk from 'redux-thunk'; +import configureStore from 'redux-mock-store'; +import LoginForm from './LoginForm'; +import * as actions from '../actions'; +import { initialTestState } from '../../../testData/testReduxStore'; +import { reduxRender, screen, fireEvent, act } from '../../../test-utils'; + +const mockStore = configureStore([thunk]); +const store = mockStore(initialTestState); + +jest.mock('../actions', () => ({ + ...jest.requireActual('../actions'), + validateAndLoginUser: jest.fn().mockReturnValue( + (dispatch) => + new Promise((resolve) => { + setTimeout(() => { + dispatch({ type: 'AUTH_USER', payload: {} }); + dispatch({ type: 'SET_PREFERENCES', payload: {} }); + resolve(); + }, 100); + }) + ) +})); + +const subject = () => { + reduxRender(, { + store + }); +}; + +describe('', () => { + test('Renders form with the correct fields.', () => { + subject(); + const emailTextElement = screen.getByText(/email or username/i); + expect(emailTextElement).toBeInTheDocument(); + + const emailInputElement = screen.getByRole('textbox', { + name: /email or username/i + }); + expect(emailInputElement).toBeInTheDocument(); + + const passwordTextElement = screen.getByText(/password/i); + expect(passwordTextElement).toBeInTheDocument(); + + const passwordInputElement = screen.getByLabelText(/password/i); + expect(passwordInputElement).toBeInTheDocument(); + + const loginButtonElement = screen.getByRole('button', { + name: /log in/i + }); + expect(loginButtonElement).toBeInTheDocument(); + }); + test('Validate and login user is called with the correct values.', async () => { + subject(); + + const emailElement = screen.getByRole('textbox', { + name: /email or username/i + }); + fireEvent.change(emailElement, { + target: { + value: 'correctuser@gmail.com' + } + }); + + const passwordElement = screen.getByLabelText(/password/i); + + fireEvent.change(passwordElement, { + target: { + value: 'correctpassword' + } + }); + + const loginButton = screen.getByRole('button', { + name: /log in/i + }); + await act(async () => { + fireEvent.click(loginButton); + }); + + expect(actions.validateAndLoginUser).toHaveBeenCalledWith({ + email: 'correctuser@gmail.com', + password: 'correctpassword' + }); + }); +}); diff --git a/client/modules/User/components/NewPasswordForm.unit.test.jsx b/client/modules/User/components/NewPasswordForm.unit.test.jsx new file mode 100644 index 000000000..dbddf3c8c --- /dev/null +++ b/client/modules/User/components/NewPasswordForm.unit.test.jsx @@ -0,0 +1,84 @@ +import React from 'react'; +import thunk from 'redux-thunk'; +import configureStore from 'redux-mock-store'; +import { fireEvent } from '@storybook/testing-library'; +import { reduxRender, screen, act, waitFor } from '../../../test-utils'; +import { initialTestState } from '../../../testData/testReduxStore'; +import NewPasswordForm from './NewPasswordForm'; + +const mockStore = configureStore([thunk]); +const store = mockStore(initialTestState); + +const mockResetPasswordToken = 'mockResetToken'; +const subject = () => { + reduxRender(, { + store + }); +}; +const mockDispatch = jest.fn(); + +jest.mock('react-redux', () => ({ + ...jest.requireActual('react-redux'), + useDispatch: () => mockDispatch +})); + +jest.mock('../../../utils/reduxFormUtils', () => ({ + validateNewPassword: jest.fn() +})); + +jest.mock('../actions', () => ({ + updatePassword: jest.fn().mockReturnValue( + new Promise((resolve) => { + resolve(); + }) + ) +})); + +describe('', () => { + beforeEach(() => { + mockDispatch.mockClear(); + jest.clearAllMocks(); + }); + test('renders form fields correctly', () => { + subject(); + + const passwordInputElements = screen.getAllByLabelText(/Password/i); + expect(passwordInputElements).toHaveLength(2); + + const passwordInputElement = passwordInputElements[0]; + expect(passwordInputElement).toBeInTheDocument(); + + const confirmPasswordInputElement = passwordInputElements[1]; + expect(confirmPasswordInputElement).toBeInTheDocument(); + + const submitElemement = screen.getByRole('button', { + name: /set new password/i + }); + expect(submitElemement).toBeInTheDocument(); + }); + + test('submits form with valid data', async () => { + subject(); + const passwordInputElements = screen.getAllByLabelText(/Password/i); + + const passwordInputElement = passwordInputElements[0]; + fireEvent.change(passwordInputElement, { + target: { value: 'password123' } + }); + + const confirmPasswordInputElement = passwordInputElements[1]; + fireEvent.change(confirmPasswordInputElement, { + target: { value: 'password123' } + }); + + const submitElemement = screen.getByRole('button', { + name: /set new password/i + }); + + await act(async () => { + fireEvent.click(submitElemement); + }); + + await waitFor(() => expect(mockDispatch).toHaveBeenCalled()); + }); +});