Skip to content

Commit

Permalink
[TRAH] Sergei / TRAH - 3679 / Add a full screen modal after Sign up -…
Browse files Browse the repository at this point in the history
… to allow users to go to demo or proceed with real account creation (deriv-com#15758)

* feat: add account icon

* feat: create modal and add styles

* feat: rename modal

* feat: add final logic and add analytics for open and cta

* feat: add tests for after-signup-flow component

* feat: create tests for modal

* feat: complete tests for modal

* feat: add tests for modal-content

* feat: modify tests, add check for passkey modal, modify modal for tablet view

* refactor: optimize unit tests

* feat: rename analytic action
  • Loading branch information
sergei-deriv authored Jul 19, 2024
1 parent 8030369 commit 78a22e0
Show file tree
Hide file tree
Showing 18 changed files with 583 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import React from 'react';
import AfterSignupFlow from '../after-signup-flow';
import { render } from '@testing-library/react';
import { StoreProvider, mockStore } from '@deriv/stores';
import { useContentFlag, useGrowthbookGetFeatureValue } from '@deriv/hooks';

jest.mock('@deriv/hooks', () => ({
...jest.requireActual('@deriv/hooks'),
useContentFlag: jest.fn(() => ({ is_cr_demo: true, is_eu_demo: false })),
useGrowthbookGetFeatureValue: jest.fn(() => [false, true]),
}));

describe('AfterSignupFlow', () => {
const mockedOpenRealAccountSignup = jest.fn();
const mockedSetIsFromSignupAccount = jest.fn();
const mockedSetIsSetupRealAccountOrGoToDemoModalVisible = jest.fn();

const mockDefault = mockStore({
client: {
is_logged_in: true,
has_active_real_account: false,
},
ui: {
is_from_signup_account: true,
openRealAccountSignup: mockedOpenRealAccountSignup,
setIsFromSignupAccount: mockedSetIsFromSignupAccount,
},
traders_hub: {
setIsSetupRealAccountOrGoToDemoModalVisible: mockedSetIsSetupRealAccountOrGoToDemoModalVisible,
},
});

const wrapper = (mock: ReturnType<typeof mockStore> = mockDefault) => {
const Component = ({ children }: { children: JSX.Element }) => (
<StoreProvider store={mock}>{children}</StoreProvider>
);
return Component;
};

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

it('check if growthbook feature flags are false', () => {
(useGrowthbookGetFeatureValue as jest.Mock).mockReturnValue([false, true]);

render(<AfterSignupFlow />, {
wrapper: wrapper(),
});

expect(mockedOpenRealAccountSignup).not.toBeCalled();
expect(mockedSetIsFromSignupAccount).not.toBeCalled();
expect(mockedSetIsSetupRealAccountOrGoToDemoModalVisible).not.toBeCalled();
});

it('check if growthbook feature flags "direct-real-account-creation-flow" is true for is_cr_demo = true', () => {
(useGrowthbookGetFeatureValue as jest.Mock).mockReturnValueOnce([true, true]);
(useContentFlag as jest.Mock).mockReturnValue({ is_cr_demo: true, is_eu_demo: false });

render(<AfterSignupFlow />, {
wrapper: wrapper(),
});

expect(mockedOpenRealAccountSignup).toBeCalled();
expect(mockedSetIsFromSignupAccount).toBeCalled();
expect(mockedSetIsSetupRealAccountOrGoToDemoModalVisible).not.toBeCalled();
});

it('check if growthbook feature flags "direct-real-account-creation-flow" is true for is_eu_demo = true', () => {
(useGrowthbookGetFeatureValue as jest.Mock).mockReturnValueOnce([true, true]);
(useContentFlag as jest.Mock).mockReturnValue({ is_cr_demo: false, is_eu_demo: true });

render(<AfterSignupFlow />, {
wrapper: wrapper(),
});

expect(mockedOpenRealAccountSignup).toBeCalled();
expect(mockedSetIsFromSignupAccount).toBeCalled();
expect(mockedSetIsSetupRealAccountOrGoToDemoModalVisible).not.toBeCalled();
});

it('check if growthbook feature flags "show_setup_real_or_go_demo" is true', () => {
(useGrowthbookGetFeatureValue as jest.Mock).mockReturnValueOnce([false, true]);
(useGrowthbookGetFeatureValue as jest.Mock).mockReturnValueOnce([true, true]);

render(<AfterSignupFlow />, {
wrapper: wrapper(),
});

expect(mockedOpenRealAccountSignup).not.toBeCalled();
expect(mockedSetIsFromSignupAccount).toBeCalled();
expect(mockedSetIsSetupRealAccountOrGoToDemoModalVisible).toBeCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, { useEffect } from 'react';
import { useContentFlag, useGrowthbookGetFeatureValue } from '@deriv/hooks';
import { observer, useStore } from '@deriv/stores';
import { Jurisdiction } from '@deriv/shared';

const AfterSignupFlow = observer(() => {
const { client, ui, traders_hub } = useStore();
const { openRealAccountSignup, is_from_signup_account, setIsFromSignupAccount } = ui;
const { is_logged_in, has_active_real_account } = client;
const { setIsSetupRealAccountOrGoToDemoModalVisible } = traders_hub;

const { is_cr_demo, is_eu_demo } = useContentFlag();

const [direct_to_real_account_creation] = useGrowthbookGetFeatureValue({
featureFlag: 'direct-real-account-creation-flow',
defaultValue: false,
});

const [show_setup_real_or_go_demo] = useGrowthbookGetFeatureValue({
featureFlag: 'show_setup_real_or_go_demo',
defaultValue: false,
});

useEffect(() => {
if (!has_active_real_account && is_from_signup_account && is_logged_in) {
// move the user directly to setup real account
if (direct_to_real_account_creation && !show_setup_real_or_go_demo) {
if (is_cr_demo) {
openRealAccountSignup(Jurisdiction.SVG);
setIsFromSignupAccount(false);
} else if (is_eu_demo) {
openRealAccountSignup(Jurisdiction.MALTA_INVEST);
setIsFromSignupAccount(false);
}
}

// show setup real or go to demo modal for user
if (!direct_to_real_account_creation && show_setup_real_or_go_demo) {
setIsSetupRealAccountOrGoToDemoModalVisible(true);
setIsFromSignupAccount(false);
}
}
}, [
is_cr_demo,
is_eu_demo,
has_active_real_account,
is_from_signup_account,
is_logged_in,
direct_to_real_account_creation,
openRealAccountSignup,
setIsFromSignupAccount,
]);

return null;
});

export default AfterSignupFlow;
3 changes: 3 additions & 0 deletions packages/appstore/src/components/after-signup-flow/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import AfterSignupFlow from './after-signup-flow';

export default AfterSignupFlow;
2 changes: 2 additions & 0 deletions packages/appstore/src/components/app.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@import url('https://fonts.googleapis.com/css2?family=Ubuntu:wght@700&display=swap');

.dashboard {
@include desktop-screen {
height: calc(100vh - #{$HEADER_HEIGHT} - #{$FOOTER_HEIGHT});
Expand Down
13 changes: 13 additions & 0 deletions packages/appstore/src/components/modals/modal-manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,17 @@ const CTraderTransferModal = makeLazyLoader(
() => <Loading />
)();

const SetupRealAccountOrGoToDemoModal = makeLazyLoader(
() =>
moduleLoader(
() =>
import(
/* webpackChunkName: "modal_setup-real-account-or-go-to-demo" */ './setup-real-account-or-go-to-demo-modal'
)
),
() => <Loading />
)();

type TCurrentList = DetailsOfEachMT5Loginid & {
enabled: number;
};
Expand Down Expand Up @@ -221,6 +232,7 @@ const ModalManager = () => {
is_failed_verification_modal_visible,
is_regulators_compare_modal_visible,
is_wallet_migration_failed,
is_setup_real_account_or_go_to_demo_modal_visible,
} = traders_hub;

const [password_manager, setPasswordManager] = React.useState<{
Expand Down Expand Up @@ -351,6 +363,7 @@ const ModalManager = () => {
{(is_eligible || is_real_wallets_upgrade_on || is_in_progress) && <WalletsUpgradeModal />}
</React.Fragment>
)}
{is_setup_real_account_or_go_to_demo_modal_visible && <SetupRealAccountOrGoToDemoModal />}
</React.Fragment>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import React from 'react';
import { SetupRealAccountOrGoToDemoModalContent } from '../setup-real-account-or-go-to-demo-modal-content';
import { render, screen } from '@testing-library/react';
import { Analytics } from '@deriv-com/analytics';
import { useContentFlag } from '@deriv/hooks';
import { StoreProvider, mockStore } from '@deriv/stores';
import userEvent from '@testing-library/user-event';
import { Jurisdiction } from '@deriv/shared';

jest.mock('@deriv/hooks', () => ({
...jest.requireActual('@deriv/hooks'),
useContentFlag: jest.fn(() => ({ is_cr_demo: true, is_eu_demo: false })),
}));

jest.mock('@deriv-com/analytics', () => ({
...jest.requireActual('@deriv-com/analytics'),
Analytics: {
trackEvent: jest.fn(),
},
}));

describe('SetupRealAccountOrGoToDemoModalContent', () => {
const mockedOpenRealAccountSignup = jest.fn();
const mockedSetIsFromSignupAccount = jest.fn();
const mockedSetIsSetupRealAccountOrGoToDemoModalVisible = jest.fn();
const mockTrack = Analytics.trackEvent;

const mockDefault = mockStore({
ui: {
is_from_signup_account: true,
openRealAccountSignup: mockedOpenRealAccountSignup,
setIsFromSignupAccount: mockedSetIsFromSignupAccount,
},
traders_hub: {
is_setup_real_account_or_go_to_demo_modal_visible: true,
setIsSetupRealAccountOrGoToDemoModalVisible: mockedSetIsSetupRealAccountOrGoToDemoModalVisible,
},
});

const wrapper = (mock: ReturnType<typeof mockStore> = mockDefault) => {
const Component = ({ children }: { children: JSX.Element }) => (
<StoreProvider store={mock}>{children}</StoreProvider>
);
return Component;
};

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

it('Should render correctly in desktop', () => {
const { container } = render(<SetupRealAccountOrGoToDemoModalContent />, {
wrapper: wrapper(),
});

const title = screen.getByText(/start your trading journey/i);

expect(container).toBeInTheDocument();
expect(title).toBeInTheDocument();
});

it('Should render correctly in responsive', () => {
const { container } = render(<SetupRealAccountOrGoToDemoModalContent is_responsive />, {
wrapper: wrapper(),
});

const title = screen.getByText(/start your trading journey/i);

expect(container).toBeInTheDocument();
expect(title).toBeInTheDocument();
});

it('User click "setup real account" button when is_cr_demo = true', () => {
(useContentFlag as jest.Mock).mockReturnValue({ is_cr_demo: true, is_eu_demo: false });

const { container } = render(<SetupRealAccountOrGoToDemoModalContent />, {
wrapper: wrapper(),
});

const setup_btn = screen.getByRole('button', {
name: /set up your real account/i,
});

expect(container).toBeInTheDocument();

userEvent.click(setup_btn);

expect(mockTrack).toBeCalledTimes(1);

expect(mockedOpenRealAccountSignup).toBeCalledTimes(1);
expect(mockedOpenRealAccountSignup).toBeCalledWith(Jurisdiction.SVG);

expect(mockedSetIsFromSignupAccount).toBeCalledTimes(1);
expect(mockedSetIsFromSignupAccount).toBeCalledWith(false);

expect(mockedSetIsSetupRealAccountOrGoToDemoModalVisible).toBeCalledTimes(1);
expect(mockedSetIsSetupRealAccountOrGoToDemoModalVisible).toBeCalledWith(false);
});

it('User click "setup real account" button when is_eu_demo = true', () => {
(useContentFlag as jest.Mock).mockReturnValue({ is_cr_demo: false, is_eu_demo: true });

const { container } = render(<SetupRealAccountOrGoToDemoModalContent />, {
wrapper: wrapper(),
});

const setup_btn = screen.getByRole('button', {
name: /set up your real account/i,
});

expect(container).toBeInTheDocument();

userEvent.click(setup_btn);

expect(mockTrack).toBeCalledTimes(1);

expect(mockedOpenRealAccountSignup).toBeCalledTimes(1);
expect(mockedOpenRealAccountSignup).toBeCalledWith(Jurisdiction.MALTA_INVEST);

expect(mockedSetIsFromSignupAccount).toBeCalledTimes(1);
expect(mockedSetIsFromSignupAccount).toBeCalledWith(false);

expect(mockedSetIsSetupRealAccountOrGoToDemoModalVisible).toBeCalledTimes(1);
expect(mockedSetIsSetupRealAccountOrGoToDemoModalVisible).toBeCalledWith(false);
});

it('User click "take me to demo" button', () => {
const { container } = render(<SetupRealAccountOrGoToDemoModalContent />, {
wrapper: wrapper(),
});

const demo_btn = screen.getByRole('button', {
name: /take me to demo/i,
});

expect(container).toBeInTheDocument();

userEvent.click(demo_btn);

expect(mockTrack).toBeCalledTimes(1);

expect(mockedSetIsFromSignupAccount).toBeCalledTimes(1);
expect(mockedSetIsFromSignupAccount).toBeCalledWith(false);

expect(mockedSetIsSetupRealAccountOrGoToDemoModalVisible).toBeCalledTimes(1);
expect(mockedSetIsSetupRealAccountOrGoToDemoModalVisible).toBeCalledWith(false);
});
});
Loading

0 comments on commit 78a22e0

Please sign in to comment.