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

[CFDS]/Hasan/CFDS-4006/Ctrader strategy provider #16680

Open
wants to merge 73 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
300e3cb
feat: initialize ctrader modal
hasan-deriv May 14, 2024
c3d227d
feat: initialize ctrader trade screen
hasan-deriv May 14, 2024
6ec07a2
feat: added account list
hasan-deriv May 15, 2024
093ba4c
feat: added available ctrader accounts hook
hasan-deriv May 15, 2024
effde9f
feat: added accordion
hasan-deriv May 15, 2024
a8d6e9f
feat: added service maintenance message
hasan-deriv May 15, 2024
03baf5f
feat: added download link
hasan-deriv May 15, 2024
64f33e0
feat: removed clipboard
hasan-deriv May 15, 2024
63f6e20
feat: added get more account create function
hasan-deriv May 15, 2024
a9e4004
feat: removed console and unused import
hasan-deriv May 15, 2024
6837156
feat: initialize ctrader account add success modal
hasan-deriv May 15, 2024
5f2272c
feat: added ctrader add success modal
hasan-deriv May 17, 2024
d293887
feat: added ctrader success modal css
hasan-deriv May 17, 2024
901e346
feat: added ctrader success modal maybe later functionality
hasan-deriv May 17, 2024
61f0538
feat: added ctrader success modal transfer functionality
hasan-deriv May 20, 2024
c2613f8
fix: wallet button group vertical variant
hasan-deriv May 20, 2024
bcab1e0
feat: added acction buttons to ctrader account list
hasan-deriv May 20, 2024
6761687
fix: transfer button variant
hasan-deriv May 20, 2024
b81bb2d
fix: removed action buttons
hasan-deriv May 20, 2024
52380da
fix: AddedCTraderAccountsList
hasan-deriv May 20, 2024
ef09f1b
feat: added ctrader options to mt5tradescreen
hasan-deriv May 20, 2024
a8b7e99
fix: overheight issue in ModalStepWrapper
hasan-deriv May 21, 2024
4232aff
fix: get more button condition
hasan-deriv May 21, 2024
7bd2880
fix: removed max_count type
hasan-deriv May 21, 2024
635b7a7
fix: removed ctrader trade modal
hasan-deriv May 21, 2024
2674ded
fix: add test cases and comment to calculateTotalBalance utility
hasan-deriv May 21, 2024
3caffcc
fix: removed ctrader modal references
hasan-deriv May 21, 2024
f8a0f7b
feat: added test cases for useAvailableCtraderAccounts hooks
hasan-deriv May 21, 2024
204d533
feat: added test cases for AddedCTraderAccountList component
hasan-deriv May 21, 2024
1b8d4f7
feat: added test cases for CTraderAddAccountSuccessModal component
hasan-deriv May 21, 2024
2eac615
feat: updated deriv ui version
hasan-deriv May 23, 2024
a219ed3
feat: customize accordion css
hasan-deriv May 23, 2024
5ed752f
feat: customize accordion header css
hasan-deriv May 23, 2024
7871b54
feat: updated deriv ui
hasan-deriv May 23, 2024
236c24b
feat: customized accordion
hasan-deriv May 23, 2024
a161b4b
fix: ctrader utils
hasan-deriv May 23, 2024
a463afa
feat: added sorted account list function
hasan-deriv May 23, 2024
16473a8
fix: update mt5TradeScreen
hasan-deriv May 23, 2024
1b1d74f
fix: added redirect transfer to ctrader success modal
hasan-deriv May 23, 2024
5bc567b
fix: ctrader success modal test
hasan-deriv May 23, 2024
a4b861f
feat: added test cases to sort util
hasan-deriv May 23, 2024
5dc50f6
fix: transfer button click handler
hasan-deriv May 24, 2024
9d7eb39
fix: sort account list utility
hasan-deriv May 24, 2024
c0ff16a
chore: updated total balance utility
hasan-deriv May 28, 2024
da52929
chore: added comment calculate total utility function
hasan-deriv May 28, 2024
64ed213
chore: Merge branch 'master' into hasan/wallets-strategy-provider
hasan-deriv May 28, 2024
6adda5e
chore: reverted poof
hasan-deriv May 28, 2024
e7f92b5
fix: something
hasan-deriv May 28, 2024
3e5b056
chore: updated cashier link
hasan-deriv May 28, 2024
de13ba3
chore: moved successIcon
hasan-deriv May 28, 2024
cdf3a25
chore: updated classNames
hasan-deriv May 28, 2024
0fcaffb
chore: added fragment
hasan-deriv May 28, 2024
b531b1b
chore: updated success icon css
hasan-deriv May 28, 2024
29f765b
chore: included displayMoney utility
hasan-deriv May 28, 2024
e354b04
chore: resolve merge conflicts
hasan-deriv May 28, 2024
6ca101b
chore: fixed wallets card
hasan-deriv May 28, 2024
14dedad
chore: Merge branch 'master' into hasan/wallets-strategy-provider
hasan-deriv Jun 10, 2024
1a8afec
fix: accounts display balance
hasan-deriv Jun 10, 2024
befb7cc
fix: update use available ctrader acccounts
hasan-deriv Jun 10, 2024
a5a3a0c
chore: removed weird things in poof
hasan-deriv Jun 10, 2024
b1c610a
fix: available trader accounts hooks test
hasan-deriv Jun 10, 2024
9a3aa4e
Merge branch 'master' into hasan/wallets-strategy-provider
hasan-deriv Jun 19, 2024
ea6437d
fix: ctrader duplicate text
hasan-deriv Jun 19, 2024
07f3310
fix: merge master
hasan-deriv Aug 30, 2024
5823b13
fix: remove commit bugs
hasan-deriv Aug 30, 2024
d39aa3b
fix: failed test
hasan-deriv Sep 2, 2024
55a1eca
chore: resolve merge conflict
shontzu-deriv Sep 27, 2024
03cbbfa
fix: build and test fail
shontzu-deriv Sep 27, 2024
64bc43c
Merge branch 'master' into hasan/wallets-strategy-provider
shontzu-deriv Oct 3, 2024
67d9410
fix: addressed comments
hasan-deriv Oct 16, 2024
c1c1e0e
fix: addressed comments
hasan-deriv Oct 16, 2024
b9e7ef3
fix: addressed comments 2
hasan-deriv Oct 17, 2024
171ac60
fix: localize mt5tradescreen texts
hasan-deriv Oct 17, 2024
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
@@ -0,0 +1,78 @@
import { renderHook } from '@testing-library/react-hooks';
import useQuery from '../../useQuery';
import useAvailableCTraderAccounts from '../useAvailableCTraderAccounts';

jest.mock('../../useQuery');

describe('useAvailableCTraderAccounts', () => {
const mockUseQuery = useQuery as jest.Mock;

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

it('returns modified accounts when data is available', () => {
const mockData = {
trading_platform_available_accounts: [
{ market_type: 'gaming', someOtherField: 'value1' },
{ market_type: 'financial', someOtherField: 'value2' },
],
};

mockUseQuery.mockReturnValue({
data: mockData,
isLoading: false,
isError: false,
});

const { result } = renderHook(() => useAvailableCTraderAccounts());

expect(result.current.data).toEqual([
{ market_type: 'synthetic', platform: 'ctrader', leverage: 500, someOtherField: 'value1' },
{ market_type: 'financial', platform: 'ctrader', leverage: 1000, someOtherField: 'value2' },
]);
});

it('returns empty array when no accounts are available', () => {
mockUseQuery.mockReturnValue({
data: null,
isLoading: false,
isError: false,
});

const { result } = renderHook(() => useAvailableCTraderAccounts());

expect(result.current.data).toBe(undefined);
});

it('passes through other fields from useAuthorizedQuery', () => {
mockUseQuery.mockReturnValue({
data: null,
isLoading: true,
isError: false,
});

const { result } = renderHook(() => useAvailableCTraderAccounts());

expect(result.current.isLoading).toBe(true);
expect(result.current.isError).toBe(false);
});

it('handles account types not in the mapper', () => {
const mockData = {
trading_platform_available_accounts: [{ market_type: 'unknown', someOtherField: 'value' }],
};

mockUseQuery.mockReturnValue({
data: mockData,
isLoading: false,
isError: false,
});

const { result } = renderHook(() => useAvailableCTraderAccounts());

expect(result.current.data).toEqual([
{ market_type: 'unknown', platform: 'ctrader', leverage: undefined, someOtherField: 'value' },
]);
});
});
1 change: 1 addition & 0 deletions packages/api-v2/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export { default as useAllWalletAccounts } from './useAllWalletAccounts';
export { default as useAuthentication } from './useAuthentication';
export { default as useAuthorize } from './useAuthorize';
export { default as useAccesiblePlatforms } from './useAccesiblePlatforms';
export { default as useAvailableCTraderAccounts } from './useAvailableCTraderAccounts';
export { default as useAvailableMT5Accounts } from './useAvailableMT5Accounts';
export { default as useAvailableWallets } from './useAvailableWallets';
export { default as useBalance } from './useBalance';
Expand Down
42 changes: 42 additions & 0 deletions packages/api-v2/src/hooks/useAvailableCTraderAccounts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useMemo } from 'react';
import useQuery from '../useQuery';

const market_type_to_leverage_mapper: Record<string, number> = {
gaming: 500,
financial: 1000,
all: 100,
hasan-deriv marked this conversation as resolved.
Show resolved Hide resolved
};

/** A custom hook to get the list of available CTrader accounts. */
const useAvailableCTraderAccounts = () => {
const { data: ctrader_available_accounts, ...rest } = useQuery('trading_platform_available_accounts', {
payload: { platform: 'ctrader' },
});

const modified_ctrader_available_accounts = useMemo(
() =>
ctrader_available_accounts?.trading_platform_available_accounts?.map(account => {
return {
...account,
/** The market type for the account */
market_type: account.market_type === 'gaming' ? 'synthetic' : account.market_type,
/** The platform for the account */
platform: 'ctrader',
/** Leverage for the account */
leverage:
market_type_to_leverage_mapper[
account.market_type as keyof typeof market_type_to_leverage_mapper
],
} as const;
}),
[ctrader_available_accounts?.trading_platform_available_accounts]
);

return {
/** The available ctrader accounts */
data: modified_ctrader_available_accounts,
...rest,
};
};

export default useAvailableCTraderAccounts;
4 changes: 4 additions & 0 deletions packages/api-v2/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1612,6 +1612,10 @@ type TPrivateSocketEndpoints = {
*/
trading_platform_available_accounts?:
| {
/**
* The number of accounts a user has already created on the trading platform.
*/
available_count?: number;
/**
* A list of Deriv landing companies that can work with this account type
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
}

&--vertical {
flex-direction: column;
grid-auto-flow: row;
grid-template-columns: 1fr;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type TModalState = {
selectedJurisdiction?: THooks.AvailableMT5Accounts['shortcode'];
};

type TModalContext = {
export type TModalContext = {
getModalState: <T extends keyof TModalState>(key: T) => TModalState[T];
hide: () => void;
isOpen: boolean;
Expand Down
4 changes: 2 additions & 2 deletions packages/wallets/src/components/ModalProvider/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import ModalProvider, { useModal } from './ModalProvider';
import ModalProvider, { type TModalContext, useModal } from './ModalProvider';

export { ModalProvider, useModal };
export { ModalProvider, TModalContext, useModal };
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import React, { useMemo } from 'react';
import { useCtraderAccountsList } from '@deriv/api-v2';
import { displayMoney } from '@deriv/api-v2/src/utils';
import {
LabelPairedChevronLeftCaptionRegularIcon,
LabelPairedChevronRightCaptionRegularIcon,
Expand All @@ -8,14 +9,27 @@ import { Text } from '@deriv-com/ui';
import { TradingAccountCard } from '../../../../../components';
import { useModal } from '../../../../../components/ModalProvider';
import useIsRtl from '../../../../../hooks/useIsRtl';
import { calculateTotalByKey } from '../../../../../utils/calculate-total-by-key';
import { PlatformDetails } from '../../../constants';
import { MT5TradeModal } from '../../../modals';

const AddedCTraderAccountsList: React.FC = () => {
const { data: cTraderAccounts } = useCtraderAccountsList();
const account = cTraderAccounts?.[0];
const { show } = useModal();
const isRtl = useIsRtl();

const totalBalance = useMemo(() => {
if (cTraderAccounts) {
return calculateTotalByKey(cTraderAccounts, 'display_balance');
}
return 0;
}, [cTraderAccounts]);

const displayBalance = displayMoney(totalBalance, account?.currency || 'USD', {
fractional_digits: account?.currency_config?.fractional_digits,
});

return (
<React.Fragment>
{cTraderAccounts?.map((account, index) => (
Expand All @@ -28,9 +42,11 @@ const AddedCTraderAccountsList: React.FC = () => {
<Text align='start' size='sm'>
{PlatformDetails.ctrader.title}
</Text>
<Text align='start' size='sm' weight='bold'>
{account?.display_balance}
</Text>
{totalBalance !== undefined && (
<Text align='start' size='sm' weight='bold'>
{displayBalance}
</Text>
)}
<Text align='start' size='xs'>
hasan-deriv marked this conversation as resolved.
Show resolved Hide resolved
{account.login}
</Text>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,66 +1,73 @@
import React from 'react';
import { createMemoryHistory } from 'history';
import { Router } from 'react-router-dom';
import React, { PropsWithChildren } from 'react';
import { useCtraderAccountsList } from '@deriv/api-v2';
import { fireEvent, render, screen } from '@testing-library/react';
import { ModalProvider } from '../../../../../../components/ModalProvider';
import { ModalProvider, TModalContext, useModal } from '../../../../../../components/ModalProvider';
import { calculateTotalByKey } from '../../../../../../utils/calculate-total-by-key';
import { PlatformDetails } from '../../../../constants';
import { MT5TradeModal } from '../../../../modals';
import AddedCTraderAccountsList from '../AddedCTraderAccountsList';

jest.mock('@deriv/api-v2', () => ({
useCtraderAccountsList: jest.fn(),
jest.mock('@deriv/api-v2');
jest.mock('../../../../../../utils/calculate-total-by-key', () => ({
calculateTotalByKey: jest.fn(),
}));

jest.mock('../../../../modals', () => ({
MT5TradeModal: () => <div>Mocked MT5 Trade Modal</div>,
const mockModalShow = jest.fn();
jest.mock('../../../../../../components/ModalProvider', () => ({
...jest.requireActual('../../../../../../components/ModalProvider'),
useModal: jest.fn(() => ({
...jest.requireActual('../../../../../../components/ModalProvider').useModal(),
show: mockModalShow,
})),
}));

const mockAccount = { balance: 2000, currency: 'USD', market_type: 'financial' };

const wrapper = ({ children }: PropsWithChildren) => <ModalProvider>{children}</ModalProvider>;

describe('AddedCTraderAccountsList', () => {
let $modalContainer: HTMLDivElement;
const history = createMemoryHistory();
const mockAccounts = [
{ display_balance: '1000', login: '123' },
{ display_balance: '2000', login: '456' },
];
const mockUseCtraderAccountsList = useCtraderAccountsList as jest.Mock;
const mockUseModal = useModal as jest.MockedFunction<() => TModalContext>;
const mockCalculateTotalBalance = calculateTotalByKey as jest.MockedFunction<typeof calculateTotalByKey>;
const mockShowModal = jest.fn();

beforeEach(() => {
jest.clearAllMocks();
$modalContainer = document.createElement('div');
$modalContainer.id = 'wallets_modal_root';
document.body.appendChild($modalContainer);
(useCtraderAccountsList as jest.Mock).mockReturnValue({
data: mockAccounts,
mockUseModal.mockReturnValue({
getModalState: jest.fn(),
hide: jest.fn(),
isOpen: false,
setModalOptions: jest.fn(),
setModalState: jest.fn(),
show: mockShowModal,
});
});

afterEach(() => {
document.body.removeChild($modalContainer);
it('should render nothing when there are no accounts', () => {
mockUseCtraderAccountsList.mockReturnValue({ data: null });

render(<AddedCTraderAccountsList />, { wrapper });

expect(screen.queryByTestId('dt_wallets_trading_account_card')).not.toBeInTheDocument();
});

it('renders TradingAccountCard with cTraderAccounts', () => {
render(
<Router history={history}>
<ModalProvider>
<AddedCTraderAccountsList />
</ModalProvider>
</Router>
);
it('should render the trading account card with account details', () => {
mockUseCtraderAccountsList.mockReturnValue({ data: [mockAccount] });
mockCalculateTotalBalance.mockReturnValue('2000.00');

mockAccounts.forEach(account => {
expect(screen.getByText(account.login)).toBeInTheDocument();
expect(screen.getByText(account.display_balance)).toBeInTheDocument();
});
render(<AddedCTraderAccountsList />, { wrapper });
expect(screen.getByTestId('dt_wallets_trading_account_card')).toBeInTheDocument();
expect(screen.getByText(PlatformDetails.ctrader.title)).toBeInTheDocument();
expect(screen.getByText('2,000.00 USD')).toBeInTheDocument();
});

it('should show modal on click of cTrader account', () => {
render(
<Router history={history}>
<ModalProvider>
<AddedCTraderAccountsList />
</ModalProvider>
</Router>
);
it('should open the MT5TradeModal when the trading account card is clicked', () => {
mockUseCtraderAccountsList.mockReturnValue({ data: [mockAccount] });
mockCalculateTotalBalance.mockReturnValue('2000.00');

render(<AddedCTraderAccountsList />);

fireEvent.click(screen.getByTestId('dt_wallets_trading_account_card'));

fireEvent.click(screen.getAllByTestId('dt_wallets_trading_account_card')[0]);
expect(screen.getByText('Mocked MT5 Trade Modal')).toBeInTheDocument();
expect(mockShowModal).toHaveBeenCalledWith(<MT5TradeModal platform={PlatformDetails.ctrader.platform} />);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.wallets-ctrader-account-add-success-modal {
max-width: 44rem;
padding: 2.4rem;
display: flex;
flex-direction: column;
gap: 2.4rem;
background-color: var(--general-main-1, #ffffff);
border-radius: 0.8rem;

@include mobile {
width: 100%;
padding-inline: 1.6rem;
gap: 1.6rem;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';
import { useHistory } from 'react-router-dom';
import { Localize } from '@deriv-com/translations';
import { ActionScreen, Button } from '@deriv-com/ui';
import { ModalWrapper, WalletButtonGroup } from '../../../../components';
import { useModal } from '../../../../components/ModalProvider';
import SuccessIcon from './SuccessIcon';
import './CTraderAddAccountSuccessModal.scss';

const CTraderAddAccountSuccessModal = () => {
const history = useHistory();
const { hide } = useModal();
const onClickTransferNow = () => {
hide();
history.push('/wallet/withdrawal');
};
return (
<ModalWrapper>
<div className='wallets-ctrader-account-add-success-modal'>
<ActionScreen
actionButtons={
<WalletButtonGroup>
<Button onClick={hide} size='lg' variant='outlined'>
<Localize i18n_default_text='Maybe later' />
</Button>
<Button onClick={onClickTransferNow} size='lg' variant='contained'>
<Localize i18n_default_text='Transfer now' />
</Button>
</WalletButtonGroup>
}
description={
<Localize i18n_default_text='Congratulations, you have successfully created your real Deriv cTrader account. To start trading,transfer funds from your Deriv account into this account.' />
}
descriptionSize='sm'
icon={<SuccessIcon />}
title={<Localize i18n_default_text='Success!' />}
/>
</div>
</ModalWrapper>
);
};

export default CTraderAddAccountSuccessModal;
Loading
Loading