Commit 8548b33b authored by Vignesh Mohankumar's avatar Vignesh Mohankumar Committed by GitHub

feat: show injected options in wallet browsers (#3995)

* feat: show injected options in wallet browsers

* initial testing

* more mocking

* mock more

* mobile tests

* updates

* add data test

* finally got the mock to work

* WORKING

* uncomment

* rm console.log

* fix

* check length

* fix tests to use useWeb3React

* rm

* rename tests
parent 0e148bb1
...@@ -22,20 +22,21 @@ jest.mock( ...@@ -22,20 +22,21 @@ jest.mock(
`CurrencyLogo currency=${currency.symbol}` `CurrencyLogo currency=${currency.symbol}`
) )
jest.mock('hooks/useActiveWeb3React', () => { jest.mock('@web3-react/core', () => {
const web3React = jest.requireActual('@web3-react/core')
return { return {
__esModule: true, useWeb3React: () => ({
default: () => ({
account: '123', account: '123',
active: true, isActive: true,
}), }),
...web3React,
} }
}) })
jest.mock('../../../state/wallet/hooks', () => { jest.mock('../../../state/wallet/hooks', () => {
return { return {
useCurrencyBalance: (currency: Currency) => { useCurrencyBalance: (currency: Currency) => {
return mockCurrencyAmt[(currency as mockToken).address] return mockCurrencyAmt[(currency as mockToken)?.address]
}, },
} }
}) })
......
import { Trans } from '@lingui/macro' import { Trans } from '@lingui/macro'
import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { LightGreyCard } from 'components/Card' import { LightGreyCard } from 'components/Card'
import QuestionHelper from 'components/QuestionHelper' import QuestionHelper from 'components/QuestionHelper'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import useTheme from 'hooks/useTheme' import useTheme from 'hooks/useTheme'
import { CSSProperties, MutableRefObject, useCallback, useMemo } from 'react' import { CSSProperties, MutableRefObject, useCallback, useMemo } from 'react'
import { FixedSizeList } from 'react-window' import { FixedSizeList } from 'react-window'
...@@ -114,7 +114,7 @@ function CurrencyRow({ ...@@ -114,7 +114,7 @@ function CurrencyRow({
style: CSSProperties style: CSSProperties
showCurrencyAmount?: boolean showCurrencyAmount?: boolean
}) { }) {
const { account } = useActiveWeb3React() const { account } = useWeb3React()
const key = currencyKey(currency) const key = currencyKey(currency)
const selectedTokenList = useCombinedActiveList() const selectedTokenList = useCombinedActiveList()
const isOnSelectedList = isTokenOnList(selectedTokenList, currency.isToken ? currency : undefined) const isOnSelectedList = isTokenOnList(selectedTokenList, currency.isToken ? currency : undefined)
......
...@@ -112,7 +112,13 @@ export default function Option({ ...@@ -112,7 +112,13 @@ export default function Option({
id: string id: string
}) { }) {
const content = ( const content = (
<OptionCardClickable id={id} onClick={onClick} clickable={clickable && !active} active={active}> <OptionCardClickable
id={id}
onClick={onClick}
clickable={clickable && !active}
active={active}
data-testid="wallet-modal-option"
>
<OptionCardLeft> <OptionCardLeft>
<HeaderText color={color}> <HeaderText color={color}>
{active ? ( {active ? (
......
import { ApplicationModal } from 'state/application/reducer'
import { render, screen } from '../../test-utils'
import WalletModal from './index'
beforeEach(() => {
delete global.window.ethereum
})
afterAll(() => {
delete global.window.ethereum
})
const UserAgentMock = jest.requireMock('utils/userAgent')
jest.mock('utils/userAgent', () => ({
isMobile: false,
}))
jest.mock('.../../state/application/hooks', () => {
return {
useModalOpen: (_modal: ApplicationModal) => true,
useWalletModalToggle: () => {
return
},
}
})
jest.mock('@web3-react/core', () => {
const web3React = jest.requireActual('@web3-react/core')
return {
useWeb3React: () => ({
account: undefined,
isActive: false,
isActivating: false,
connector: undefined,
}),
...web3React,
}
})
it('loads Wallet Modal on desktop', async () => {
render(<WalletModal pendingTransactions={[]} confirmedTransactions={[]} />)
expect(screen.getByText('Install MetaMask')).toBeInTheDocument()
expect(screen.getByText('Coinbase Wallet')).toBeInTheDocument()
expect(screen.getByText('WalletConnect')).toBeInTheDocument()
expect(screen.getByText('Fortmatic')).toBeInTheDocument()
expect(screen.getAllByTestId('wallet-modal-option')).toHaveLength(4)
})
it('loads Wallet Modal on desktop with MetaMask installed', async () => {
global.window.ethereum = { isMetaMask: true }
render(<WalletModal pendingTransactions={[]} confirmedTransactions={[]} />)
expect(screen.getByText('MetaMask')).toBeInTheDocument()
expect(screen.getByText('Coinbase Wallet')).toBeInTheDocument()
expect(screen.getByText('WalletConnect')).toBeInTheDocument()
expect(screen.getByText('Fortmatic')).toBeInTheDocument()
expect(screen.getAllByTestId('wallet-modal-option')).toHaveLength(4)
})
it('loads Wallet Modal on mobile', async () => {
UserAgentMock.isMobile = true
render(<WalletModal pendingTransactions={[]} confirmedTransactions={[]} />)
expect(screen.getByText('Open in Coinbase Wallet')).toBeInTheDocument()
expect(screen.getByText('WalletConnect')).toBeInTheDocument()
expect(screen.getByText('Fortmatic')).toBeInTheDocument()
expect(screen.getAllByTestId('wallet-modal-option')).toHaveLength(3)
})
it('loads Wallet Modal on MetaMask browser', async () => {
UserAgentMock.isMobile = true
global.window.ethereum = { isMetaMask: true }
render(<WalletModal pendingTransactions={[]} confirmedTransactions={[]} />)
expect(screen.getByText('MetaMask')).toBeInTheDocument()
expect(screen.getAllByTestId('wallet-modal-option')).toHaveLength(1)
})
it('loads Wallet Modal on Coinbase Wallet browser', async () => {
UserAgentMock.isMobile = true
global.window.ethereum = { isCoinbaseWallet: true }
render(<WalletModal pendingTransactions={[]} confirmedTransactions={[]} />)
expect(screen.getByText('Coinbase Wallet')).toBeInTheDocument()
expect(screen.getAllByTestId('wallet-modal-option')).toHaveLength(1)
})
...@@ -184,8 +184,9 @@ export default function WalletModal({ ...@@ -184,8 +184,9 @@ export default function WalletModal({
// get wallets user can switch too, depending on device/browser // get wallets user can switch too, depending on device/browser
function getOptions() { function getOptions() {
const isMetamask = !!window.ethereum?.isMetaMask const isMetaMask = !!window.ethereum?.isMetaMask
const isTally = !!window.ethereum?.isTally const isTally = !!window.ethereum?.isTally
const isCoinbaseWallet = !!window.ethereum?.isCoinbaseWallet
return Object.keys(SUPPORTED_WALLETS).map((key) => { return Object.keys(SUPPORTED_WALLETS).map((key) => {
const option = SUPPORTED_WALLETS[key] const option = SUPPORTED_WALLETS[key]
const isActive = option.connector === connector const isActive = option.connector === connector
...@@ -202,7 +203,11 @@ export default function WalletModal({ ...@@ -202,7 +203,11 @@ export default function WalletModal({
// check for mobile options // check for mobile options
if (isMobile) { if (isMobile) {
if (!window.web3 && !window.ethereum && option.mobile) { if (
(!window.web3 && !window.ethereum && option.mobile) ||
(isMetaMask && option.name === 'MetaMask') ||
(isCoinbaseWallet && option.name === 'Coinbase Wallet')
) {
return ( return (
<Option <Option
{...optionProps} {...optionProps}
...@@ -228,7 +233,7 @@ export default function WalletModal({ ...@@ -228,7 +233,7 @@ export default function WalletModal({
id={`connect-${key}`} id={`connect-${key}`}
key={key} key={key}
color={'#E8831D'} color={'#E8831D'}
header={<Trans>Install Metamask</Trans>} header={<Trans>Install MetaMask</Trans>}
subheader={null} subheader={null}
link={'https://metamask.io/'} link={'https://metamask.io/'}
icon={MetamaskIcon} icon={MetamaskIcon}
...@@ -239,11 +244,11 @@ export default function WalletModal({ ...@@ -239,11 +244,11 @@ export default function WalletModal({
} }
} }
// don't return metamask if injected provider isn't metamask // don't return metamask if injected provider isn't metamask
else if (option.name === 'MetaMask' && !isMetamask) { else if (option.name === 'MetaMask' && !isMetaMask) {
return null return null
} }
// likewise for generic // likewise for generic
else if (option.name === 'Injected' && isMetamask) { else if (option.name === 'Injected' && isMetaMask) {
return null return null
} else if (option.name === 'Injected' && isTally) { } else if (option.name === 'Injected' && isTally) {
return ( return (
......
import { render, RenderOptions } from '@testing-library/react' import { i18n } from '@lingui/core'
import React, { FC, ReactElement, ReactNode } from 'react' import { I18nProvider } from '@lingui/react'
import { render } from '@testing-library/react'
import { Web3ReactProvider } from '@web3-react/core'
import { injected, injectedHooks } from 'connectors'
import { DEFAULT_LOCALE } from 'constants/locales'
import { en } from 'make-plural/plurals'
import { ReactElement, ReactNode } from 'react'
import { Provider } from 'react-redux' import { Provider } from 'react-redux'
import store from 'state' import store from 'state'
import ThemeProvider from 'theme' import ThemeProvider from 'theme'
const WithProviders: FC = ({ children }: { children?: ReactNode }) => { import catalog from './locales/en-US'
i18n.load({
[DEFAULT_LOCALE]: catalog.messages,
})
i18n.loadLocaleData({
[DEFAULT_LOCALE]: { plurals: en },
})
i18n.activate(DEFAULT_LOCALE)
const MockedI18nProvider = ({ children }: any) => <I18nProvider i18n={i18n}>{children}</I18nProvider>
const WithProviders = ({ children }: { children?: ReactNode }) => {
return ( return (
<MockedI18nProvider>
<Provider store={store}> <Provider store={store}>
<Web3ReactProvider connectors={[[injected, injectedHooks]]}>
<ThemeProvider>{children}</ThemeProvider> <ThemeProvider>{children}</ThemeProvider>
</Web3ReactProvider>
</Provider> </Provider>
</MockedI18nProvider>
) )
} }
const customRender = (ui: ReactElement, options?: Omit<RenderOptions, 'wrapper'>) => const customRender = (ui: ReactElement) => render(ui, { wrapper: WithProviders })
render(ui, { wrapper: WithProviders, ...options })
export * from '@testing-library/react' export * from '@testing-library/react'
export { customRender as render } export { customRender as render }
...@@ -113,7 +113,7 @@ function colors(darkMode: boolean): Colors { ...@@ -113,7 +113,7 @@ function colors(darkMode: boolean): Colors {
} }
} }
function theme(darkMode: boolean): DefaultTheme { function getTheme(darkMode: boolean): DefaultTheme {
return { return {
...colors(darkMode), ...colors(darkMode),
...@@ -144,7 +144,7 @@ function theme(darkMode: boolean): DefaultTheme { ...@@ -144,7 +144,7 @@ function theme(darkMode: boolean): DefaultTheme {
export default function ThemeProvider({ children }: { children: React.ReactNode }) { export default function ThemeProvider({ children }: { children: React.ReactNode }) {
const darkMode = useIsDarkMode() const darkMode = useIsDarkMode()
const themeObject = useMemo(() => theme(darkMode), [darkMode]) const themeObject = useMemo(() => getTheme(darkMode), [darkMode])
return <StyledComponentsThemeProvider theme={themeObject}>{children}</StyledComponentsThemeProvider> return <StyledComponentsThemeProvider theme={themeObject}>{children}</StyledComponentsThemeProvider>
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment