Add StepThreeFormDutchAuction tests

This commit is contained in:
fernandomg 2018-11-01 11:46:01 -03:00
parent c5cf74b199
commit ce04343fed
3 changed files with 738 additions and 55 deletions

View File

@ -15,33 +15,26 @@ import {
isDecimalPlacesNotGreaterThan,
isGreaterOrEqualThan
} from '../../utils/validations'
import { gweiToWei, navigateTo } from '../../utils/utils'
import { gweiToWei, navigateTo, toBigNumber } from '../../utils/utils'
import { RadioButton } from '../Common/RadioButton'
const logger = logdown('TW:StepThree')
const { VALID } = VALIDATION_TYPES
const { WALLET_ADDRESS, BURN_EXCESS } = TEXT_FIELDS
const logger = logdown('TW:StepThree')
export const StepThreeFormDutchAuction = ({
errors,
form,
handleSubmit,
history,
invalid,
pristine,
reload,
firstLoad,
submitting,
values,
...props
}) => {
const status = !(submitting || invalid)
/**
* Set gas type selected on gas price input
* @param value
*/
const updateGasTypeSelected = value => {
const { updateGasTypeSelected } = props
updateGasTypeSelected(value)
}
const { setFieldTouched } = form.mutators
const handleValidateGasPrice = value => {
const errors = composeValidators(
@ -57,52 +50,52 @@ export const StepThreeFormDutchAuction = ({
input.onChange(value)
}
const setFieldAsTouched = ({ values, errors }) => {
if (reload) {
const tiers = values && values.tiers ? values.tiers : []
tiers.forEach((tier, index) => {
form.mutators.setFieldTouched(`tiers[${index}].tier`, true)
form.mutators.setFieldTouched(`tiers[${index}].updatable`, true)
form.mutators.setFieldTouched(`tiers[${index}].whitelistEnabled`, true)
form.mutators.setFieldTouched(`tiers[${index}].startTime`, true)
form.mutators.setFieldTouched(`tiers[${index}].rate`, true)
form.mutators.setFieldTouched(`tiers[${index}].endTime`, true)
form.mutators.setFieldTouched(`tiers[${index}].minRate`, true)
form.mutators.setFieldTouched(`tiers[${index}].maxRate`, true)
form.mutators.setFieldTouched(`tiers[${index}].supply`, true)
form.mutators.setFieldTouched(`tiers[${index}].minCap`, true)
const setFieldAsTouched = ({ values }) => {
if (firstLoad) {
values.tiers.forEach((tier, index) => {
setFieldTouched(`tiers[${index}].tier`, true)
setFieldTouched(`tiers[${index}].updatable`, true)
setFieldTouched(`tiers[${index}].whitelistEnabled`, true)
setFieldTouched(`tiers[${index}].startTime`, true)
setFieldTouched(`tiers[${index}].rate`, true)
setFieldTouched(`tiers[${index}].endTime`, true)
setFieldTouched(`tiers[${index}].minRate`, true)
setFieldTouched(`tiers[${index}].maxRate`, true)
setFieldTouched(`tiers[${index}].supply`, true)
setFieldTouched(`tiers[${index}].minCap`, true)
})
form.mutators.setFieldTouched(`gasPrice`, true)
setFieldTouched(`gasPrice`, true)
}
}
const handleOnChange = ({ values, errors }) => {
props.tierStore.updateWalletAddress(values.walletAddress, VALID)
props.tierStore.updateBurnExcess(values.burnExcess, VALID)
props.generalStore.setGasPrice(gweiToWei(values.gasPrice.price))
const handleOnChange = ({ values }) => {
const { tierStore, generalStore, crowdsaleStore } = props
const { walletAddress, gasPrice, burnExcess, tiers } = values
let totalSupply = 0
tierStore.updateWalletAddress(walletAddress, VALID)
tierStore.updateBurnExcess(burnExcess, VALID)
const tiers = values && values.tiers ? values.tiers : []
tiers.forEach((tier, index) => {
totalSupply += Number(tier.supply)
props.tierStore.setTierProperty(tier.minCap, 'minCap', index)
props.tierStore.setTierProperty(tier.startTime, 'startTime', index)
props.tierStore.setTierProperty(tier.endTime, 'endTime', index)
props.tierStore.updateMinRate(tier.minRate, VALID, index)
props.tierStore.updateMaxRate(tier.maxRate, VALID, index)
props.tierStore.setTierProperty(tier.supply, 'supply', index)
props.tierStore.setTierProperty(tier.whitelistEnabled, 'whitelistEnabled', index)
props.tierStore.validateTiers('supply', index)
tierStore.setTierProperty(tier.minCap, 'minCap', index)
tierStore.setTierProperty(tier.startTime, 'startTime', index)
tierStore.setTierProperty(tier.endTime, 'endTime', index)
tierStore.updateMinRate(tier.minRate, VALID, index)
tierStore.updateMaxRate(tier.maxRate, VALID, index)
tierStore.setTierProperty(tier.supply, 'supply', index)
tierStore.setTierProperty(tier.whitelistEnabled, 'whitelistEnabled', index)
tierStore.validateTiers('supply', index)
})
const endTime = tiers.length > 0 ? tiers[tiers.length - 1].endTime : null
const totalSupply = tiers.reduce((acc, { supply }) => acc.plus(toBigNumber(supply)), toBigNumber(0)).toFixed()
crowdsaleStore.setProperty('supply', totalSupply)
props.crowdsaleStore.setProperty('supply', totalSupply)
props.crowdsaleStore.setProperty('endTime', endTime)
const endTime = tiers.length > 0 ? tiers[tiers.length - 1].endTime : null
crowdsaleStore.setProperty('endTime', endTime)
generalStore.setGasPrice(gweiToWei(gasPrice.price))
// Set fields as touched
setFieldAsTouched({ values, errors })
setFieldAsTouched({ values })
}
const goBack = async () => {
@ -147,21 +140,22 @@ export const StepThreeFormDutchAuction = ({
<Field
component={InputField2}
description={DESCRIPTION.WALLET}
extraClassName="sw-InputField2-DutchAuctionWalletAddress"
label={WALLET_ADDRESS}
name="walletAddress"
placeholder="Enter here"
validate={isAddress()}
value={values.walletAddress}
extraClassName="sw-InputField2-DutchAuctionWalletAddress"
/>
<Field
component={GasPriceInput}
extraClassName="sw-GasPriceInput-DutchAuction"
gasPrices={props.gasPricesInGwei}
id="gasPrice"
gasPrices={props.gasPriceStore.gasPricesInGwei}
name="gasPrice"
side="right"
updateGasTypeSelected={updateGasTypeSelected}
updateGasTypeSelected={value => props.updateGasTypeSelected(value)}
validate={value => handleValidateGasPrice(value)}
id="gasPrice"
side="right"
extraClassName="sw-GasPriceInput-DutchAuction"
/>
<Field
name="burnExcess"
@ -175,12 +169,19 @@ export const StepThreeFormDutchAuction = ({
)}
/>
<FieldArray name="tiers">
{({ fields }) => <DutchAuctionBlock fields={fields} decimals={props.decimals} />}
{({ fields }) => (
<DutchAuctionBlock
fields={fields}
decimals={props.tokenStore.decimals}
tierStore={props.tierStore}
tokenStore={props.tokenStore}
/>
)}
</FieldArray>
</div>
<div className="st-StepContent_Buttons">
<ButtonBack onClick={goBack} />
<ButtonContinue onClick={handleSubmit} status={status} />
<ButtonContinue onClick={handleSubmit} status={!submitting && !invalid} />
</div>
<FormSpy subscription={{ values: true }} onChange={handleOnChange} />
</form>

View File

@ -0,0 +1,110 @@
import React from 'react'
import { Form } from 'react-final-form'
import { StepThreeFormDutchAuction } from '../../../src/components/StepThree/StepThreeFormDutchAuction'
import { Provider } from 'mobx-react'
import renderer from 'react-test-renderer'
import Adapter from 'enzyme-adapter-react-15'
import { configure, mount } from 'enzyme'
import setFieldTouched from 'final-form-set-field-touched'
import arrayMutators from 'final-form-arrays'
import { GAS_PRICE } from '../../../src/utils/constants'
import {
crowdsaleStore,
gasPriceStore,
generalStore,
reservedTokenStore,
tierStore,
tokenStore
} from '../../../src/stores/index'
import MockDate from 'mockdate'
import { weiToGwei } from '../../../src/utils/utils'
configure({ adapter: new Adapter() })
jest.mock('react-dropzone', () => () => <span>Dropzone</span>)
const currentTime = '2018-03-12T11:00:00'
MockDate.set(currentTime)
describe('StepThreeFormDutchAuction', () => {
const stores = { crowdsaleStore, gasPriceStore, generalStore, reservedTokenStore, tierStore, tokenStore }
const walletAddress = '0xAC7022d55dA6C8BB229b1Ba3Ce8A16724FF79c4A'
beforeEach(() => {
tierStore.addCrowdsale(walletAddress)
generalStore.setGasTypeSelected(GAS_PRICE.SLOW)
})
afterEach(() => {
tierStore.reset()
generalStore.reset()
})
it(`should render StepThreeFormDutchAuction`, () => {
// Given
const component = renderer.create(
<Provider {...stores}>
<Form
onSubmit={jest.fn()}
decorators={[jest.fn()]}
initialValues={{
burnExcess: false,
gasPrice: GAS_PRICE.SLOW,
tiers: tierStore.tiers.slice(),
walletAddress: walletAddress,
whitelistEnabled: 'no'
}}
mutators={{ ...arrayMutators, setFieldTouched }}
history={{ push: jest.fn() }}
firstLoad={true}
updateGasTypeSelected={jest.fn()}
component={StepThreeFormDutchAuction}
{...stores}
/>
</Provider>
)
// When
const tree = component.toJSON()
// Then
expect(tree).toMatchSnapshot()
})
it(`should call updateGasTypeSelected`, () => {
// Given
const updateGasTypeSelected = jest.fn()
const wrapper = mount(
<Provider {...stores}>
<Form
onSubmit={jest.fn()}
decorators={[jest.fn()]}
initialValues={{
gasPrice: GAS_PRICE.SLOW,
tiers: tierStore.tiers.slice(),
walletAddress: walletAddress,
whitelistEnabled: 'no'
}}
mutators={{ ...arrayMutators, setFieldTouched }}
history={{ push: jest.fn() }}
firstLoad={false}
updateGasTypeSelected={updateGasTypeSelected}
component={StepThreeFormDutchAuction}
{...stores}
/>
</Provider>
)
const fast = gasPriceStore.gasPrices[2]
fast.price = weiToGwei(fast.price)
// When
wrapper
.find('.sw-GasPriceInput_SelectInput')
.find(`[value="${GAS_PRICE.FAST.id}"]`)
.simulate('click')
// Then
expect(updateGasTypeSelected).toHaveBeenCalled()
expect(updateGasTypeSelected).toHaveBeenCalledTimes(1)
expect(updateGasTypeSelected).toHaveBeenCalledWith(fast)
})
})

View File

@ -0,0 +1,572 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`StepThreeFormDutchAuction should render StepThreeFormDutchAuction 1`] = `
<form
className="st-StepContent_FormFullHeight"
onSubmit={[Function]}
>
<h2
className="sw-BorderedBlockTitle"
>
Global settings
</h2>
<div
className="sw-BorderedBlock sw-BorderedBlock-CrowdSaleSetupGlobalSettingsDutchAuction"
tabIndex="0"
>
<div
className="sw-InputField2 sw-InputField2-DutchAuctionWalletAddress"
>
<div
className="sw-FormControlTitle"
>
<label
className="sw-FormControlTitle_Label"
>
Wallet Address
</label>
<div
className="sw-FormControlTitle_Info"
tabIndex="-1"
>
<span
className="sw-FormControlTitle_Tooltip"
>
Where the money goes after contributors transactions. Immediately after each transaction. We
recommend to setup a multisig wallet with hardware based signers.
</span>
</div>
</div>
<input
className="sw-TextField"
id="walletAddress"
name="walletAddress"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
placeholder="Enter here"
value="0xAC7022d55dA6C8BB229b1Ba3Ce8A16724FF79c4A"
/>
</div>
<div
className="sw-GasPriceInput sw-GasPriceInput-DutchAuction"
>
<div
className="sw-FormControlTitle"
>
<label
className="sw-FormControlTitle_Label"
>
Gas Price
</label>
<div
className="sw-FormControlTitle_Info"
tabIndex="-1"
>
<span
className="sw-FormControlTitle_Tooltip"
>
Slow is cheap, fast is expensive.
</span>
</div>
</div>
<div
className="sw-GasPriceInput_Select"
>
<button
className="sw-GasPriceInput_SelectButton"
onClick={[Function]}
type="button"
>
<span
className="sw-GasPriceInput_SelectButtonText"
>
Safe and Cheap (5 GWei)
</span>
<span
className="sw-GasPriceInput_SelectButtonChevron"
/>
</button>
<div
className="sw-GasPriceInput_SelectList"
onClick={[Function]}
>
<label
className="sw-GasPriceInput_SelectItem"
onClick={[Function]}
>
<input
checked={true}
className="sw-GasPriceInput_SelectInput"
id="slow"
name="gas-price"
type="radio"
value="slow"
/>
<span
className="sw-GasPriceInput_SelectText"
>
Safe and Cheap (5 GWei)
</span>
</label>
<label
className="sw-GasPriceInput_SelectItem"
onClick={[Function]}
>
<input
checked={false}
className="sw-GasPriceInput_SelectInput"
id="normal"
name="gas-price"
type="radio"
value="normal"
/>
<span
className="sw-GasPriceInput_SelectText"
>
Normal (10 GWei)
</span>
</label>
<label
className="sw-GasPriceInput_SelectItem"
onClick={[Function]}
>
<input
checked={false}
className="sw-GasPriceInput_SelectInput"
id="fast"
name="gas-price"
type="radio"
value="fast"
/>
<span
className="sw-GasPriceInput_SelectText"
>
Fast and Expensive (15 GWei)
</span>
</label>
<label
className="sw-GasPriceInput_SelectItem"
onClick={[Function]}
>
<input
checked={false}
className="sw-GasPriceInput_SelectInput"
id="custom"
name="gas-price"
type="radio"
value="custom"
/>
<span
className="sw-GasPriceInput_SelectText"
>
Custom
</span>
</label>
</div>
</div>
</div>
<div
className="sw-RadioButton sw-RadioButton-DutchAuctionBurnExcess"
>
<div
className="sw-FormControlTitle"
>
<label
className="sw-FormControlTitle_Label"
>
Burn Excess
</label>
<div
className="sw-FormControlTitle_Info"
tabIndex="-1"
>
<span
className="sw-FormControlTitle_Tooltip"
>
Whether the unsold tokens will be burnt on finalization, or be sent to the team wallet
</span>
</div>
</div>
<div
className="sw-RadioButton_Container"
>
<label
className="sw-RadioButton_Label"
>
<input
checked={false}
className="sw-RadioButton_Input"
id="burnExcessRadioButtons.enable_whitelisting_yes"
name="burnExcessRadioButtons"
onChange={[Function]}
type="radio"
value="yes"
/>
<span
className="sw-RadioButton_Button"
>
Yes
</span>
</label>
<label
className="sw-RadioButton_Label"
>
<input
checked={false}
className="sw-RadioButton_Input"
id="burnExcessRadioButtons.enable_whitelisting_no"
name="burnExcessRadioButtons"
onChange={[Function]}
type="radio"
value="no"
/>
<span
className="sw-RadioButton_Button"
>
No
</span>
</label>
</div>
</div>
<div
className="sw-DutchAuctionBlock"
>
<div>
<div
className="sw-InputField2 sw-InputField2-DutchAuctionStartTime"
>
<div
className="sw-FormControlTitle"
>
<label
className="sw-FormControlTitle_Label"
>
Start Time
</label>
<div
className="sw-FormControlTitle_Info"
tabIndex="-1"
>
<span
className="sw-FormControlTitle_Tooltip"
>
Date and time when the tier starts. Can't be in the past from the current moment.
</span>
</div>
</div>
<input
className="sw-TextField"
id="tiers[0].startTime"
name="tiers[0].startTime"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
type="datetime-local"
value="2018-03-12T11:05"
/>
</div>
<div
className="sw-InputField2 sw-InputField2-DutchAuctionEndTime"
>
<div
className="sw-FormControlTitle"
>
<label
className="sw-FormControlTitle_Label"
>
End Time
</label>
<div
className="sw-FormControlTitle_Info"
tabIndex="-1"
>
<span
className="sw-FormControlTitle_Tooltip"
>
Date and time when the tier ends. Can be only in the future.
</span>
</div>
</div>
<input
className="sw-TextField"
id="tiers[0].endTime"
name="tiers[0].endTime"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
type="datetime-local"
value="2018-03-16T00:00"
/>
</div>
<div
className="sw-InputField2 sw-InputField2-DutchAuctionMinRate"
>
<div
className="sw-FormControlTitle"
>
<label
className="sw-FormControlTitle_Label"
>
Min Rate
</label>
<div
className="sw-FormControlTitle_Info"
tabIndex="-1"
>
<span
className="sw-FormControlTitle_Tooltip"
>
Exchange rate Ethereum to Tokens. If it's 100, then for 1 Ether you can buy 100 tokens
</span>
</div>
</div>
<input
className="sw-TextField"
id="tiers[0].minRate"
min="0"
name="tiers[0].minRate"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
placeholder="Enter here"
type="number"
value=""
/>
<div
className="sw-Errors"
>
<div
className="sw-Errors_Item"
>
Please enter a valid number greater than 0
</div>
</div>
</div>
<div
className="sw-InputField2 sw-InputField2-DutchAuctionMaxRate"
>
<div
className="sw-FormControlTitle"
>
<label
className="sw-FormControlTitle_Label"
>
Max Rate
</label>
<div
className="sw-FormControlTitle_Info"
tabIndex="-1"
>
<span
className="sw-FormControlTitle_Tooltip"
>
Exchange rate Ethereum to Tokens. If it's 100, then for 1 Ether you can buy 100 tokens
</span>
</div>
</div>
<input
className="sw-TextField"
id="tiers[0].maxRate"
min="0"
name="tiers[0].maxRate"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
placeholder="Enter here"
type="number"
value=""
/>
<div
className="sw-Errors"
>
<div
className="sw-Errors_Item"
>
Please enter a valid number greater than 0
</div>
</div>
</div>
<div
className="sw-InputField2 sw-InputField2-DutchAuctionSupply"
>
<div
className="sw-FormControlTitle"
>
<label
className="sw-FormControlTitle_Label"
>
Supply
</label>
<div
className="sw-FormControlTitle_Info"
tabIndex="-1"
>
<span
className="sw-FormControlTitle_Tooltip"
>
How many tokens will be sold on this tier. Cap of crowdsale equals to sum of supply of all tiers
</span>
</div>
</div>
<input
className="sw-TextField"
disabled={false}
id="tiers[0].supply"
max={0}
min="0"
name="tiers[0].supply"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
placeholder="Enter here"
type="number"
value=""
/>
<div
className="sw-Errors"
>
<div
className="sw-Errors_Item"
>
Please enter a valid number greater than 0
</div>
</div>
</div>
<div
className="sw-InputField2 sw-InputField2-DutchAuctionMinCap"
>
<div
className="sw-FormControlTitle"
>
<label
className="sw-FormControlTitle_Label"
>
Contributor min cap
</label>
<div
className="sw-FormControlTitle_Info"
tabIndex="-1"
>
<span
className="sw-FormControlTitle_Tooltip"
>
Minimum amount of tokens to buy. Not the minimal amount for every transaction: if minCap is 1
and a user already has 1 token from a previous transaction, they can buy any amount they want.
</span>
</div>
</div>
<input
className="sw-TextField"
disabled={false}
id="tiers[0].minCap"
max=""
min="0"
name="tiers[0].minCap"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
placeholder="Enter here"
type="number"
value="0"
/>
<div
className="sw-Errors"
>
<div
className="sw-Errors_Item"
>
Decimals should not exceed undefined places
</div>
</div>
</div>
<div
className="sw-RadioButton sw-RadioButton-DutchAuctionWhiteList"
>
<div
className="sw-FormControlTitle"
>
<label
className="sw-FormControlTitle_Label"
>
Enable whitelisting
</label>
<div
className="sw-FormControlTitle_Info"
tabIndex="-1"
>
<span
className="sw-FormControlTitle_Tooltip"
>
Enables whitelisting. If disabled, anyone can participate in the crowdsale.
</span>
</div>
</div>
<div
className="sw-RadioButton_Container"
>
<label
className="sw-RadioButton_Label"
>
<input
checked={false}
className="sw-RadioButton_Input"
id="tiers[0].whitelistEnabled.enable_whitelisting_yes"
name="tiers[0].whitelistEnabled"
onChange={[Function]}
type="radio"
value="yes"
/>
<span
className="sw-RadioButton_Button"
>
Yes
</span>
</label>
<label
className="sw-RadioButton_Label"
>
<input
checked={true}
className="sw-RadioButton_Input"
id="tiers[0].whitelistEnabled.enable_whitelisting_no"
name="tiers[0].whitelistEnabled"
onChange={[Function]}
type="radio"
value="no"
/>
<span
className="sw-RadioButton_Button"
>
No
</span>
</label>
</div>
</div>
</div>
</div>
</div>
<div
className="st-StepContent_Buttons"
>
<button
className="sw-ButtonBack "
onClick={[Function]}
type="button"
>
Back
</button>
<button
className="sw-ButtonContinue "
disabled={true}
onClick={[Function]}
>
<span
className="sw-ButtonContinue_Text"
>
Continue
</span>
</button>
</div>
</form>
`;