Skip to content

Commit

Permalink
Merge branch 'master' into add-whitelist-address-in-manage-#596
Browse files Browse the repository at this point in the history
  • Loading branch information
fernandomg committed Mar 12, 2018
2 parents 4d66cc4 + 3e31b35 commit 555a331
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 19 deletions.
18 changes: 6 additions & 12 deletions src/components/Common/WhitelistInputBlock.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import Papa from 'papaparse'
import '../../assets/stylesheets/application.css';
import { InputField } from './InputField'
import { TEXT_FIELDS, VALIDATION_TYPES } from '../../utils/constants'
import { validateAddress } from '../../utils/utils'
import { WhitelistItem } from './WhitelistItem'
import { inject, observer } from 'mobx-react'
import { whitelistImported } from '../../utils/alerts'
import processWhitelist from '../../utils/processWhitelist'
const { ADDRESS, MIN, MAX } = TEXT_FIELDS
const {VALID, INVALID} = VALIDATION_TYPES;

Expand Down Expand Up @@ -80,23 +81,16 @@ export class WhitelistInputBlock extends React.Component {
this.setState(newState)
}

isAddress = (address) => validateAddress(address)
isNumber = (number) => !isNaN(parseFloat(number))

onDrop = (acceptedFiles, rejectedFiles) => {
acceptedFiles.forEach(file => {
Papa.parse(file, {
skipEmptyLines: true,
complete: results => {
results.data.forEach((row) => {
if (row.length !== 3) return

const [addr, min, max] = row

if (!this.isAddress(addr) || !this.isNumber(min) || !this.isNumber(max)) return

this.props.tierStore.addWhitelistItem({ addr, min, max }, this.props.num)
const { called } = processWhitelist(results.data, item => {
this.props.tierStore.addWhitelistItem(item, this.props.num)
})

whitelistImported(called)
}
})
})
Expand Down
9 changes: 5 additions & 4 deletions src/components/manage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ export class Manage extends Component {

tierHasStarted = (index) => {
const initialTierValues = this.props.crowdsaleStore.selected.initialTiersValues[index]
return initialTierValues ? Date.now() > new Date(initialTierValues.startTime).getTime() : true
return initialTierValues && new Date(initialTierValues.startTime).getTime() < Date.now()
}

tierHasEnded = (index) => {
Expand All @@ -466,7 +466,8 @@ export class Manage extends Component {
const { formPristine, canFinalize, shouldDistribute, canDistribute, crowdsaleHasEnded, ownerCurrentUser } = this.state
const { generalStore, tierStore, tokenStore, crowdsaleStore } = this.props
const { address: crowdsaleAddress, finalized, updatable } = crowdsaleStore.selected
let disabled = !ownerCurrentUser || canDistribute || canFinalize || finalized

const canEditTier = ownerCurrentUser && !canDistribute && !canFinalize && !finalized

const distributeTokensStep = (
<div className="steps-content container">
Expand Down Expand Up @@ -544,7 +545,7 @@ export class Manage extends Component {
}

const tierStartAndEndTime = (tier, index) => {
disabled = disabled || !tier.updatable || this.tierHasEnded(index)
const disabled = !canEditTier || !tier.updatable || this.tierHasEnded(index)

return <div className='input-block-container'>
<InputField
Expand Down Expand Up @@ -573,7 +574,7 @@ export class Manage extends Component {
}

const tierRateAndSupply = (tier, index) => {
disabled = disabled || !tier.updatable || this.tierHasEnded(index) || this.tierHasStarted(index)
const disabled = !canEditTier || !tier.updatable || this.tierHasEnded(index) || this.tierHasStarted(index)

return <div className='input-block-container'>
<InputField
Expand Down
1 change: 1 addition & 0 deletions src/components/stepFour/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export class stepFour extends React.Component {
modal: false,
transactionFailed: false
}
this.props.deploymentStore.setDeploymentStep(0)
}

contractDownloadSuccess = options => {
Expand Down
7 changes: 7 additions & 0 deletions src/utils/alerts.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,10 @@ export function skippingTransaction() {
reverseButtons: true
})
}
export function whitelistImported(count) {
return sweetAlert2({
title: 'Addresses imported',
html: `${count} addresses were added to the whitelist`,
type: 'info'
})
}
29 changes: 29 additions & 0 deletions src/utils/processWhitelist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Web3 from 'web3'

const isNumber = (number) => !isNaN(parseFloat(number))

/**
* Execute a callback with each valid whitelist item in the given list
*
* @param {Array} rows Array of whitelist items. Each element in the array has the structure `[address, min, max]`, for
* example: `['0x1234567890123456789012345678901234567890', '1', '10']`
* @param {Function} cb The function to be called with each valid item
* @returns {Object} Object with a `called` property, indicating the number of times the callback was called
*/
export default function (rows, cb) {
let called = 0
rows.forEach((row) => {
if (row.length !== 3) return

const [addr, min, max] = row

if (!Web3.utils.isAddress(addr) || !isNumber(min) || !isNumber(max)) return

cb({ addr, min, max })

called++
})

return { called }
}

91 changes: 91 additions & 0 deletions src/utils/processWhitelist.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import processWhitelist from './processWhitelist'

describe('processWhitelist function', () => {
it('should call the callback for each whitelist item', () => {
// Given
const rows = [
['0x1111111111111111111111111111111111111111', '1', '10'],
['0x2222222222222222222222222222222222222222', '1', '10'],
['0x3333333333333333333333333333333333333333', '1', '10']
]
const cb = jest.fn()

// When
processWhitelist(rows, cb)

// Then
expect(cb).toHaveBeenCalledTimes(3)
expect(cb.mock.calls[0]).toEqual([{ addr: rows[0][0], min: rows[0][1], max: rows[0][2] }])
expect(cb.mock.calls[1]).toEqual([{ addr: rows[1][0], min: rows[1][1], max: rows[1][2] }])
expect(cb.mock.calls[2]).toEqual([{ addr: rows[2][0], min: rows[2][1], max: rows[2][2] }])
})

it('should ignore items that don\t have 3 elements', () => {
// Given
const rows = [
['1', '10'],
['0x2222222222222222222222222222222222222222', '10'],
['0x3333333333333333333333333333333333333333', '1'],
['0x4444444444444444444444444444444444444444'],
[],
['0x4444444444444444444444444444444444444444', '1', '10', '100'],
]
const cb = jest.fn()

// When
processWhitelist(rows, cb)

// Then
expect(cb).toHaveBeenCalledTimes(0)
})

it('should return the number of times the callback was called', () => {
// Given
const rows = [
['0x1111111111111111111111111111111111111111', '1', '10'],
['0x2222222222222222222222222222222222222222', '1', '10'],
['0x3333333333333333333333333333333333333333', '1', '10']
]
const cb = jest.fn()

// When
const { called } = processWhitelist(rows, cb)

// Then
expect(called).toBe(3)
})

it('should ignore invalid numbers', () => {
// Given
const rows = [
['0x1111111111111111111111111111111111111111', 'foo', '10'],
['0x2222222222222222222222222222222222222222', '1', 'bar'],
['0x3333333333333333333333333333333333333333', '', '10'],
['0x4444444444444444444444444444444444444444', '1', '']
]
const cb = jest.fn()

// When
const { called } = processWhitelist(rows, cb)

// Then
expect(called).toBe(0)
})

it('should ignore invalid addresses', () => {
// Given
const rows = [
['0x123456789012345678901234567890123456789', '1', '10'], // 41 characters
['0x12345678901234567890123456789012345678901', '1', '10'], // 43 characters
['0x90F8bf6A479f320ead074411a4B0e7944Ea8c9CG', '1', '10'], // invalid character
['0x90F8bf6A479f320ead074411a4B0e7944Ea8c9c1', '1', '10'] // invalid checksum
]
const cb = jest.fn()

// When
const { called } = processWhitelist(rows, cb)

// Then
expect(called).toBe(0)
})
})
2 changes: 1 addition & 1 deletion src/utils/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export const getStepClass = (step, activeStep) => step === activeStep ? "step-na

export const validateTier = (tier) => typeof tier === 'string' && tier.length > 0 && tier.length < 30

export const validateName = (name) => typeof name === 'string' && name.length > 0 && name.length < 30
export const validateName = (name) => typeof name === 'string' && name.length > 0 && name.length <= 30

export const validateSupply = (supply) => isNaN(Number(supply)) === false && Number(supply) > 0

Expand Down
21 changes: 20 additions & 1 deletion src/utils/utils.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { countDecimalPlaces, validateTicker } from './utils'
import { countDecimalPlaces, validateName, validateTicker } from './utils'

describe('countDecimalPlaces', () => {
[
Expand Down Expand Up @@ -55,3 +55,22 @@ describe('validateTicker', () => {
})
})
})

describe('validateName', () => {
[
{value: '', expected: false},
{value: 'T', expected: true},
{value: 'MyToken', expected: true},
{value: '123456789012345678901234567890', expected: true},
{value: '1234567890123456789012345678901', expected: false},
{value: 23, expected: false},
{value: ['my', 'token'], expected: false},
{value: { a: 1 }, expected: false},
].forEach(testCase => {
const action = testCase.expected ? 'pass' : 'fail'

it(`Should ${action} for '${testCase.value}'`, () => {
expect(validateName(testCase.value)).toBe(testCase.expected)
})
})
})
2 changes: 1 addition & 1 deletion submodules/token-wizard-test-automation

0 comments on commit 555a331

Please sign in to comment.