From 0107135fb2c2f44f04eab224363867ef30943cb5 Mon Sep 17 00:00:00 2001 From: Likhith Kolayari <98398322+likhith-deriv@users.noreply.github.com> Date: Tue, 15 Aug 2023 15:48:40 +0400 Subject: [PATCH] likhith/chore: incorporated the API response to save the opt-out user data (#9618) * chore: incorporated the API response to save the opt-out user data * chore: incorporated the API response to save the opt-out user data * chore: incorporated the API response to save the opt-out user data * chore: incorporated the API response to save the opt-out user data * chore: incorporated review comments * chore: incorporated review comments * chore: incorporated review comments * fix: error displayed for MF account creation * Merge branch 'master' into likhith/KYC-362/track-idv-opt-out-during-account-creation --- .../idv-document-submit.tsx | 38 ++++++++--------- .../proof-of-identity-submission-for-mt5.jsx | 12 +----- .../account/src/Types/common-prop.type.ts | 19 +++++++++ .../__tests__/account-wizard.spec.tsx | 35 ++++++++++++++-- .../RealAccountSignup/account-wizard.jsx | 29 ++++++------- .../utils/config/__tests__/adapters.spec.ts | 33 +++++++++++++++ packages/shared/src/utils/config/adapters.ts | 41 +++++++++++++++++++ packages/shared/src/utils/config/index.ts | 1 + .../shared/src/utils/constants/idv-options.ts | 6 ++- 9 files changed, 163 insertions(+), 51 deletions(-) create mode 100644 packages/shared/src/utils/config/__tests__/adapters.spec.ts create mode 100644 packages/shared/src/utils/config/adapters.ts diff --git a/packages/account/src/Components/poi/idv-document-submit/idv-document-submit.tsx b/packages/account/src/Components/poi/idv-document-submit/idv-document-submit.tsx index 73a1a77afd62..c81e867290ea 100644 --- a/packages/account/src/Components/poi/idv-document-submit/idv-document-submit.tsx +++ b/packages/account/src/Components/poi/idv-document-submit/idv-document-submit.tsx @@ -10,6 +10,7 @@ import { filterObjProperties, isDesktop, removeEmptyPropertiesFromObject, + formatIDVFormValues, } from '@deriv/shared'; import { documentAdditionalError, getRegex, validate, makeSettingsRequest, validateName } from 'Helpers/utils'; import FormFooter from 'Components/form-footer'; @@ -17,10 +18,10 @@ import BackButtonIcon from 'Assets/ic-poi-back-btn.svg'; import IDVForm from 'Components/forms/idv-form'; import PersonalDetailsForm from 'Components/forms/personal-details-form'; import FormSubHeader from 'Components/form-sub-header'; -import { GetSettings, ResidenceList } from '@deriv/api-types'; -import { TIDVVerificationResponse, TDocumentList, TInputFieldValues } from 'Types'; +import { GetSettings, IdentityVerificationAddDocumentResponse, ResidenceList } from '@deriv/api-types'; +import { TIDVFormValues, TInputFieldValues, TDocumentList } from 'Types'; -type TIdvDocumentSubmit = { +type TIDVDocumentSubmitProps = { account_settings: GetSettings; getChangeableFields: () => Array; handleBack: React.MouseEventHandler; @@ -36,7 +37,7 @@ const IdvDocumentSubmit = ({ is_from_external, account_settings, getChangeableFields, -}: TIdvDocumentSubmit) => { +}: TIDVDocumentSubmitProps) => { const visible_settings = ['first_name', 'last_name', 'date_of_birth']; const form_initial_values = filterObjProperties(account_settings, visible_settings) || {}; @@ -94,8 +95,8 @@ const IdvDocumentSubmit = ({ return undefined; }; - const validateFields = values => { - const errors: TInputFieldValues = {}; + const validateFields = (values: TIDVFormValues) => { + const errors: Partial = {}; const { document_type, document_number, document_additional } = values; const needs_additional_document = !!document_type.additional; @@ -138,25 +139,22 @@ const IdvDocumentSubmit = ({ setSubmitting(false); return; } + const submit_data = { identity_verification_document_add: 1, - document_number: values.document_number, - document_additional: values.document_additional || '', - document_type: values.document_type.id, - issuing_country: selected_country.value, + ...formatIDVFormValues(values, selected_country.value), }; - if (submit_data.document_type === IDV_NOT_APPLICABLE_OPTION.id) { - return; - } - WS.send(submit_data).then((response: TIDVVerificationResponse) => { - setSubmitting(false); - if (response.error) { - setErrors({ error_message: response.error.message }); - return; + WS.send(submit_data).then( + (response: IdentityVerificationAddDocumentResponse & { error: { message: string } }) => { + setSubmitting(false); + if (response.error) { + setErrors({ error_message: response.error.message }); + return; + } + handleViewComplete(); } - handleViewComplete(); - }); + ); }; return ( diff --git a/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-submission-for-mt5.jsx b/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-submission-for-mt5.jsx index f724b2988c46..98c8c33a6953 100644 --- a/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-submission-for-mt5.jsx +++ b/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-submission-for-mt5.jsx @@ -1,6 +1,6 @@ /* eslint-disable react-hooks/exhaustive-deps */ import React from 'react'; -import { WS, isVerificationServiceSupported, IDV_NOT_APPLICABLE_OPTION } from '@deriv/shared'; +import { WS, isVerificationServiceSupported, formatIDVFormValues } from '@deriv/shared'; import Unsupported from 'Components/poi/status/unsupported'; import OnfidoUpload from './onfido-sdk-view-container'; import { identity_status_codes, submission_status_code, service_code } from './proof-of-identity-utils'; @@ -51,7 +51,6 @@ const POISubmissionForMT5 = ({ const handleIdvSubmit = async (values, { setSubmitting, setErrors }) => { setSubmitting(true); - const { document_number, document_type } = values; const request = makeSettingsRequest(values, [...getChangeableFields()]); @@ -72,16 +71,9 @@ const POISubmissionForMT5 = ({ const submit_data = { identity_verification_document_add: 1, - document_number, - document_type: document_type.id, - issuing_country: citizen_data.value, + ...formatIDVFormValues(values, citizen_data.value), }; - if (submit_data.document_type === IDV_NOT_APPLICABLE_OPTION.id) { - handlePOIComplete(); - return; - } - WS.send(submit_data).then(response => { setSubmitting(false); if (response.error) { diff --git a/packages/account/src/Types/common-prop.type.ts b/packages/account/src/Types/common-prop.type.ts index 24df6a3c94e6..5298105baa8b 100644 --- a/packages/account/src/Types/common-prop.type.ts +++ b/packages/account/src/Types/common-prop.type.ts @@ -182,3 +182,22 @@ export type TIDVForm = { export type TVerificationStatus = Readonly< Record<'none' | 'pending' | 'rejected' | 'verified' | 'expired' | 'suspected', string> >; + +type TDocumentList = Array<{ + id: string; + text: string; + value?: string; + sample_image?: string; + example_format?: string; + additional?: { + display_name: string; + format: string; + }; +}>; + +export type TIDVFormValues = { + document_type: TDocumentList[0]; + document_number: string; + document_additional?: string; + error_message?: string; +}; diff --git a/packages/core/src/App/Containers/RealAccountSignup/__tests__/account-wizard.spec.tsx b/packages/core/src/App/Containers/RealAccountSignup/__tests__/account-wizard.spec.tsx index f40e05d982f8..6b6057b203d9 100644 --- a/packages/core/src/App/Containers/RealAccountSignup/__tests__/account-wizard.spec.tsx +++ b/packages/core/src/App/Containers/RealAccountSignup/__tests__/account-wizard.spec.tsx @@ -1,5 +1,7 @@ import React from 'react'; -import { render, screen } from '@testing-library/react'; +import { WS } from '@deriv/shared'; +import { render, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import AccountWizard from '../account-wizard'; jest.mock('Stores/connect', () => ({ @@ -29,7 +31,21 @@ jest.mock('../account-wizard-form', () => ({ ]), })); -const Test = () =>
TestComponent
; +jest.mock('@deriv/shared', () => ({ + ...jest.requireActual('@deriv/shared'), + WS: { + send: jest.fn().mockResolvedValue({}), + }, +})); + +const mock_form_data = { name: 'Test', document_number: 'none', document_type: { id: 'none' } }; + +const Test = ({ onSubmit }) => ( +
+ TestComponent + +
+); jest.mock('../account-wizard-form', () => ({ getItems: jest.fn(() => [ @@ -66,13 +82,17 @@ describe('', () => { has_residence: true, is_virtual: true, real_account_signup_target: 'svg', - realAccountSignup: jest.fn(), + onFinishSuccess: jest.fn(), + realAccountSignup: jest.fn().mockResolvedValue({ new_account_real: { currency: 'USD' } }), + setIsRiskWarningVisible: jest.fn(), refreshNotifications: jest.fn(), + onError: jest.fn(), residence: 'id', setIsRealAccountSignupModalVisible: jest.fn(), setIsTradingAssessmentForNewUserEnabled: jest.fn(), setShouldShowAppropriatenessWarningModal: jest.fn(), setShouldShowRiskWarningModal: jest.fn(), + setRealAccountFormData: jest.fn(), upgrade_info: '', setSubSectionIndex: jest.fn(), sub_section_index: 0, @@ -185,4 +205,13 @@ describe('', () => { expect(mock_props.fetchStatesList).toBeCalledTimes(1); expect(screen.getByText('TestComponent')).toBeInTheDocument(); }); + + it('should invoke Create account and IDV data submission APIs on click of Submit button', async () => { + render(); + const ele_submit_btn = screen.getByRole('button', { name: 'Submit' }); + await waitFor(() => { + userEvent.click(ele_submit_btn); + }); + expect(WS.send).toHaveBeenCalled(); + }); }); diff --git a/packages/core/src/App/Containers/RealAccountSignup/account-wizard.jsx b/packages/core/src/App/Containers/RealAccountSignup/account-wizard.jsx index d50613d9ccef..016eb563667b 100644 --- a/packages/core/src/App/Containers/RealAccountSignup/account-wizard.jsx +++ b/packages/core/src/App/Containers/RealAccountSignup/account-wizard.jsx @@ -5,7 +5,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { DesktopWrapper, FormProgress, MobileWrapper, Text, Wizard } from '@deriv/components'; -import { WS, getLocation, toMoment, getIDVNotApplicableOption } from '@deriv/shared'; +import { WS, getLocation, toMoment, formatIDVFormValues, getIDVNotApplicableOption } from '@deriv/shared'; import { Localize } from '@deriv/translations'; import { connect } from 'Stores/connect'; import AcceptRiskForm from './accept-risk-form.jsx'; @@ -260,22 +260,11 @@ const AccountWizard = props => { return properties; }; - const submitIDVData = async (document_type, document_number, document_additional = '', country_code) => { - const idv_submit_data = { - identity_verification_document_add: 1, - document_number, - document_additional, - document_type: document_type.id, - issuing_country: country_code, - }; - await WS.send(idv_submit_data); - }; - const createRealAccount = (payload = undefined) => { setLoading(true); const form_data = { ...form_values() }; submitForm(payload) - .then(response => { + .then(async response => { props.setIsRiskWarningVisible(false); if (props.real_account_signup_target === 'maltainvest') { props.onFinishSuccess(response.new_account_maltainvest.currency.toLowerCase()); @@ -284,10 +273,16 @@ const AccountWizard = props => { } else { props.onFinishSuccess(response.new_account_real.currency.toLowerCase()); } - const { document_type, document_number, document_additional } = { ...form_values() }; - if (document_type && document_type.id !== IDV_NOT_APPLICABLE_OPTION.id && document_number) { - const country_code = props.account_settings.citizen || props.residence; - submitIDVData(document_type, document_number, document_additional, country_code); + const country_code = props.account_settings.citizen || props.residence; + /** + * If IDV details are present, then submit IDV details + */ + if (form_data.document_type) { + const idv_submit_data = { + identity_verification_document_add: 1, + ...formatIDVFormValues(form_data, country_code), + }; + await WS.send(idv_submit_data); } }) .catch(error => { diff --git a/packages/shared/src/utils/config/__tests__/adapters.spec.ts b/packages/shared/src/utils/config/__tests__/adapters.spec.ts new file mode 100644 index 000000000000..ffb37e01b3b6 --- /dev/null +++ b/packages/shared/src/utils/config/__tests__/adapters.spec.ts @@ -0,0 +1,33 @@ +import { formatIDVFormValues } from '../adapters'; + +describe('Adapter functions tests', () => { + it('should return the correct IDV form values', () => { + const form_data = { + document_type: { id: 'test' }, + document_additional: 'additional text', + document_number: '123456789', + }; + + expect(formatIDVFormValues(form_data, 'US')).toEqual({ + document_number: '123456789', + document_additional: 'additional text', + document_type: 'test', + issuing_country: 'US', + }); + }); + + it('should render the correct IDV form values when document type is not applicable', () => { + const form_data = { + document_type: { id: 'none' }, + document_additional: '', + document_number: '123456789', + }; + + expect(formatIDVFormValues(form_data, 'US')).toEqual({ + document_number: 'none', + document_additional: '', + document_type: 'none', + issuing_country: 'US', + }); + }); +}); diff --git a/packages/shared/src/utils/config/adapters.ts b/packages/shared/src/utils/config/adapters.ts new file mode 100644 index 000000000000..1ebf67cdcf21 --- /dev/null +++ b/packages/shared/src/utils/config/adapters.ts @@ -0,0 +1,41 @@ +import { getIDVNotApplicableOption } from '../constants/idv-options'; +import { FormikValues } from 'formik'; + +type TDocumentList = Array<{ + id: string; + text: string; + value?: string; + sample_image?: string; + example_format?: string; + additional?: { + display_name: string; + format: string; + }; +}>; + +type TIDVFormValues = { + document_type: TDocumentList[0]; + document_number: string; + document_additional?: string; + error_message?: string; +}; + +/** + * Formats the IDV form values to be sent to the API + * @param idv_form_value - Formik values of the IDV form + * @param country_code - Country code of the user + * @returns IDV form values + */ +export const formatIDVFormValues = (idv_form_value: FormikValues, country_code: string) => { + const IDV_NOT_APPLICABLE_OPTION = getIDVNotApplicableOption(); + const idv_submit_data = { + document_number: + idv_form_value.document_type.id === IDV_NOT_APPLICABLE_OPTION.id + ? IDV_NOT_APPLICABLE_OPTION.value + : idv_form_value.document_number, + document_additional: idv_form_value.document_additional, + document_type: idv_form_value.document_type.id, + issuing_country: country_code, + }; + return idv_submit_data; +}; diff --git a/packages/shared/src/utils/config/index.ts b/packages/shared/src/utils/config/index.ts index 4f0ea387c232..4dc0fbde9576 100644 --- a/packages/shared/src/utils/config/index.ts +++ b/packages/shared/src/utils/config/index.ts @@ -1,3 +1,4 @@ export * from './config'; export * from './app-config'; export * from './platform-config'; +export * from './adapters'; diff --git a/packages/shared/src/utils/constants/idv-options.ts b/packages/shared/src/utils/constants/idv-options.ts index 0d8d69e3d6c1..8edf847066a9 100644 --- a/packages/shared/src/utils/constants/idv-options.ts +++ b/packages/shared/src/utils/constants/idv-options.ts @@ -1,3 +1,7 @@ import { localize } from '@deriv/translations'; -export const getIDVNotApplicableOption = () => ({ id: '#NA', text: localize('I don’t have any of these') }); +export const getIDVNotApplicableOption = () => ({ + id: 'none', + text: localize('I want to do this later'), + value: 'none', +});