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

Likhith/wall 1714/enhance error message handling for poi #56

Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Icon } from '@deriv/components';
import { ONFIDO_ERROR_STATUS } from '@deriv/shared';
import { StoreProvider, mockStore } from '@deriv/stores';
import IconMessageList from '../icon-message-list';

jest.mock('@deriv/components', () => {
Expand All @@ -11,67 +14,61 @@ jest.mock('@deriv/components', () => {
};
});
describe('<IconMessageList/>', () => {
const messages_list = ['Sample Text1', 'Sample Text2', 'Sample Text3', 'Sample Text4'];
const messages_list = ['DataComparisonDocumentNumbers', 'CompromisedDocument', 'VisualAuthenticityFonts'];

const mock_props: React.ComponentProps<typeof IconMessageList> = {
icon: <Icon icon='sampleIcon' />,
message: 'Lorem Ipsom',
message_list: messages_list,
onContinue: jest.fn(),
};

const store_config = mockStore({ ui: { is_desktop: true } });

const renderComponent = ({ props = mock_props, store = store_config }) =>
render(
<StoreProvider store={store}>
<IconMessageList {...props} />
</StoreProvider>
);

it('should render IconMessageList component', () => {
render(<IconMessageList />);
renderComponent({});
expect(screen.getByTestId('dt_icon_message_list')).toBeInTheDocument();
});

it('should render icon passed to the component', () => {
render(<IconMessageList icon={<Icon icon='sampleIcon' />} />);
expect(screen.getByTestId('mocked_icon')).toBeInTheDocument();
renderComponent({});
expect(screen.getByText('sampleIcon')).toBeInTheDocument();
expect(screen.getByText('IcCloseCircle')).toBeInTheDocument();
});

it('should show message passed to the component', () => {
render(<IconMessageList message={'Lorem Ipsom'} />);
renderComponent({});
expect(screen.getByText(/lorem ipsom/i)).toBeInTheDocument();
});
it('when the length of message_list is less than 3, it should show messages with icons ', () => {
render(<IconMessageList message_list={['Sample Text1', 'Sample Text2']} />);
expect(screen.getByText(/Sample Text1/i)).toBeInTheDocument();
expect(screen.getByText(/Sample Text2/i)).toBeInTheDocument();
expect(screen.getAllByText(/icclosecircle/i).length).toBe(2);
});

it('should show first 3 msgs and show_more_btn when the message_list is more than 3', () => {
render(<IconMessageList message_list={messages_list} />);
expect(screen.getByText(/sample text1/i)).toBeInTheDocument();
expect(screen.getByText(/sample text2/i)).toBeInTheDocument();
expect(screen.getByText(/sample text3/i)).toBeInTheDocument();
expect(screen.queryByText(/sample text4/i)).not.toBeInTheDocument();
expect(
screen.getByRole('button', {
name: /show more/i,
})
).toBeInTheDocument();
expect(
screen.queryByRole('button', {
name: /show less/i,
})
).not.toBeInTheDocument();
});

it('should show all messages and show_less_btn when show_more btn is clicked', () => {
render(<IconMessageList message_list={messages_list} />);

const show_more_btn = screen.getByRole('button', {
name: /show more/i,
});

fireEvent.click(show_more_btn);
expect(screen.getByText(/sample text1/i)).toBeInTheDocument();
expect(screen.getByText(/sample text2/i)).toBeInTheDocument();
expect(screen.getByText(/sample text3/i)).toBeInTheDocument();
expect(screen.getByText(/sample text4/i)).toBeInTheDocument();
expect(screen.queryByRole('button', { name: /show less/i })).toBeInTheDocument();
expect(screen.queryByRole('button', { name: /show more/i })).not.toBeInTheDocument();
it('should render the messages based on Onfido Error codes', () => {
const new_props = {
...mock_props,
message_list: ['DataComparisonDocumentNumbers', 'CompromisedDocument'],
};
renderComponent({ props: new_props });
expect(screen.getByText(ONFIDO_ERROR_STATUS.DataComparisonDocumentNumbers.message)).toBeInTheDocument();
expect(screen.getByText(ONFIDO_ERROR_STATUS.CompromisedDocument.message)).toBeInTheDocument();
});

it('should show continue_btn if OnContinue is passed', () => {
const onContinuefn = jest.fn();
render(<IconMessageList message_list={['Sample Text1']} onContinue={onContinuefn} />);
const upload_btn = screen.queryByRole('button', { name: /upload document/i });
const new_props = {
...mock_props,
messages_list: ['DataComparisonDocumentNumbers'],
onContinue: onContinuefn,
};
renderComponent({ props: new_props });
const upload_btn = screen.queryByRole('button', { name: /verify again/i });
expect(upload_btn).toBeInTheDocument();
fireEvent.click(upload_btn);
userEvent.click(upload_btn);
expect(onContinuefn).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import ListItem from '../list-item';

describe('<ListItem/>', () => {
it('should render ListItem component', () => {
render(<ListItem text='test' />);
expect(screen.getByText('test')).toBeInTheDocument();
});

it('should render ListItem component with index', () => {
render(<ListItem text='test' index={1} />);
expect(screen.getByText('1.')).toBeInTheDocument();
expect(screen.getByText('test')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -1,44 +1,27 @@
import React from 'react';
import classNames from 'classnames';
import { localize } from '@deriv/translations';
import { Div100vhContainer, Text, Button, Icon, ThemedScrollbars } from '@deriv/components';
import { isDesktop, isMobile } from '@deriv/shared';
import { Localize } from '@deriv/translations';
import { Div100vhContainer, Text, Button, ThemedScrollbars, Icon } from '@deriv/components';
import { formatOnfidoError } from '@deriv/shared';
import { observer, useStore } from '@deriv/stores';
import ListItem from './list-item';

type TListItem = {
text?: string;
};

type TMessage_list = {
message_list?: string[];
};

type TIconMessageList = TMessage_list & {
className: string;
type TIconMessageList = {
className?: string;
icon: React.ReactElement;
message: string;
message_list: Array<string>;
onContinue: () => void;
};

const ListItem = ({ text }: TListItem) => (
<div className='account-management__list-message'>
<div className='account-management__list-icon'>
<Icon icon='IcCloseCircle' color='red' />
</div>
<div className='account-management__list-text-container'>
<Text size='xs' className='account-management__list-text'>
{text}
</Text>
</div>
</div>
);

const IconMessageList = ({ className, icon, message, message_list = [], onContinue }: Partial<TIconMessageList>) => {
const has_maximum_list = message_list.length > 3;
const IconMessageList = observer(({ className, icon, message, message_list = [], onContinue }: TIconMessageList) => {
const { ui } = useStore();
const { is_mobile, is_desktop } = ui;
return (
<ThemedScrollbars is_bypassed={isMobile()}>
<ThemedScrollbars is_bypassed={is_mobile}>
<Div100vhContainer
className='account-management__message-wrapper'
is_disabled={isDesktop()}
is_disabled={is_desktop}
height_offset='110px'
>
<div
Expand Down Expand Up @@ -68,11 +51,24 @@ const IconMessageList = ({ className, icon, message, message_list = [], onContin

{message_list && (
<div className='account-management__list-container'>
{has_maximum_list ? (
<MaximumList message_list={message_list} />
) : (
message_list.map((text, idx) => <ListItem key={idx} text={text} />)
)}
<div className='account-management__list-message'>
<div className='account-management__list-icon'>
<Icon icon='IcCloseCircle' color='red' />
</div>
<section>
{message_list.length < 2 ? (
<ListItem text={formatOnfidoError(message_list[0])} />
) : (
message_list.map((text, idx) => (
<ListItem
key={text}
text={formatOnfidoError(message_list[idx])}
index={idx + 1}
/>
))
)}
</section>
</div>
</div>
)}
{onContinue && (
Expand All @@ -81,49 +77,15 @@ const IconMessageList = ({ className, icon, message, message_list = [], onContin
className='account-management__continue'
onClick={onContinue}
large
text={localize('Upload Document')}
primary
/>
>
<Localize i18n_default_text='Verify again' />
</Button>
)}
</div>
</Div100vhContainer>
</ThemedScrollbars>
);
};

const MaximumList = ({ message_list }: TMessage_list) => {
const [show_more, setShowMore] = React.useState(false);
const maximum_list = message_list.slice(0, 3);

return show_more ? (
<React.Fragment>
{message_list.map(text => (
<ListItem key={text} text={text} />
))}
<Button
type='button'
className='account-management__list-button'
onClick={() => setShowMore(false)}
large
text={localize('Show less')}
tertiary
/>
</React.Fragment>
) : (
<React.Fragment>
{maximum_list.map(text => (
<ListItem key={text} text={text} />
))}
<Button
type='button'
className='account-management__list-button'
onClick={() => setShowMore(true)}
large
text={localize('Show more')}
tertiary
/>
</React.Fragment>
);
};
});

export default IconMessageList;
23 changes: 23 additions & 0 deletions packages/account/src/Components/icon-message-list/list-item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import { Text } from '@deriv/components';
import { Localize } from '@deriv/translations';

type TListItem = {
text: string;
index?: number;
};

const ListItem = ({ text, index }: TListItem) => (
<div className='account-management__list-text-container'>
{index && (
<Text size='xs' className='account-management__list-text'>
<Localize i18n_default_text='{{index}}.' values={{ index }} />{' '}
</Text>
)}
<Text size='xs' className='account-management__list-text'>
<Localize i18n_default_text='{{text}}' values={{ text }} />
</Text>
</div>
);

export default ListItem;
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import IdvFailed from '../idv-failed';
import { idv_error_statuses } from '@deriv/shared';
import { IDV_ERROR_STATUS } from '@deriv/shared';
import { StoreProvider, mockStore } from '@deriv/stores';

jest.mock('@deriv/shared', () => ({
Expand Down Expand Up @@ -39,7 +39,7 @@ describe('<IdvFailed/>', () => {
account_settings: {
citizen: 'gh',
},
mismatch_status: idv_error_statuses.poi_name_mismatch,
mismatch_status: IDV_ERROR_STATUS.NameMismatch.code,
latest_status: {},
};

Expand All @@ -56,48 +56,48 @@ describe('<IdvFailed/>', () => {
renderComponent({});

await waitFor(() => {
expect(screen.getByTestId(idv_error_statuses.poi_name_mismatch)).toBeInTheDocument();
expect(screen.getByTestId(IDV_ERROR_STATUS.NameMismatch.code)).toBeInTheDocument();
expect(screen.getByText('PersonalDetailsForm')).toBeInTheDocument();
});
});

it('should render IDVfailed component with dob mismatch message', async () => {
const new_props = { ...mock_props, mismatch_status: idv_error_statuses.poi_dob_mismatch };
const new_props = { ...mock_props, mismatch_status: IDV_ERROR_STATUS.DobMismatch.code };
renderComponent({ props: new_props });

await waitFor(() => {
expect(screen.getByTestId(idv_error_statuses.poi_dob_mismatch)).toBeInTheDocument();
expect(screen.getByTestId(IDV_ERROR_STATUS.DobMismatch.code)).toBeInTheDocument();
expect(screen.queryByText('IDVForm')).not.toBeInTheDocument();
});
});

it('should render IDVfailed component with name & DOB mismatch message', async () => {
const new_props = { ...mock_props, mismatch_status: idv_error_statuses.poi_name_dob_mismatch };
const new_props = { ...mock_props, mismatch_status: IDV_ERROR_STATUS.NameDOBMismatch.code };
renderComponent({ props: new_props });

await waitFor(() => {
expect(screen.getByTestId(idv_error_statuses.poi_name_dob_mismatch)).toBeInTheDocument();
expect(screen.getByTestId(IDV_ERROR_STATUS.NameDOBMismatch.code)).toBeInTheDocument();
expect(screen.getByRole('button', { name: /Update profile/i })).toBeInTheDocument();
});
});

it('should render IDVfailed component with expired message', async () => {
const new_props = { ...mock_props, mismatch_status: idv_error_statuses.poi_expired };
const new_props = { ...mock_props, mismatch_status: IDV_ERROR_STATUS.Expired.code };
renderComponent({ props: new_props });

await waitFor(() => {
expect(screen.getByTestId(idv_error_statuses.poi_expired)).toBeInTheDocument();
expect(screen.getByTestId(IDV_ERROR_STATUS.Expired.code)).toBeInTheDocument();
expect(screen.getByText('IDVForm')).toBeInTheDocument();
expect(screen.getByText('PersonalDetailsForm')).toBeInTheDocument();
});
});

it('should render IDVfailed component with verification failed message', async () => {
const new_props = { ...mock_props, mismatch_status: idv_error_statuses.poi_failed };
const new_props = { ...mock_props, mismatch_status: IDV_ERROR_STATUS.Failed.code };
renderComponent({ props: new_props });

await waitFor(() => {
expect(screen.getByTestId(idv_error_statuses.poi_failed)).toBeInTheDocument();
expect(screen.getByTestId(IDV_ERROR_STATUS.Failed.code)).toBeInTheDocument();
expect(screen.getByRole('button', { name: /Verify/i })).toBeInTheDocument();
});
});
Expand Down
Loading
Loading