add a test for adding custom token
This commit is contained in:
parent
4b0cac5eb0
commit
4625ed1aa5
|
@ -29,6 +29,7 @@ class TextField extends React.PureComponent<TextFieldProps> {
|
||||||
text,
|
text,
|
||||||
inputAdornment,
|
inputAdornment,
|
||||||
classes,
|
classes,
|
||||||
|
testId,
|
||||||
...rest
|
...rest
|
||||||
} = this.props
|
} = this.props
|
||||||
const helperText = value ? text : undefined
|
const helperText = value ? text : undefined
|
||||||
|
@ -36,7 +37,7 @@ class TextField extends React.PureComponent<TextFieldProps> {
|
||||||
const underline = meta.active || (meta.visited && !meta.valid)
|
const underline = meta.active || (meta.visited && !meta.valid)
|
||||||
|
|
||||||
const inputRoot = helperText ? classes.root : undefined
|
const inputRoot = helperText ? classes.root : undefined
|
||||||
const inputProps = { ...restInput, autoComplete: 'off' }
|
const inputProps = { ...restInput, autoComplete: 'off', 'data-testid': testId }
|
||||||
const inputRootProps = { ...inputAdornment, disableUnderline: !underline, className: inputRoot }
|
const inputRootProps = { ...inputAdornment, disableUnderline: !underline, className: inputRoot }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -51,6 +52,7 @@ class TextField extends React.PureComponent<TextFieldProps> {
|
||||||
inputProps={inputProps}
|
inputProps={inputProps}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
value={value}
|
value={value}
|
||||||
|
// data-testid={testId}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ const styles = {
|
||||||
type Props = {
|
type Props = {
|
||||||
minWidth?: number,
|
minWidth?: number,
|
||||||
minHeight?: number,
|
minHeight?: number,
|
||||||
|
testId: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
const calculateStyleBased = (minWidth, minHeight) => ({
|
const calculateStyleBased = (minWidth, minHeight) => ({
|
||||||
|
@ -19,10 +20,10 @@ const calculateStyleBased = (minWidth, minHeight) => ({
|
||||||
minHeight: minHeight && `${minHeight}px`,
|
minHeight: minHeight && `${minHeight}px`,
|
||||||
})
|
})
|
||||||
|
|
||||||
const GnoButton = ({ minWidth, minHeight, ...props }: Props) => {
|
const GnoButton = ({ minWidth, minHeight, testId = '', ...props }: Props) => {
|
||||||
const style = calculateStyleBased(minWidth, minHeight)
|
const style = calculateStyleBased(minWidth, minHeight)
|
||||||
|
|
||||||
return <Button style={style} {...props} />
|
return <Button style={style} data-testid={testId} {...props} />
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withStyles(styles)(GnoButton)
|
export default withStyles(styles)(GnoButton)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
/* eslint-disable react/button-has-type */
|
/* eslint-disable react/button-has-type */
|
||||||
/* eslint-disable react/default-props-match-prop-types */
|
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import cn from 'classnames/bind'
|
import cn from 'classnames/bind'
|
||||||
import styles from './index.scss'
|
import styles from './index.scss'
|
||||||
|
@ -12,20 +11,16 @@ type Props = {
|
||||||
size?: 'sm' | 'md' | 'lg' | 'xl' | 'xxl',
|
size?: 'sm' | 'md' | 'lg' | 'xl' | 'xxl',
|
||||||
weight?: 'light' | 'regular' | 'bolder' | 'bold',
|
weight?: 'light' | 'regular' | 'bolder' | 'bold',
|
||||||
color?: 'soft' | 'medium' | 'dark' | 'white' | 'fancy' | 'primary' | 'secondary' | 'warning' | 'disabled',
|
color?: 'soft' | 'medium' | 'dark' | 'white' | 'fancy' | 'primary' | 'secondary' | 'warning' | 'disabled',
|
||||||
|
testId?: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
const GnoButtonLink = ({
|
const GnoButtonLink = ({
|
||||||
type, size, weight, color, ...props
|
type = 'button',
|
||||||
}: Props) => (
|
size = 'md',
|
||||||
<button type={type} className={cx(styles.btnLink, size, color, weight)} {...props} />
|
weight = 'regular',
|
||||||
)
|
color = 'secondary',
|
||||||
|
testId = '',
|
||||||
|
...props
|
||||||
GnoButtonLink.defaultProps = {
|
}: Props) => <button type={type} className={cx(styles.btnLink, size, color, weight)} data-testid={testId} {...props} />
|
||||||
type: 'button',
|
|
||||||
size: 'md',
|
|
||||||
weight: 'regular',
|
|
||||||
color: 'secondary',
|
|
||||||
}
|
|
||||||
|
|
||||||
export default GnoButtonLink
|
export default GnoButtonLink
|
||||||
|
|
|
@ -23,6 +23,11 @@ import { addressIsTokenContract, doesntExistInTokenList } from './validators'
|
||||||
import { styles } from './style'
|
import { styles } from './style'
|
||||||
import { getSymbolAndDecimalsFromContract } from './utils'
|
import { getSymbolAndDecimalsFromContract } from './utils'
|
||||||
|
|
||||||
|
export const ADD_CUSTOM_TOKEN_ADDRESS_INPUT_TEST_ID = 'add-custom-token-address-input'
|
||||||
|
export const ADD_CUSTOM_TOKEN_SYMBOLS_INPUT_TEST_ID = 'add-custom-token-symbols-input'
|
||||||
|
export const ADD_CUSTOM_TOKEN_DECIMALS_INPUT_TEST_ID = 'add-custom-token-decimals-input'
|
||||||
|
export const ADD_CUSTOM_TOKEN_FORM = 'add-custom-token-form'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
classes: Object,
|
classes: Object,
|
||||||
addToken: Function,
|
addToken: Function,
|
||||||
|
@ -115,7 +120,7 @@ const AddCustomToken = (props: Props) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<GnoForm onSubmit={handleSubmit} initialValues={formValues}>
|
<GnoForm onSubmit={handleSubmit} initialValues={formValues} testId={ADD_CUSTOM_TOKEN_FORM}>
|
||||||
{() => (
|
{() => (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Block className={classes.formContainer}>
|
<Block className={classes.formContainer}>
|
||||||
|
@ -135,6 +140,7 @@ const AddCustomToken = (props: Props) => {
|
||||||
placeholder="Token contract address*"
|
placeholder="Token contract address*"
|
||||||
text="Token contract address*"
|
text="Token contract address*"
|
||||||
className={classes.addressInput}
|
className={classes.addressInput}
|
||||||
|
testId={ADD_CUSTOM_TOKEN_ADDRESS_INPUT_TEST_ID}
|
||||||
/>
|
/>
|
||||||
<FormSpy
|
<FormSpy
|
||||||
subscription={{
|
subscription={{
|
||||||
|
@ -156,6 +162,7 @@ const AddCustomToken = (props: Props) => {
|
||||||
placeholder="Token symbol*"
|
placeholder="Token symbol*"
|
||||||
text="Token symbol"
|
text="Token symbol"
|
||||||
className={classes.addressInput}
|
className={classes.addressInput}
|
||||||
|
testId={ADD_CUSTOM_TOKEN_SYMBOLS_INPUT_TEST_ID}
|
||||||
/>
|
/>
|
||||||
<Field
|
<Field
|
||||||
name="decimals"
|
name="decimals"
|
||||||
|
@ -165,6 +172,7 @@ const AddCustomToken = (props: Props) => {
|
||||||
placeholder="Token decimals*"
|
placeholder="Token decimals*"
|
||||||
text="Token decimals*"
|
text="Token decimals*"
|
||||||
className={classes.addressInput}
|
className={classes.addressInput}
|
||||||
|
testId={ADD_CUSTOM_TOKEN_DECIMALS_INPUT_TEST_ID}
|
||||||
/>
|
/>
|
||||||
<Block align="left">
|
<Block align="left">
|
||||||
<Field name="showForAllSafes" component={Checkbox} type="checkbox" className={classes.checkbox} />
|
<Field name="showForAllSafes" component={Checkbox} type="checkbox" className={classes.checkbox} />
|
||||||
|
|
|
@ -24,6 +24,8 @@ import { type Token } from '~/logic/tokens/store/model/token'
|
||||||
import { setImageToPlaceholder } from '~/routes/safe/components/Balances/utils'
|
import { setImageToPlaceholder } from '~/routes/safe/components/Balances/utils'
|
||||||
import { styles } from './style'
|
import { styles } from './style'
|
||||||
|
|
||||||
|
export const ADD_CUSTOM_TOKEN_BUTTON_TEST_ID = 'add-custom-token-btn'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
classes: Object,
|
classes: Object,
|
||||||
tokens: List<Token>,
|
tokens: List<Token>,
|
||||||
|
@ -141,6 +143,7 @@ class Tokens extends React.Component<Props, State> {
|
||||||
color="secondary"
|
color="secondary"
|
||||||
className={classes.add}
|
className={classes.add}
|
||||||
onClick={switchToAddCustomTokenScreen}
|
onClick={switchToAddCustomTokenScreen}
|
||||||
|
testId={ADD_CUSTOM_TOKEN_BUTTON_TEST_ID}
|
||||||
>
|
>
|
||||||
+ ADD CUSTOM TOKEN
|
+ ADD CUSTOM TOKEN
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -26,6 +26,9 @@ import SendModal from './SendModal'
|
||||||
import Receive from './Receive'
|
import Receive from './Receive'
|
||||||
import { styles } from './style'
|
import { styles } from './style'
|
||||||
|
|
||||||
|
export const MANAGE_TOKENS_BUTTON_TEST_ID = 'manage-tokens-btn'
|
||||||
|
export const BALANCE_ROW_TEST_ID = 'balance-row'
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
hideZero: boolean,
|
hideZero: boolean,
|
||||||
showToken: boolean,
|
showToken: boolean,
|
||||||
|
@ -128,7 +131,7 @@ class Balances extends React.Component<Props, State> {
|
||||||
<Paragraph className={classes.zero}>Hide zero balances</Paragraph>
|
<Paragraph className={classes.zero}>Hide zero balances</Paragraph>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={6} end="sm">
|
<Col xs={6} end="sm">
|
||||||
<ButtonLink onClick={this.onShow('Token')}>Manage Tokens</ButtonLink>
|
<ButtonLink onClick={this.onShow('Token')} testId="manage-tokens-btn">Manage Tokens</ButtonLink>
|
||||||
<Modal
|
<Modal
|
||||||
title="Manage Tokens"
|
title="Manage Tokens"
|
||||||
description="Enable and disable tokens to be listed"
|
description="Enable and disable tokens to be listed"
|
||||||
|
@ -153,7 +156,7 @@ class Balances extends React.Component<Props, State> {
|
||||||
defaultFixed
|
defaultFixed
|
||||||
>
|
>
|
||||||
{(sortedData: Array<BalanceRow>) => sortedData.map((row: any, index: number) => (
|
{(sortedData: Array<BalanceRow>) => sortedData.map((row: any, index: number) => (
|
||||||
<TableRow tabIndex={-1} key={index} className={classes.hide} data-testid="balance-row">
|
<TableRow tabIndex={-1} key={index} className={classes.hide} data-testid={BALANCE_ROW_TEST_ID}>
|
||||||
{autoColumns.map((column: Column) => (
|
{autoColumns.map((column: Column) => (
|
||||||
<TableCell key={column.id} style={cellWidth(column.width)} align={column.align} component="td">
|
<TableCell key={column.id} style={cellWidth(column.width)} align={column.align} component="td">
|
||||||
{column.id === BALANCE_TABLE_ASSET_ID ? <AssetTableCell asset={row[column.id]} /> : row[column.id]}
|
{column.id === BALANCE_TABLE_ASSET_ID ? <AssetTableCell asset={row[column.id]} /> : row[column.id]}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { calculateBalanceOf } from '~/routes/safe/store/actions/fetchTokenBalanc
|
||||||
import updateActiveTokens from '~/routes/safe/store/actions/updateActiveTokens'
|
import updateActiveTokens from '~/routes/safe/store/actions/updateActiveTokens'
|
||||||
import 'jest-dom/extend-expect'
|
import 'jest-dom/extend-expect'
|
||||||
import updateSafe from '~/routes/safe/store/actions/updateSafe'
|
import updateSafe from '~/routes/safe/store/actions/updateSafe'
|
||||||
|
import { BALANCE_ROW_TEST_ID } from '~/routes/safe/components/Balances'
|
||||||
|
|
||||||
afterEach(cleanup)
|
afterEach(cleanup)
|
||||||
|
|
||||||
|
@ -37,7 +38,7 @@ describe('DOM > Feature > Funds', () => {
|
||||||
await sleep(1300)
|
await sleep(1300)
|
||||||
|
|
||||||
// Open send funds modal
|
// Open send funds modal
|
||||||
const balanceRows = SafeDom.getAllByTestId('balance-row')
|
const balanceRows = SafeDom.getAllByTestId(BALANCE_ROW_TEST_ID)
|
||||||
expect(balanceRows[0]).toHaveTextContent(`${ethAmount} ETH`)
|
expect(balanceRows[0]).toHaveTextContent(`${ethAmount} ETH`)
|
||||||
const sendButton = SafeDom.getByTestId('balance-send-btn')
|
const sendButton = SafeDom.getByTestId('balance-send-btn')
|
||||||
fireEvent.click(sendButton)
|
fireEvent.click(sendButton)
|
||||||
|
@ -91,7 +92,7 @@ describe('DOM > Feature > Funds', () => {
|
||||||
await sleep(1000)
|
await sleep(1000)
|
||||||
|
|
||||||
// Open send funds modal
|
// Open send funds modal
|
||||||
const balanceRows = SafeDom.getAllByTestId('balance-row')
|
const balanceRows = SafeDom.getAllByTestId(BALANCE_ROW_TEST_ID)
|
||||||
expect(balanceRows.length).toBe(2)
|
expect(balanceRows.length).toBe(2)
|
||||||
const sendButtons = SafeDom.getAllByTestId('balance-send-btn')
|
const sendButtons = SafeDom.getAllByTestId('balance-send-btn')
|
||||||
expect(sendButtons.length).toBe(2)
|
expect(sendButtons.length).toBe(2)
|
||||||
|
|
|
@ -1,65 +1,83 @@
|
||||||
// @flow
|
// @flow
|
||||||
import { getWeb3 } from '~/logic/wallets/getWeb3'
|
import { getWeb3 } from '~/logic/wallets/getWeb3'
|
||||||
import { type Match } from 'react-router-dom'
|
import { fireEvent } from '@testing-library/react'
|
||||||
import { getFirstTokenContract, getSecondTokenContract } from '~/test/utils/tokenMovements'
|
import { getFirstTokenContract } from '~/test/utils/tokenMovements'
|
||||||
import { aNewStore } from '~/store'
|
import { aNewStore } from '~/store'
|
||||||
import { aMinedSafe } from '~/test/builder/safe.redux.builder'
|
import { aMinedSafe } from '~/test/builder/safe.redux.builder'
|
||||||
import { travelToTokens } from '~/test/builder/safe.dom.utils'
|
import { renderSafeView } from '~/test/builder/safe.dom.utils'
|
||||||
import { sleep } from '~/utils/timer'
|
import { sleep } from '~/utils/timer'
|
||||||
import { buildMatchPropsFrom } from '~/test/utils/buildReactRouterProps'
|
import { clickOnManageTokens, clickOnAddCustomToken } from '~/test/utils/DOMNavigation'
|
||||||
import { tokenListSelector } from '~/logic/tokens/store/selectors'
|
|
||||||
import { testToken } from '~/test/builder/tokens.dom.utils'
|
|
||||||
import * as fetchTokensModule from '~/logic/tokens/store/actions/fetchTokens'
|
import * as fetchTokensModule from '~/logic/tokens/store/actions/fetchTokens'
|
||||||
import * as enhancedFetchModule from '~/utils/fetch'
|
import {
|
||||||
|
ADD_CUSTOM_TOKEN_ADDRESS_INPUT_TEST_ID,
|
||||||
|
ADD_CUSTOM_TOKEN_SYMBOLS_INPUT_TEST_ID,
|
||||||
|
ADD_CUSTOM_TOKEN_DECIMALS_INPUT_TEST_ID,
|
||||||
|
ADD_CUSTOM_TOKEN_FORM,
|
||||||
|
} from '~/routes/safe/components/Balances/Tokens/screens/AddCustomToken'
|
||||||
|
import { BALANCE_ROW_TEST_ID } from '~/routes/safe/components/Balances/'
|
||||||
|
import 'jest-dom/extend-expect'
|
||||||
|
|
||||||
describe('DOM > Feature > Add new ERC 20 Tokens', () => {
|
// https://github.com/testing-library/@testing-library/react/issues/281
|
||||||
|
const originalError = console.error
|
||||||
|
beforeAll(() => {
|
||||||
|
console.error = (...args) => {
|
||||||
|
if (/Warning.*not wrapped in act/.test(args[0])) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
originalError.call(console, ...args)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
console.error = originalError
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('DOM > Feature > Add custom ERC 20 Tokens', () => {
|
||||||
let web3
|
let web3
|
||||||
let accounts
|
let accounts
|
||||||
let firstErc20Token
|
let erc20Token
|
||||||
let secondErc20Token
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
web3 = getWeb3()
|
web3 = getWeb3()
|
||||||
accounts = await web3.eth.getAccounts()
|
accounts = await web3.eth.getAccounts()
|
||||||
firstErc20Token = await getFirstTokenContract(web3, accounts[0])
|
erc20Token = await getFirstTokenContract(web3, accounts[0])
|
||||||
secondErc20Token = await getSecondTokenContract(web3, accounts[0])
|
|
||||||
|
|
||||||
// $FlowFixMe
|
|
||||||
enhancedFetchModule.enhancedFetch = jest.fn()
|
|
||||||
enhancedFetchModule.enhancedFetch.mockImplementation(() => Promise.resolve({
|
|
||||||
results: [
|
|
||||||
{
|
|
||||||
address: firstErc20Token.address,
|
|
||||||
name: 'First Token Example',
|
|
||||||
symbol: 'FTE',
|
|
||||||
decimals: 18,
|
|
||||||
logoUri: 'https://upload.wikimedia.org/wikipedia/commons/c/c0/Earth_simple_icon.png',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('adds a second erc 20 token filling the form', async () => {
|
it('adds and displays an erc 20 token after filling the form', async () => {
|
||||||
// GIVEN
|
// GIVEN
|
||||||
const store = aNewStore()
|
const store = aNewStore()
|
||||||
const safeAddress = await aMinedSafe(store)
|
const safeAddress = await aMinedSafe(store)
|
||||||
await store.dispatch(fetchTokensModule.fetchTokens(safeAddress))
|
await store.dispatch(fetchTokensModule.fetchTokens())
|
||||||
const TokensDom = await travelToTokens(store, safeAddress)
|
const TokensDom = renderSafeView(store, safeAddress)
|
||||||
await sleep(400)
|
await sleep(400)
|
||||||
const tokens = TestUtils.scryRenderedComponentsWithType(TokensDom, TokenComponent)
|
|
||||||
expect(tokens.length).toBe(2)
|
|
||||||
testToken(tokens[0].props.token, 'FTE', false)
|
|
||||||
testToken(tokens[1].props.token, 'ETH', true)
|
|
||||||
// WHEN
|
// WHEN
|
||||||
await clickOnAddToken(TokensDom)
|
clickOnManageTokens(TokensDom)
|
||||||
await fillAddress(TokensDom, secondErc20Token)
|
clickOnAddCustomToken(TokensDom)
|
||||||
await fillHumanReadableInfo(TokensDom)
|
await sleep(200)
|
||||||
// THEN
|
|
||||||
const match: Match = buildMathPropsFrom(safeAddress)
|
// Fill address
|
||||||
const tokenList = tokenListSelector(store.getState(), { match })
|
const addTokenForm = TokensDom.getByTestId(ADD_CUSTOM_TOKEN_FORM)
|
||||||
expect(tokenList.count()).toBe(3)
|
const addressInput = TokensDom.getByTestId(ADD_CUSTOM_TOKEN_ADDRESS_INPUT_TEST_ID)
|
||||||
testToken(tokenList.get(0), 'FTE', false)
|
fireEvent.change(addressInput, { target: { value: erc20Token.address } })
|
||||||
testToken(tokenList.get(1), 'ETH', true)
|
await sleep(500)
|
||||||
testToken(tokenList.get(2), 'TKN', true)
|
|
||||||
|
// Check if it loaded symbol/decimals correctly
|
||||||
|
const symbolInput = TokensDom.getByTestId(ADD_CUSTOM_TOKEN_SYMBOLS_INPUT_TEST_ID)
|
||||||
|
const decimalsInput = TokensDom.getByTestId(ADD_CUSTOM_TOKEN_DECIMALS_INPUT_TEST_ID)
|
||||||
|
|
||||||
|
const tokenSymbol = await erc20Token.symbol()
|
||||||
|
const tokenDecimals = await erc20Token.decimals()
|
||||||
|
expect(symbolInput.value).toBe(tokenSymbol)
|
||||||
|
expect(decimalsInput.value).toBe(tokenDecimals.toString())
|
||||||
|
|
||||||
|
// Submit form
|
||||||
|
fireEvent.submit(addTokenForm)
|
||||||
|
await sleep(300)
|
||||||
|
|
||||||
|
// check if token is displayed
|
||||||
|
const balanceRows = TokensDom.getAllByTestId(BALANCE_ROW_TEST_ID)
|
||||||
|
expect(balanceRows.length).toBe(2)
|
||||||
|
expect(balanceRows[1]).toHaveTextContent(tokenSymbol)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
// @flow
|
// @flow
|
||||||
import { fireEvent } from '@testing-library/reactß'
|
import { fireEvent } from '@testing-library/react'
|
||||||
|
import { MANAGE_TOKENS_BUTTON_TEST_ID } from '~/routes/safe/components/Balances'
|
||||||
|
import { ADD_CUSTOM_TOKEN_BUTTON_TEST_ID } from '~/routes/safe/components/Balances/Tokens/screens/TokenList'
|
||||||
|
|
||||||
const clickOnManageTokens = (dom) => {
|
export const clickOnManageTokens = (dom: any): void => {
|
||||||
const btn = dom.findByTestId()
|
const btn = dom.getByTestId(MANAGE_TOKENS_BUTTON_TEST_ID)
|
||||||
|
|
||||||
|
fireEvent.click(btn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const clickOnAddCustomToken = (dom: any): void => {
|
||||||
|
const btn = dom.getByTestId(ADD_CUSTOM_TOKEN_BUTTON_TEST_ID)
|
||||||
|
|
||||||
|
fireEvent.click(btn)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue