Skip to content

Commit

Permalink
refactor: [UPM-1080]/evgeniy/refactor passkeys code, rename flow (der…
Browse files Browse the repository at this point in the history
…iv-com#15237)

* refactor: [UPM-1080]/evgeniy/refactor passkeys code

* refactor: tests and code completion

* refactor: do not fetch passkeys list, when no need to open the modal

* refactor: tests fix, remove btn loader status, icons change

* refactor: do not call for passkeys if local storage value is false

* chore: added todo for tracking events

* refactor: client store remove unnecessary checks, tests, icons

* chore: skip changing the state for created status

* refactor: removed unnecessary li tag in modal

* chore: rename passkey (#44)

* chore: rename passkey

* chore: test fix

* fix: styles for rtl

* refactor: call success event only after success renaming

* chore: hide disabled dropdown item

* chore: review comments

* fix: accidentally removed clsx import

* chore: snackbar fix, loading while fething new name
  • Loading branch information
yauheni-deriv authored Jun 10, 2024
1 parent b7e377d commit 132ea1f
Show file tree
Hide file tree
Showing 46 changed files with 1,314 additions and 881 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useGetPasskeysList, useRegisterPasskey } from '@deriv/hooks';
import { routes } from '@deriv/shared';
import { mockStore, StoreProvider } from '@deriv/stores';
import Passkeys from '../passkeys';
import PasskeysList from '../components/passkeys-list';
import { PasskeysList } from '../components/passkeys-list';

const passkey_name_1 = 'Test Passkey 1';
const passkey_name_2 = 'Test Passkey 2';
Expand All @@ -19,7 +19,7 @@ export const mock_passkeys_list: React.ComponentProps<typeof PasskeysList>['pass
name: passkey_name_1,
last_used: 1633024800000,
created_at: 1633024800000,
stored_on: '',
stored_on: 'Test device 1',
icon: 'Test Icon 1',
passkey_id: 'mock-id-1',
},
Expand All @@ -28,7 +28,7 @@ export const mock_passkeys_list: React.ComponentProps<typeof PasskeysList>['pass
name: passkey_name_2,
last_used: 1633124800000,
created_at: 1634024800000,
stored_on: '',
stored_on: 'Test device 2',
icon: 'Test Icon 2',
passkey_id: 'mock-id-2',
},
Expand Down Expand Up @@ -64,6 +64,8 @@ describe('Passkeys', () => {
const create_passkey = 'Create passkey';
const error_message = 'We’re experiencing a temporary issue in processing your request. Please try again later.';
const error_title = 'Unable to process your request';
const ok_button = /ok/i;
const continue_button = /continue/i;

const tracking_event = 'ce_passkey_account_settings_form';
const getAnalyticsParams = (
Expand Down Expand Up @@ -98,13 +100,17 @@ describe('Passkeys', () => {
jest.clearAllMocks();
});

const RenderWrapper = ({ children }: React.PropsWithChildren) => (
<MemoryRouter>
<APIProvider>
<StoreProvider store={mock_store}>{children}</StoreProvider>
</APIProvider>
</MemoryRouter>
);
const renderComponent = () => {
render(
<MemoryRouter>
<APIProvider>
<StoreProvider store={mock_store}>
<Passkeys />
</StoreProvider>
</APIProvider>
</MemoryRouter>
);
};

const mockCreatePasskey = jest.fn();
const mockStartPasskeyRegistration = jest.fn();
Expand All @@ -117,11 +123,7 @@ describe('Passkeys', () => {
});

mock_store.ui.is_mobile = false;
render(
<RenderWrapper>
<Passkeys />
</RenderWrapper>
);
renderComponent();

expect(screen.queryByText(passkey_name_1)).not.toBeInTheDocument();
expect(screen.queryByText(passkey_name_2)).not.toBeInTheDocument();
Expand All @@ -134,11 +136,7 @@ describe('Passkeys', () => {
is_passkeys_list_loading: true,
});

render(
<RenderWrapper>
<Passkeys />
</RenderWrapper>
);
renderComponent();

expect(screen.getByText('MockLoading')).toBeInTheDocument();
expect(Analytics.trackEvent).not.toHaveBeenCalled();
Expand All @@ -151,29 +149,22 @@ describe('Passkeys', () => {

mock_store.common.network_status.class = 'offline';

render(
<RenderWrapper>
<Passkeys />
</RenderWrapper>
);
renderComponent();

expect(screen.getByText('MockLoading')).toBeInTheDocument();
expect(Analytics.trackEvent).not.toHaveBeenCalled();
});

it('renders existed passkeys correctly and triggers new passkey creation', async () => {
it('renders existed passkeys correctly and triggers new passkey creation', () => {
(useGetPasskeysList as jest.Mock).mockReturnValue({
passkeys_list: mock_passkeys_list,
});
(useRegisterPasskey as jest.Mock).mockReturnValue({
startPasskeyRegistration: mockStartPasskeyRegistration,
});

render(
<RenderWrapper>
<Passkeys />
</RenderWrapper>
);
renderComponent();

expect(screen.getByText(passkey_name_1)).toBeInTheDocument();
expect(screen.getByText(passkey_name_2)).toBeInTheDocument();

Expand All @@ -194,11 +185,7 @@ describe('Passkeys', () => {
startPasskeyRegistration: mockStartPasskeyRegistration,
});

render(
<RenderWrapper>
<Passkeys />
</RenderWrapper>
);
renderComponent();

expect(screen.getByText('Experience safer logins')).toBeInTheDocument();
const learn_more_button = screen.getByRole('button', { name: 'Learn more' });
Expand All @@ -221,11 +208,7 @@ describe('Passkeys', () => {
is_passkey_registered: true,
});

render(
<RenderWrapper>
<Passkeys />
</RenderWrapper>
);
renderComponent();

expect(screen.getByText('Success!')).toBeInTheDocument();
expect(Analytics.trackEvent).toHaveBeenCalledWith(
Expand All @@ -236,7 +219,7 @@ describe('Passkeys', () => {
userEvent.click(add_more_passkeys_button);
expect(Analytics.trackEvent).toHaveBeenCalledWith(tracking_event, getAnalyticsParams('add_more_passkeys'));

const create_passkey_button = screen.getByRole('button', { name: 'Create passkey' });
const create_passkey_button = screen.getByRole('button', { name: create_passkey });
expect(create_passkey_button).toBeInTheDocument();
expect(screen.queryByText('Success!')).not.toBeInTheDocument();
});
Expand All @@ -246,11 +229,7 @@ describe('Passkeys', () => {
is_passkey_registered: true,
});

render(
<RenderWrapper>
<Passkeys />
</RenderWrapper>
);
renderComponent();

expect(screen.getByText('Success!')).toBeInTheDocument();
expect(Analytics.trackEvent).toHaveBeenCalledWith(
Expand All @@ -276,21 +255,18 @@ describe('Passkeys', () => {
(useRegisterPasskey as jest.Mock).mockReturnValue({
createPasskey: mockCreatePasskey,
is_passkey_registration_started: true,
startPasskeyRegistration: mockStartPasskeyRegistration,
});

render(
<RenderWrapper>
<Passkeys />
</RenderWrapper>
);
renderComponent();

userEvent.click(screen.getByRole('button', { name: create_passkey }));
expect(screen.getByText('Just a reminder')).toBeInTheDocument();
expect(screen.getByText('Enable screen lock on your device.')).toBeInTheDocument();
expect(screen.getByText('Enable bluetooth.')).toBeInTheDocument();
expect(screen.getByText('Sign in to your Google or iCloud account.')).toBeInTheDocument();

const continue_button = screen.getByRole('button', { name: /continue/i });
userEvent.click(continue_button);
userEvent.click(screen.getByRole('button', { name: continue_button }));
expect(mockCreatePasskey).toBeCalledTimes(1);
expect(Analytics.trackEvent).toHaveBeenCalledWith(
tracking_event,
Expand All @@ -302,18 +278,21 @@ describe('Passkeys', () => {
(useRegisterPasskey as jest.Mock).mockReturnValue({
passkey_registration_error: { message: 'error' },
clearPasskeyRegistrationError: mockClearPasskeyRegistrationError,
startPasskeyRegistration: mockStartPasskeyRegistration,
createPasskey: mockCreatePasskey,
});

render(
<RenderWrapper>
<Passkeys />
</RenderWrapper>
);
renderComponent();

userEvent.click(screen.getByRole('button', { name: create_passkey }));
userEvent.click(screen.getByRole('button', { name: continue_button }));

await waitFor(() => {
expect(screen.getByText(error_message)).toBeInTheDocument();
expect(screen.getByText(error_title)).toBeInTheDocument();
});

const ok_button = screen.getByRole('button', { name: /ok/i });
expect(screen.getByText(error_message)).toBeInTheDocument();
expect(screen.getByText(error_title)).toBeInTheDocument();
userEvent.click(ok_button);
userEvent.click(screen.getByRole('button', { name: ok_button }));
await waitFor(() => {
expect(mockClearPasskeyRegistrationError).toBeCalledTimes(1);
expect(mockHistoryPush).toHaveBeenCalledWith(routes.traders_hub);
Expand All @@ -323,23 +302,26 @@ describe('Passkeys', () => {
it('renders passkeys list error modal and triggers closing', async () => {
(useRegisterPasskey as jest.Mock).mockReturnValue({
passkey_registration_error: null,
startPasskeyRegistration: mockStartPasskeyRegistration,
createPasskey: mockCreatePasskey,
});

(useGetPasskeysList as jest.Mock).mockReturnValue({
passkeys_list_error: { message: 'error' },
reloadPasskeysList: mockReloadPasskeysList,
});

render(
<RenderWrapper>
<Passkeys />
</RenderWrapper>
);
renderComponent();

userEvent.click(screen.getByRole('button', { name: create_passkey }));
userEvent.click(screen.getByRole('button', { name: continue_button }));

await waitFor(() => {
expect(screen.getByText(error_message)).toBeInTheDocument();
expect(screen.getByText(error_title)).toBeInTheDocument();
});

const ok_button = screen.getByRole('button', { name: /ok/i });
expect(screen.getByText(error_message)).toBeInTheDocument();
expect(screen.getByText(error_title)).toBeInTheDocument();
userEvent.click(ok_button);
userEvent.click(screen.getByRole('button', { name: ok_button }));
expect(mockHistoryPush).toHaveBeenCalledWith(routes.traders_hub);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { NoPasskeys } from '../no-passkeys';

describe('NoPasskeys', () => {
const mockOnPrimaryButtonClick = jest.fn();
const mockOnSecondaryButtonClick = jest.fn();

it('renders NoPasskeys component correctly', () => {
render(
<NoPasskeys
onPrimaryButtonClick={mockOnPrimaryButtonClick}
onSecondaryButtonClick={mockOnSecondaryButtonClick}
/>
);

expect(screen.getByText('Enhanced security is just a tap away.')).toBeInTheDocument();
expect(screen.getByText('Experience safer logins')).toBeInTheDocument();
userEvent.click(screen.getByRole('button', { name: /create passkey/i }));
userEvent.click(screen.getByRole('button', { name: /learn more/i }));
expect(mockOnPrimaryButtonClick).toHaveBeenCalled();
expect(mockOnSecondaryButtonClick).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
import React from 'react';
import { screen, render } from '@testing-library/react';
import PasskeyCard from '../passkey-card';
import userEvent from '@testing-library/user-event';
import { PasskeyCard } from '../passkey-card';
import { mock_passkeys_list } from '../../__tests__/passkeys.spec';

jest.mock('@deriv/shared', () => ({
...jest.requireActual('@deriv/shared'),
getOSNameWithUAParser: () => 'test OS',
}));

describe('PasskeyCard', () => {
it('renders the passkey card correctly', () => {
const mock_card: React.ComponentProps<typeof PasskeyCard> = {
id: 1,
name: 'Test Passkey',
last_used: 1633024800,
created_at: 1633024800,
stored_on: 'Device',
icon: 'IcPasskey',
passkey_id: 'mock-id',
};
const mockOnPasskeyMenuClick = jest.fn();
const mock_card = mock_passkeys_list[0];

render(<PasskeyCard {...mock_card} />);
it('renders the passkey card correctly and trigger menu with renaming', () => {
render(<PasskeyCard {...mock_card} onPasskeyMenuClick={mockOnPasskeyMenuClick} />);

expect(screen.getByText(/test passkey/i)).toBeInTheDocument();
expect(screen.getByText(/stored on/i)).toBeInTheDocument();
expect(screen.getByText(/last used/i)).toBeInTheDocument();
expect(screen.queryByText('Rename')).not.toBeInTheDocument();
const menu_button = screen.getByTestId('dt_dropdown_display');
userEvent.click(menu_button);
const rename_option = screen.getByText('Rename');
expect(rename_option).toBeInTheDocument();
userEvent.click(rename_option);
expect(mockOnPasskeyMenuClick).toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { PasskeyCreated } from '../passkey-created';

describe('PasskeyCreated', () => {
const mockOnPrimaryButtonClick = jest.fn();
const mockOnSecondaryButtonClick = jest.fn();

it('renders PasskeyCreated component correctly', () => {
render(
<PasskeyCreated
onPrimaryButtonClick={mockOnPrimaryButtonClick}
onSecondaryButtonClick={mockOnSecondaryButtonClick}
/>
);

expect(screen.getByText('Success!')).toBeInTheDocument();
expect(screen.getByText(/Your account is now secured with a passkey/)).toBeInTheDocument();
expect(screen.getByText(/Manage your passkey/)).toBeInTheDocument();
userEvent.click(screen.getByRole('button', { name: /continue trading/i }));
userEvent.click(screen.getByRole('button', { name: /add more passkeys/i }));
expect(mockOnPrimaryButtonClick).toHaveBeenCalled();
expect(mockOnSecondaryButtonClick).toHaveBeenCalled();
});
});
Loading

0 comments on commit 132ea1f

Please sign in to comment.