Skip to content

Commit

Permalink
Jim/wall 2947/unit tests for change password (#16659)
Browse files Browse the repository at this point in the history
* chore: add testid to sent email content wrapper

* chore: add unit tests for change password mt5 change password and trading platform change password

* chore: add unit tests for investor password screens

* chore: remove unused import

* test: fix failing tests and update description

* test: add more tests

* test: refactor tests

* chore: resolve sonarcloud issue
  • Loading branch information
jim-deriv committed Aug 29, 2024
1 parent 540748a commit af088c6
Show file tree
Hide file tree
Showing 7 changed files with 657 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import React, { PropsWithChildren } from 'react';
import { useTradingPlatformInvestorPasswordChange } from '@deriv/api-v2';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { WalletButton } from '../../../../../../components/Base';
import { useModal } from '../../../../../../components/ModalProvider';
import useDevice from '../../../../../../hooks/useDevice';
import { validPasswordMT5 } from '../../../../../../utils/password-validation';
import MT5ChangeInvestorPasswordInputsScreen from '../MT5ChangeInvestorPasswordInputsScreen';

jest.mock('@deriv/api-v2', () => ({
useTradingPlatformInvestorPasswordChange: jest.fn(),
}));

jest.mock('../../../../../../components', () => ({
...jest.requireActual('../../../../../../components'),
WalletsActionScreen: jest.fn(({ description, renderButtons }) => (
<div>
{description}
{renderButtons()}
</div>
)),
}));

jest.mock('../../../../../../components/Base', () => ({
...jest.requireActual('../../../../../../components/Base'),
WalletButton: jest.fn(
({
children,
disabled,
isLoading,
onClick,
type,
}: PropsWithChildren<{
disabled: boolean;
isLoading: boolean;
onClick: () => void;
textSize: string;
type?: 'button' | 'reset' | 'submit';
}>) => {
return (
<button disabled={disabled} onClick={onClick} type={type}>
{isLoading && <>Loading...</>}
{!isLoading ? children : null}
</button>
);
}
),
}));

jest.mock('../../../../../../components/ModalProvider', () => ({
...jest.requireActual('../../../../../../components/ModalProvider'),
useModal: jest.fn(),
}));

jest.mock('../../../../../../components/Base/WalletPasswordField/PasswordViewerIcon', () =>
jest.fn(({ setViewPassword, viewPassword }) => (
<button onClick={() => setViewPassword(!viewPassword)}>PasswordViewerIcon</button>
))
);

jest.mock('../../../../../../hooks/useDevice', () => jest.fn());

jest.mock('../../../../../../utils/password-validation', () => ({
...jest.requireActual('../../../../../../utils/password-validation'),
validPasswordMT5: jest.fn(),
}));

describe('MT5ChangeInvestorPasswordInputsScreen', () => {
beforeEach(() => {
(useTradingPlatformInvestorPasswordChange as jest.Mock).mockReturnValue({
mutateAsync: jest.fn(),
status: 'idle',
});
(useModal as jest.Mock).mockReturnValue({
getModalState: jest.fn().mockReturnValue('test-account-id'),
});
(useDevice as jest.Mock).mockReturnValue({
isMobile: false,
});
(validPasswordMT5 as jest.Mock).mockReturnValue(true);
});

it('renders the component correctly', () => {
render(<MT5ChangeInvestorPasswordInputsScreen />);

expect(screen.getByText(/Use this password to grant viewing access to another user/)).toBeInTheDocument();
expect(screen.getByText(/Change investor password/)).toBeInTheDocument();
expect(screen.getByText(/Create or reset investor password/)).toBeInTheDocument();
});

it('validates current password field', async () => {
render(<MT5ChangeInvestorPasswordInputsScreen />);

userEvent.click(screen.getByText('Current investor password'));
userEvent.tab();
userEvent.click(screen.getByText(/Change investor password/));

await waitFor(() => {
expect(screen.getByText('The field is required')).toBeInTheDocument();
});
});

it('submits the form with valid data', async () => {
const newPassword = 'newPassword123';
const oldPassword = 'oldPassword123';
const mockChangePassword = jest.fn().mockResolvedValue({});
(useTradingPlatformInvestorPasswordChange as jest.Mock).mockReturnValue({
mutateAsync: mockChangePassword,
});

render(<MT5ChangeInvestorPasswordInputsScreen setNextScreen={jest.fn()} />);

userEvent.type(screen.getByLabelText(/Current investor password/), oldPassword);
const newInvestorPasswordInput = await screen.findByText('New investor password');
userEvent.type(newInvestorPasswordInput, newPassword);

userEvent.click(screen.getByText(/Change investor password/));

await waitFor(() => {
expect(mockChangePassword).toHaveBeenCalledWith({
account_id: 'test-account-id',
new_password: newPassword,
old_password: oldPassword,
platform: 'mt5',
});
});
});

it('displays error message if change password fails', () => {
(useTradingPlatformInvestorPasswordChange as jest.Mock).mockReturnValue({
error: { error: { message: 'Error changing password' } },
mutateAsync: jest.fn(),
});

render(<MT5ChangeInvestorPasswordInputsScreen />);

expect(screen.getByText('Error changing password')).toBeInTheDocument();
});

it('shows the loader when the change investor password mutation is loading', () => {
(useTradingPlatformInvestorPasswordChange as jest.Mock).mockReturnValue({
status: 'loading',
});

render(<MT5ChangeInvestorPasswordInputsScreen />);

expect(
screen.getByRole('button', {
name: 'Loading...',
})
).toBeInTheDocument();
});

it('displays the password when the viewer icon is clicked', () => {
render(<MT5ChangeInvestorPasswordInputsScreen />);

const currentPasswordInput = screen.getByPlaceholderText('Current investor password');
expect(currentPasswordInput).toHaveAttribute('type', 'password');

userEvent.click(screen.getAllByRole('button', { name: 'PasswordViewerIcon' })[0]);

expect(currentPasswordInput).toHaveAttribute('type', 'text');
});

it('renders button with correct text size for mobile', () => {
(useDevice as jest.Mock).mockReturnValue({ isMobile: true });

render(<MT5ChangeInvestorPasswordInputsScreen />);

expect(WalletButton).toHaveBeenCalledWith(expect.objectContaining({ textSize: 'md' }), {});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import MT5ChangeInvestorPasswordSavedScreen from '../MT5ChangeInvestorPasswordSavedScreen';

describe('MT5ChangeInvestorPasswordSavedScreen', () => {
it('renders without crashing', () => {
render(<MT5ChangeInvestorPasswordSavedScreen />);

expect(screen.getByText('Your investor password has been changed.')).toBeInTheDocument();
expect(screen.getByText('Password saved')).toBeInTheDocument();
expect(screen.getByRole('button', { name: /OK/i })).toBeInTheDocument();
});

it('calls setNextScreen when OK button is clicked', () => {
const setNextScreenMock = jest.fn();

render(<MT5ChangeInvestorPasswordSavedScreen setNextScreen={setNextScreenMock} />);

userEvent.click(screen.getByRole('button', { name: /OK/i }));

expect(setNextScreenMock).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React from 'react';
import { useActiveWalletAccount, useSettings, useVerifyEmail } from '@deriv/api-v2';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { useModal } from '../../../../../../components/ModalProvider';
import MT5ChangeInvestorPasswordScreens from '../MT5ChangeInvestorPasswordScreens';

jest.mock('@deriv/api-v2', () => ({
...jest.requireActual('@deriv/api-v2'),
useActiveWalletAccount: jest.fn(),
useSettings: jest.fn(),
useVerifyEmail: jest.fn(),
}));

jest.mock('../../../../../../components/ModalProvider', () => ({
...jest.requireActual('../../../../../../components/ModalProvider'),
useModal: jest.fn(),
}));

jest.mock('../MT5ChangeInvestorPasswordInputsScreen', () =>
jest.fn(({ sendEmail, setNextScreen }) => (
<div>
<button onClick={sendEmail}>Send Email</button>
<button onClick={setNextScreen}>Next Screen</button>
</div>
))
);

jest.mock('../MT5ChangeInvestorPasswordSavedScreen', () =>
jest.fn(({ setNextScreen }) => (
<div>
<button onClick={setNextScreen}>Close</button>
</div>
))
);

describe('MT5ChangeInvestorPasswordScreens', () => {
beforeEach(() => {
(useModal as jest.Mock).mockReturnValue({
getModalState: jest.fn().mockReturnValue('account-id'),
hide: jest.fn(),
});
(useSettings as jest.Mock).mockReturnValue({ data: { email: 'user@example.com' } });
(useVerifyEmail as jest.Mock).mockReturnValue({ mutate: jest.fn() });
(useActiveWalletAccount as jest.Mock).mockReturnValue({ data: { is_virtual: false } });
});

it('renders intro screen by default', () => {
render(<MT5ChangeInvestorPasswordScreens />);

expect(screen.getByRole('button', { name: 'Send Email' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Next Screen' })).toBeInTheDocument();
});

it('handles send email button click', async () => {
const setShowEmailSentScreen = jest.fn();
const { mutate } = useVerifyEmail();

render(<MT5ChangeInvestorPasswordScreens setShowEmailSentScreen={setShowEmailSentScreen} />);

userEvent.click(screen.getByRole('button', { name: 'Send Email' }));

await waitFor(() => {
expect(mutate).toHaveBeenCalled();
expect(setShowEmailSentScreen).toHaveBeenCalledWith(true);
expect(localStorage.getItem('trading_platform_investor_password_reset_account_id')).toBe('account-id');
});
});

it('switches to saved screen on next screen button click', () => {
render(<MT5ChangeInvestorPasswordScreens />);

userEvent.click(screen.getByRole('button', { name: 'Next Screen' }));

expect(screen.getByRole('button', { name: 'Close' })).toBeInTheDocument();
});

it('calls hide modal on close button click in saved screen', () => {
const { hide } = useModal();

render(<MT5ChangeInvestorPasswordScreens />);

userEvent.click(screen.getByRole('button', { name: 'Next Screen' }));
userEvent.click(screen.getByRole('button', { name: 'Close' }));

expect(hide).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ const MT5ChangePasswordScreens = () => {
const { title } = PlatformDetails[platform];

return showSentEmailContentWithoutTabs ? (
<div className='wallets-change-password__sent-email-content-wrapper--mt5-investor'>
<div
className='wallets-change-password__sent-email-content-wrapper--mt5-investor'
data-testid='dt_change_password_sent_email_content_wrapper'
>
<SentEmailContent
description={localize('Please click on the link in the email to reset your password.')}
isInvestorPassword
Expand Down
Loading

0 comments on commit af088c6

Please sign in to comment.