From f6f566fa44c14032a2f88f73ef52a5432ad6acc8 Mon Sep 17 00:00:00 2001 From: amina-deriv <84661147+amina-deriv@users.noreply.github.com> Date: Thu, 26 Oct 2023 10:20:35 +0400 Subject: [PATCH] Amina/comp 515/amend financial information dropdown (#10360) * fix: set form values * fix: common formating function * fix: add id * fix: aa * fix: account signup * fix: revert unrelated file change * fix: test case * fix: revert eslint fix on unrelated files * fix: revert eslint fix on unrelated files * fix: revert eslint fix on unrelated files * fix: merge master * fix: function to common * fix: mobile * fix: validation * fix: add optional parameter * fix: eslint * chore: merge master --- .../__tests__/financial-details.spec.tsx | 77 +++++++++++++++--- .../financial-details-partials.tsx | 81 +++++++++++++++++-- .../financial-details/financial-details.tsx | 12 ++- .../src/Configs/financial-details-config.ts | 6 ++ .../src/Constants/financial-details.ts | 4 + .../__tests__/financial-assessment.spec.tsx | 75 +++++++++++++++++ .../financial-assessment.tsx | 44 ++++++++-- .../RealAccountSignup/account-wizard.jsx | 3 + 8 files changed, 274 insertions(+), 28 deletions(-) create mode 100644 packages/account/src/Constants/financial-details.ts create mode 100644 packages/account/src/Sections/Assessment/FinancialAssessment/__tests__/financial-assessment.spec.tsx diff --git a/packages/account/src/Components/financial-details/__tests__/financial-details.spec.tsx b/packages/account/src/Components/financial-details/__tests__/financial-details.spec.tsx index b7683eef5b7f..ef4216e9be33 100644 --- a/packages/account/src/Components/financial-details/__tests__/financial-details.spec.tsx +++ b/packages/account/src/Components/financial-details/__tests__/financial-details.spec.tsx @@ -1,7 +1,7 @@ -import { FormikValues } from 'formik'; import React from 'react'; +import { FormikValues } from 'formik'; import { isDesktop, isMobile } from '@deriv/shared'; -import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import FinancialDetails from '../financial-details'; @@ -25,6 +25,7 @@ describe('', () => { validate: jest.fn(() => ({ errors: {} })), goToPreviousStep: jest.fn(() => ({ errors: {} })), value: {}, + employment_status: '', }; const fieldsRenderCheck = () => { @@ -73,7 +74,7 @@ describe('', () => { const btns = screen.getAllByRole('button'); expect(btns[0]).toHaveTextContent('Previous'); - fireEvent.click(btns[0]); + userEvent.click(btns[0]); expect(mock_props.getCurrentStep).toHaveBeenCalledTimes(1); }); @@ -103,21 +104,21 @@ describe('', () => { (option: FormikValues) => option.name === 'source_of_wealth' ); - fireEvent.change(account_turnover_select as HTMLElement, { target: { value: 'account turnover 1' } }); + userEvent.type(account_turnover_select as HTMLElement, 'account turnover 1'); - fireEvent.change(education_level_select as HTMLElement, { target: { value: 'education level 2' } }); - fireEvent.change(employment_indystry_select as HTMLElement, { target: { value: 'employment industry 1' } }); - fireEvent.change(estimated_worth_select as HTMLElement, { target: { value: 'estimated worth 2' } }); - fireEvent.change(income_source_select as HTMLElement, { target: { value: 'income source 1' } }); - fireEvent.change(net_income_select as HTMLElement, { target: { value: 'net income 1' } }); - fireEvent.change(occuppation_select as HTMLElement, { target: { value: 'occupation 2' } }); + userEvent.type(education_level_select as HTMLElement, 'education level 2'); + userEvent.type(employment_indystry_select as HTMLElement, 'employment industry 1'); + userEvent.type(estimated_worth_select as HTMLElement, 'estimated worth 2'); + userEvent.type(income_source_select as HTMLElement, 'income source 1'); + userEvent.type(net_income_select as HTMLElement, 'net income 1'); + userEvent.type(occuppation_select as HTMLElement, 'Government Officers'); - fireEvent.change(source_of_wealth_select as HTMLElement, { target: { value: 'source of wealth 1' } }); + userEvent.type(source_of_wealth_select as HTMLElement, 'source of wealth 1'); const btns = screen.getAllByRole('button'); expect(btns[1]).toHaveTextContent('Next'); - fireEvent.click(btns[1]); + userEvent.click(btns[1]); await waitFor(() => { expect(mock_props.onSubmit).toHaveBeenCalledTimes(1); }); @@ -137,4 +138,56 @@ describe('', () => { expect(screen.getByRole('option', { name: 'Salaried Employee' }).selected).toBe(true); }); + + it('should show "Unemployed" in occupation list if employment status is not "Employed"', async () => { + (isDesktop as jest.Mock).mockReturnValue(false); + (isMobile as jest.Mock).mockReturnValue(true); + const new_mock_props = { + ...mock_props, + employment_status: 'Pensioner', + }; + render(); + + fieldsRenderCheck(); + + const select_inputs = screen.getAllByRole('combobox'); + + const account_turnover_select = select_inputs.find( + (option: FormikValues) => option.name === 'account_turnover' + ); + const education_level_select = select_inputs.find((option: FormikValues) => option.name === 'education_level'); + const employment_indystry_select = select_inputs.find( + (option: FormikValues) => option.name === 'employment_industry' + ); + const estimated_worth_select = select_inputs.find((option: FormikValues) => option.name === 'estimated_worth'); + const income_source_select = select_inputs.find((option: FormikValues) => option.name === 'income_source'); + const net_income_select = select_inputs.find((option: FormikValues) => option.name === 'net_income'); + + const source_of_wealth_select = select_inputs.find( + (option: FormikValues) => option.name === 'source_of_wealth' + ); + const occuppation_select = select_inputs.find((option: FormikValues) => option.name === 'occupation'); + + userEvent.type(account_turnover_select as HTMLElement, 'account turnover 1'); + + userEvent.type(education_level_select as HTMLElement, 'education level 2'); + userEvent.type(employment_indystry_select as HTMLElement, 'employment industry 1'); + userEvent.type(estimated_worth_select as HTMLElement, 'estimated worth 2'); + userEvent.type(income_source_select as HTMLElement, 'income source 1'); + userEvent.type(net_income_select as HTMLElement, 'net income 1'); + userEvent.type(source_of_wealth_select as HTMLElement, 'source of wealth 1'); + + const occupation_text = screen.getAllByText('Unemployed')[0]; + expect(occupation_text).toBeInTheDocument(); + + userEvent.type(occuppation_select as HTMLElement, 'Unemployed'); + + const next_btn = screen.getByRole('button', { name: 'Next' }); + expect(next_btn).toBeEnabled(); + + userEvent.click(next_btn); + await waitFor(() => { + expect(mock_props.onSubmit).toHaveBeenCalled(); + }); + }); }); diff --git a/packages/account/src/Components/financial-details/financial-details-partials.tsx b/packages/account/src/Components/financial-details/financial-details-partials.tsx index 1887f19849d0..c4fbd50aab62 100644 --- a/packages/account/src/Components/financial-details/financial-details-partials.tsx +++ b/packages/account/src/Components/financial-details/financial-details-partials.tsx @@ -9,15 +9,21 @@ import { getEstimatedWorthList, getIncomeSourceList, getNetIncomeList, - getOccupationList, + getFormattedOccupationList, getSourceOfWealthList, } from 'Configs/financial-details-config'; +import { EMPLOYMENT_VALUES } from 'Constants/financial-details'; type TFinancialDetailsDropdownFieldProps = { dropdown_list: Array; field_key: string; placeholder?: string; label: string; + employment_status?: string; +}; + +type TFinancialInformationProps = { + employment_status?: string; }; /** @@ -27,6 +33,7 @@ type TFinancialDetailsDropdownFieldProps = { * @param {string} field_key - field reference of the field * @param {string} placeholder - placeholder of the field * @param {string} label - label of the field + * @param {string} employment_status - selected employment_status, * @returns {JSX.Element} */ const FinancialDetailsDropdownField = ({ @@ -49,13 +56,13 @@ const FinancialDetailsDropdownField = ({ is_align_text_left name={field.name} list={dropdown_list} - value={values[field_key]} + value={values?.[field_key]} onChange={handleChange} handleBlur={handleBlur} error={touched?.[field_key] && errors?.[field_key]} list_portal_id='modal_root' - {...field} required + {...field} /> @@ -70,8 +77,8 @@ const FinancialDetailsDropdownField = ({ handleChange(e); setFieldValue('field_key', e.target.value, true); }} - {...field} required + {...field} /> @@ -80,12 +87,71 @@ const FinancialDetailsDropdownField = ({ ); }; +const FinancialDetailsOccupationDropdownField = ({ + dropdown_list, + field_key, + placeholder = localize('Please select'), + label, + employment_status, +}: TFinancialDetailsDropdownFieldProps) => { + const { values, handleChange, handleBlur, touched, errors, setFieldValue } = useFormikContext<{ + [key: string]: string; + }>(); + + const getFormattedOccupationValues = () => + employment_status === EMPLOYMENT_VALUES.EMPLOYED && values?.occupation === EMPLOYMENT_VALUES.UNEMPLOYED + ? '' + : values?.occupation; + + return ( + + {({ field }: FormikValues) => ( + + + { + setFieldValue('field_key', getFormattedOccupationValues(), true); + handleChange(e); + }} + handleBlur={handleBlur} + error={touched?.[field_key] && errors?.[field_key]} + list_portal_id='modal_root' + required + /> + + + ) => { + setFieldValue('field_key', getFormattedOccupationValues(), true); + handleChange(e); + }} + required + /> + + + )} + + ); +}; /** * Wrapper for financial details form fields. * @name FinancialInformation * @returns {JSX.Element} */ -const FinancialInformation = () => { +const FinancialInformation = ({ employment_status }: TFinancialInformationProps) => { return ( { field_key='employment_industry' label={localize('Industry of employment')} /> - void) => void; validate: (values: TFinancialDetailsFormValues) => object; value: TFinancialDetailsFormValues; + employment_status: string; }; /** @@ -104,13 +106,19 @@ const FinancialDetails = (props: TFinancialDetails) => { 'financial-assessment__form' )} > - + [ }, ]; +export const getFormattedOccupationList = (employment_status?: string) => + employment_status && employment_status === EMPLOYMENT_VALUES.EMPLOYED + ? getOccupationList().filter(item => item.value !== EMPLOYMENT_VALUES.UNEMPLOYED) + : getOccupationList(); + export default financialDetailsConfig; diff --git a/packages/account/src/Constants/financial-details.ts b/packages/account/src/Constants/financial-details.ts new file mode 100644 index 000000000000..0b107d4652f7 --- /dev/null +++ b/packages/account/src/Constants/financial-details.ts @@ -0,0 +1,4 @@ +export const EMPLOYMENT_VALUES = { + EMPLOYED: 'Employed', + UNEMPLOYED: 'Unemployed', +}; diff --git a/packages/account/src/Sections/Assessment/FinancialAssessment/__tests__/financial-assessment.spec.tsx b/packages/account/src/Sections/Assessment/FinancialAssessment/__tests__/financial-assessment.spec.tsx new file mode 100644 index 000000000000..a4550777315c --- /dev/null +++ b/packages/account/src/Sections/Assessment/FinancialAssessment/__tests__/financial-assessment.spec.tsx @@ -0,0 +1,75 @@ +import React from 'react'; +import { render, screen, waitFor } from '@testing-library/react'; +import { BrowserRouter } from 'react-router-dom'; +import { StoreProvider, mockStore } from '@deriv/stores'; +import FinancialAssessment from '../financial-assessment'; + +jest.mock('@deriv/shared/src/services/ws-methods', () => ({ + __esModule: true, + default: 'mockedDefaultExport', + WS: { + wait: jest.fn(() => Promise.resolve()), + setSettings: jest.fn(() => Promise.resolve({ error: '' })), + authorized: { + storage: { + getFinancialAssessment: jest.fn(() => + Promise.resolve({ + get_financial_assessment: { + account_turnover: '', + cfd_score: 0, + education_level: '', + employment_industry: '', + employment_status: 'Employed', + estimated_worth: '', + financial_information_score: '', + income_source: '', + net_income: '', + occupation: '', + source_of_wealth: '', + total_score: '', + trading_score: '', + }, + }) + ), + }, + }, + }, + useWS: () => undefined, +})); +jest.mock('@deriv/components', () => { + const original_module = jest.requireActual('@deriv/components'); + return { + ...original_module, + Loading: jest.fn(() => 'mockedLoading'), + }; +}); +describe('', () => { + const mock = mockStore({}); + const rendercomponent = () => { + const wrapper = ({ children }: { children: JSX.Element }) => ( + {children} + ); + render( + + + , + { wrapper } + ); + }; + it('should render FinancialAssessment component', async () => { + rendercomponent(); + await waitFor(() => { + expect(screen.getByText('Financial information')).toBeInTheDocument(); + expect(screen.getByText('Source of income')).toBeInTheDocument(); + expect(screen.getByText('Employment status')).toBeInTheDocument(); + expect(screen.getByText('Industry of employment')).toBeInTheDocument(); + expect(screen.getByText('Occupation')).toBeInTheDocument(); + expect(screen.getByText('Source of wealth')).toBeInTheDocument(); + expect(screen.getByText('Level of education')).toBeInTheDocument(); + expect(screen.getByText('Net annual income')).toBeInTheDocument(); + expect(screen.getByText('Source of wealth')).toBeInTheDocument(); + expect(screen.getByText('Estimated net worth')).toBeInTheDocument(); + expect(screen.getByText('Anticipated account turnover')).toBeInTheDocument(); + }); + }); +}); diff --git a/packages/account/src/Sections/Assessment/FinancialAssessment/financial-assessment.tsx b/packages/account/src/Sections/Assessment/FinancialAssessment/financial-assessment.tsx index fb7902c9a9cb..31ebb7f62af5 100644 --- a/packages/account/src/Sections/Assessment/FinancialAssessment/financial-assessment.tsx +++ b/packages/account/src/Sections/Assessment/FinancialAssessment/financial-assessment.tsx @@ -33,7 +33,6 @@ import { getEstimatedWorthList, getIncomeSourceList, getNetIncomeList, - getOccupationList, getSourceOfWealthList, getBinaryOptionsTradingExperienceList, getBinaryOptionsTradingFrequencyList, @@ -46,6 +45,8 @@ import { } from './financial-information-list'; import type { TCoreStores } from '@deriv/stores/types'; import { GetFinancialAssessment, GetFinancialAssessmentResponse } from '@deriv/api-types'; +import { getFormattedOccupationList } from 'Configs/financial-details-config'; +import { EMPLOYMENT_VALUES } from 'Constants/financial-details'; type TConfirmationPage = { toggleModal: (prop: boolean) => void; @@ -342,6 +343,11 @@ const FinancialAssessment = observer(() => { return '80px'; }; + const getFormattedOccupationValues = values => + values?.employment_status === EMPLOYMENT_VALUES.EMPLOYED && values?.occupation === EMPLOYMENT_VALUES.UNEMPLOYED + ? '' + : values?.occupation; + if (is_loading) return ; if (api_initial_load_error) return ; if (is_virtual) return ; @@ -395,6 +401,7 @@ const FinancialAssessment = observer(() => { isSubmitting, setFieldTouched, dirty, + setFieldValue, }) => ( {!is_appstore && isMobile() && is_confirmation_visible && ( @@ -540,11 +547,19 @@ const FinancialAssessment = observer(() => { placeholder={localize('Occupation')} is_align_text_left name='occupation' - list={getOccupationList()} - value={values.occupation} - onChange={handleChange} + list={getFormattedOccupationList(values.employment_status ?? '')} + value={getFormattedOccupationValues(values)} + onChange={e => { + setFieldValue( + 'occupation', + getFormattedOccupationValues(values), + true + ); + handleChange(e); + }} handleBlur={handleBlur} error={touched.occupation && errors.occupation} + test_id='occupation' /> @@ -552,13 +567,21 @@ const FinancialAssessment = observer(() => { placeholder={localize('Please select')} name='occupation' label={localize('Occupation')} - list_items={getOccupationList()} - value={values.occupation} + list_items={getFormattedOccupationList( + values.employment_status ?? '' + )} + value={getFormattedOccupationValues(values)} error={touched.occupation ? errors.occupation : undefined} onChange={e => { + setFieldValue( + 'occupation', + getFormattedOccupationValues(values), + true + ); setFieldTouched('occupation', true); handleChange(e); }} + data_testid='occupation' /> @@ -1039,7 +1062,14 @@ const FinancialAssessment = observer(() => { })} onClick={() => onClickSubmit(handleSubmit)} is_disabled={ - isSubmitting || !dirty || is_btn_loading || Object.keys(errors).length > 0 + isSubmitting || + !dirty || + is_btn_loading || + Object.keys(errors).length > 0 || + !!( + values?.employment_status === EMPLOYMENT_VALUES.EMPLOYED && + values?.occupation === EMPLOYMENT_VALUES.UNEMPLOYED + ) } has_effect is_loading={is_btn_loading} diff --git a/packages/core/src/App/Containers/RealAccountSignup/account-wizard.jsx b/packages/core/src/App/Containers/RealAccountSignup/account-wizard.jsx index 0aae229ec628..a35c7136ff66 100644 --- a/packages/core/src/App/Containers/RealAccountSignup/account-wizard.jsx +++ b/packages/core/src/App/Containers/RealAccountSignup/account-wizard.jsx @@ -344,6 +344,8 @@ const AccountWizard = props => { if (!mounted) return null; if (!finished) { + const employment_status = + state_items.find(item => item.form_value.employment_status)?.form_value?.employment_status || ''; const wizard_steps = state_items.map((step, step_index) => { const passthrough = getPropsForChild(step_index); const BodyComponent = step.body; @@ -360,6 +362,7 @@ const AccountWizard = props => { form_error={form_error} {...passthrough} key={step_index} + employment_status={employment_status} /> ); });