Skip to content

Commit

Permalink
test: add jest.asMock (Uniswap#6310)
Browse files Browse the repository at this point in the history
* test: add jest.asMock

* test: use mocked instead

* test: split test-utils to prevent interaction

* test: whoops missed one

* Merge but actually this time
  • Loading branch information
zzmp authored Apr 13, 2023
1 parent ef9ecd9 commit 4888fe2
Show file tree
Hide file tree
Showing 21 changed files with 78 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
TransactionDetails,
TransactionType,
} from 'state/transactions/types'
import { renderHook } from 'test-utils'
import { renderHook } from 'test-utils/render'

import { parseLocalActivity, useLocalActivities } from './parseLocal'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { BigNumber } from '@ethersproject/bignumber'
import { SupportedChainId, WETH9 } from '@uniswap/sdk-core'
import { FeeAmount, Pool, Position } from '@uniswap/v3-sdk'
import { USDC_MAINNET } from 'constants/tokens'
import { render } from 'test-utils'
import { mocked } from 'test-utils/mocked'
import { render } from 'test-utils/render'

import Pools from '.'
import useMultiChainPositions from './useMultiChainPositions'

jest.mock('./useMultiChainPositions')
const mockUseMultiChainPositions = useMultiChainPositions as jest.MockedFunction<typeof useMultiChainPositions>

const owner = '0xf5b6bb25f5beaea03dd014c6ef9fa9f3926bf36c'

Expand Down Expand Up @@ -58,7 +58,7 @@ const useMultiChainPositionsReturnValue = {
}

beforeEach(() => {
mockUseMultiChainPositions.mockReturnValue(useMultiChainPositionsReturnValue)
mocked(useMultiChainPositions).mockReturnValue(useMultiChainPositionsReturnValue)
})
test('Pools should render LP positions', () => {
const props = { account: owner }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { SupportedChainId } from '@uniswap/sdk-core'
import { DAI_ARBITRUM } from '@uniswap/smart-order-router'
import { DAI, USDC_ARBITRUM, USDC_MAINNET } from 'constants/tokens'
import { render } from 'test-utils'
import { render } from 'test-utils/render'

import { PortfolioLogo } from './PortfolioLogo'

Expand Down
2 changes: 1 addition & 1 deletion src/components/Identicon/StatusIcon.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getConnections } from 'connection'
import { render } from 'test-utils'
import { render } from 'test-utils/render'

import StatusIcon from './StatusIcon'

Expand Down
12 changes: 5 additions & 7 deletions src/components/PositionListItem/PositionListItem.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,17 @@ import { USDC_MAINNET } from 'constants/tokens'
import { useToken } from 'hooks/Tokens'
import { usePool } from 'hooks/usePools'
import { PoolState } from 'hooks/usePools'
import { render } from 'test-utils'
import { mocked } from 'test-utils/mocked'
import { render } from 'test-utils/render'
import { unwrappedToken } from 'utils/unwrappedToken'

import PositionListItem from '.'

jest.mock('utils/unwrappedToken')
const mockUnwrappedToken = unwrappedToken as jest.MockedFunction<typeof unwrappedToken>

jest.mock('hooks/usePools')
const mockUsePool = usePool as jest.MockedFunction<typeof usePool>

jest.mock('hooks/Tokens')
const mockUseToken = useToken as jest.MockedFunction<typeof useToken>

// eslint-disable-next-line react/display-name
jest.mock('components/DoubleLogo', () => () => <div />)
Expand All @@ -36,16 +34,16 @@ const susToken0Address = '0x39AA39c021dfbaE8faC545936693aC917d5E7563'

beforeEach(() => {
const susToken0 = new Token(1, susToken0Address, 8, 'https://www.example.com', 'example.com coin')
mockUseToken.mockImplementation((tokenAddress?: string | null | undefined) => {
mocked(useToken).mockImplementation((tokenAddress?: string | null | undefined) => {
if (!tokenAddress) return null
if (tokenAddress === susToken0.address) return susToken0
return new Token(1, tokenAddress, 8, 'symbol', 'name')
})
mockUsePool.mockReturnValue([
mocked(usePool).mockReturnValue([
PoolState.EXISTS,
new Pool(susToken0, USDC_MAINNET, FeeAmount.HIGH, '2437312313659959819381354528', '10272714736694327408', -69633),
])
mockUnwrappedToken.mockReturnValue(susToken0)
mocked(unwrappedToken).mockReturnValue(susToken0)
})

test('PositionListItem should not render when token0 symbol contains a url', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/components/RoutingDiagram/RoutingDiagram.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Currency, Percent } from '@uniswap/sdk-core'
import { FeeAmount } from '@uniswap/v3-sdk'
import { RoutingDiagramEntry } from 'components/swap/SwapRoute'
import { DAI, USDC_MAINNET, WBTC } from 'constants/tokens'
import { render } from 'test-utils'
import { render } from 'test-utils/render'

import RoutingDiagram from './RoutingDiagram'

Expand Down
2 changes: 1 addition & 1 deletion src/components/SearchModal/CurrencyList/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { screen } from '@testing-library/react'
import { Currency, CurrencyAmount as mockCurrencyAmount, Token as mockToken } from '@uniswap/sdk-core'
import { DAI, USDC_MAINNET, WBTC } from 'constants/tokens'
import * as mockJSBI from 'jsbi'
import { render } from 'test-utils'
import { render } from 'test-utils/render'

import CurrencyList from '.'

Expand Down
2 changes: 1 addition & 1 deletion src/components/TextInput/index.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fireEvent, render, screen } from 'test-utils'
import { fireEvent, render, screen } from 'test-utils/render'

import { ResizingTextArea, TextInput } from './'

Expand Down
2 changes: 1 addition & 1 deletion src/components/swap/SwapBuyFiatButton.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import userEvent from '@testing-library/user-event'
import { useWeb3React } from '@web3-react/core'
import { useAccountDrawer } from 'components/AccountDrawer'
import { fireEvent, render, screen } from 'test-utils'
import { fireEvent, render, screen } from 'test-utils/render'

import { useFiatOnrampAvailability, useOpenModal } from '../../state/application/hooks'
import SwapBuyFiatButton, { MOONPAY_REGION_AVAILABILITY_ARTICLE } from './SwapBuyFiatButton'
Expand Down
2 changes: 1 addition & 1 deletion src/connection/index.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import INJECTED_DARK_ICON from 'assets/svg/browser-wallet-dark.svg'
import INJECTED_LIGHT_ICON from 'assets/svg/browser-wallet-light.svg'
import { ConnectionType, getConnections, useGetConnection } from 'connection'
import { renderHook } from 'test-utils'
import { renderHook } from 'test-utils/render'

beforeEach(() => {
jest.resetModules()
Expand Down
61 changes: 27 additions & 34 deletions src/hooks/useBestTrade.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { DAI, USDC_MAINNET } from 'constants/tokens'
import { RouterPreference } from 'state/routing/slice'
import { TradeState } from 'state/routing/types'
import { useClientSideRouter } from 'state/user/hooks'
import { mocked } from 'test-utils/mocked'

import { useRoutingAPITrade } from '../state/routing/useRoutingAPITrade'
import useAutoRouterSupported from './useAutoRouterSupported'
Expand All @@ -29,53 +30,45 @@ jest.mock('./useIsWindowVisible')
jest.mock('state/routing/useRoutingAPITrade')
jest.mock('state/user/hooks')

const mockUseDebounce = useDebounce as jest.MockedFunction<typeof useDebounce>
const mockUseAutoRouterSupported = useAutoRouterSupported as jest.MockedFunction<typeof useAutoRouterSupported>
const mockUseIsWindowVisible = useIsWindowVisible as jest.MockedFunction<typeof useIsWindowVisible>

const mockUseRoutingAPITrade = useRoutingAPITrade as jest.MockedFunction<typeof useRoutingAPITrade>
const mockUseClientSideRouter = useClientSideRouter as jest.MockedFunction<typeof useClientSideRouter>
const mockUseClientSideV3Trade = useClientSideV3Trade as jest.MockedFunction<typeof useClientSideV3Trade>

// helpers to set mock expectations
const expectRouterMock = (state: TradeState) => {
mockUseRoutingAPITrade.mockReturnValue({ state, trade: undefined })
mocked(useRoutingAPITrade).mockReturnValue({ state, trade: undefined })
}

const expectClientSideMock = (state: TradeState) => {
mockUseClientSideV3Trade.mockReturnValue({ state, trade: undefined })
mocked(useClientSideV3Trade).mockReturnValue({ state, trade: undefined })
}

beforeEach(() => {
// ignore debounced value
mockUseDebounce.mockImplementation((value) => value)
mocked(useDebounce).mockImplementation((value) => value)

mockUseIsWindowVisible.mockReturnValue(true)
mockUseAutoRouterSupported.mockReturnValue(true)
mockUseClientSideRouter.mockReturnValue([true, () => undefined])
mocked(useIsWindowVisible).mockReturnValue(true)
mocked(useAutoRouterSupported).mockReturnValue(true)
mocked(useClientSideRouter).mockReturnValue([true, () => undefined])
})

describe('#useBestV3Trade ExactIn', () => {
it('does not compute routing api trade when routing API is not supported', async () => {
mockUseAutoRouterSupported.mockReturnValue(false)
mocked(useAutoRouterSupported).mockReturnValue(false)
expectRouterMock(TradeState.INVALID)
expectClientSideMock(TradeState.VALID)

const { result } = renderHook(() => useBestTrade(TradeType.EXACT_INPUT, USDCAmount, DAI))

expect(mockUseRoutingAPITrade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, DAI, RouterPreference.CLIENT)
expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, USDCAmount, DAI)
expect(useRoutingAPITrade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, DAI, RouterPreference.CLIENT)
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, USDCAmount, DAI)
expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
})

it('does not compute routing api trade when window is not focused', async () => {
mockUseIsWindowVisible.mockReturnValue(false)
mocked(useIsWindowVisible).mockReturnValue(false)
expectRouterMock(TradeState.NO_ROUTE_FOUND)
expectClientSideMock(TradeState.VALID)

const { result } = renderHook(() => useBestTrade(TradeType.EXACT_INPUT, USDCAmount, DAI))

expect(mockUseRoutingAPITrade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, DAI, RouterPreference.CLIENT)
expect(useRoutingAPITrade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, DAI, RouterPreference.CLIENT)
expect(result.current).toEqual({ state: TradeState.NO_ROUTE_FOUND, trade: undefined })
})

Expand All @@ -85,7 +78,7 @@ describe('#useBestV3Trade ExactIn', () => {

const { result } = renderHook(() => useBestTrade(TradeType.EXACT_INPUT, USDCAmount, DAI))

expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, undefined)
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, undefined)
expect(result.current).toEqual({ state: TradeState.LOADING, trade: undefined })
})

Expand All @@ -94,7 +87,7 @@ describe('#useBestV3Trade ExactIn', () => {

const { result } = renderHook(() => useBestTrade(TradeType.EXACT_INPUT, USDCAmount, DAI))

expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, undefined)
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, undefined)
expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
})

Expand All @@ -103,7 +96,7 @@ describe('#useBestV3Trade ExactIn', () => {

const { result } = renderHook(() => useBestTrade(TradeType.EXACT_INPUT, USDCAmount, DAI))

expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, undefined)
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, undefined)
expect(result.current).toEqual({ state: TradeState.SYNCING, trade: undefined })
})
})
Expand All @@ -115,7 +108,7 @@ describe('#useBestV3Trade ExactIn', () => {

renderHook(() => useBestTrade(TradeType.EXACT_INPUT, USDCAmount, DAI))

expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, undefined)
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, undefined)
})

it('computes client side v3 trade if routing api is NO_ROUTE_FOUND', () => {
Expand All @@ -124,38 +117,38 @@ describe('#useBestV3Trade ExactIn', () => {

const { result } = renderHook(() => useBestTrade(TradeType.EXACT_INPUT, USDCAmount, DAI))

expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, USDCAmount, DAI)
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, USDCAmount, DAI)
expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
})
})
})

describe('#useBestV3Trade ExactOut', () => {
it('does not compute routing api trade when routing API is not supported', () => {
mockUseAutoRouterSupported.mockReturnValue(false)
mocked(useAutoRouterSupported).mockReturnValue(false)
expectRouterMock(TradeState.INVALID)
expectClientSideMock(TradeState.VALID)

const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))

expect(mockUseRoutingAPITrade).toHaveBeenCalledWith(
expect(useRoutingAPITrade).toHaveBeenCalledWith(
TradeType.EXACT_OUTPUT,
undefined,
USDC_MAINNET,
RouterPreference.CLIENT
)
expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET)
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET)
expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
})

it('does not compute routing api trade when window is not focused', () => {
mockUseIsWindowVisible.mockReturnValue(false)
mocked(useIsWindowVisible).mockReturnValue(false)
expectRouterMock(TradeState.NO_ROUTE_FOUND)
expectClientSideMock(TradeState.VALID)

const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))

expect(mockUseRoutingAPITrade).toHaveBeenCalledWith(
expect(useRoutingAPITrade).toHaveBeenCalledWith(
TradeType.EXACT_OUTPUT,
undefined,
USDC_MAINNET,
Expand All @@ -169,7 +162,7 @@ describe('#useBestV3Trade ExactOut', () => {

const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))

expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
expect(result.current).toEqual({ state: TradeState.LOADING, trade: undefined })
})

Expand All @@ -178,7 +171,7 @@ describe('#useBestV3Trade ExactOut', () => {

const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))

expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
})

Expand All @@ -187,7 +180,7 @@ describe('#useBestV3Trade ExactOut', () => {

const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))

expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
expect(result.current).toEqual({ state: TradeState.SYNCING, trade: undefined })
})
})
Expand All @@ -199,7 +192,7 @@ describe('#useBestV3Trade ExactOut', () => {

renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))

expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
})

it('computes client side v3 trade if routing api is NO_ROUTE_FOUND', () => {
Expand All @@ -208,7 +201,7 @@ describe('#useBestV3Trade ExactOut', () => {

const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))

expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET)
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET)
expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
})
})
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useTokenWarningColor.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { WARNING_LEVEL } from 'constants/tokenSafety'
import { renderHook } from 'test-utils'
import { renderHook } from 'test-utils/render'
import { lightTheme } from 'theme/colors'

import { useTokenWarningColor, useTokenWarningTextColor } from './useTokenWarningColor'
Expand Down
2 changes: 1 addition & 1 deletion src/nft/components/bag/bag.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { render } from 'test-utils'
import { render } from 'test-utils/render'

import Bag from './Bag'

Expand Down
2 changes: 1 addition & 1 deletion src/nft/components/card/MarketplaceContainer.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Markets } from 'nft/types'
import { render } from 'test-utils'
import { render } from 'test-utils/render'

import { MarketplaceContainer } from './icons'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { render } from 'test-utils'
import { render } from 'test-utils/render'

import { EmptyWalletModule } from './EmptyWalletContent'

Expand Down
2 changes: 1 addition & 1 deletion src/pages/Pool/CTACards.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as useV3Positions from 'hooks/useV3Positions'
import { render, screen } from 'test-utils'
import { render, screen } from 'test-utils/render'

import CTACards from './CTACards'

Expand Down
2 changes: 1 addition & 1 deletion src/pages/Pool/index.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as chains from 'constants/chains'
import * as useV3Positions from 'hooks/useV3Positions'
import { render, screen } from 'test-utils'
import { render, screen } from 'test-utils/render'

import Pool from '.'

Expand Down
2 changes: 1 addition & 1 deletion src/state/wallets/hooks.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { act, renderHook } from 'test-utils'
import { act, renderHook } from 'test-utils/render'

import { useConnectedWallets } from './hooks'
import { Wallet } from './types'
Expand Down
18 changes: 18 additions & 0 deletions src/test-utils/mocked.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Casts the passed function as a jest.Mock.
* Use this in combination with jest.mock() to safely access functions from mocked modules.
*
* @example
*
* import { useExample } from 'example'
* jest.mock('example', () => ({ useExample: jest.fn() }))
* beforeEach(() => {
* asMock(useExample).mockImplementation(() => ...)
* })
*/
// jest expects mocks to be coerced (eg fn as jest.MockedFunction<T>), but this is not ergonomic when using ASI.
// Instead, we use this utility function to improve readability and add a check to ensure the function is a mock.
export function mocked<T extends (...args: any) => any>(fn: T) {
if (!jest.isMockFunction(fn)) throw new Error('fn is not a mock')
return fn as jest.MockedFunction<T>
}
Loading

0 comments on commit 4888fe2

Please sign in to comment.