From 20c88bb7fafb8d446f56f17e0dbefffa57606585 Mon Sep 17 00:00:00 2001 From: Akmal Djumakhodjaev Date: Thu, 18 Apr 2024 16:51:00 +0800 Subject: [PATCH 1/4] fix: add sell error modals in reports package --- ...ccount-verification-pending-modal.spec.tsx | 54 +++++++++ .../account-verification-pending-modal.tsx | 29 +++++ .../AccountVerificationPendingModal/index.ts | 3 + .../__tests__/market-unavailable.spec.tsx | 27 +++++ .../Modals/MarketUnavailableModal/index.ts | 3 + .../market-unavailable.tsx | 46 +++++++ ...count-verification-required-modal.spec.tsx | 108 +++++++++++++++++ .../authorization-required-modal.spec.tsx | 78 ++++++++++++ ...company-wide-limit-exceeded-modal.spec.tsx | 68 +++++++++++ .../__tests__/constants.spec.ts | 19 +++ .../insufficient-balance-modal.spec.tsx | 92 ++++++++++++++ .../__tests__/services-error-modal.spec.tsx | 94 +++++++++++++++ .../account-verification-required-modal.tsx | 48 ++++++++ .../authorization-required-modal.tsx | 44 +++++++ .../company-wide-limit-exceeded-modal.tsx | 36 ++++++ .../Modals/ServicesErrorModal/constants.ts | 16 +++ .../Modals/ServicesErrorModal/index.ts | 3 + .../insufficient-balance-modal.tsx | 49 ++++++++ .../services-error-modal.tsx | 68 +++++++++++ .../unsupported-contract-modal.spec.tsx | 28 +++++ .../Modals/UnsupportedContractModal/index.ts | 3 + .../unsupported-contract-modal.tsx | 39 ++++++ .../Modals/__tests__/trade-modals.spec.tsx | 112 ++++++++++++++++++ .../reports/src/Components/Modals/index.ts | 3 + .../src/Components/Modals/trade-modals.tsx | 70 +++++++++++ packages/reports/src/app.tsx | 7 ++ 26 files changed, 1147 insertions(+) create mode 100644 packages/reports/src/Components/Elements/Modals/AccountVerificationPendingModal/__tests__/account-verification-pending-modal.spec.tsx create mode 100644 packages/reports/src/Components/Elements/Modals/AccountVerificationPendingModal/account-verification-pending-modal.tsx create mode 100644 packages/reports/src/Components/Elements/Modals/AccountVerificationPendingModal/index.ts create mode 100644 packages/reports/src/Components/Elements/Modals/MarketUnavailableModal/__tests__/market-unavailable.spec.tsx create mode 100644 packages/reports/src/Components/Elements/Modals/MarketUnavailableModal/index.ts create mode 100644 packages/reports/src/Components/Elements/Modals/MarketUnavailableModal/market-unavailable.tsx create mode 100644 packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/account-verification-required-modal.spec.tsx create mode 100644 packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/authorization-required-modal.spec.tsx create mode 100644 packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/company-wide-limit-exceeded-modal.spec.tsx create mode 100644 packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/constants.spec.ts create mode 100644 packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/insufficient-balance-modal.spec.tsx create mode 100644 packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/services-error-modal.spec.tsx create mode 100644 packages/reports/src/Components/Elements/Modals/ServicesErrorModal/account-verification-required-modal.tsx create mode 100644 packages/reports/src/Components/Elements/Modals/ServicesErrorModal/authorization-required-modal.tsx create mode 100644 packages/reports/src/Components/Elements/Modals/ServicesErrorModal/company-wide-limit-exceeded-modal.tsx create mode 100644 packages/reports/src/Components/Elements/Modals/ServicesErrorModal/constants.ts create mode 100644 packages/reports/src/Components/Elements/Modals/ServicesErrorModal/index.ts create mode 100644 packages/reports/src/Components/Elements/Modals/ServicesErrorModal/insufficient-balance-modal.tsx create mode 100644 packages/reports/src/Components/Elements/Modals/ServicesErrorModal/services-error-modal.tsx create mode 100644 packages/reports/src/Components/Elements/Modals/UnsupportedContractModal/__tests__/unsupported-contract-modal.spec.tsx create mode 100644 packages/reports/src/Components/Elements/Modals/UnsupportedContractModal/index.ts create mode 100644 packages/reports/src/Components/Elements/Modals/UnsupportedContractModal/unsupported-contract-modal.tsx create mode 100644 packages/reports/src/Components/Modals/__tests__/trade-modals.spec.tsx create mode 100644 packages/reports/src/Components/Modals/index.ts create mode 100644 packages/reports/src/Components/Modals/trade-modals.tsx diff --git a/packages/reports/src/Components/Elements/Modals/AccountVerificationPendingModal/__tests__/account-verification-pending-modal.spec.tsx b/packages/reports/src/Components/Elements/Modals/AccountVerificationPendingModal/__tests__/account-verification-pending-modal.spec.tsx new file mode 100644 index 000000000000..6b0cf144d549 --- /dev/null +++ b/packages/reports/src/Components/Elements/Modals/AccountVerificationPendingModal/__tests__/account-verification-pending-modal.spec.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import AccountVerificationPendingModal from '../account-verification-pending-modal'; + +describe('', () => { + let modal_root_el: HTMLDivElement; + + beforeAll(() => { + modal_root_el = document.createElement('div'); + modal_root_el.setAttribute('id', 'modal_root'); + document.body.appendChild(modal_root_el); + }); + + afterAll(() => { + document.body.removeChild(modal_root_el); + }); + + const mock_props: React.ComponentProps = { + is_visible: true, + onConfirm: jest.fn(), + }; + + const modal_heading = /Pending verification/; + const modal_desc = + /You cannot trade as your documents are still under review. We will notify you by email once your verification is approved./i; + + it('should render the component AccountVerificationPendingModal if is_visible is true', () => { + render(); + + expect(screen.getByRole('heading', { name: modal_heading })).toBeInTheDocument(); + expect(screen.getByText(modal_desc)).toBeInTheDocument(); + + const confirm_ok_btn = screen.getByRole('button', { name: /OK/i }); + expect(confirm_ok_btn).toBeInTheDocument(); + expect(confirm_ok_btn).toBeEnabled(); + }); + + it('should call onConfirm when clicking on OK button', () => { + render(); + + const confirm_ok_btn = screen.getByRole('button', { name: /OK/i }); + userEvent.click(confirm_ok_btn); + expect(mock_props.onConfirm).toBeCalledTimes(1); + }); + + it('should not render the component if is_visible is false ', () => { + render(); + + expect(screen.queryByRole('heading', { name: modal_heading })).not.toBeInTheDocument(); + expect(screen.queryByText(modal_desc)).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: /OK/i })).not.toBeInTheDocument(); + }); +}); diff --git a/packages/reports/src/Components/Elements/Modals/AccountVerificationPendingModal/account-verification-pending-modal.tsx b/packages/reports/src/Components/Elements/Modals/AccountVerificationPendingModal/account-verification-pending-modal.tsx new file mode 100644 index 000000000000..1444b27e90f0 --- /dev/null +++ b/packages/reports/src/Components/Elements/Modals/AccountVerificationPendingModal/account-verification-pending-modal.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { Button, Modal } from '@deriv/components'; +import { localize, Localize } from '@deriv/translations'; + +type TAccountVerificationPendingModalProps = { + is_visible: boolean; + onConfirm: () => void; +}; + +const AccountVerificationPendingModal = ({ is_visible, onConfirm }: TAccountVerificationPendingModalProps) => ( + + + + + + + + +); + +export default AccountVerificationPendingModal; diff --git a/packages/reports/src/Components/Elements/Modals/AccountVerificationPendingModal/index.ts b/packages/reports/src/Components/Elements/Modals/AccountVerificationPendingModal/index.ts new file mode 100644 index 000000000000..1f0c69529153 --- /dev/null +++ b/packages/reports/src/Components/Elements/Modals/AccountVerificationPendingModal/index.ts @@ -0,0 +1,3 @@ +import AccountVerificationPendingModal from './account-verification-pending-modal'; + +export default AccountVerificationPendingModal; diff --git a/packages/reports/src/Components/Elements/Modals/MarketUnavailableModal/__tests__/market-unavailable.spec.tsx b/packages/reports/src/Components/Elements/Modals/MarketUnavailableModal/__tests__/market-unavailable.spec.tsx new file mode 100644 index 000000000000..be3f73570db3 --- /dev/null +++ b/packages/reports/src/Components/Elements/Modals/MarketUnavailableModal/__tests__/market-unavailable.spec.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { render, screen } from '@testing-library/react'; +import MarketUnavailableModal from '../market-unavailable'; +import { mockStore } from '@deriv/stores'; +import ReportsProviders from '../../../../../reports-providers'; + +const mock_props = { + onCancel: jest.fn(), + onConfirm: jest.fn(), +}; + +describe('MarketUnavailableModal', () => { + it('should render modal component', () => { + const mock_root_store = mockStore({ ui: { has_only_forward_starting_contracts: true } }); + + (ReactDOM.createPortal as jest.Mock) = jest.fn(component => { + return component; + }); + + render(, { + wrapper: ({ children }) => {children}, + }); + + expect(screen.getByText(/This market is not yet/i)).toBeInTheDocument(); + }); +}); diff --git a/packages/reports/src/Components/Elements/Modals/MarketUnavailableModal/index.ts b/packages/reports/src/Components/Elements/Modals/MarketUnavailableModal/index.ts new file mode 100644 index 000000000000..62c77323d14e --- /dev/null +++ b/packages/reports/src/Components/Elements/Modals/MarketUnavailableModal/index.ts @@ -0,0 +1,3 @@ +import MarketUnavailableModal from './market-unavailable'; + +export default MarketUnavailableModal; diff --git a/packages/reports/src/Components/Elements/Modals/MarketUnavailableModal/market-unavailable.tsx b/packages/reports/src/Components/Elements/Modals/MarketUnavailableModal/market-unavailable.tsx new file mode 100644 index 000000000000..9764dad5faea --- /dev/null +++ b/packages/reports/src/Components/Elements/Modals/MarketUnavailableModal/market-unavailable.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { Dialog } from '@deriv/components'; +import { getPlatformSettings } from '@deriv/shared'; +import { localize, Localize } from '@deriv/translations'; +import { observer, useStore } from '@deriv/stores'; + +type TMarketUnavailableModalProps = { + is_loading?: boolean; + onCancel: () => void; + onConfirm: () => void; +}; + +const MarketUnavailableModal = observer(({ is_loading, onCancel, onConfirm }: TMarketUnavailableModalProps) => { + const { ui } = useStore(); + const { disableApp, enableApp, has_only_forward_starting_contracts: is_visible } = ui; + + return ( + + + + ); +}); + +export default MarketUnavailableModal; diff --git a/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/account-verification-required-modal.spec.tsx b/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/account-verification-required-modal.spec.tsx new file mode 100644 index 000000000000..5b078a65f760 --- /dev/null +++ b/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/account-verification-required-modal.spec.tsx @@ -0,0 +1,108 @@ +import React from 'react'; +import { createBrowserHistory } from 'history'; +import { Router } from 'react-router-dom'; +import { screen, render } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { routes } from '@deriv/shared'; +import { useStore } from '@deriv/stores'; +import AccountVerificationRequiredModal from '../account-verification-required-modal'; + +type TModal = React.FC<{ + children: React.ReactNode; + is_open: boolean; + title: string; + height: string; +}> & { + Body?: React.FC<{ + children: React.ReactNode; + }>; + Footer?: React.FC<{ + children: React.ReactNode; + }>; +}; + +jest.mock('@deriv/stores', () => ({ + ...jest.requireActual('@deriv/stores'), + observer: jest.fn(x => x), + useStore: jest.fn(() => ({ + ui: { + is_mobile: true, + }, + })), +})); + +jest.mock('@deriv/shared', () => ({ + ...jest.requireActual('@deriv/shared'), + isMobile: jest.fn(() => true), +})); + +jest.mock('@deriv/components', () => { + const original_module = jest.requireActual('@deriv/components'); + const Modal: TModal = jest.fn(({ children, is_open, title, height }) => { + if (is_open) { + return ( +
+

{title}

+

{height}

+ {children} +
+ ); + } + return null; + }); + Modal.Body = jest.fn(({ children }) =>
{children}
); + Modal.Footer = jest.fn(({ children }) =>
{children}
); + + return { + ...original_module, + Modal, + }; +}); +describe('', () => { + let mocked_props: React.ComponentProps; + const history = createBrowserHistory(); + const renderWithRouter = (component: React.ReactElement) => { + return render({component}); + }; + + beforeEach(() => { + mocked_props = { + onConfirm: jest.fn(), + is_visible: true, + }; + }); + it('height should be auto if isMobile is true', () => { + render(); + expect(screen.getByText('auto')).toBeInTheDocument(); + }); + it('height should be 220px if isMobile is false', () => { + (useStore as jest.Mock).mockReturnValue({ + ui: { + is_mobile: false, + }, + }); + render(); + expect(screen.getByText('220px')).toBeInTheDocument(); + }); + it('should render modal title, modal description, and submit button.', () => { + render(); + expect(screen.getByText(/account verification required/i)).toBeInTheDocument(); + expect( + screen.getByText( + /Please submit your proof of identity and proof of address to verify your account and continue trading./i + ) + ).toBeInTheDocument(); + expect(screen.getByText(/submit proof/i)).toBeInTheDocument(); + }); + it('should return null when is_visible is false', () => { + mocked_props.is_visible = false; + const { container } = render(); + expect(container).toBeEmptyDOMElement(); + }); + it('should navigate to proof_of_identity url on clicking on submit button', () => { + renderWithRouter(); + const submit_proof_button = screen.getByText(/submit proof/i); + userEvent.click(submit_proof_button); + expect(history.location.pathname).toBe(routes.proof_of_identity); + }); +}); diff --git a/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/authorization-required-modal.spec.tsx b/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/authorization-required-modal.spec.tsx new file mode 100644 index 000000000000..795173213716 --- /dev/null +++ b/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/authorization-required-modal.spec.tsx @@ -0,0 +1,78 @@ +import React from 'react'; +import { screen, render } from '@testing-library/react'; +import AuthorizationRequiredModal from '../authorization-required-modal'; +import { redirectToLogin, redirectToSignUp } from '@deriv/shared'; +import userEvent from '@testing-library/user-event'; + +type TModal = React.FC<{ + children: React.ReactNode; + is_open: boolean; + title: string; +}> & { + Body?: React.FC<{ + children: React.ReactNode; + }>; + Footer?: React.FC<{ + children: React.ReactNode; + }>; +}; + +jest.mock('@deriv/components', () => { + const original_module = jest.requireActual('@deriv/components'); + const Modal: TModal = jest.fn(({ children, is_open, title }) => { + if (is_open) { + return ( +
+

{title}

+ {children} +
+ ); + } + return null; + }); + Modal.Body = jest.fn(({ children }) =>
{children}
); + Modal.Footer = jest.fn(({ children }) =>
{children}
); + + return { + ...original_module, + Modal, + }; +}); + +jest.mock('@deriv/shared', () => ({ + ...jest.requireActual('@deriv/shared'), + redirectToLogin: jest.fn(), + redirectToSignUp: jest.fn(), +})); + +describe('', () => { + const mocked_props = { + is_visible: true, + toggleModal: jest.fn(), + is_logged_in: true, + is_appstore: true, + }; + + it('modal title, modal description, log in button, and signup button to be rendered', () => { + render(); + expect(screen.getByText(/start trading with us/i)).toBeInTheDocument(); + expect(screen.getByText(/Log in or create a free account to place a trade/i)).toBeInTheDocument(); + expect(screen.getByText('Log in')).toBeInTheDocument(); + expect(screen.getByText(/create free account/i)).toBeInTheDocument(); + }); + it('redirectToLogin should be called when Log in button is clicked', () => { + render(); + userEvent.click(screen.getByText('Log in')); + expect(redirectToLogin).toHaveBeenCalled(); + }); + it('redirectToSignUp should be called when Log in button is clicked', () => { + render(); + userEvent.click(screen.getByText(/create free account/i)); + expect(redirectToSignUp).toHaveBeenCalled(); + }); + it('should return null when is_visible is false', () => { + mocked_props.is_visible = false; + const { container } = render(); + expect(container).toBeEmptyDOMElement(); + }); +}); diff --git a/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/company-wide-limit-exceeded-modal.spec.tsx b/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/company-wide-limit-exceeded-modal.spec.tsx new file mode 100644 index 000000000000..8c839fe66352 --- /dev/null +++ b/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/company-wide-limit-exceeded-modal.spec.tsx @@ -0,0 +1,68 @@ +import React from 'react'; +import { screen, render } from '@testing-library/react'; +import CompanyWideLimitExceededModal from '../company-wide-limit-exceeded-modal'; + +type TModal = React.FC<{ + children: React.ReactNode; + is_open: boolean; + title: string; +}> & { + Body?: React.FC<{ + children: React.ReactNode; + }>; + Footer?: React.FC<{ + children: React.ReactNode; + }>; +}; + +jest.mock('@deriv/components', () => { + const original_module = jest.requireActual('@deriv/components'); + const Modal: TModal = jest.fn(({ children, is_open, title }) => { + if (is_open) { + return ( +
+

{title}

+ {children} +
+ ); + } + return null; + }); + Modal.Body = jest.fn(({ children }) =>
{children}
); + Modal.Footer = jest.fn(({ children }) =>
{children}
); + + return { + ...original_module, + Modal, + }; +}); + +jest.mock('@deriv/stores', () => ({ + ...jest.requireActual('@deriv/stores'), + observer: jest.fn(x => x), + useStore: jest.fn(() => ({ + ui: { + is_mobile: false, + }, + })), +})); + +describe('', () => { + const mocked_props = { + is_visible: true, + onConfirm: jest.fn(), + }; + it('should render modal title, modal description, and modal button.', () => { + render(); + expect(screen.getByText(/purchase error/i)).toBeInTheDocument(); + expect( + screen.getByText(/no further trading is allowed on this contract type for the current trading session./i) + ).toBeInTheDocument(); + expect(screen.getByText(/ok/i)).toBeInTheDocument(); + }); + it('should return null when is_visible is false', () => { + mocked_props.is_visible = false; + const { container } = render(); + expect(container).toBeEmptyDOMElement(); + }); +}); diff --git a/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/constants.spec.ts b/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/constants.spec.ts new file mode 100644 index 000000000000..af82277e5c9f --- /dev/null +++ b/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/constants.spec.ts @@ -0,0 +1,19 @@ +import { getTitle } from '../constants'; + +describe('getTitle', () => { + it('should return "Purchase Error" if input is buy', () => { + expect(getTitle('buy')).toBe('Purchase Error'); + }); + it('should return "Deal Cancellation Error" if input is cancel', () => { + expect(getTitle('cancel')).toBe('Deal Cancellation Error'); + }); + it('should return "Contract Update Error" if input is buy', () => { + expect(getTitle('contract_update')).toBe('Contract Update Error'); + }); + it('should return "Sell Error" if input is buy', () => { + expect(getTitle('sell')).toBe('Sell Error'); + }); + it('should return "Error" if input is not a defined type', () => { + expect(getTitle('test')).toBe('Error'); + }); +}); diff --git a/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/insufficient-balance-modal.spec.tsx b/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/insufficient-balance-modal.spec.tsx new file mode 100644 index 000000000000..fe4cdde3c975 --- /dev/null +++ b/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/insufficient-balance-modal.spec.tsx @@ -0,0 +1,92 @@ +import React from 'react'; +import { screen, render } from '@testing-library/react'; +import InsufficientBalanceModal from '../insufficient-balance-modal'; +import { createBrowserHistory } from 'history'; +import { Router } from 'react-router-dom'; +import { routes } from '@deriv/shared'; +import userEvent from '@testing-library/user-event'; + +type TModal = React.FC<{ + children: React.ReactNode; + is_open: boolean; + title: string; +}> & { + Body?: React.FC<{ + children: React.ReactNode; + }>; + Footer?: React.FC<{ + children: React.ReactNode; + }>; +}; + +jest.mock('@deriv/stores', () => ({ + ...jest.requireActual('@deriv/stores'), + observer: jest.fn(x => x), + useStore: jest.fn(() => ({ + ui: { + is_mobile: false, + }, + })), +})); + +jest.mock('@deriv/components', () => { + const original_module = jest.requireActual('@deriv/components'); + const Modal: TModal = jest.fn(({ children, is_open, title }) => { + if (is_open) { + return ( +
+

{title}

+ {children} +
+ ); + } + return null; + }); + Modal.Body = jest.fn(({ children }) =>
{children}
); + Modal.Footer = jest.fn(({ children }) =>
{children}
); + + return { + ...original_module, + Modal, + }; +}); + +describe('', () => { + const mocked_props = { + is_virtual: true, + is_visible: true, + message: 'test', + toggleModal: jest.fn(), + }; + + const history = createBrowserHistory(); + const renderWithRouter = (component: React.ReactElement) => { + return render({component}); + }; + + it('modal title, and modal description should be rendered', () => { + renderWithRouter(); + expect(screen.getByText(/insufficient balance/i)).toBeInTheDocument(); + expect(screen.getByText(/test/i)).toBeInTheDocument(); + }); + it('button text should be OK if is_virtual is true and toggleModal should be called if user clicks on the button', () => { + renderWithRouter(); + const button = screen.getByText(/ok/i); + expect(button).toBeInTheDocument(); + userEvent.click(button); + expect(mocked_props.toggleModal).toHaveBeenCalled(); + }); + it('button text should be "Deposit now" if is_virtual is false and should navigate to bla bla if you click on the button', () => { + mocked_props.is_virtual = false; + renderWithRouter(); + const button = screen.getByText(/deposit now/i); + expect(button).toBeInTheDocument(); + userEvent.click(button); + expect(history.location.pathname).toBe(routes.cashier_deposit); + }); + it('should return null when is_visible is false', () => { + mocked_props.is_visible = false; + const { container } = renderWithRouter(); + expect(container).toBeEmptyDOMElement(); + }); +}); diff --git a/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/services-error-modal.spec.tsx b/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/services-error-modal.spec.tsx new file mode 100644 index 000000000000..993d314439ae --- /dev/null +++ b/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/__tests__/services-error-modal.spec.tsx @@ -0,0 +1,94 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import ServicesErrorModal from '../services-error-modal'; + +jest.mock('../authorization-required-modal', () => jest.fn(() => 'AuthorizationRequiredModal')); +jest.mock('../insufficient-balance-modal', () => jest.fn(() => 'InsufficientBalanceModal')); +jest.mock('../company-wide-limit-exceeded-modal', () => jest.fn(() => 'CompanyWideLimitExceededModal')); +jest.mock('../account-verification-required-modal', () => jest.fn(() => 'AccountVerificationRequiredModal')); + +type TModal = { + (): JSX.Element; + Body?: React.FC; + Footer?: React.FC; +}; +jest.mock('@deriv/components', () => { + const original_module = jest.requireActual('@deriv/components'); + const Modal: TModal = jest.fn(() =>
Modal
); + Modal.Body = jest.fn(() =>
); + Modal.Footer = jest.fn(() =>
); + return { + ...original_module, + Modal, + }; +}); + +describe('', () => { + const modal_props = { + is_visible: true, + onConfirm: jest.fn(), + is_logged_in: true, + }; + it('Should return null if code or message is missing', () => { + const services_error_mock = { + code: '', + message: '', + type: '', + }; + const { container } = render( + + ); + expect(container).toBeEmptyDOMElement(); + }); + + it('AuthorizationRequiredModal should render when code is AuthorizationRequired', () => { + const services_error_mock = { + code: 'AuthorizationRequired', + message: 'AuthorizationRequired', + type: '', + }; + render(); + expect(screen.getByText('AuthorizationRequiredModal')).toBeInTheDocument(); + }); + + it('InsufficientBalanceModal should render when code is InsufficientBalance', () => { + const services_error_mock = { + code: 'InsufficientBalance', + message: 'InsufficientBalance', + type: '', + }; + render(); + expect(screen.getByText('InsufficientBalanceModal')).toBeInTheDocument(); + }); + + it('CompanyWideLimitExceededModal should render when code is CompanyWideLimitExceeded', () => { + const services_error_mock = { + code: 'CompanyWideLimitExceeded', + message: 'CompanyWideLimitExceeded', + type: '', + }; + render(); + expect(screen.getByText('CompanyWideLimitExceededModal')).toBeInTheDocument(); + }); + + it('AccountVerificationRequiredModal should render when code is PleaseAuthenticate', () => { + const services_error_mock = { + code: 'PleaseAuthenticate', + message: 'PleaseAuthenticate', + type: '', + }; + render(); + expect(screen.getByText('AccountVerificationRequiredModal')).toBeInTheDocument(); + }); + + it('Default case should render when code is not specified in switch case', () => { + const services_error_mock = { + code: 'Default Error', + message: 'Default Error', + title: 'Default Error', + type: '', + }; + render(); + expect(screen.getByText('Modal')).toBeInTheDocument(); + }); +}); diff --git a/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/account-verification-required-modal.tsx b/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/account-verification-required-modal.tsx new file mode 100644 index 000000000000..d04e470a271a --- /dev/null +++ b/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/account-verification-required-modal.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { useHistory } from 'react-router-dom'; +import { Button, Modal } from '@deriv/components'; +import { routes } from '@deriv/shared'; +import { observer, useStore } from '@deriv/stores'; +import { localize, Localize } from '@deriv/translations'; + +type TAccountVerificationRequiredModalProps = { + is_visible: boolean; + onConfirm: () => void; +}; + +const AccountVerificationRequiredModal = observer( + ({ is_visible, onConfirm }: TAccountVerificationRequiredModalProps) => { + const history = useHistory(); + const { + ui: { is_mobile }, + } = useStore(); + return ( + + + + + +
+ +
+
+ ); + } +); + +export default AccountVerificationRequiredModal; diff --git a/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/authorization-required-modal.tsx b/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/authorization-required-modal.tsx new file mode 100644 index 000000000000..69779bab16ea --- /dev/null +++ b/packages/reports/src/Components/Elements/Modals/ServicesErrorModal/authorization-required-modal.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { Button, Modal } from '@deriv/components'; +import { getLanguage, localize } from '@deriv/translations'; +import { redirectToLogin, redirectToSignUp } from '@deriv/shared'; + +type TAuthorizationRequiredModal = { + is_visible: boolean; + toggleModal: () => void; + is_appstore?: boolean; + is_logged_in: boolean; +}; + +const AuthorizationRequiredModal = ({ + is_visible, + toggleModal, + is_appstore, + is_logged_in, +}: TAuthorizationRequiredModal) => ( + + {localize('Log in or create a free account to place a trade.')} + + + +
+ )) +); +jest.mock('../../Elements/Modals/MarketUnavailableModal', () => + jest.fn(props => ( +
+
Market unavailable modal
+ + +
+ )) +); +jest.mock('../../Elements/Modals/ServicesErrorModal', () => + jest.fn(props => ( +
+
Services error modal
+ +
+ )) +); + +window.open = jest.fn(); + +describe('TradeModals', () => { + const mockTradeModals = (mocked_store: TCoreStores) => { + return ( + + + + ); + }; + + it('should render modal', () => { + const mock_root_store = mockStore({}); + + render(mockTradeModals(mock_root_store)); + + expect(screen.getByText('Unsupported contract modal')).toBeInTheDocument(); + expect(screen.getByText('Market unavailable modal')).toBeInTheDocument(); + expect(screen.getByText('Services error modal')).toBeInTheDocument(); + }); + it('should call function unsupportedContractOnConfirm if button onConfirm in UnsupportedContractModal component was clicked', () => { + const mock_root_store = mockStore({}); + + render(mockTradeModals(mock_root_store)); + userEvent.click(screen.getByText('onConfirm unsupported')); + + expect(mock_root_store.ui.toggleUnsupportedContractModal).toHaveBeenCalled(); + }); + it('should call function unsupportedContractOnClose if button onClose in UnsupportedContractModal component was clicked', () => { + const mock_root_store = mockStore({}); + + render(mockTradeModals(mock_root_store)); + userEvent.click(screen.getByText('onClose unsupported')); + + expect(mock_root_store.ui.toggleUnsupportedContractModal).toHaveBeenCalled(); + }); + it('should call function marketUnavailableOnConfirm if button onConfirm in MarketUnavailableModal component was clicked', () => { + const mock_root_store = mockStore({}); + + render(mockTradeModals(mock_root_store)); + userEvent.click(screen.getByText('onConfirm market')); + + expect(mock_root_store.ui.setHasOnlyForwardingContracts).toHaveBeenCalled(); + }); + it('should call function marketUnavailableOnCancel if button onCancel in MarketUnavailableModal component was clicked', () => { + const mock_root_store = mockStore({}); + + render(mockTradeModals(mock_root_store)); + userEvent.click(screen.getByText('onCancel market')); + + expect(mock_root_store.ui.setHasOnlyForwardingContracts).toHaveBeenCalled(); + }); + it('should call function servicesErrorModalOnConfirm if button onConfirm in ServicesErrorModal component was clicked', () => { + const mock_root_store = mockStore({}); + + render(mockTradeModals(mock_root_store)); + userEvent.click(screen.getByText('onConfirm services')); + + expect(mock_root_store.ui.toggleServicesErrorModal).toHaveBeenCalled(); + }); + it('should call function servicesErrorModalOnConfirm and clearPurchaseInfo and requestProposal if button onConfirm in ServicesErrorModal component was clicked and type of services_error is equal to buy', () => { + const mock_root_store = mockStore({ + common: { + services_error: { + code: 'test', + message: 'test', + type: 'buy', + }, + }, + }); + + render(mockTradeModals(mock_root_store)); + userEvent.click(screen.getByText('onConfirm services')); + + expect(mock_root_store.ui.toggleServicesErrorModal).toHaveBeenCalled(); + }); +}); diff --git a/packages/reports/src/Components/Modals/index.ts b/packages/reports/src/Components/Modals/index.ts new file mode 100644 index 000000000000..af8b1307fab3 --- /dev/null +++ b/packages/reports/src/Components/Modals/index.ts @@ -0,0 +1,3 @@ +import TradeModals from './trade-modals'; + +export default TradeModals; diff --git a/packages/reports/src/Components/Modals/trade-modals.tsx b/packages/reports/src/Components/Modals/trade-modals.tsx new file mode 100644 index 000000000000..59824321767c --- /dev/null +++ b/packages/reports/src/Components/Modals/trade-modals.tsx @@ -0,0 +1,70 @@ +import React from 'react'; +import { getUrlSmartTrader, getStaticUrl } from '@deriv/shared'; +import UnsupportedContractModal from '../Elements/Modals/UnsupportedContractModal'; +import MarketUnavailableModal from '../Elements/Modals/MarketUnavailableModal'; +import ServicesErrorModal from '../Elements/Modals/ServicesErrorModal'; +import AccountVerificationPendingModal from '../Elements/Modals/AccountVerificationPendingModal'; +import { observer, useStore } from '@deriv/stores'; + +const TradeModals = observer(() => { + const { ui, client, common } = useStore(); + const { is_virtual, is_logged_in } = client; + + const { services_error } = common; + const { + is_mf_verification_pending_modal_visible, + is_services_error_visible, + setHasOnlyForwardingContracts, + setIsMFVericationPendingModal, + toggleServicesErrorModal, + toggleUnsupportedContractModal, + } = ui; + const resetToPreviousMarket = () => { + setHasOnlyForwardingContracts(false); + }; + + const marketUnavailableOnConfirm = () => { + resetToPreviousMarket(); + }; + + const marketUnavailableOnCancel = () => { + window.open(getUrlSmartTrader()); + resetToPreviousMarket(); + }; + + const servicesErrorModalOnConfirm = () => { + toggleServicesErrorModal(false); + }; + + const unsupportedContractOnConfirm = () => { + toggleUnsupportedContractModal(false); + }; + + const unsupportedContractOnClose = () => { + window.open(getStaticUrl('/')); + unsupportedContractOnConfirm(); + }; + + return ( + + + + + + + + setIsMFVericationPendingModal(false)} + /> + + ); +}); + +export default TradeModals; diff --git a/packages/reports/src/app.tsx b/packages/reports/src/app.tsx index fa9f31db13d0..549fb71a5c1e 100644 --- a/packages/reports/src/app.tsx +++ b/packages/reports/src/app.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import Loadable from 'react-loadable'; import Routes from 'Containers/routes'; import ReportsProviders from './reports-providers'; import 'Sass/app.scss'; @@ -10,10 +11,16 @@ type TAppProps = { }; }; +const TradeModals = Loadable({ + loader: () => import(/* webpackChunkName: "trade-modals", webpackPrefetch: true */ './Components/Modals'), + loading: () => null, +}); + const App = ({ passthrough }: TAppProps) => { return ( + ); }; From 56fac03d147fb8142df14885daddc1bdda423c7c Mon Sep 17 00:00:00 2001 From: Akmal Djumakhodjaev Date: Fri, 19 Apr 2024 11:23:51 +0800 Subject: [PATCH 2/4] feat: trigger deployment From 0d18ac6d7000d563bac8e8a2f9f8371f2b1dcfb0 Mon Sep 17 00:00:00 2001 From: Akmal Djumakhodjaev Date: Fri, 19 Apr 2024 13:07:35 +0800 Subject: [PATCH 3/4] fix: remove unused component and test cases --- .../unsupported-contract-modal.spec.tsx | 28 ------------- .../Modals/UnsupportedContractModal/index.ts | 3 -- .../unsupported-contract-modal.tsx | 39 ------------------- .../Modals/__tests__/trade-modals.spec.tsx | 26 ------------- .../src/Components/Modals/trade-modals.tsx | 15 +------ 5 files changed, 1 insertion(+), 110 deletions(-) delete mode 100644 packages/reports/src/Components/Elements/Modals/UnsupportedContractModal/__tests__/unsupported-contract-modal.spec.tsx delete mode 100644 packages/reports/src/Components/Elements/Modals/UnsupportedContractModal/index.ts delete mode 100644 packages/reports/src/Components/Elements/Modals/UnsupportedContractModal/unsupported-contract-modal.tsx diff --git a/packages/reports/src/Components/Elements/Modals/UnsupportedContractModal/__tests__/unsupported-contract-modal.spec.tsx b/packages/reports/src/Components/Elements/Modals/UnsupportedContractModal/__tests__/unsupported-contract-modal.spec.tsx deleted file mode 100644 index 83f6c8810c81..000000000000 --- a/packages/reports/src/Components/Elements/Modals/UnsupportedContractModal/__tests__/unsupported-contract-modal.spec.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import { render, screen } from '@testing-library/react'; -import UnsupportedContractModal from '../unsupported-contract-modal'; -import { mockStore } from '@deriv/stores'; -import ReportsProviders from '../../../../../reports-providers'; - -const mock_props = { - onClose: jest.fn(), - onConfirm: jest.fn(), -}; - -describe('UnsupportedContractModal', () => { - it('should render modal component', () => { - const mock_root_store = mockStore({ ui: { is_unsupported_contract_modal_visible: true } }); - - (ReactDOM.createPortal as jest.Mock) = jest.fn(component => { - return component; - }); - - render(, { - wrapper: ({ children }) => {children}, - }); - - expect(screen.getByText(/You’ve selected a trade type that is currently unsupported/i)).toBeInTheDocument(); - expect(screen.getByText(/Go to Deriv.com/i)).toBeInTheDocument(); - }); -}); diff --git a/packages/reports/src/Components/Elements/Modals/UnsupportedContractModal/index.ts b/packages/reports/src/Components/Elements/Modals/UnsupportedContractModal/index.ts deleted file mode 100644 index f001335a990e..000000000000 --- a/packages/reports/src/Components/Elements/Modals/UnsupportedContractModal/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import UnsupportedContractModal from './unsupported-contract-modal'; - -export default UnsupportedContractModal; diff --git a/packages/reports/src/Components/Elements/Modals/UnsupportedContractModal/unsupported-contract-modal.tsx b/packages/reports/src/Components/Elements/Modals/UnsupportedContractModal/unsupported-contract-modal.tsx deleted file mode 100644 index cbf6d4d48365..000000000000 --- a/packages/reports/src/Components/Elements/Modals/UnsupportedContractModal/unsupported-contract-modal.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; -import { Dialog } from '@deriv/components'; -import { localize, Localize } from '@deriv/translations'; -import { website_name } from '@deriv/shared'; -import { observer, useStore } from '@deriv/stores'; - -type TUnsupportedContractModalProps = { - is_loading?: boolean; - is_visible?: boolean; - onClose: () => void; - onConfirm: () => void; -}; - -const UnsupportedContractModal = observer( - ({ is_loading, is_visible: is_modal_visible, onConfirm, onClose }: TUnsupportedContractModalProps) => { - const { ui } = useStore(); - const { disableApp, enableApp, is_unsupported_contract_modal_visible } = ui; - const is_visible = !!(is_unsupported_contract_modal_visible || is_modal_visible); - - return ( - - - - ); - } -); - -export default UnsupportedContractModal; diff --git a/packages/reports/src/Components/Modals/__tests__/trade-modals.spec.tsx b/packages/reports/src/Components/Modals/__tests__/trade-modals.spec.tsx index 1a0924e15205..f421830ce490 100644 --- a/packages/reports/src/Components/Modals/__tests__/trade-modals.spec.tsx +++ b/packages/reports/src/Components/Modals/__tests__/trade-modals.spec.tsx @@ -6,15 +6,6 @@ import { TCoreStores } from '@deriv/stores/types'; import ReportsProviders from '../../../reports-providers'; import TradeModals from '../trade-modals'; -jest.mock('../../Elements/Modals/UnsupportedContractModal', () => - jest.fn(props => ( -
-
Unsupported contract modal
- - -
- )) -); jest.mock('../../Elements/Modals/MarketUnavailableModal', () => jest.fn(props => (
@@ -49,26 +40,9 @@ describe('TradeModals', () => { render(mockTradeModals(mock_root_store)); - expect(screen.getByText('Unsupported contract modal')).toBeInTheDocument(); expect(screen.getByText('Market unavailable modal')).toBeInTheDocument(); expect(screen.getByText('Services error modal')).toBeInTheDocument(); }); - it('should call function unsupportedContractOnConfirm if button onConfirm in UnsupportedContractModal component was clicked', () => { - const mock_root_store = mockStore({}); - - render(mockTradeModals(mock_root_store)); - userEvent.click(screen.getByText('onConfirm unsupported')); - - expect(mock_root_store.ui.toggleUnsupportedContractModal).toHaveBeenCalled(); - }); - it('should call function unsupportedContractOnClose if button onClose in UnsupportedContractModal component was clicked', () => { - const mock_root_store = mockStore({}); - - render(mockTradeModals(mock_root_store)); - userEvent.click(screen.getByText('onClose unsupported')); - - expect(mock_root_store.ui.toggleUnsupportedContractModal).toHaveBeenCalled(); - }); it('should call function marketUnavailableOnConfirm if button onConfirm in MarketUnavailableModal component was clicked', () => { const mock_root_store = mockStore({}); diff --git a/packages/reports/src/Components/Modals/trade-modals.tsx b/packages/reports/src/Components/Modals/trade-modals.tsx index 59824321767c..2994644cbf76 100644 --- a/packages/reports/src/Components/Modals/trade-modals.tsx +++ b/packages/reports/src/Components/Modals/trade-modals.tsx @@ -1,6 +1,5 @@ import React from 'react'; -import { getUrlSmartTrader, getStaticUrl } from '@deriv/shared'; -import UnsupportedContractModal from '../Elements/Modals/UnsupportedContractModal'; +import { getUrlSmartTrader } from '@deriv/shared'; import MarketUnavailableModal from '../Elements/Modals/MarketUnavailableModal'; import ServicesErrorModal from '../Elements/Modals/ServicesErrorModal'; import AccountVerificationPendingModal from '../Elements/Modals/AccountVerificationPendingModal'; @@ -17,7 +16,6 @@ const TradeModals = observer(() => { setHasOnlyForwardingContracts, setIsMFVericationPendingModal, toggleServicesErrorModal, - toggleUnsupportedContractModal, } = ui; const resetToPreviousMarket = () => { setHasOnlyForwardingContracts(false); @@ -36,19 +34,8 @@ const TradeModals = observer(() => { toggleServicesErrorModal(false); }; - const unsupportedContractOnConfirm = () => { - toggleUnsupportedContractModal(false); - }; - - const unsupportedContractOnClose = () => { - window.open(getStaticUrl('/')); - unsupportedContractOnConfirm(); - }; - return ( - - Date: Wed, 8 May 2024 14:05:34 +0800 Subject: [PATCH 4/4] fix: swtich to Localize --- .../account-verification-pending-modal.tsx | 4 ++-- .../Modals/MarketUnavailableModal/market-unavailable.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/reports/src/Components/Elements/Modals/AccountVerificationPendingModal/account-verification-pending-modal.tsx b/packages/reports/src/Components/Elements/Modals/AccountVerificationPendingModal/account-verification-pending-modal.tsx index 1444b27e90f0..d0df8a11878e 100644 --- a/packages/reports/src/Components/Elements/Modals/AccountVerificationPendingModal/account-verification-pending-modal.tsx +++ b/packages/reports/src/Components/Elements/Modals/AccountVerificationPendingModal/account-verification-pending-modal.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Button, Modal } from '@deriv/components'; -import { localize, Localize } from '@deriv/translations'; +import { Localize } from '@deriv/translations'; type TAccountVerificationPendingModalProps = { is_visible: boolean; @@ -11,7 +11,7 @@ const AccountVerificationPendingModal = ({ is_visible, onConfirm }: TAccountVeri } toggleModal={onConfirm} className='account-verification-pending-modal' > diff --git a/packages/reports/src/Components/Elements/Modals/MarketUnavailableModal/market-unavailable.tsx b/packages/reports/src/Components/Elements/Modals/MarketUnavailableModal/market-unavailable.tsx index 9764dad5faea..2d1154801113 100644 --- a/packages/reports/src/Components/Elements/Modals/MarketUnavailableModal/market-unavailable.tsx +++ b/packages/reports/src/Components/Elements/Modals/MarketUnavailableModal/market-unavailable.tsx @@ -17,7 +17,7 @@ const MarketUnavailableModal = observer(({ is_loading, onCancel, onConfirm }: TM return ( } confirm_button_text={localize('Stay on {{platform_name_trader}}', { platform_name_trader: getPlatformSettings('trader').name, })}