diff --git a/config/deploy/deploy.sh b/config/deploy/deploy.sh index c375961f..851809aa 100644 --- a/config/deploy/deploy.sh +++ b/config/deploy/deploy.sh @@ -3,6 +3,11 @@ echo "Deployment script for gnosis-safe-team" +RANGE=500 + +number=$RANDOM +let "number %= $RANGE" + # Split on "/", ref: http://stackoverflow.com/a/5257398/689223 REPO_SLUG_ARRAY=(${TRAVIS_REPO_SLUG//\// }) REPO_OWNER=${REPO_SLUG_ARRAY[0]} @@ -17,9 +22,9 @@ if [ "$TRAVIS_PULL_REQUEST" != "false" ] then if [ "$NODE_ENV" == "production" ] then - DEPLOY_SUBDOMAIN_UNFORMATTED_LIST+=(release-${TRAVIS_PULL_REQUEST}-pr) + DEPLOY_SUBDOMAIN_UNFORMATTED_LIST+=(release-${TRAVIS_PULL_REQUEST}-pr-${number}) else - DEPLOY_SUBDOMAIN_UNFORMATTED_LIST+=(staging-${TRAVIS_PULL_REQUEST}-pr) + DEPLOY_SUBDOMAIN_UNFORMATTED_LIST+=(staging-${TRAVIS_PULL_REQUEST}-pr-${number}) fi elif [ -n "${TRAVIS_TAG// }" ] #TAG is not empty then diff --git a/src/components/forms/TextField/index.jsx b/src/components/forms/TextField/index.jsx index b1ca1bd6..cbc88c88 100644 --- a/src/components/forms/TextField/index.jsx +++ b/src/components/forms/TextField/index.jsx @@ -2,30 +2,39 @@ import React from 'react' import MuiTextField, { TextFieldProps } from 'material-ui/TextField' -const TextField = ({ - input: { - name, onChange, value, ...restInput - }, - meta, - render, - text, - ...rest -}: TextFieldProps) => { - const helperText = value ? text : undefined - const showError = meta.touched && !meta.valid +// Neded for solving a fix in Windows browsers +const overflowStyle = { + overflow: 'hidden', +} - return ( - - ) +class TextField extends React.PureComponent { + render() { + const { + input: { + name, onChange, value, ...restInput + }, + meta, + render, + text, + ...rest + } = this.props + const helperText = value ? text : undefined + const showError = (meta.touched || !meta.pristine) && !meta.valid + + return ( + + ) + } } export default TextField diff --git a/src/components/layout/Block/index.scss b/src/components/layout/Block/index.scss index 5ce04657..b8e523a0 100644 --- a/src/components/layout/Block/index.scss +++ b/src/components/layout/Block/index.scss @@ -1,7 +1,7 @@ .block { display: inline-block; width: 100%; - overflow-x: hidden; + overflow: hidden; } .sm { diff --git a/src/components/layout/Table/index.jsx b/src/components/layout/Table/index.jsx index 77c7089b..817b7c2d 100644 --- a/src/components/layout/Table/index.jsx +++ b/src/components/layout/Table/index.jsx @@ -13,14 +13,20 @@ const buildWidthFrom = (size: number) => ({ minWidth: `${size}px`, }) +const overflowStyle = { + overflowX: 'scroll', +} + // see: https://css-tricks.com/responsive-data-tables/ const GnoTable = ({ size, children }: Props) => { const style = size ? buildWidthFrom(size) : undefined return ( - - {children} -
+
+ + {children} +
+
) } diff --git a/src/routes/open/components/Layout.test.js b/src/routes/open/components/Layout.test.js index 38467270..b41ed29f 100644 --- a/src/routes/open/components/Layout.test.js +++ b/src/routes/open/components/Layout.test.js @@ -66,16 +66,13 @@ describe('React DOM TESTS > Create Safe form', () => { await sleep(1500) // THEN - const Deployed = TestUtils.findRenderedDOMComponentWithClass(open, DEPLOYED_COMPONENT_ID) + const deployed = TestUtils.findRenderedDOMComponentWithClass(open, DEPLOYED_COMPONENT_ID) - const addressHtml = Deployed.getElementsByTagName('p')[0].innerHTML - const contractAddress = addressHtml.slice(addressHtml.lastIndexOf('>') + 1) - const transactionHash = JSON.parse(Deployed.getElementsByTagName('pre')[0].innerHTML) - delete transactionHash.logsBloom - // eslint-disable-next-line - console.log('Deployed safe address is: ' + contractAddress) - - // eslint-disable-next-line - console.log(transactionHash) + if (deployed) { + const transactionHash = JSON.parse(deployed.getElementsByTagName('pre')[0].innerHTML) + delete transactionHash.logsBloom + // eslint-disable-next-line + console.log(transactionHash) + } }) }) diff --git a/src/routes/open/components/SafeForm/Confirmations/index.test.js b/src/routes/open/components/SafeForm/Confirmations/index.test.js new file mode 100644 index 00000000..38fb0e01 --- /dev/null +++ b/src/routes/open/components/SafeForm/Confirmations/index.test.js @@ -0,0 +1,51 @@ +// @flow +import TextField from '~/components/forms/TextField' +import * as React from 'react' +import * as TestUtils from 'react-dom/test-utils' +import Layout from '~/routes/open/components/Layout' +import { FIELD_CONFIRMATIONS, FIELD_OWNERS } from '~/routes/open/components/fields' +import { getProviderInfo } from '~/wallets/getWeb3' +import Wrapper from '~/test/Wrapper' +import { CONFIRMATIONS_ERROR } from '~/routes/open/components/SafeForm' + +describe('React DOM TESTS > Create Safe form', () => { + beforeEach(async () => { + // init app web3 instance + await getProviderInfo() + }) + + it('should not allow to continue if confirmations are higher than owners', async () => { + // GIVEN + const open = TestUtils.renderIntoDocument(( + + { }} + /> + + )) + + const inputs = TestUtils.scryRenderedDOMComponentsWithTag(open, 'input') + + const fieldOwners = inputs[1] + expect(fieldOwners.name).toEqual(FIELD_OWNERS) + TestUtils.Simulate.change(fieldOwners, { target: { value: '1' } }) + + const fieldConfirmations = inputs[2] + expect(fieldConfirmations.name).toEqual(FIELD_CONFIRMATIONS) + + // WHEN + TestUtils.Simulate.change(fieldConfirmations, { target: { value: '2' } }) + + // THEN + const muiFields = TestUtils.scryRenderedComponentsWithType(open, TextField) + expect(5).toEqual(muiFields.length) + const confirmationsField = muiFields[4] + + expect(confirmationsField.props.meta.valid).toBe(false) + expect(confirmationsField.props.meta.error).toBe(CONFIRMATIONS_ERROR) + }) +}) diff --git a/src/routes/open/components/SafeForm/Owners/index.jsx b/src/routes/open/components/SafeForm/Owners/index.jsx index 7158e0a3..c6423275 100644 --- a/src/routes/open/components/SafeForm/Owners/index.jsx +++ b/src/routes/open/components/SafeForm/Owners/index.jsx @@ -2,7 +2,7 @@ import * as React from 'react' import Field from '~/components/forms/Field' import TextField from '~/components/forms/TextField' -import { composeValidators, minValue, mustBeNumber, mustBeEthereumAddress, required } from '~/components/forms/validator' +import { composeValidators, minValue, maxValue, mustBeNumber, mustBeEthereumAddress, required } from '~/components/forms/validator' import Block from '~/components/layout/Block' import Col from '~/components/layout/Col' import Heading from '~/components/layout/Heading' @@ -14,49 +14,57 @@ type Props = { numOwners: number, } -const Owners = ({ numOwners }: Props) => ( - - Owners - - +const MAX_NUMBER_OWNERS = 50 + +const Owners = (props: Props) => { + const { numOwners } = props + const validNumber = numOwners && Number.isInteger(Number(numOwners)) + const renderOwners = validNumber && Number(numOwners) <= MAX_NUMBER_OWNERS + + return ( + + Owners + + + + { renderOwners && [...Array(Number(numOwners))].map((x, index) => ( + + + + Owner Nº {index + 1} + + + + + + + + + + )) } - { numOwners && Number.isInteger(Number(numOwners)) && [...Array(Number(numOwners))].map((x, index) => ( - - - - Owner Nº {index + 1} - - - - - - - - - - )) } - -) + ) +} export default Owners diff --git a/src/routes/open/components/SafeForm/index.jsx b/src/routes/open/components/SafeForm/index.jsx index 425480a7..bf3c56e1 100644 --- a/src/routes/open/components/SafeForm/index.jsx +++ b/src/routes/open/components/SafeForm/index.jsx @@ -6,11 +6,13 @@ import Name from './Name' import Owners from './Owners' import Confirmations from './Confirmations' +export const CONFIRMATIONS_ERROR = 'Number of confirmations can not be higher than the number of owners' + export const safeFieldsValidation = (values: Object) => { const errors = {} if (values.owners < values.confirmations) { - errors.confirmations = 'Number of confirmations can not be higher than the number of owners' + errors.confirmations = CONFIRMATIONS_ERROR } return errors diff --git a/src/routes/open/container/Open.jsx b/src/routes/open/container/Open.jsx index c2cddd6b..c80aadf4 100644 --- a/src/routes/open/container/Open.jsx +++ b/src/routes/open/container/Open.jsx @@ -21,6 +21,20 @@ type State = { safeTx: string, } +const createSafe = async (safeContract, values, userAccount, addSafe) => { + const accounts = getAccountsFrom(values) + const numConfirmations = getThresholdFrom(values) + const name = getSafeNameFrom(values) + const owners = getNamesFrom(values) + + const web3 = getWeb3() + safeContract.setProvider(web3.currentProvider) + + const safe = await safeContract.new(accounts, numConfirmations, 0, 0, { from: userAccount, gas: '5000000' }) + addSafe(name, safe.address, numConfirmations, owners, accounts) + return safe +} + class Open extends React.Component { constructor() { super() @@ -36,19 +50,13 @@ class Open extends React.Component { onCallSafeContractSubmit = async (values) => { try { const { userAccount, addSafe } = this.props - const accounts = getAccountsFrom(values) - const numConfirmations = getThresholdFrom(values) - const name = getSafeNameFrom(values) - const owners = getNamesFrom(values) - const web3 = getWeb3() - this.safe.setProvider(web3.currentProvider) - const safeInstance = await this.safe.new(accounts, numConfirmations, 0, 0, { from: userAccount, gas: '5000000' }) + const safeInstance = await createSafe(this.safe, values, userAccount, addSafe) const { address, transactionHash } = safeInstance + const transactionReceipt = await promisify(cb => web3.eth.getTransactionReceipt(transactionHash, cb)) - addSafe(name, address, numConfirmations, owners, accounts) this.setState({ safeAddress: address, safeTx: transactionReceipt }) } catch (error) { // eslint-disable-next-line diff --git a/src/routes/safe/component/Safe.jsx b/src/routes/safe/component/Safe.jsx index a69e9145..7c7b4dd9 100644 --- a/src/routes/safe/component/Safe.jsx +++ b/src/routes/safe/component/Safe.jsx @@ -49,7 +49,7 @@ const GnoSafe = ({ safe }: SafeProps) => ( - +
Name diff --git a/src/routes/safeList/container/index.jsx b/src/routes/safeList/container/index.jsx index 0a504ccc..b878dea9 100644 --- a/src/routes/safeList/container/index.jsx +++ b/src/routes/safeList/container/index.jsx @@ -12,7 +12,7 @@ type Props = { } const SafeList = ({ safes }: Props) => ( - + ) diff --git a/src/test/Wrapper.jsx b/src/test/Wrapper.jsx new file mode 100644 index 00000000..3351c228 --- /dev/null +++ b/src/test/Wrapper.jsx @@ -0,0 +1,18 @@ +// @flow +import * as React from 'react' + +type WrapperProps = { + children: React$Node +} + +class Wrapper extends React.PureComponent { + render() { + return ( + + { this.props.children } + + ) + } +} + +export default Wrapper diff --git a/test/TestSimpleStorage.sol b/test/TestSimpleStorage.sol deleted file mode 100644 index c8ee668c..00000000 --- a/test/TestSimpleStorage.sol +++ /dev/null @@ -1,19 +0,0 @@ -pragma solidity ^0.4.2; - -import "truffle/Assert.sol"; -import "truffle/DeployedAddresses.sol"; -import "../contracts/SimpleStorage.sol"; - -contract TestSimpleStorage { - - function testItStoresAValue() public { - SimpleStorage simpleStorage = SimpleStorage(DeployedAddresses.SimpleStorage()); - - simpleStorage.set(89); - - uint expected = 89; - - Assert.equal(simpleStorage.get(), expected, "It should store the value 89."); - } - -} diff --git a/test/simplestorage.js b/test/simplestorage.js deleted file mode 100644 index 36ebf824..00000000 --- a/test/simplestorage.js +++ /dev/null @@ -1,17 +0,0 @@ -var SimpleStorage = artifacts.require("./SimpleStorage.sol"); - -contract('SimpleStorage', function(accounts) { - - it("...should store the value 89.", function() { - return SimpleStorage.deployed().then(function(instance) { - simpleStorageInstance = instance; - - return simpleStorageInstance.set(89, {from: accounts[0]}); - }).then(function() { - return simpleStorageInstance.get.call(); - }).then(function(storedData) { - assert.equal(storedData, 89, "The value 89 was not stored."); - }); - }); - -});