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

Suisin/upm921/routing from email verification link #98

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ describe('OTPVerification', () => {
expect(mockSendEmailOTPVerification).toBeCalledTimes(1);
});

it('should render setOtpVerification when is_email_verified is true', () => {
it('should render setOtpVerification and setVerificationCode when is_email_verified is true', () => {
store.ui.should_show_phone_number_otp = false;
(useSendOTPVerificationCode as jest.Mock).mockReturnValue({
is_email_verified: true,
Expand All @@ -169,6 +169,7 @@ describe('OTPVerification', () => {
userEvent.type(otp_textfield, '123456');
expect(verify_button).toBeEnabled();
userEvent.click(verify_button);
expect(store.client.setVerificationCode).toBeCalled();
expect(mockSetOtpVerification).toBeCalledWith({ phone_verification_type: '', show_otp_verification: false });
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,87 @@ import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import PhoneVerificationPage from '../phone-verification-page';
import { StoreProvider, mockStore } from '@deriv/stores';
import { useSendOTPVerificationCode } from '@deriv/hooks';

jest.mock('../otp-verification.tsx', () => jest.fn(() => <div>Confirm Your Email</div>));
jest.mock('../confirm-phone-number.tsx', () => jest.fn(() => <div>Confirm Phone Number</div>));
jest.mock('../cancel-phone-verification-modal', () => jest.fn(() => <div>Cancel Phone Verification Modal</div>));
jest.mock('../verification-link-expired-modal', () => jest.fn(() => <div>Verification Link Expired Modal</div>));
jest.mock('@deriv/hooks', () => ({
...jest.requireActual('@deriv/hooks'),
useSendOTPVerificationCode: jest.fn(() => ({
email_otp_error: undefined,
is_email_verified: false,
sendEmailOTPVerification: jest.fn(),
})),
}));
jest.mock('@deriv/components', () => ({
...jest.requireActual('@deriv/components'),
Loading: jest.fn(() => 'mockedLoading'),
}));

describe('ConfirmPhoneNumber', () => {
let mock_store_data = mockStore({});
const renderComponent = () => {
render(
<StoreProvider store={mock_store_data}>
<PhoneVerificationPage />
</StoreProvider>
);
};
beforeEach(() => {
mock_store_data = mockStore({
client: {
verification_code: {
phone_number_verification: '',
},
},
});
});

afterEach(() => {
jest.clearAllMocks();
});

it('should render ConfirmPhoneNumber', () => {
render(<PhoneVerificationPage />);
renderComponent();
expect(screen.getByText(/Phone number verification/)).toBeInTheDocument();
expect(screen.getByText(/Confirm Your Email/)).toBeInTheDocument();
});

it('should display cancel phone verification modal when back button is clicked', () => {
render(<PhoneVerificationPage />);
renderComponent();
const backButton = screen.getByTestId('dt_phone_verification_back_btn');
userEvent.click(backButton);
expect(screen.getByText(/Cancel Phone Verification Modal/)).toBeInTheDocument();
});

it('should display mockedLoading and render sendEmailOTPVerification when phone_number_verification has value', () => {
const mockSendEmailOTPVerification = jest.fn();
(useSendOTPVerificationCode as jest.Mock).mockReturnValue({
sendEmailOTPVerification: mockSendEmailOTPVerification,
});
mock_store_data.client.verification_code.phone_number_verification = '123456';
renderComponent();
expect(screen.getByText(/mockedLoading/)).toBeInTheDocument();
expect(mockSendEmailOTPVerification).toBeCalledTimes(1);
});

it('should display Verification Link Expired Modal when hook returns error', async () => {
(useSendOTPVerificationCode as jest.Mock).mockReturnValue({
email_otp_error: { code: 'InvalidToken', message: '' },
sendEmailOTPVerification: jest.fn(),
});
renderComponent();
expect(screen.getByText(/Verification Link Expired Modal/)).toBeInTheDocument();
});

it('should display Confirm Phone Number when is_email_verified is true', async () => {
(useSendOTPVerificationCode as jest.Mock).mockReturnValue({
is_email_verified: true,
});
renderComponent();
expect(screen.getByText(/Confirm Phone Number/)).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type TOTPVerification = {

const OTPVerification = observer(({ phone_verification_type, setOtpVerification }: TOTPVerification) => {
const { client, ui } = useStore();
const { account_settings, email } = client;
const { account_settings, email, setVerificationCode } = client;
const { phone } = account_settings;
const [should_show_phone_number_verified_modal, setShouldShowPhoneNumberVerifiedModal] = React.useState(false);
const [should_show_didnt_get_the_code_modal, setShouldShowDidntGetTheCodeModal] = React.useState(false);
Expand All @@ -39,7 +39,7 @@ const OTPVerification = observer(({ phone_verification_type, setOtpVerification
if (is_phone_number_verified) {
setShouldShowPhoneNumberVerifiedModal(true);
} else if (is_email_verified) {
localStorage.setItem('email_otp_code', otp);
setVerificationCode('phone_number_verification', otp);
setOtpVerification({ show_otp_verification: false, phone_verification_type: '' });
} else if (!should_show_phone_number_otp) {
send();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,51 @@ import ConfirmPhoneNumber from './confirm-phone-number';
import OTPVerification from './otp-verification';
import CancelPhoneVerificationModal from './cancel-phone-verification-modal';
import VerificationLinkExpiredModal from './verification-link-expired-modal';
import { observer, useStore } from '@deriv/stores';
import { useSendOTPVerificationCode } from '@deriv/hooks';
import { Loading } from '@deriv/components';
suisin-deriv marked this conversation as resolved.
Show resolved Hide resolved

const PhoneVerificationPage = () => {
const PhoneVerificationPage = observer(() => {
const [otp_verification, setOtpVerification] = React.useState({
show_otp_verification: true,
phone_verification_type: '',
});
const [is_loading, setIsLoading] = React.useState(false);
const [should_show_cancel_verification_modal, setShouldShowCancelVerificationModal] = React.useState(false);
const [should_show_verification_link_expired_modal, setShouldShowVerificationLinkExpiredModal] =
React.useState(false);
const handleBackButton = () => {
setShouldShowCancelVerificationModal(true);
};
const { sendEmailOTPVerification, email_otp_error, is_email_verified } = useSendOTPVerificationCode();

const { client } = useStore();
const {
verification_code: { phone_number_verification },
suisin-deriv marked this conversation as resolved.
Show resolved Hide resolved
} = client;

React.useEffect(() => {
setIsLoading(false);
suisin-deriv marked this conversation as resolved.
Show resolved Hide resolved
if (email_otp_error) {
setShouldShowVerificationLinkExpiredModal(true);
} else if (is_email_verified) {
setOtpVerification({
show_otp_verification: false,
phone_verification_type: '',
});
}
}, [email_otp_error, is_email_verified]);

React.useEffect(() => {
if (phone_number_verification) {
setIsLoading(true);
sendEmailOTPVerification(phone_number_verification);
}
}, [phone_number_verification]);

suisin-deriv marked this conversation as resolved.
Show resolved Hide resolved
if (is_loading) {
return <Loading is_fullscreen={false} />;
}

return (
<div>
Expand Down Expand Up @@ -52,6 +85,6 @@ const PhoneVerificationPage = () => {
)}
</div>
);
};
});

export default PhoneVerificationPage;
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Localize } from '@deriv/translations';
import { useHistory } from 'react-router';
import { observer, useStore } from '@deriv/stores';
import { LabelPairedCircleXmarkLgRegularIcon } from '@deriv/quill-icons';
import { useVerifyEmail } from '@deriv/hooks';

type TVerificationLinkExpiredModal = {
should_show_verification_link_expired_modal: boolean;
Expand All @@ -20,6 +21,11 @@ const VerificationLinkExpiredModal = observer(
setShouldShowVerificationLinkExpiredModal(false);
history.goBack();
};
const { send } = useVerifyEmail('phone_number_verification');
const handleSendNewLinkButton = () => {
send();
setShouldShowVerificationLinkExpiredModal(false);
};
const { ui } = useStore();
const { is_mobile } = ui;

Expand All @@ -28,7 +34,7 @@ const VerificationLinkExpiredModal = observer(
isMobile={is_mobile}
showHandleBar
isOpened={should_show_verification_link_expired_modal}
primaryButtonCallback={() => setShouldShowVerificationLinkExpiredModal(false)}
primaryButtonCallback={handleSendNewLinkButton}
primaryButtonLabel={<Localize i18n_default_text='Send new link' />}
disableCloseOnOverlay
showSecondaryButton
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/App/Containers/Redirect/redirect.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ const Redirect = observer(() => {
setResetTradingPasswordModalOpen(true);
break;
}
case 'phone_number_verification': {
history.push(routes.phone_verification);
redirected_to_route = true;
break;
}
case 'payment_deposit': {
if (has_wallet) {
history.push(routes.wallets_deposit);
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/Stores/client-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ export default class ClientStore extends BaseStore {
reset_password: '',
payment_withdraw: '',
payment_agent_withdraw: '',
phone_number_verification: '',
trading_platform_mt5_password_reset: '',
trading_platform_dxtrade_password_reset: '',
request_email: '',
Expand Down
50 changes: 43 additions & 7 deletions packages/hooks/src/__tests__/useRequestPhoneNumberOTP.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import useRequestPhoneNumberOTP from '../useRequestPhoneNumberOTP';
import { VERIFICATION_SERVICES } from '@deriv/shared';
import React from 'react';
import useSettings from '../useSettings';
import { StoreProvider, mockStore } from '@deriv/stores';

jest.mock('@deriv/api', () => ({
...jest.requireActual('@deriv/api'),
Expand All @@ -26,6 +27,10 @@ const mock_set_settings_response = {
isSuccess: true,
};

const mock_store_data = mockStore({
client: { verification_code: { phone_number_verification: '' } },
});

const mock_set_settings = jest.fn().mockResolvedValue(mock_set_settings_response);

describe('useRequestPhoneNumberOTP', () => {
Expand All @@ -34,10 +39,13 @@ describe('useRequestPhoneNumberOTP', () => {
mutation: { mutateAsync: mock_set_settings },
});
});
const wrapper = ({ children }: { children: JSX.Element }) => (
<StoreProvider store={mock_store_data}>{children}</StoreProvider>
);

it('should call mutate with correct payload for SMS request and return correct response', () => {
(useMutation as jest.Mock).mockReturnValueOnce(mock_response);
const { result } = renderHook(() => useRequestPhoneNumberOTP());
const { result } = renderHook(() => useRequestPhoneNumberOTP(), { wrapper });

result.current.requestOnSMS();

Expand All @@ -50,7 +58,7 @@ describe('useRequestPhoneNumberOTP', () => {

it('should call mutate with correct payload for WhatsApp request and return correct response', () => {
(useMutation as jest.Mock).mockReturnValueOnce(mock_response);
const { result } = renderHook(() => useRequestPhoneNumberOTP());
const { result } = renderHook(() => useRequestPhoneNumberOTP(), { wrapper });

result.current.requestOnWhatsApp();

Expand All @@ -61,9 +69,37 @@ describe('useRequestPhoneNumberOTP', () => {
expect(result.current.data).toEqual(1);
});

it('should call mutate with code given in phone_number_verification for SMS request and return correct response', () => {
mock_store_data.client.verification_code.phone_number_verification = '121212';
(useMutation as jest.Mock).mockReturnValueOnce(mock_response);
const { result } = renderHook(() => useRequestPhoneNumberOTP(), { wrapper });

result.current.requestOnSMS();

expect(useMutation).toHaveBeenCalledWith('phone_number_challenge');
expect(result.current.mutate).toHaveBeenCalledWith({
payload: { carrier: VERIFICATION_SERVICES.SMS, email_code: '121212' },
});
expect(result.current.data).toEqual(1);
});

it('should call mutate with code given in phone_number_verification for WhatsApp request and return correct response', () => {
mock_store_data.client.verification_code.phone_number_verification = '121212';
(useMutation as jest.Mock).mockReturnValueOnce(mock_response);
const { result } = renderHook(() => useRequestPhoneNumberOTP(), { wrapper });

result.current.requestOnWhatsApp();

expect(useMutation).toHaveBeenCalledWith('phone_number_challenge');
expect(result.current.mutate).toHaveBeenCalledWith({
payload: { carrier: VERIFICATION_SERVICES.WHATSAPP, email_code: '121212' },
});
expect(result.current.data).toEqual(1);
});

it('should call mutate with correct payload for sendEmailOTPVerification request and return correct response', () => {
(useMutation as jest.Mock).mockReturnValueOnce(mock_response);
const { result } = renderHook(() => useRequestPhoneNumberOTP());
const { result } = renderHook(() => useRequestPhoneNumberOTP(), { wrapper });

result.current.sendEmailOTPVerification('123456');

Expand All @@ -76,7 +112,7 @@ describe('useRequestPhoneNumberOTP', () => {

it('should return Localized error message when PhoneNumberTaken error code is passed inside', () => {
(useMutation as jest.Mock).mockReturnValue(mock_response);
const { result } = renderHook(() => useRequestPhoneNumberOTP());
const { result } = renderHook(() => useRequestPhoneNumberOTP(), { wrapper });

act(() => {
result.current.formatError({ code: 'PhoneNumberTaken', message: '' });
Expand All @@ -89,7 +125,7 @@ describe('useRequestPhoneNumberOTP', () => {
});

it('should return given error message when Other error code is passed inside', () => {
const { result } = renderHook(() => useRequestPhoneNumberOTP());
const { result } = renderHook(() => useRequestPhoneNumberOTP(), { wrapper });

act(() => {
result.current.formatError({ code: 'OtherError', message: 'This is an error message' });
Expand All @@ -99,7 +135,7 @@ describe('useRequestPhoneNumberOTP', () => {
});

it('should call setSettings with correct payload and handle success response', async () => {
const { result } = renderHook(() => useRequestPhoneNumberOTP());
const { result } = renderHook(() => useRequestPhoneNumberOTP(), { wrapper });

await act(async () => {
const response = await result.current.setUsersPhoneNumber({ phone: '+1234567890' });
Expand All @@ -118,7 +154,7 @@ describe('useRequestPhoneNumberOTP', () => {
mutation: { mutateAsync: jest.fn().mockRejectedValue(mock_set_settings_error_response) },
});

const { result } = renderHook(() => useRequestPhoneNumberOTP());
const { result } = renderHook(() => useRequestPhoneNumberOTP(), { wrapper });

await act(async () => {
const response = await result.current.setUsersPhoneNumber({ phone: '+1234567890' });
Expand Down
11 changes: 9 additions & 2 deletions packages/hooks/src/useRequestPhoneNumberOTP.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useMutation } from '@deriv/api';
import { VERIFICATION_SERVICES } from '@deriv/shared';
import { Localize } from '@deriv/translations';
import useSettings from './useSettings';
import { useStore } from '@deriv/stores';

type TFormatError = {
code: string;
Expand All @@ -19,22 +20,28 @@ const useRequestPhoneNumberOTP = () => {
...rest
} = useMutation('phone_number_challenge');
const [error_message, setErrorMessage] = React.useState<React.ReactNode>('');
const { client } = useStore();
const { verification_code } = client;
const { phone_number_verification } = verification_code;
const {
mutation: { mutateAsync: updateSettings },
} = useSettings();

//TODOs: need to wait confirmation from the team whether to stay at phone number page when refresh or restart the email verification process again
const requestOnSMS = () => {
mutate({
payload: { carrier: VERIFICATION_SERVICES.SMS, email_code: localStorage.getItem('email_otp_code') || '' },
payload: {
carrier: VERIFICATION_SERVICES.SMS,
email_code: phone_number_verification || '',
},
});
};
//TODOs: need to wait confirmation from the team whether to stay at phone number page when refresh or restart the email verification process again
const requestOnWhatsApp = () => {
mutate({
payload: {
carrier: VERIFICATION_SERVICES.WHATSAPP,
email_code: localStorage.getItem('email_otp_code') || '',
email_code: phone_number_verification || '',
},
});
};
Expand Down
1 change: 1 addition & 0 deletions packages/hooks/src/useSendOTPVerificationCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ const useSendOTPVerificationCode = () => {
data,
sendPhoneOTPVerification,
sendEmailOTPVerification,
email_otp_error,
phone_otp_error,
phone_otp_error_message,
setPhoneOtpErrorMessage,
Expand Down
Loading
Loading