Skip to content

Commit

Permalink
feat: refactor inverstor password manager component
Browse files Browse the repository at this point in the history
  • Loading branch information
hasan-deriv committed Aug 15, 2024
1 parent 51d367f commit 7301ff9
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import React from 'react';
import { screen, render, waitFor, fireEvent } from '@testing-library/react';
import InvestorPasswordManager from '../investor-password-manager';
import { localize } from '@deriv/translations';

type TValidLengthOptions = {
min?: number;
max?: number;
};

jest.mock('@deriv/shared/src/services/ws-methods', () => ({
__esModule: true,
default: 'mockedDefaultExport',
WS: {
verifyEmail: jest.fn(() => Promise.resolve()),
},
}));

const validLengthMock = (value = '', options: TValidLengthOptions) =>
(options.min ? value.length >= options.min : true) && (options.max ? value.length <= options.max : true);

const mock_errors = {
password: () => localize('Password should have lower and uppercase English letters with numbers.'),
repeated_chars_are_easy: () => localize('Repeats like "aaa" are easy to guess'),
repeated_patterns_are_easy: () => localize('Repeats like "abcabcabc" are only slightly harder to guess than "abc"'),
recent_years_are_easy: () => localize('Recent years are easy to guess'),
};

jest.mock('@deriv/shared/src/utils/validation/declarative-validation-rules.ts', () => ({
getErrorMessages: jest.fn(() => ({
password_warnings: mock_errors,
})),
validLength: jest.fn((value, options) => validLengthMock(value, options)),
}));

describe('<InvestorPasswordManager> ', () => {
const mock_props = {
error_message_investor: 'Forgot your password? Please reset your password.',
is_submit_success_investor: false,
multi_step_ref: { current: { goNextStep: jest.fn(), goPrevStep: jest.fn() } },
onSubmit: jest.fn(),
setPasswordType: jest.fn(() => 'investor'),
toggleModal: jest.fn(),
validatePassword: jest.fn(),
};

it('should render the correct texts ', async () => {
render(<InvestorPasswordManager {...mock_props} />);
expect(await screen.findByText(/new investor password/i)).toBeInTheDocument();
expect(
screen.getByText(
/use this password to grant viewing access to another user. While they may view your trading account, they will not be able to trade or take any other actions/i
)
).toBeInTheDocument();
expect(
screen.getByText(
/if this is the first time you try to create a password, or you have forgotten your password, please reset it/i
)
).toBeInTheDocument();
expect(screen.getByText(/current investor password/i)).toBeInTheDocument();
expect(screen.getByText(/new investor password/i)).toBeInTheDocument();
});

it('should fill the password field and trigger the appropriate error message for repeated password pattern', async () => {
render(<InvestorPasswordManager {...mock_props} />);
expect(await screen.findByText(/new investor password/i)).toBeInTheDocument();
const new_investor = screen.getByLabelText(/new investor password/i);
await waitFor(() => {
fireEvent.change(new_investor, { target: { value: 'abcabcabc' } });
});
expect(
await screen.findByText(/repeats like "abcabcabc" are only slightly harder to guess than "abc"/i)
).toBeInTheDocument();
});

it('should fill the password field and trigger the appropriate error message for repeated characters', async () => {
render(<InvestorPasswordManager {...mock_props} />);
expect(await screen.findByText(/new investor password/i)).toBeInTheDocument();
const new_investor = screen.getByLabelText(/new investor password/i);
await waitFor(() => {
fireEvent.change(new_investor, { target: { value: 'aaaaa' } });
});
expect(await screen.findByText(/repeats like "aaa" are easy to guess/i)).toBeInTheDocument();
});

it('should fill the password field and trigger the appropriate error message for using recent years', async () => {
render(<InvestorPasswordManager {...mock_props} />);
expect(await screen.findByText(/new investor password/i)).toBeInTheDocument();
const new_investor = screen.getByLabelText(/new investor password/i);
await waitFor(() => {
fireEvent.change(new_investor, { target: { value: '1996' } });
});

expect(await screen.findByText(/recent years are easy to guess/i)).toBeInTheDocument();
});

it('should fill the password field and trigger the appropriate message for strong and valid password', async () => {
render(<InvestorPasswordManager {...mock_props} />);
expect(await screen.findByText(/new investor password/i)).toBeInTheDocument();
const new_investor = screen.getByLabelText(/new investor password/i);
await waitFor(() => {
fireEvent.change(new_investor, { target: { value: 'Qzzxcc!lopi1' } });
expect(
screen.getAllByText(
/strong passwords contain at least 8 characters, combine uppercase and lowercase letters and numbers/i
)[0]
).toBeInTheDocument();
});
});

it('should fill the password fields and trigger the appropriate message and enable the change password button', async () => {
const mockOnSubmit = jest.fn();
render(<InvestorPasswordManager {...mock_props} onSubmit={mockOnSubmit} />);
expect(await screen.findByText(/new investor password/i)).toBeInTheDocument();
const current_investor = screen.getByLabelText(/current investor password/i);
const new_investor = screen.getByLabelText(/new investor password/i);
const change_investor_password_btn = screen.getByText(/change investor password/i);
await waitFor(() => {
fireEvent.change(current_investor, { target: { value: 'Testing1234' } });
fireEvent.change(new_investor, { target: { value: 'Qzzxcc!lopi1' } });
expect(
screen.getAllByText(
/strong passwords contain at least 8 characters, combine uppercase and lowercase letters and numbers/i
)[0]
).toBeInTheDocument();
expect(change_investor_password_btn).toBeEnabled();
fireEvent.click(change_investor_password_btn);
expect(screen.getByTestId('dt_error_message_investor')).toBeInTheDocument();
expect(mockOnSubmit).toHaveBeenCalled();
});
});

it('should render success message if the user clicks on create or reset investor passwords', async () => {
render(<InvestorPasswordManager {...mock_props} is_submit_success_investor={true} />);
expect(screen.getByText(/your investor password has been changed/i)).toBeInTheDocument();
expect(screen.getByRole('button', { name: /ok/i })).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { Text, Button, Icon } from '@deriv/components';
import { localize, Localize } from '@deriv/translations';
import { TCFDPasswordSuccessMessage } from '../props.types';

const CFDPasswordSuccessMessage = ({ toggleModal, is_investor }: TCFDPasswordSuccessMessage) => (
<div className='cfd-password-manager__success'>
<Icon icon='IcPasswordUpdated' size={128} />
<Text as='p' size='xxs' align='center'>
{is_investor ? (
<Localize i18n_default_text='Your investor password has been changed.' />
) : (
<Localize i18n_default_text='Your password has been changed.' />
)}
</Text>
<Button onClick={toggleModal} className='cfd-password-manager__success-btn' primary large>
<p className='dc-btn__text'>{localize('OK')}</p>
</Button>
</div>
);

export default CFDPasswordSuccessMessage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import InvestorPasswordManager from './investor-password-manager';

export default InvestorPasswordManager;
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
import PropTypes from 'prop-types';
import React from 'react';
import { Field, Form, Formik, FieldProps } from 'formik';
import { PasswordInput, PasswordMeter, Text, Button, Icon } from '@deriv/components';
import { PasswordInput, PasswordMeter, Text, Button } from '@deriv/components';
import { localize, Localize } from '@deriv/translations';
import { getErrorMessages } from '@deriv/shared';
import { TCFDPasswordSuccessMessage, TInvestorPasswordManager, TPasswordManagerModalFormValues } from './props.types';
import CFDPasswordSuccessMessage from './cfd-password-success-message';
import { TMultiStepRefProps, TPasswordManagerModalFormValues } from '../props.types';

const CFDPasswordSuccessMessage = ({ toggleModal, is_investor }: TCFDPasswordSuccessMessage) => (
<div className='cfd-password-manager__success'>
<Icon icon='IcPasswordUpdated' size={128} />
<Text as='p' size='xxs' align='center'>
{is_investor ? (
<Localize i18n_default_text='Your investor password has been changed.' />
) : (
<Localize i18n_default_text='Your password has been changed.' />
)}
</Text>
<Button onClick={toggleModal} className='cfd-password-manager__success-btn' primary large>
<p className='dc-btn__text'>{localize('OK')}</p>
</Button>
</div>
);
type TInvestorPasswordManagerProps = {
error_message_investor: string;
is_submit_success_investor: boolean;
multi_step_ref: React.MutableRefObject<TMultiStepRefProps | undefined>;
onSubmit: (values: TPasswordManagerModalFormValues) => Promise<void>;
setPasswordType: (value: string) => void;
toggleModal: () => void;
validatePassword: (values: { old_password: string; new_password: string; password_type: string }) => void | object;
};

const InvestorPasswordManager = ({
error_message_investor,
Expand All @@ -30,7 +24,7 @@ const InvestorPasswordManager = ({
setPasswordType,
toggleModal,
validatePassword,
}: TInvestorPasswordManager) => {
}: TInvestorPasswordManagerProps) => {
if (is_submit_success_investor) {
return <CFDPasswordSuccessMessage toggleModal={toggleModal} is_investor />;
}
Expand Down Expand Up @@ -135,14 +129,4 @@ const InvestorPasswordManager = ({
);
};

InvestorPasswordManager.propTypes = {
error_message_investor: PropTypes.string,
is_submit_success_investor: PropTypes.bool,
multi_step_ref: PropTypes.object,
onSubmit: PropTypes.func,
setPasswordType: PropTypes.func,
toggleModal: PropTypes.func,
validatePassword: PropTypes.func,
};

export default InvestorPasswordManager;
10 changes: 0 additions & 10 deletions packages/cfd/src/Containers/props.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,16 +106,6 @@ export type TMultiStepRefProps = {
goPrevStep: () => void;
};

export type TInvestorPasswordManager = {
error_message_investor: string;
is_submit_success_investor: boolean;
multi_step_ref: React.MutableRefObject<TMultiStepRefProps | undefined>;
onSubmit: (values: TPasswordManagerModalFormValues) => Promise<void>;
setPasswordType: (value: string) => void;
toggleModal: () => void;
validatePassword: (values: { old_password: string; new_password: string; password_type: string }) => void | object;
};

export type TCountdownComponent = {
count_from: number;
onTimeout: () => void;
Expand Down

0 comments on commit 7301ff9

Please sign in to comment.