Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 12 additions & 0 deletions app/_locales/en/messages.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions app/_locales/en_GB/messages.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions test/e2e/page-objects/pages/onboarding/secure-wallet-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ class SecureWalletPage {
private readonly revealSecretRecoveryPhraseButton =
'[data-testid="recovery-phrase-reveal"]';

private readonly skipAccountSecurityMessage = {
text: 'Skip account security?',
tag: 'h3',
};

private readonly skipSRPBackupCheckbox =
'[data-testid="skip-srp-backup-checkbox"]';

private readonly skipSRPBackupConfirmButton =
'[data-testid="skip-srp-backup-button"]';

private readonly secureWalletRecommendedButton =
'[data-testid="recovery-phrase-remind-later"]';

Expand Down Expand Up @@ -158,6 +169,11 @@ class SecureWalletPage {
async skipSRPBackup(): Promise<void> {
console.log('Skip SRP backup on Reveal SRP Onboarding page');
await this.driver.clickElement(this.secureWalletRecommendedButton);
await this.driver.waitForSelector(this.skipAccountSecurityMessage);
await this.driver.clickElement(this.skipSRPBackupCheckbox);
await this.driver.clickElementAndWaitToDisappear(
this.skipSRPBackupConfirmButton,
);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`SkipSRPBackup should match snapshot 1`] = `<div />`;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: SkipSRPBackup Snapshot Missing Modal Content

The snapshot for SkipSRPBackup shows only <div />, which indicates the component is not rendering its modal content (Icon, Text, Checkbox, Buttons, etc.). This is a broken snapshot that doesn't validate the actual component output. The snapshot should contain the full Modal structure with ModalOverlay, ModalContent, ModalHeader with Icon and Text, Checkbox, and Button elements.

Fix in Cursor Fix in Web

Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import React, { useState, useContext, useCallback, useEffect } from 'react';
import { useNavigate, useLocation } from 'react-router-dom-v5-compat';
import { useSelector, useDispatch } from 'react-redux';
import { useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import { useI18nContext } from '../../../hooks/useI18nContext';
import {
ONBOARDING_CONFIRM_SRP_ROUTE,
ONBOARDING_METAMETRICS,
ONBOARDING_REVEAL_SRP_ROUTE,
ONBOARDING_COMPLETION_ROUTE,
REVEAL_SRP_LIST_ROUTE,
} from '../../../helpers/constants/routes';
import {
Expand Down Expand Up @@ -39,26 +37,21 @@ import {
MetaMetricsEventName,
} from '../../../../shared/constants/metametrics';
import { MetaMetricsContext } from '../../../contexts/metametrics';
import { getHDEntropyIndex, getFirstTimeFlowType } from '../../../selectors';
import { getHDEntropyIndex } from '../../../selectors';
import SRPDetailsModal from '../../../components/app/srp-details-modal';
import { setSeedPhraseBackedUp } from '../../../store/actions';
import { TraceName } from '../../../../shared/lib/trace';
import { getBrowserName } from '../../../../shared/modules/browser-runtime.utils';
import { PLATFORM_FIREFOX } from '../../../../shared/constants/app';
import { FirstTimeFlowType } from '../../../../shared/constants/onboarding';
import RecoveryPhraseChips from './recovery-phrase-chips';
import SkipSRPBackup from './skip-srp-backup-popover';

export default function RecoveryPhrase({ secretRecoveryPhrase }) {
const navigate = useNavigate();
const t = useI18nContext();
const { search } = useLocation();
const dispatch = useDispatch();
const firstTimeFlowType = useSelector(getFirstTimeFlowType);
const trackEvent = useContext(MetaMetricsContext);
const { bufferedEndTrace } = trackEvent;
const hdEntropyIndex = useSelector(getHDEntropyIndex);
const [phraseRevealed, setPhraseRevealed] = useState(false);
const [showSrpDetailsModal, setShowSrpDetailsModal] = useState(false);
const [showSkipSRPBackupPopover, setShowSkipSRPBackupPopover] =
useState(false);
const searchParams = new URLSearchParams(search);
const isFromReminder = searchParams.get('isFromReminder');
const isFromSettingsSecurity = searchParams.get('isFromSettingsSecurity');
Expand Down Expand Up @@ -112,37 +105,16 @@ export default function RecoveryPhrase({ secretRecoveryPhrase }) {
setShowSrpDetailsModal(true);
}, [trackEvent]);

const handleRemindLater = useCallback(async () => {
await dispatch(setSeedPhraseBackedUp(false));

const handleClickNotRecommended = () => {
trackEvent({
category: MetaMetricsEventCategory.Onboarding,
event: MetaMetricsEventName.OnboardingWalletSecuritySkipConfirmed,
event: MetaMetricsEventName.OnboardingWalletSecuritySkipInitiated,
properties: {
// TODO: Fix in https://github.com/MetaMask/metamask-extension/issues/31860
// eslint-disable-next-line @typescript-eslint/naming-convention
hd_entropy_index: hdEntropyIndex,
hd_entropy_index: hdEntropyIndex ?? 0,
},
});
bufferedEndTrace?.({ name: TraceName.OnboardingNewSrpCreateWallet });
bufferedEndTrace?.({ name: TraceName.OnboardingJourneyOverall });

if (
getBrowserName() === PLATFORM_FIREFOX ||
firstTimeFlowType === FirstTimeFlowType.restore
) {
navigate(ONBOARDING_COMPLETION_ROUTE, { replace: true });
} else {
navigate(ONBOARDING_METAMETRICS, { replace: true });
}
}, [
bufferedEndTrace,
dispatch,
firstTimeFlowType,
hdEntropyIndex,
navigate,
trackEvent,
]);
setShowSkipSRPBackupPopover(true);
};

const handleBack = useCallback(() => {
navigate(
Expand All @@ -169,6 +141,14 @@ export default function RecoveryPhrase({ secretRecoveryPhrase }) {
data-testid="recovery-phrase"
>
<Box>
{showSkipSRPBackupPopover &&
!isFromReminder &&
!isFromSettingsSecurity && (
<SkipSRPBackup
onClose={() => setShowSkipSRPBackupPopover(false)}
secureYourWallet={handleContinue}
/>
)}
{showSrpDetailsModal && (
<SRPDetailsModal onClose={() => setShowSrpDetailsModal(false)} />
)}
Expand Down Expand Up @@ -274,7 +254,7 @@ export default function RecoveryPhrase({ secretRecoveryPhrase }) {
width={BlockSize.Full}
variant={ButtonVariant.Link}
size={ButtonSize.Lg}
onClick={handleRemindLater}
onClick={handleClickNotRecommended}
type="button"
data-testid="recovery-phrase-remind-later"
>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fireEvent } from '@testing-library/react';
import { fireEvent, waitFor } from '@testing-library/react';
import React from 'react';
import configureMockStore from 'redux-mock-store';
import { renderWithProvider } from '../../../../test/lib/render-helpers-navigate';
Expand Down Expand Up @@ -170,4 +170,30 @@ describe('Review Recovery Phrase Component', () => {
replace: true,
});
});

it('should show skip srp backup popover when not from reminder and not from settings security', async () => {
const { getByTestId } = renderWithProvider(
<RecoveryPhrase {...props} />,
mockStore,
);

const remindLaterButton = getByTestId('recovery-phrase-remind-later');

fireEvent.click(remindLaterButton);

expect(getByTestId('skip-srp-backup-modal')).toBeInTheDocument();

const checkbox = getByTestId('skip-srp-backup-checkbox');
expect(checkbox).toBeInTheDocument();

const confirmSkip = getByTestId('skip-srp-backup-button');
expect(confirmSkip).toBeInTheDocument();
expect(confirmSkip).toBeDisabled();

fireEvent.click(checkbox);

await waitFor(() => {
expect(confirmSkip).toBeEnabled();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import React from 'react';
import { fireEvent, waitFor } from '@testing-library/react';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { renderWithProvider } from '../../../../test/lib/render-helpers-navigate';
import * as browserRuntime from '../../../../shared/modules/browser-runtime.utils';
import {
PLATFORM_FIREFOX,
PLATFORM_CHROME,
} from '../../../../shared/constants/app';
import { FirstTimeFlowType } from '../../../../shared/constants/onboarding';
import {
ONBOARDING_COMPLETION_ROUTE,
ONBOARDING_METAMETRICS,
} from '../../../helpers/constants/routes';
import SkipSRPBackup from './skip-srp-backup-popover';

const mockNavigate = jest.fn();

jest.mock('react-router-dom-v5-compat', () => {
return {
...jest.requireActual('react-router-dom-v5-compat'),
useNavigate: () => mockNavigate,
};
});

describe('SkipSRPBackup', () => {
const mockStore = {
metamask: {
firstTimeFlowType: FirstTimeFlowType.create,
networkConfigurationsByChainId: {
'0x1': {
chainId: '0x1',
name: 'Ethereum Mainnet',
nativeCurrency: 'ETH',
defaultRpcEndpointIndex: 0,
rpcEndpoints: [
{
type: 'custom',
url: 'https://mainnet.infura.io',
networkClientId: 'mainnet',
},
],
blockExplorerUrls: [],
},
},
selectedNetworkClientId: 'mainnet',
networksMetadata: {
mainnet: {
EIPS: { 1559: true },
status: 'available',
},
},
internalAccounts: {
accounts: {
accountId: {
address: '0x0000000000000000000000000000000000000000',
metadata: {
keyring: 'HD Key Tree',
},
},
},
selectedAccount: 'accountId',
},
keyrings: [
{
type: 'HD Key Tree',
accounts: ['0x0000000000000000000000000000000000000000'],
},
],
},
localeMessages: {
currentLocale: 'en',
},
};

const store = configureMockStore([thunk])(mockStore);

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

it('should match snapshot', () => {
const { container } = renderWithProvider(
<SkipSRPBackup onClose={jest.fn()} secureYourWallet={jest.fn()} />,
store,
);
expect(container).toMatchSnapshot();
});

it('should navigate to onboarding metametrics when skip is confirmed on non-Firefox browser', async () => {
jest
.spyOn(browserRuntime, 'getBrowserName')
.mockReturnValue(PLATFORM_CHROME);

const { getByTestId } = renderWithProvider(
<SkipSRPBackup onClose={jest.fn()} secureYourWallet={jest.fn()} />,
store,
);

const checkbox = getByTestId('skip-srp-backup-checkbox');
expect(checkbox).toBeInTheDocument();

const confirmSkip = getByTestId('skip-srp-backup-button');
expect(confirmSkip).toBeInTheDocument();
expect(confirmSkip).toBeDisabled();

fireEvent.click(checkbox);

await waitFor(() => {
expect(confirmSkip).toBeEnabled();
});

fireEvent.click(confirmSkip);

await waitFor(() => {
expect(mockNavigate).toHaveBeenCalledWith(ONBOARDING_METAMETRICS);
});
});

it('should navigate to onboarding completion when skip is confirmed on Firefox', async () => {
jest
.spyOn(browserRuntime, 'getBrowserName')
.mockReturnValue(PLATFORM_FIREFOX);

const { getByTestId } = renderWithProvider(
<SkipSRPBackup onClose={jest.fn()} secureYourWallet={jest.fn()} />,
store,
);

const checkbox = getByTestId('skip-srp-backup-checkbox');
expect(checkbox).toBeInTheDocument();

const confirmSkip = getByTestId('skip-srp-backup-button');
expect(confirmSkip).toBeInTheDocument();
expect(confirmSkip).toBeDisabled();

fireEvent.click(checkbox);

await waitFor(() => {
expect(confirmSkip).toBeEnabled();
});

fireEvent.click(confirmSkip);

await waitFor(() => {
expect(mockNavigate).toHaveBeenCalledWith(ONBOARDING_COMPLETION_ROUTE);
});
});
});
Loading
Loading