1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-23 02:10:12 +01:00

18566 firefox ledger u2f message (#18570)

* check for user agent

* fix regex

* fix lint

* update link

* add test

* update message

* update coverage

* fix test names

* update text

* add whats new notification for firefox and ledger users

* update action button link

* update notification text

* update viewed notification

* update coverage

* update coverage
This commit is contained in:
Monte Lai 2023-04-20 05:53:07 +08:00 committed by Dan J Miller
parent baf0ad5c21
commit f9a3bc8070
9 changed files with 381 additions and 13 deletions

View File

@ -2515,6 +2515,19 @@
"message": "Swapping on mobile is here!",
"description": "Title for a notification in the 'See What's New' popup. Tells users that they can now use MetaMask Swaps on Mobile."
},
"notifications20ActionText": {
"message": "Learn more",
"description": "The 'call to action' on the button, or link, of the 'Stay secure' notification. Upon clicking, users will be taken to a ledger page to resolve the U2F connection issue."
},
"notifications20Description": {
"message": "If you're on the latest version of Firefox, you might be experiencing an issue related to Firefox dropping U2F support.",
"description": "Description of a notification in the 'See What's New' popup. Describes the U2F support being dropped by firefox and that it affects ledger users."
},
"notifications20Title": {
"message": "Ledger and Firefox Users Experiencing Connection Issues",
"description": "Title for a notification in the 'See What's New' popup. Tells users that latest firefox users using U2F may experience connection issues."
},
"notifications3ActionText": {
"message": "Read more",
"description": "The 'call to action' on the button, or link, of the 'Stay secure' notification. Upon clicking, users will be taken to a page about security on the metamask support website."
@ -4518,6 +4531,22 @@
"transferFrom": {
"message": "Transfer from"
},
"troubleConnectingToLedgerU2FOnFirefox": {
"message": "We're having trouble connecting your Ledger. $1",
"description": "$1 is a link to the wallet connection guide;"
},
"troubleConnectingToLedgerU2FOnFirefox2": {
"message": "Review our hardware wallet connection guide and try again.",
"description": "$1 of the ledger wallet connection guide"
},
"troubleConnectingToLedgerU2FOnFirefoxLedgerSolution": {
"message": "If you're on the latest version of Firefox, you might be experiencing an issue related to Firefox dropping U2F support. Learn how to fix this issue $1.",
"description": "It is a link to the ledger website for the workaround."
},
"troubleConnectingToLedgerU2FOnFirefoxLedgerSolution2": {
"message": "here",
"description": "Second part of the error message; It is a link to the ledger website for the workaround."
},
"troubleConnectingToWallet": {
"message": "We had trouble connecting to your $1, try reviewing $2 and try again.",
"description": "$1 is the wallet device name; $2 is a link to wallet connection guide"

View File

@ -6,10 +6,10 @@
// subset of files to check against these targets.
module.exports = {
global: {
lines: 66,
branches: 54.4,
statements: 65,
functions: 58.5,
lines: 66.7,
branches: 54.9,
statements: 65.75,
functions: 59.55,
},
transforms: {
branches: 100,

View File

@ -102,6 +102,10 @@ export const UI_NOTIFICATIONS = {
width: '100%',
},
},
20: {
id: 20,
date: null,
},
};
export const getTranslatedUINotifications = (t, locale) => {
@ -279,5 +283,16 @@ export const getTranslatedUINotifications = (t, locale) => {
)
: '',
},
20: {
...UI_NOTIFICATIONS[20],
title: t('notifications20Title'),
description: [t('notifications20Description')],
actionText: t('notifications20ActionText'),
date: UI_NOTIFICATIONS[20].date
? new Intl.DateTimeFormat(formattedLocale).format(
new Date(UI_NOTIFICATIONS[20].date),
)
: '',
},
};
};

View File

@ -73,6 +73,12 @@ function getActionFunctionById(id, history) {
updateViewedNotifications({ 19: true });
history.push(`${EXPERIMENTAL_ROUTE}#autodetect-nfts`);
},
20: () => {
updateViewedNotifications({ 20: true });
global.platform.openTab({
url: ZENDESK_URLS.LEDGER_FIREFOX_U2F_GUIDE,
});
},
};
return actionFunctions[id];

View File

@ -15,6 +15,8 @@ const ZENDESK_URLS = {
'https://metamask.zendesk.com/hc/en-us/articles/360015289932',
INFURA_BLOCKAGE:
'https://metamask.zendesk.com/hc/en-us/articles/360059386712',
LEDGER_FIREFOX_U2F_GUIDE:
'https://support.ledger.com/hc/en-us/articles/10371387758493-MetaMask-Firefox-Ledger-Integration-Issue?support=true',
LEGACY_WEB3: 'https://metamask.zendesk.com/hc/en-us/articles/360053147012',
NFT_TOKENS:
'https://metamask.zendesk.com/hc/en-us/articles/360058238591-NFT-tokens-in-MetaMask-wallet',

File diff suppressed because one or more lines are too long

View File

@ -20,7 +20,14 @@ import {
HardwareDeviceNames,
LedgerTransportTypes,
} from '../../../../shared/constants/hardware-wallets';
import {
BUTTON_TYPES,
BUTTON_SIZES,
Button,
Text,
} from '../../../components/component-library';
import ZENDESK_URLS from '../../../helpers/constants/zendesk-url';
import { TextColor } from '../../../helpers/constants/design-system';
import SelectHardware from './select-hardware';
import AccountList from './account-list';
@ -74,6 +81,7 @@ class ConnectHardwareForm extends Component {
browserSupported: true,
unlocked: false,
device: null,
isFirefox: false,
};
UNSAFE_componentWillReceiveProps(nextProps) {
@ -89,6 +97,10 @@ class ConnectHardwareForm extends Component {
componentDidMount() {
this.checkIfUnlocked();
const useAgent = window.navigator.userAgent;
if (/Firefox/u.test(useAgent)) {
this.setState({ isFirefox: true });
}
}
async checkIfUnlocked() {
@ -287,23 +299,64 @@ class ConnectHardwareForm extends Component {
renderError() {
if (this.state.error === U2F_ERROR) {
if (this.state.device === 'ledger' && this.state.isFirefox) {
return (
<>
<Text color={TextColor.warningDefault} margin={[5, 5, 2]}>
{this.context.t('troubleConnectingToLedgerU2FOnFirefox', [
// eslint-disable-next-line react/jsx-key
<Button
type={BUTTON_TYPES.LINK}
href={ZENDESK_URLS.HARDWARE_CONNECTION}
size={BUTTON_SIZES.INHERIT}
key="u2f-error-1"
as="a"
block={false}
target="_blank"
rel="noopener noreferrer"
>
{this.context.t('troubleConnectingToLedgerU2FOnFirefox2')}
</Button>,
])}
</Text>
<Text color={TextColor.warningDefault} margin={[5, 5, 2]}>
{this.context.t(
'troubleConnectingToLedgerU2FOnFirefoxLedgerSolution',
[
// eslint-disable-next-line react/jsx-key
<Button
type={BUTTON_TYPES.LINK}
href={ZENDESK_URLS.LEDGER_FIREFOX_U2F_GUIDE}
size={BUTTON_SIZES.INHERIT}
key="u2f-error-1"
as="a"
target="_blank"
rel="noopener noreferrer"
>
{this.context.t(
'troubleConnectingToLedgerU2FOnFirefoxLedgerSolution2',
)}
</Button>,
],
)}
</Text>
</>
);
}
return (
<p className="hw-connect__error">
<Text color={TextColor.warningDefault} margin={[5, 5, 2]}>
{this.context.t('troubleConnectingToWallet', [
this.state.device,
// eslint-disable-next-line react/jsx-key
<a
<Button
type={BUTTON_TYPES.LINK}
href={ZENDESK_URLS.HARDWARE_CONNECTION}
key="hardware-connection-guide"
target="_blank"
rel="noopener noreferrer"
className="hw-connect__link"
style={{ marginLeft: '5px', marginRight: '5px' }}
key="u2f-error-1"
>
{this.context.t('walletConnectionGuide')}
</a>,
</Button>,
])}
</p>
</Text>
);
}
return this.state.error ? (

View File

@ -0,0 +1,151 @@
import configureMockStore from 'redux-mock-store';
import { fireEvent, waitFor } from '@testing-library/react';
import thunk from 'redux-thunk';
import React from 'react';
import { renderWithProvider } from '../../../../test/lib/render-helpers';
import {
LedgerTransportTypes,
HardwareDeviceNames,
} from '../../../../shared/constants/hardware-wallets';
import ConnectHardwareForm from '.';
const mockConnectHardware = jest.fn();
const mockCheckHardwareStatus = jest.fn().mockResolvedValue(false);
jest.mock('../../../store/actions', () => ({
connectHardware: () => mockConnectHardware,
checkHardwareStatus: () => mockCheckHardwareStatus,
}));
jest.mock('../../../selectors', () => ({
getCurrentChainId: () => jest.fn().mockResolvedValue('0x1'),
getRpcPrefsForCurrentProvider: () => jest.fn().mockResolvedValue({}),
getMetaMaskAccountsConnected: () => jest.fn().mockResolvedValue([]),
getMetaMaskAccounts: () => jest.fn().mockResolvedValue([]),
}));
jest.mock('../../../ducks/history/history', () => ({
getMostRecentOverviewPage: () => jest.fn().mockResolvedValue('/'),
}));
const mockProps = {
forgetDevice: jest.fn(),
showAlert: jest.fn(),
hideAlert: jest.fn(),
unlockHardwareWalletAccount: jest.fn(),
setHardwareWalletDefaultHdPath: jest.fn(),
history: {},
defaultHdPath: "m/44'/60'/0'/0",
mostRecentOverviewPage: '',
};
const mockState = {
metamask: {
provider: {
chainId: '0x1',
},
},
appState: {
networkDropdownOpen: false,
gasIsLoading: false,
isLoading: false,
modal: {
open: false,
modalState: {
name: null,
props: {},
},
previousModalState: {
name: null,
},
},
warning: null,
chainId: '0x1',
rpcPrefs: null,
accounts: [],
connectedAccounts: [],
defaultHdPaths: {
[HardwareDeviceNames.lattice]: "m/44'/60'/0'/0",
[HardwareDeviceNames.ledger]: "m/44'/60'/0'/0",
[HardwareDeviceNames.trezor]: "m/44'/60'/0'/0",
},
mostRecentOverviewPage: '',
ledgerTransportType: LedgerTransportTypes.webhid,
},
};
describe('ConnectHardwareForm', () => {
const mockStore = configureMockStore([thunk])(mockState);
it('should match snapshot', () => {
const { container } = renderWithProvider(
<ConnectHardwareForm {...mockProps} />,
mockStore,
);
expect(container).toMatchSnapshot();
});
describe('U2F Error', () => {
it('should render a U2F error', async () => {
mockConnectHardware.mockRejectedValue(new Error('U2F Error'));
const mockStateWithU2F = Object.assign(mockState, {});
mockStateWithU2F.appState.ledgerTransportType = LedgerTransportTypes.u2f;
const mockStoreWithU2F = configureMockStore([thunk])(mockStateWithU2F);
const { getByText, getByLabelText, queryByText } = renderWithProvider(
<ConnectHardwareForm {...mockProps} />,
mockStoreWithU2F,
);
const ledgerButton = getByLabelText('Ledger');
const continueButton = getByText('Continue');
fireEvent.click(ledgerButton);
fireEvent.click(continueButton);
await waitFor(() => {
expect(
getByText('We had trouble connecting to your ledger, try reviewing', {
exact: false,
}),
).toBeInTheDocument();
const firefoxError = queryByText(
"If you're on the latest version of Firefox, you might be experiencing an issue related to Firefox dropping U2F support. Learn how to fix this issue",
{ exact: false },
);
expect(firefoxError).not.toBeInTheDocument();
});
});
it('should render a different U2F error for firefox', async () => {
jest
.spyOn(window.navigator, 'userAgent', 'get')
.mockReturnValue(
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:95.0) Gecko/20100101 Firefox/95.0',
);
mockConnectHardware.mockRejectedValue(new Error('U2F Error'));
const mockStateWithU2F = Object.assign(mockState, {});
mockStateWithU2F.appState.ledgerTransportType = LedgerTransportTypes.u2f;
const mockStoreWithU2F = configureMockStore([thunk])(mockStateWithU2F);
const { getByText, getByLabelText } = renderWithProvider(
<ConnectHardwareForm {...mockProps} />,
mockStoreWithU2F,
);
const ledgerButton = getByLabelText('Ledger');
const continueButton = getByText('Continue');
fireEvent.click(ledgerButton);
fireEvent.click(continueButton);
await waitFor(() => {
expect(
getByText(
"If you're on the latest version of Firefox, you might be experiencing an issue related to Firefox dropping U2F support. Learn how to fix this issue",
{ exact: false },
),
).toBeInTheDocument();
});
});
});
});

View File

@ -939,6 +939,7 @@ function getAllowedAnnouncementIds(state) {
const supportsWebHid = window.navigator.hid !== undefined;
const currentlyUsingLedgerLive =
getLedgerTransportType(state) === LedgerTransportTypes.live;
const isFirefox = window.navigator.userAgent.includes('Firefox');
return {
1: false,
@ -960,6 +961,7 @@ function getAllowedAnnouncementIds(state) {
17: false,
18: true,
19: true,
20: currentKeyringIsLedger && isFirefox,
};
}