Add more unit tests to utils

This commit is contained in:
Moody Salem 2020-05-13 14:24:03 -04:00
parent 3a2566b4e7
commit 1e1a049990
No known key found for this signature in database
GPG Key ID: 8CB5CD10385138DB
5 changed files with 92 additions and 33 deletions

@ -21,11 +21,6 @@ export const COMMON_BASES = {
[ChainId.KOVAN]: [WETH[ChainId.KOVAN]] [ChainId.KOVAN]: [WETH[ChainId.KOVAN]]
} }
export const SUPPORTED_THEMES = {
DARK: 'DARK',
LIGHT: 'LIGHT'
}
const MAINNET_WALLETS = { const MAINNET_WALLETS = {
INJECTED: { INJECTED: {
connector: injected, connector: injected,

@ -8,8 +8,7 @@ import styled, {
} from 'styled-components' } from 'styled-components'
import { AppDispatch, AppState } from '../state' import { AppDispatch, AppState } from '../state'
import { updateUserDarkMode } from '../state/user/actions' import { updateUserDarkMode } from '../state/user/actions'
import { getQueryParam, checkSupportedTheme } from '../utils' import { getQueryParam } from '../utils'
import { SUPPORTED_THEMES } from '../constants'
import { useIsDarkMode } from '../state/user/hooks' import { useIsDarkMode } from '../state/user/hooks'
import { Text, TextProps } from 'rebass' import { Text, TextProps } from 'rebass'
import { Colors } from './styled' import { Colors } from './styled'
@ -122,11 +121,11 @@ export default function ThemeProvider({ children }: { children: React.ReactNode
const userDarkMode = useSelector<AppState, boolean | null>(state => state.user.userDarkMode) const userDarkMode = useSelector<AppState, boolean | null>(state => state.user.userDarkMode)
const darkMode = useIsDarkMode() const darkMode = useIsDarkMode()
const themeURL = checkSupportedTheme(getQueryParam(window.location, 'theme')) const themeURL = getQueryParam(window.location, 'theme')
const urlContainsDarkMode: boolean | null = themeURL const urlContainsDarkMode: boolean | null = themeURL
? themeURL.toUpperCase() === SUPPORTED_THEMES.DARK ? themeURL.toUpperCase() === 'DARK'
? true ? true
: themeURL.toUpperCase() === SUPPORTED_THEMES.LIGHT : themeURL.toUpperCase() === 'LIGHT'
? false ? false
: null : null
: null : null

@ -1,7 +1,15 @@
import { BigNumber } from '@ethersproject/bignumber'
import { AddressZero } from '@ethersproject/constants' import { AddressZero } from '@ethersproject/constants'
import { TokenAmount, Token, ChainId } from '@uniswap/sdk' import { TokenAmount, Token, ChainId, Percent, JSBI } from '@uniswap/sdk'
import { getEtherscanLink, calculateSlippageAmount } from '.' import {
getEtherscanLink,
calculateSlippageAmount,
isAddress,
shortenAddress,
calculateGasMargin,
basisPointsToPercent
} from '.'
describe('utils', () => { describe('utils', () => {
describe('#getEtherscanLink', () => { describe('#getEtherscanLink', () => {
@ -11,6 +19,15 @@ describe('utils', () => {
it('correct for address', () => { it('correct for address', () => {
expect(getEtherscanLink(1, 'abc', 'address')).toEqual('https://etherscan.io/address/abc') expect(getEtherscanLink(1, 'abc', 'address')).toEqual('https://etherscan.io/address/abc')
}) })
it('unrecognized chain id defaults to mainnet', () => {
expect(getEtherscanLink(2, 'abc', 'address')).toEqual('https://etherscan.io/address/abc')
})
it('ropsten', () => {
expect(getEtherscanLink(3, 'abc', 'address')).toEqual('https://ropsten.etherscan.io/address/abc')
})
it('enum', () => {
expect(getEtherscanLink(ChainId.RINKEBY, 'abc', 'address')).toEqual('https://rinkeby.etherscan.io/address/abc')
})
}) })
describe('#calculateSlippageAmount', () => { describe('#calculateSlippageAmount', () => {
@ -24,4 +41,59 @@ describe('utils', () => {
expect(() => calculateSlippageAmount(tokenAmount, 10001)).toThrow() expect(() => calculateSlippageAmount(tokenAmount, 10001)).toThrow()
}) })
}) })
describe('#isAddress', () => {
it('returns false if not', () => {
expect(isAddress('')).toBe(false)
expect(isAddress('0x0000')).toBe(false)
expect(isAddress(1)).toBe(false)
expect(isAddress({})).toBe(false)
expect(isAddress(undefined)).toBe(false)
})
it('returns the checksummed address', () => {
expect(isAddress('0xf164fc0ec4e93095b804a4795bbe1e041497b92a')).toBe('0xf164fC0Ec4E93095b804a4795bBe1e041497b92a')
expect(isAddress('0xf164fC0Ec4E93095b804a4795bBe1e041497b92a')).toBe('0xf164fC0Ec4E93095b804a4795bBe1e041497b92a')
})
it('succeeds even without prefix', () => {
expect(isAddress('f164fc0ec4e93095b804a4795bbe1e041497b92a')).toBe('0xf164fC0Ec4E93095b804a4795bBe1e041497b92a')
})
it('fails if too long', () => {
expect(isAddress('f164fc0ec4e93095b804a4795bbe1e041497b92a0')).toBe(false)
})
})
describe('#shortenAddress', () => {
it('throws on invalid address', () => {
expect(() => shortenAddress('abc')).toThrow("Invalid 'address'")
})
it('truncates middle characters', () => {
expect(shortenAddress('0xf164fc0ec4e93095b804a4795bbe1e041497b92a')).toBe('0xf164...b92a')
})
it('truncates middle characters even without prefix', () => {
expect(shortenAddress('f164fc0ec4e93095b804a4795bbe1e041497b92a')).toBe('0xf164...b92a')
})
it('renders checksummed address', () => {
expect(shortenAddress('0x2E1b342132A67Ea578e4E3B814bae2107dc254CC'.toLowerCase())).toBe('0x2E1b...54CC')
})
})
describe('#calculateGasMargin', () => {
it('adds 10%', () => {
expect(calculateGasMargin(BigNumber.from(1000)).toString()).toEqual('1100')
expect(calculateGasMargin(BigNumber.from(50)).toString()).toEqual('55')
})
})
describe('#basisPointsToPercent', () => {
it('converts basis points numbers to percents', () => {
expect(basisPointsToPercent(100).equalTo(new Percent(JSBI.BigInt(1), JSBI.BigInt(100)))).toBeTruthy()
expect(basisPointsToPercent(500).equalTo(new Percent(JSBI.BigInt(5), JSBI.BigInt(100)))).toBeTruthy()
expect(basisPointsToPercent(50).equalTo(new Percent(JSBI.BigInt(5), JSBI.BigInt(1000)))).toBeTruthy()
})
})
}) })

@ -7,15 +7,16 @@ import { BigNumber } from '@ethersproject/bignumber'
import { abi as IUniswapV2PairABI } from '@uniswap/v2-core/build/IUniswapV2Pair.json' import { abi as IUniswapV2PairABI } from '@uniswap/v2-core/build/IUniswapV2Pair.json'
import { abi as IUniswapV2Router01ABI } from '@uniswap/v2-periphery/build/IUniswapV2Router01.json' import { abi as IUniswapV2Router01ABI } from '@uniswap/v2-periphery/build/IUniswapV2Router01.json'
import { ROUTER_ADDRESS, SUPPORTED_THEMES } from '../constants' import { ROUTER_ADDRESS } from '../constants'
import ERC20_ABI from '../constants/abis/erc20.json' import ERC20_ABI from '../constants/abis/erc20.json'
import ERC20_BYTES32_ABI from '../constants/abis/erc20_bytes32.json' import ERC20_BYTES32_ABI from '../constants/abis/erc20_bytes32.json'
import { JSBI, Percent, TokenAmount } from '@uniswap/sdk' import { ChainId, JSBI, Percent, TokenAmount } from '@uniswap/sdk'
// returns the checksummed address if the address is valid, otherwise returns false
export function isAddress(value: any): string | false { export function isAddress(value: any): string | false {
try { try {
return getAddress(value.toLowerCase()) return getAddress(value)
} catch { } catch {
return false return false
} }
@ -26,7 +27,7 @@ export enum ERROR_CODES {
TOKEN_DECIMALS = 2 TOKEN_DECIMALS = 2
} }
const ETHERSCAN_PREFIXES = { const ETHERSCAN_PREFIXES: { [chainId in ChainId]: string } = {
1: '', 1: '',
3: 'ropsten.', 3: 'ropsten.',
4: 'rinkeby.', 4: 'rinkeby.',
@ -34,12 +35,8 @@ const ETHERSCAN_PREFIXES = {
42: 'kovan.' 42: 'kovan.'
} }
export function getEtherscanLink( export function getEtherscanLink(chainId: ChainId, data: string, type: 'transaction' | 'address'): string {
networkId: 1 | 3 | 4 | 5 | 42 | string | number, const prefix = `https://${ETHERSCAN_PREFIXES[chainId] || ETHERSCAN_PREFIXES[1]}etherscan.io`
data: string,
type: 'transaction' | 'address'
) {
const prefix = `https://${ETHERSCAN_PREFIXES[networkId] || ETHERSCAN_PREFIXES[1]}etherscan.io`
switch (type) { switch (type) {
case 'transaction': { case 'transaction': {
@ -89,22 +86,18 @@ export function getAllQueryParams(): QueryParams {
} }
} }
export function checkSupportedTheme(themeName: string) { // shorten the checksummed version of the input address to have 0x + 4 characters at start and end
if (themeName && themeName.toUpperCase() in SUPPORTED_THEMES) {
return themeName.toUpperCase()
}
return null
}
export function shortenAddress(address: string, chars = 4): string { export function shortenAddress(address: string, chars = 4): string {
if (!isAddress(address)) { const parsed = isAddress(address)
if (!parsed) {
throw Error(`Invalid 'address' parameter '${address}'.`) throw Error(`Invalid 'address' parameter '${address}'.`)
} }
return `${address.substring(0, chars + 2)}...${address.substring(42 - chars)}` return `${parsed.substring(0, chars + 2)}...${parsed.substring(42 - chars)}`
} }
// add 10%
export function calculateGasMargin(value: BigNumber): BigNumber { export function calculateGasMargin(value: BigNumber): BigNumber {
return value.mul(BigNumber.from(10000).add(BigNumber.from(1000))).div(BigNumber.from(10000)) // add 10% return value.mul(BigNumber.from(10000).add(BigNumber.from(1000))).div(BigNumber.from(10000))
} }
// converts a basis points value to a sdk percent // converts a basis points value to a sdk percent

@ -7205,7 +7205,7 @@ escape-html@~1.0.3:
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
escape-string-regexp@2.0.0, escape-string-regexp@^2.0.0: escape-string-regexp@2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==