Commit ccab6f01 authored by Moody Salem's avatar Moody Salem

More tests for add/remove liquidity and fix the automatic loading of tokens in those pages

parent 384c3cad
describe('Add Liquidity', () => { describe('Add Liquidity', () => {
beforeEach(() => it('loads the two correct tokens', () => {
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85-0xc778417E063141139Fce010982780140Aa0cD5Ab') cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85-0xc778417E063141139Fce010982780140Aa0cD5Ab')
) cy.get('#add-liquidity-input-token0 .token-symbol-container').should('contain.text', 'MKR')
cy.get('#add-liquidity-input-token1 .token-symbol-container').should('contain.text', 'ETH')
})
it('loads the two correct tokens', () => { it('does not crash if ETH is duplicated', () => {
cy.get('#add-liquidity-input-token0 .token-symbol-container').should('contain', 'MKR') cy.visit('/add/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xc778417E063141139Fce010982780140Aa0cD5Ab')
cy.get('#add-liquidity-input-token1 .token-symbol-container').should('contain', 'ETH') cy.get('#add-liquidity-input-token0 .token-symbol-container').should('contain.text', 'ETH')
cy.get('#add-liquidity-input-token1 .token-symbol-container').should('not.contain.text', 'ETH')
})
it('token not in storage is loaded', () => {
cy.visit('/add/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.get('#add-liquidity-input-token0 .token-symbol-container').should('contain.text', 'SKL')
cy.get('#add-liquidity-input-token1 .token-symbol-container').should('contain.text', 'MKR')
}) })
}) })
describe('Remove Liquidity', () => {
it('loads the two correct tokens', () => {
cy.visit('/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.get('#remove-liquidity-token0-symbol').should('contain.text', 'ETH')
cy.get('#remove-liquidity-token1-symbol').should('contain.text', 'MKR')
})
it('does not crash if ETH is duplicated', () => {
cy.visit('/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xc778417E063141139Fce010982780140Aa0cD5Ab')
cy.get('#remove-liquidity-token0-symbol').should('contain.text', 'ETH')
cy.get('#remove-liquidity-token1-symbol').should('not.contain.text', 'ETH')
})
it('token not in storage is loaded', () => {
cy.visit('/remove/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.get('#remove-liquidity-token0-symbol').should('contain.text', 'SKL')
cy.get('#remove-liquidity-token1-symbol').should('contain.text', 'MKR')
})
})
...@@ -35,10 +35,13 @@ export function useAllTokens(): { [address: string]: Token } { ...@@ -35,10 +35,13 @@ export function useAllTokens(): { [address: string]: Token } {
}, [userAddedTokens, chainId]) }, [userAddedTokens, chainId])
} }
export function useToken(tokenAddress: string): Token { export function useToken(tokenAddress?: string): Token | undefined {
const tokens = useAllTokens() const tokens = useAllTokens()
return useMemo(() => {
return tokens?.[tokenAddress] const validatedAddress = isAddress(tokenAddress)
if (!validatedAddress) return
return tokens[validatedAddress]
}, [tokens, tokenAddress])
} }
// gets token information by address (typically user input) and // gets token information by address (typically user input) and
...@@ -46,22 +49,22 @@ export function useToken(tokenAddress: string): Token { ...@@ -46,22 +49,22 @@ export function useToken(tokenAddress: string): Token {
export function useTokenByAddressAndAutomaticallyAdd(tokenAddress?: string): Token | undefined { export function useTokenByAddressAndAutomaticallyAdd(tokenAddress?: string): Token | undefined {
const fetchTokenByAddress = useFetchTokenByAddress() const fetchTokenByAddress = useFetchTokenByAddress()
const addToken = useAddUserToken() const addToken = useAddUserToken()
const allTokens = useAllTokens() const token = useToken(tokenAddress)
const { chainId } = useActiveWeb3React() const { chainId } = useActiveWeb3React()
useEffect(() => { useEffect(() => {
if (!chainId) return if (!chainId || !isAddress(tokenAddress)) return
const weth = WETH[chainId as ChainId] const weth = WETH[chainId as ChainId]
if (weth && weth.address === isAddress(tokenAddress)) return if (weth && weth.address === isAddress(tokenAddress)) return
if (tokenAddress && !allTokens?.[tokenAddress]) { if (tokenAddress && !token) {
fetchTokenByAddress(tokenAddress).then(token => { fetchTokenByAddress(tokenAddress).then(token => {
if (token !== null) { if (token !== null) {
addToken(token) addToken(token)
} }
}) })
} }
}, [tokenAddress, allTokens, fetchTokenByAddress, addToken, chainId]) }, [tokenAddress, token, fetchTokenByAddress, addToken, chainId])
return tokenAddress ? allTokens?.[tokenAddress] : undefined return token
} }
...@@ -27,11 +27,11 @@ import { usePair } from '../../data/Reserves' ...@@ -27,11 +27,11 @@ import { usePair } from '../../data/Reserves'
import { useTotalSupply } from '../../data/TotalSupply' import { useTotalSupply } from '../../data/TotalSupply'
import { useTokenContract, useActiveWeb3React } from '../../hooks' import { useTokenContract, useActiveWeb3React } from '../../hooks'
import { useToken } from '../../hooks/Tokens' import { useTokenByAddressAndAutomaticallyAdd } from '../../hooks/Tokens'
import { useHasPendingApproval, useTransactionAdder } from '../../state/transactions/hooks' import { useHasPendingApproval, useTransactionAdder } from '../../state/transactions/hooks'
import { useTokenBalanceTreatingWETHasETH } from '../../state/wallet/hooks' import { useTokenBalanceTreatingWETHasETH } from '../../state/wallet/hooks'
import { TYPE } from '../../theme' import { TYPE } from '../../theme'
import { calculateGasMargin, calculateSlippageAmount, getRouterContract } from '../../utils' import { calculateGasMargin, calculateSlippageAmount, getRouterContract, isAddress } from '../../utils'
import AppBody from '../AppBody' import AppBody from '../AppBody'
import { Dots, Wrapper } from '../Pool/styleds' import { Dots, Wrapper } from '../Pool/styleds'
...@@ -64,14 +64,16 @@ interface AddState { ...@@ -64,14 +64,16 @@ interface AddState {
} }
function initializeAddState(inputAddress?: string, outputAddress?: string): AddState { function initializeAddState(inputAddress?: string, outputAddress?: string): AddState {
const validatedInput = isAddress(inputAddress)
const validatedOutput = isAddress(outputAddress)
return { return {
independentField: Field.INPUT, independentField: Field.INPUT,
typedValue: '', typedValue: '',
[Field.INPUT]: { [Field.INPUT]: {
address: inputAddress address: validatedInput || ''
}, },
[Field.OUTPUT]: { [Field.OUTPUT]: {
address: outputAddress address: validatedOutput && validatedOutput !== validatedInput ? validatedOutput : ''
} }
} }
} }
...@@ -152,10 +154,13 @@ export default function AddLiquidity({ match: { params } }: RouteComponentProps< ...@@ -152,10 +154,13 @@ export default function AddLiquidity({ match: { params } }: RouteComponentProps<
const { independentField, typedValue, ...fieldData } = state const { independentField, typedValue, ...fieldData } = state
const dependentField: Field = independentField === Field.INPUT ? Field.OUTPUT : Field.INPUT const dependentField: Field = independentField === Field.INPUT ? Field.OUTPUT : Field.INPUT
const inputToken = useTokenByAddressAndAutomaticallyAdd(fieldData[Field.INPUT].address)
const outputToken = useTokenByAddressAndAutomaticallyAdd(fieldData[Field.OUTPUT].address)
// get basic SDK entities // get basic SDK entities
const tokens: { [field in Field]: Token } = { const tokens: { [field in Field]: Token } = {
[Field.INPUT]: useToken(fieldData[Field.INPUT].address), [Field.INPUT]: inputToken,
[Field.OUTPUT]: useToken(fieldData[Field.OUTPUT].address) [Field.OUTPUT]: outputToken
} }
// token contracts for approvals and direct sends // token contracts for approvals and direct sends
......
...@@ -106,14 +106,6 @@ function reducer( ...@@ -106,14 +106,6 @@ function reducer(
} }
} }
function useTokenByAddressOrETHAndAutomaticallyAdd(tokenId?: string, chainId?: number): Token | undefined {
const isWETH = tokenId?.toUpperCase() === 'ETH' || tokenId?.toUpperCase() === 'WETH'
const tokenByAddress = useTokenByAddressAndAutomaticallyAdd(isWETH ? null : tokenId)
return isWETH ? WETH[chainId] : tokenByAddress
}
export default function RemoveLiquidity({ match: { params } }: RouteComponentProps<{ tokens: string }>) { export default function RemoveLiquidity({ match: { params } }: RouteComponentProps<{ tokens: string }>) {
const [token0, token1] = params.tokens.split('-') const [token0, token1] = params.tokens.split('-')
...@@ -123,13 +115,13 @@ export default function RemoveLiquidity({ match: { params } }: RouteComponentPro ...@@ -123,13 +115,13 @@ export default function RemoveLiquidity({ match: { params } }: RouteComponentPro
const [showConfirm, setShowConfirm] = useState<boolean>(false) const [showConfirm, setShowConfirm] = useState<boolean>(false)
const [showAdvanced, setShowAdvanced] = useState<boolean>(false) const [showAdvanced, setShowAdvanced] = useState<boolean>(false)
const inputToken: Token = useTokenByAddressOrETHAndAutomaticallyAdd(token0, chainId) const inputToken: Token = useTokenByAddressAndAutomaticallyAdd(token0)
const outputToken: Token = useTokenByAddressOrETHAndAutomaticallyAdd(token1, chainId) const outputToken: Token = useTokenByAddressAndAutomaticallyAdd(token1)
// get basic SDK entities // get basic SDK entities
const tokens: { [field in Field]?: Token } = { const tokens: { [field in Field]?: Token } = {
[Field.TOKEN0]: inputToken, [Field.TOKEN0]: inputToken,
[Field.TOKEN1]: outputToken [Field.TOKEN1]: outputToken && inputToken && outputToken.equals(inputToken) ? undefined : outputToken
} }
const pair = usePair(inputToken, outputToken) const pair = usePair(inputToken, outputToken)
...@@ -726,7 +718,7 @@ export default function RemoveLiquidity({ match: { params } }: RouteComponentPro ...@@ -726,7 +718,7 @@ export default function RemoveLiquidity({ match: { params } }: RouteComponentPro
</Text> </Text>
<RowFixed> <RowFixed>
<TokenLogo address={tokens[Field.TOKEN0]?.address} style={{ marginRight: '12px' }} /> <TokenLogo address={tokens[Field.TOKEN0]?.address} style={{ marginRight: '12px' }} />
<Text fontSize={24} fontWeight={500}> <Text fontSize={24} fontWeight={500} id="remove-liquidity-token0-symbol">
{tokens[Field.TOKEN0]?.symbol} {tokens[Field.TOKEN0]?.symbol}
</Text> </Text>
</RowFixed> </RowFixed>
...@@ -737,7 +729,7 @@ export default function RemoveLiquidity({ match: { params } }: RouteComponentPro ...@@ -737,7 +729,7 @@ export default function RemoveLiquidity({ match: { params } }: RouteComponentPro
</Text> </Text>
<RowFixed> <RowFixed>
<TokenLogo address={tokens[Field.TOKEN1]?.address} style={{ marginRight: '12px' }} /> <TokenLogo address={tokens[Field.TOKEN1]?.address} style={{ marginRight: '12px' }} />
<Text fontSize={24} fontWeight={500}> <Text fontSize={24} fontWeight={500} id="remove-liquidity-token1-symbol">
{tokens[Field.TOKEN1]?.symbol} {tokens[Field.TOKEN1]?.symbol}
</Text> </Text>
</RowFixed> </RowFixed>
......
...@@ -3,7 +3,7 @@ import { useActiveWeb3React } from '../../hooks' ...@@ -3,7 +3,7 @@ import { useActiveWeb3React } from '../../hooks'
import { useCallback, useMemo } from 'react' import { useCallback, useMemo } from 'react'
import { shallowEqual, useDispatch, useSelector } from 'react-redux' import { shallowEqual, useDispatch, useSelector } from 'react-redux'
import { useAllTokens } from '../../hooks/Tokens' import { useAllTokens } from '../../hooks/Tokens'
import { getTokenDecimals, getTokenName, getTokenSymbol } from '../../utils' import { getTokenDecimals, getTokenName, getTokenSymbol, isAddress } from '../../utils'
import { AppDispatch, AppState } from '../index' import { AppDispatch, AppState } from '../index'
import { import {
addSerializedPair, addSerializedPair,
...@@ -67,6 +67,8 @@ export function useFetchTokenByAddress(): (address: string) => Promise<Token | n ...@@ -67,6 +67,8 @@ export function useFetchTokenByAddress(): (address: string) => Promise<Token | n
return useCallback( return useCallback(
async (address: string): Promise<Token | null> => { async (address: string): Promise<Token | null> => {
if (!library || !chainId) return null if (!library || !chainId) return null
const validatedAddress = isAddress(address)
if (!validatedAddress) return null
const [decimals, symbol, name] = await Promise.all([ const [decimals, symbol, name] = await Promise.all([
getTokenDecimals(address, library).catch(() => null), getTokenDecimals(address, library).catch(() => null),
getTokenSymbol(address, library).catch(() => 'UNKNOWN'), getTokenSymbol(address, library).catch(() => 'UNKNOWN'),
...@@ -76,7 +78,7 @@ export function useFetchTokenByAddress(): (address: string) => Promise<Token | n ...@@ -76,7 +78,7 @@ export function useFetchTokenByAddress(): (address: string) => Promise<Token | n
if (decimals === null) { if (decimals === null) {
return null return null
} else { } else {
return new Token(chainId, address, decimals, symbol, name) return new Token(chainId, validatedAddress, decimals, symbol, name)
} }
}, },
[library, chainId] [library, chainId]
......
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