diff --git a/packages/api-v2/src/hooks/useCurrencyConfig.ts b/packages/api-v2/src/hooks/useCurrencyConfig.ts index 2901ec1c1031..840adedd764f 100644 --- a/packages/api-v2/src/hooks/useCurrencyConfig.ts +++ b/packages/api-v2/src/hooks/useCurrencyConfig.ts @@ -5,12 +5,16 @@ import useAuthorize from './useAuthorize'; /** A custom hook to get the currency config information from `website_status` endpoint and `crypto_config` endpoint. */ const useCurrencyConfig = () => { const { isSuccess } = useAuthorize(); - const { data: website_status_data, ...rest } = useQuery('website_status', { + const { + data: website_status_data, + isLoading: isWebsiteStatusLoading, + ...rest + } = useQuery('website_status', { options: { enabled: isSuccess, }, }); - const { data: crypto_config_data } = useQuery('crypto_config', { + const { data: crypto_config_data, isLoading: isCryptConfigLoading } = useQuery('crypto_config', { options: { enabled: isSuccess, }, @@ -103,6 +107,7 @@ const useCurrencyConfig = () => { data: transformed_currencies_config, /** Returns the currency config object for the given currency */ getConfig, + isLoading: isWebsiteStatusLoading || isCryptConfigLoading, ...rest, }; }; diff --git a/packages/wallets/src/features/cashier/modules/WithdrawalLocked/WithdrawalLocked.tsx b/packages/wallets/src/features/cashier/modules/WithdrawalLocked/WithdrawalLocked.tsx index 09878769ce16..e96dd1c40849 100644 --- a/packages/wallets/src/features/cashier/modules/WithdrawalLocked/WithdrawalLocked.tsx +++ b/packages/wallets/src/features/cashier/modules/WithdrawalLocked/WithdrawalLocked.tsx @@ -6,9 +6,10 @@ import { useActiveWalletAccount, useAuthentication, useCashierValidation, + useCurrencyConfig, } from '@deriv/api-v2'; -import { WalletsActionScreen } from '../../../../components'; -import getWithdrawalLockedDesc from './WithdrawalLockedContent'; +import { Loader, WalletsActionScreen } from '../../../../components'; +import getWithdrawalLockedDesc, { getWithdrawalLimitReachedDesc } from './WithdrawalLockedContent'; import './WithdrawalLocked.scss'; const WithdrawalLocked: React.FC = ({ children }) => { @@ -17,6 +18,7 @@ const WithdrawalLocked: React.FC = ({ children }) => { const { data: cashierValidation } = useCashierValidation(); const { data: accountLimits } = useAccountLimits(); const { data: status } = useAccountStatus(); + const { isLoading: isCurrencyConfigLoading } = useCurrencyConfig(); const currency = activeWallet?.currency || 'USD'; @@ -35,13 +37,43 @@ const WithdrawalLocked: React.FC = ({ children }) => { const isWithdrawalLocked = status?.is_withdrawal_locked; const remainder = accountLimits?.remainder; - const minimumWithdrawal = activeWallet?.currency_config?.minimum_withdrawal; + const minimumWithdrawal = activeWallet?.currency_config?.is_crypto + ? activeWallet?.currency_config?.minimum_withdrawal + : 0.01; const withdrawalLimitReached = !!( typeof remainder !== 'undefined' && typeof minimumWithdrawal !== 'undefined' && +remainder < minimumWithdrawal ); + if (isCurrencyConfigLoading) { + return ; + } + + if (withdrawalLimitReached) { + return ( +
+ + } + /> +
+ ); + } + if (isWithdrawalLocked) { return (
@@ -49,15 +81,9 @@ const WithdrawalLocked: React.FC = ({ children }) => { description={ getWithdrawalLockedDesc({ askAuthenticate, - askFinancialRiskApproval, askFixDetails, financialAssessmentRequired, noWithdrawalOrTradingStatus, - poaNeedsVerification, - poaStatus, - poiNeedsVerification, - poiStatus, - withdrawalLimitReached, withdrawalLockedStatus, })?.description } diff --git a/packages/wallets/src/features/cashier/modules/WithdrawalLocked/WithdrawalLockedContent.tsx b/packages/wallets/src/features/cashier/modules/WithdrawalLocked/WithdrawalLockedContent.tsx index 749ee06fc0f4..e31c6e0a2854 100644 --- a/packages/wallets/src/features/cashier/modules/WithdrawalLocked/WithdrawalLockedContent.tsx +++ b/packages/wallets/src/features/cashier/modules/WithdrawalLocked/WithdrawalLockedContent.tsx @@ -2,34 +2,30 @@ import React from 'react'; import { Trans } from 'react-i18next'; import { WalletLink, WalletText } from '../../../../components'; -type TWithdrawalLockedDescProps = { - askAuthenticate?: boolean; +type TWithdrawalLimitReachedDescProps = { askFinancialRiskApproval?: boolean; - askFixDetails?: boolean; - financialAssessmentRequired?: boolean; - noWithdrawalOrTradingStatus?: boolean; poaNeedsVerification?: boolean; poaStatus: string; poiNeedsVerification?: boolean; poiStatus: string; - withdrawalLimitReached: boolean; +}; + +type TWithdrawalLockedDescProps = { + askAuthenticate?: boolean; + askFixDetails?: boolean; + financialAssessmentRequired?: boolean; + noWithdrawalOrTradingStatus?: boolean; withdrawalLockedStatus?: boolean; }; -const getWithdrawalLockedDesc = ({ - askAuthenticate, +export const getWithdrawalLimitReachedDesc = ({ askFinancialRiskApproval, - askFixDetails, - financialAssessmentRequired, - noWithdrawalOrTradingStatus, poaNeedsVerification, poaStatus, poiNeedsVerification, poiStatus, - withdrawalLimitReached, - withdrawalLockedStatus, -}: TWithdrawalLockedDescProps) => { - if (withdrawalLimitReached && poiNeedsVerification && poiStatus === 'none') +}: TWithdrawalLimitReachedDescProps) => { + if (poiNeedsVerification && poiStatus === 'none') return { description: ( @@ -41,7 +37,7 @@ const getWithdrawalLockedDesc = ({ ), }; - if (withdrawalLimitReached && poiNeedsVerification && poiStatus !== 'verified' && poiStatus !== 'none') + if (poiNeedsVerification && poiStatus !== 'verified' && poiStatus !== 'none') return { description: ( @@ -53,7 +49,7 @@ const getWithdrawalLockedDesc = ({ ), }; - if (withdrawalLimitReached && poaNeedsVerification && poaStatus === 'none') + if (poaNeedsVerification && poaStatus === 'none') return { description: ( @@ -65,7 +61,7 @@ const getWithdrawalLockedDesc = ({ ), }; - if (withdrawalLimitReached && poaNeedsVerification && poaStatus !== 'verified' && poaStatus !== 'none') + if (poaNeedsVerification && poaStatus !== 'verified' && poaStatus !== 'none') return { description: ( @@ -77,7 +73,7 @@ const getWithdrawalLockedDesc = ({ ), }; - if (withdrawalLimitReached && askFinancialRiskApproval) + if (askFinancialRiskApproval) return { description: ( @@ -88,7 +84,15 @@ const getWithdrawalLockedDesc = ({ ), }; +}; +const getWithdrawalLockedDesc = ({ + askAuthenticate, + askFixDetails, + financialAssessmentRequired, + noWithdrawalOrTradingStatus, + withdrawalLockedStatus, +}: TWithdrawalLockedDescProps) => { if (financialAssessmentRequired) return { description: ( diff --git a/packages/wallets/src/features/cashier/modules/WithdrawalLocked/__tests__/WithdrawalLocked.spec.tsx b/packages/wallets/src/features/cashier/modules/WithdrawalLocked/__tests__/WithdrawalLocked.spec.tsx index 8505c82a30d3..bce28272e3b6 100644 --- a/packages/wallets/src/features/cashier/modules/WithdrawalLocked/__tests__/WithdrawalLocked.spec.tsx +++ b/packages/wallets/src/features/cashier/modules/WithdrawalLocked/__tests__/WithdrawalLocked.spec.tsx @@ -5,10 +5,10 @@ import { useActiveWalletAccount, useAuthentication, useCashierValidation, + useCurrencyConfig, } from '@deriv/api-v2'; import { render, screen } from '@testing-library/react'; import WithdrawalLocked from '../WithdrawalLocked'; -import getWithdrawalLockedDesc from '../WithdrawalLockedContent'; jest.mock('@deriv/api-v2', () => ({ useAccountLimits: jest.fn(), @@ -16,15 +16,21 @@ jest.mock('@deriv/api-v2', () => ({ useActiveWalletAccount: jest.fn(), useAuthentication: jest.fn(), useCashierValidation: jest.fn(), + useCurrencyConfig: jest.fn(), +})); + +jest.mock('../../../../../components', () => ({ + ...jest.requireActual('../../../../../components'), + Loader: jest.fn(() =>
Loading...
), })); jest.mock('../WithdrawalLockedContent', () => ({ __esModule: true, - default: jest.fn(), + default: jest.fn(() => ({ description:
Locked Description
})), + getWithdrawalLimitReachedDesc: jest.fn(() => ({ description:
Locked Description
})), })); -const mockActiveWalletData = { currency_config: { minimum_withdrawal: 10 } }; -const mockAccountLimitsData = { remainder: 20 }; +const mockActiveWalletData = { currency: 'USD', currency_config: { is_crypto: false, minimum_withdrawal: 10 } }; const mockAuthenticationData = { is_poa_needed: false, is_poi_needed: false, @@ -37,24 +43,65 @@ const mockCashierValidationData = { no_withdrawal_or_trading_status: false, withdrawal_locked_status: false, }; -const mockStatusData = { is_withdrawal_locked: false }; describe('WithdrawalLocked', () => { - afterEach(() => { + beforeEach(() => { jest.clearAllMocks(); }); - it('should render locked screen when in a locked state', () => { + it('should render loader when in a loading state', () => { + const mockStatusData = { is_withdrawal_locked: false }; + const mockAccountLimitsData = { remainder: 20 }; + + (useActiveWalletAccount as jest.Mock).mockReturnValueOnce({ data: mockActiveWalletData }); + (useAccountLimits as jest.Mock).mockReturnValueOnce({ data: mockAccountLimitsData }); + (useAuthentication as jest.Mock).mockReturnValueOnce({ data: mockAuthenticationData }); + (useCashierValidation as jest.Mock).mockReturnValueOnce({ data: mockCashierValidationData }); + (useAccountStatus as jest.Mock).mockReturnValueOnce({ data: mockStatusData }); + (useCurrencyConfig as jest.Mock).mockReturnValueOnce({ isLoading: true }); + + render( + +
Test Child Component
+
+ ); + + expect(screen.queryByText('Test Child Component')).not.toBeInTheDocument(); + expect(screen.getByText('Loading...')).toBeInTheDocument(); + }); + + it('should render locked screen for withdrawal locked when in a locked state', () => { const mockLockedStatusData = { is_withdrawal_locked: true }; + const mockAccountLimitsData = { remainder: 20 }; (useActiveWalletAccount as jest.Mock).mockReturnValueOnce({ data: mockActiveWalletData }); (useAccountLimits as jest.Mock).mockReturnValueOnce({ data: mockAccountLimitsData }); (useAuthentication as jest.Mock).mockReturnValueOnce({ data: mockAuthenticationData }); (useCashierValidation as jest.Mock).mockReturnValueOnce({ data: mockCashierValidationData }); (useAccountStatus as jest.Mock).mockReturnValueOnce({ data: mockLockedStatusData }); + (useCurrencyConfig as jest.Mock).mockReturnValueOnce({ isLoading: false }); - const mockLockedState = { description: 'Locked Description' }; - (getWithdrawalLockedDesc as jest.Mock).mockReturnValueOnce(mockLockedState); + render( + +
Test Child Component
+
+ ); + + expect(screen.queryByText('Test Child Component')).not.toBeInTheDocument(); + expect(screen.getByText('Locked Description')).toBeInTheDocument(); + expect(screen.getByText('Withdrawals from your USD Wallet are temporarily locked.')).toBeInTheDocument(); + }); + + it('should render locked screen for withdrawal limit reached when in a locked state', () => { + const mockStatusData = { is_withdrawal_locked: false }; + const mockLockedAccountLimitsData = { remainder: 0 }; + + (useActiveWalletAccount as jest.Mock).mockReturnValueOnce({ data: mockActiveWalletData }); + (useAccountLimits as jest.Mock).mockReturnValueOnce({ data: mockLockedAccountLimitsData }); + (useAuthentication as jest.Mock).mockReturnValueOnce({ data: mockAuthenticationData }); + (useCashierValidation as jest.Mock).mockReturnValueOnce({ data: mockCashierValidationData }); + (useAccountStatus as jest.Mock).mockReturnValueOnce({ data: mockStatusData }); + (useCurrencyConfig as jest.Mock).mockReturnValueOnce({ isLoading: false }); render( @@ -68,13 +115,15 @@ describe('WithdrawalLocked', () => { }); it('should render children when not in a locked state', () => { + const mockStatusData = { is_withdrawal_locked: false }; + const mockAccountLimitsData = { remainder: 20 }; + (useActiveWalletAccount as jest.Mock).mockReturnValueOnce({ data: mockActiveWalletData }); (useAccountLimits as jest.Mock).mockReturnValueOnce({ data: mockAccountLimitsData }); (useAuthentication as jest.Mock).mockReturnValueOnce({ data: mockAuthenticationData }); (useCashierValidation as jest.Mock).mockReturnValueOnce({ data: mockCashierValidationData }); (useAccountStatus as jest.Mock).mockReturnValueOnce({ data: mockStatusData }); - - (getWithdrawalLockedDesc as jest.Mock).mockReturnValueOnce(null); + (useCurrencyConfig as jest.Mock).mockReturnValueOnce({ isLoading: false }); render( diff --git a/packages/wallets/src/features/cashier/modules/WithdrawalLocked/__tests__/WithdrawalLockedContent.spec.tsx b/packages/wallets/src/features/cashier/modules/WithdrawalLocked/__tests__/WithdrawalLockedContent.spec.tsx index 4b9330ecc823..2fde27da217d 100644 --- a/packages/wallets/src/features/cashier/modules/WithdrawalLocked/__tests__/WithdrawalLockedContent.spec.tsx +++ b/packages/wallets/src/features/cashier/modules/WithdrawalLocked/__tests__/WithdrawalLockedContent.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react'; -import getWithdrawalLockedDesc from '../WithdrawalLockedContent'; +import getWithdrawalLockedDesc, { getWithdrawalLimitReachedDesc } from '../WithdrawalLockedContent'; window.LC_API = { on_chat_ended: jest.fn(), @@ -7,37 +7,37 @@ window.LC_API = { }; describe('WithdrawalLockedContent', () => { - it('should render title and description as undefined when withdrawal is not locked', () => { - const result = getWithdrawalLockedDesc({ - askAuthenticate: false, + it('should render title and description as undefined when withdrawal limit is not reached', () => { + const result = getWithdrawalLimitReachedDesc({ askFinancialRiskApproval: false, - askFixDetails: false, - financialAssessmentRequired: false, - noWithdrawalOrTradingStatus: false, poaNeedsVerification: false, poaStatus: 'none', poiNeedsVerification: false, poiStatus: 'none', - withdrawalLimitReached: false, - withdrawalLockedStatus: false, }); expect(result?.description).toBeUndefined(); }); - it('should render correct message when withdrawal limit is reached and POI has not been uploaded', () => { + it('should render title and description as undefined when withdrawal is not locked', () => { const result = getWithdrawalLockedDesc({ askAuthenticate: false, - askFinancialRiskApproval: false, askFixDetails: false, financialAssessmentRequired: false, noWithdrawalOrTradingStatus: false, + withdrawalLockedStatus: false, + }); + + expect(result?.description).toBeUndefined(); + }); + + it('should render correct message when withdrawal limit is reached and POI has not been uploaded', () => { + const result = getWithdrawalLimitReachedDesc({ + askFinancialRiskApproval: false, poaNeedsVerification: false, poaStatus: 'none', poiNeedsVerification: true, poiStatus: 'none', - withdrawalLimitReached: true, - withdrawalLockedStatus: false, }); if (result) render(result.description); @@ -46,18 +46,12 @@ describe('WithdrawalLockedContent', () => { }); it('should render correct message when withdrawal limit is reached and POI has been uploaded but not yet verified', () => { - const result = getWithdrawalLockedDesc({ - askAuthenticate: false, + const result = getWithdrawalLimitReachedDesc({ askFinancialRiskApproval: false, - askFixDetails: false, - financialAssessmentRequired: false, - noWithdrawalOrTradingStatus: false, poaNeedsVerification: false, poaStatus: 'none', poiNeedsVerification: true, poiStatus: 'pending', - withdrawalLimitReached: true, - withdrawalLockedStatus: false, }); if (result) render(result.description); @@ -66,18 +60,12 @@ describe('WithdrawalLockedContent', () => { }); it('should render correct message when withdrawal limit is reached and POA has not been uploaded', () => { - const result = getWithdrawalLockedDesc({ - askAuthenticate: false, + const result = getWithdrawalLimitReachedDesc({ askFinancialRiskApproval: false, - askFixDetails: false, - financialAssessmentRequired: false, - noWithdrawalOrTradingStatus: false, poaNeedsVerification: true, poaStatus: 'none', poiNeedsVerification: false, poiStatus: 'none', - withdrawalLimitReached: true, - withdrawalLockedStatus: false, }); if (result) render(result.description); @@ -86,18 +74,12 @@ describe('WithdrawalLockedContent', () => { }); it('should render correct message when withdrawal limit is reached and POA has been uploaded but not yet verified', () => { - const result = getWithdrawalLockedDesc({ - askAuthenticate: false, + const result = getWithdrawalLimitReachedDesc({ askFinancialRiskApproval: false, - askFixDetails: false, - financialAssessmentRequired: false, - noWithdrawalOrTradingStatus: false, poaNeedsVerification: true, poaStatus: 'pending', poiNeedsVerification: false, poiStatus: 'none', - withdrawalLimitReached: true, - withdrawalLockedStatus: false, }); if (result) render(result.description); @@ -106,18 +88,12 @@ describe('WithdrawalLockedContent', () => { }); it('should render correct message when withdrawal limit is reached and askFinancialRiskApproval status received', () => { - const result = getWithdrawalLockedDesc({ - askAuthenticate: false, + const result = getWithdrawalLimitReachedDesc({ askFinancialRiskApproval: true, - askFixDetails: false, - financialAssessmentRequired: false, - noWithdrawalOrTradingStatus: false, poaNeedsVerification: false, poaStatus: 'none', poiNeedsVerification: false, poiStatus: 'none', - withdrawalLimitReached: true, - withdrawalLockedStatus: false, }); if (result) render(result.description); @@ -128,15 +104,9 @@ describe('WithdrawalLockedContent', () => { it('should render correct message when financialAssessmentRequired status received', () => { const result = getWithdrawalLockedDesc({ askAuthenticate: false, - askFinancialRiskApproval: false, askFixDetails: false, financialAssessmentRequired: true, noWithdrawalOrTradingStatus: false, - poaNeedsVerification: false, - poaStatus: 'none', - poiNeedsVerification: false, - poiStatus: 'none', - withdrawalLimitReached: false, withdrawalLockedStatus: false, }); @@ -148,15 +118,9 @@ describe('WithdrawalLockedContent', () => { it('should render correct message when askAuthenticate status received', () => { const result = getWithdrawalLockedDesc({ askAuthenticate: true, - askFinancialRiskApproval: false, askFixDetails: false, financialAssessmentRequired: false, noWithdrawalOrTradingStatus: false, - poaNeedsVerification: false, - poaStatus: 'none', - poiNeedsVerification: false, - poiStatus: 'none', - withdrawalLimitReached: false, withdrawalLockedStatus: false, }); @@ -169,15 +133,9 @@ describe('WithdrawalLockedContent', () => { it('should render correct message when askFixDetails status received', () => { const result = getWithdrawalLockedDesc({ askAuthenticate: false, - askFinancialRiskApproval: false, askFixDetails: true, financialAssessmentRequired: false, noWithdrawalOrTradingStatus: false, - poaNeedsVerification: false, - poaStatus: 'none', - poiNeedsVerification: false, - poiStatus: 'none', - withdrawalLimitReached: false, withdrawalLockedStatus: false, }); @@ -193,15 +151,9 @@ describe('WithdrawalLockedContent', () => { it('should render correct message when noWithdrawalOrTradingStatus status received', () => { const result = getWithdrawalLockedDesc({ askAuthenticate: false, - askFinancialRiskApproval: false, askFixDetails: false, financialAssessmentRequired: false, noWithdrawalOrTradingStatus: true, - poaNeedsVerification: false, - poaStatus: 'none', - poiNeedsVerification: false, - poiStatus: 'none', - withdrawalLimitReached: false, withdrawalLockedStatus: false, }); @@ -216,15 +168,9 @@ describe('WithdrawalLockedContent', () => { it('should render correct message when withdrawalLockedStatus status received', () => { const result = getWithdrawalLockedDesc({ askAuthenticate: false, - askFinancialRiskApproval: false, askFixDetails: false, financialAssessmentRequired: false, noWithdrawalOrTradingStatus: false, - poaNeedsVerification: false, - poaStatus: 'none', - poiNeedsVerification: false, - poiStatus: 'none', - withdrawalLimitReached: false, withdrawalLockedStatus: true, });