Merge pull request #113 from gnosis/110-sort-out-tests
#110 Sorting out tests: Safe creation and Safe loading, fix the validator for safe address
This commit is contained in:
commit
031766fbce
48
.babelrc
48
.babelrc
|
@ -1,48 +0,0 @@
|
|||
{
|
||||
"presets": [
|
||||
"@babel/react",
|
||||
"@babel/preset-flow",
|
||||
[
|
||||
"@babel/env",
|
||||
{
|
||||
"forceAllTransforms": true
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": [
|
||||
"react-hot-loader/babel",
|
||||
"@babel/plugin-syntax-dynamic-import",
|
||||
"@babel/plugin-transform-member-expression-literals",
|
||||
"@babel/plugin-transform-property-literals",
|
||||
"@babel/plugin-syntax-import-meta",
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-proposal-json-strings",
|
||||
[
|
||||
"@babel/plugin-proposal-decorators",
|
||||
{
|
||||
"legacy": true
|
||||
}
|
||||
],
|
||||
"@babel/plugin-proposal-function-sent",
|
||||
"@babel/plugin-proposal-export-namespace-from",
|
||||
"@babel/plugin-proposal-numeric-separator",
|
||||
"@babel/plugin-proposal-throw-expressions",
|
||||
"@babel/plugin-proposal-export-default-from",
|
||||
"@babel/plugin-proposal-logical-assignment-operators",
|
||||
"@babel/plugin-proposal-optional-chaining",
|
||||
[
|
||||
"@babel/plugin-proposal-pipeline-operator",
|
||||
{
|
||||
"proposal": "minimal"
|
||||
}
|
||||
],
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||
"@babel/plugin-proposal-do-expressions",
|
||||
"@babel/plugin-proposal-function-bind"
|
||||
],
|
||||
"env": {
|
||||
"test": {
|
||||
"plugins": ["dynamic-import-node"]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
// @flow
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@babel/react',
|
||||
'@babel/preset-flow',
|
||||
[
|
||||
'@babel/env',
|
||||
{
|
||||
forceAllTransforms: true,
|
||||
},
|
||||
],
|
||||
],
|
||||
plugins: [
|
||||
'react-hot-loader/babel',
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
'@babel/plugin-transform-member-expression-literals',
|
||||
'@babel/plugin-transform-property-literals',
|
||||
'@babel/plugin-syntax-import-meta',
|
||||
'@babel/plugin-proposal-class-properties',
|
||||
'@babel/plugin-proposal-json-strings',
|
||||
[
|
||||
'@babel/plugin-proposal-decorators',
|
||||
{
|
||||
legacy: true,
|
||||
},
|
||||
],
|
||||
'@babel/plugin-proposal-function-sent',
|
||||
'@babel/plugin-proposal-export-namespace-from',
|
||||
'@babel/plugin-proposal-numeric-separator',
|
||||
'@babel/plugin-proposal-throw-expressions',
|
||||
'@babel/plugin-proposal-export-default-from',
|
||||
'@babel/plugin-proposal-logical-assignment-operators',
|
||||
'@babel/plugin-proposal-optional-chaining',
|
||||
[
|
||||
'@babel/plugin-proposal-pipeline-operator',
|
||||
{
|
||||
proposal: 'minimal',
|
||||
},
|
||||
],
|
||||
'@babel/plugin-proposal-nullish-coalescing-operator',
|
||||
'@babel/plugin-proposal-do-expressions',
|
||||
'@babel/plugin-proposal-function-bind',
|
||||
],
|
||||
env: {
|
||||
test: {
|
||||
plugins: ['dynamic-import-node'],
|
||||
},
|
||||
},
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
// @flow
|
||||
// This is a custom Jest transformer turning style imports into empty objects.
|
||||
// http://facebook.github.io/jest/docs/tutorial-webpack.html
|
||||
|
||||
module.exports = {
|
||||
process() {
|
||||
return 'module.exports = {};';
|
||||
return 'module.exports = {};'
|
||||
},
|
||||
getCacheKey(fileData, filename) {
|
||||
// The output is always the same.
|
||||
return 'cssTransform';
|
||||
return 'cssTransform'
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
const path = require('path');
|
||||
// @flow
|
||||
const path = require('path')
|
||||
|
||||
// This is a custom Jest transformer turning file imports into filenames.
|
||||
// http://facebook.github.io/jest/docs/tutorial-webpack.html
|
||||
|
||||
module.exports = {
|
||||
process(src, filename) {
|
||||
return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';';
|
||||
return `module.exports = ${JSON.stringify(path.basename(filename))};`
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
// @flow
|
||||
const esModules = ['immortal-db'].join('|')
|
||||
|
||||
module.exports = {
|
||||
collectCoverageFrom: ['src/**/*.{js,jsx}'],
|
||||
moduleNameMapper: {
|
||||
'~(.*)$': '<rootDir>/src/$1',
|
||||
'#(.*)$': '<rootDir>/safe-contracts/build/contracts/$1',
|
||||
'^react-native$': 'react-native-web',
|
||||
},
|
||||
setupFiles: [
|
||||
'<rootDir>/config/webpack.config.test.js',
|
||||
'<rootDir>/config/polyfills.js',
|
||||
'<rootDir>/config/jest/LocalStorageMock.js',
|
||||
'<rootDir>/config/jest/Web3Mock.js',
|
||||
],
|
||||
setupFilesAfterEnv: ['<rootDir>/config/jest/jest.setup.js', 'react-testing-library/cleanup-after-each'],
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['<rootDir>/src/**/__tests__/**/*.js?(x)', '<rootDir>/src/**/?(*.)(spec|test).js?(x)'],
|
||||
testURL: 'http://localhost:8000',
|
||||
transform: {
|
||||
'^.+\\.(js|jsx)$': '<rootDir>/node_modules/babel-jest',
|
||||
'^.+\\.(css|scss)$': '<rootDir>/config/jest/cssTransform.js',
|
||||
'^(?!.*\\.(js|jsx|css|json)$)': '<rootDir>/config/jest/fileTransform.js',
|
||||
},
|
||||
transformIgnorePatterns: [`/node_modules/(?!${esModules})`],
|
||||
verbose: true,
|
||||
}
|
48
package.json
48
package.json
|
@ -29,49 +29,14 @@
|
|||
"pre-commit": [
|
||||
"precommit"
|
||||
],
|
||||
"jest": {
|
||||
"collectCoverageFrom": [
|
||||
"src/**/*.{js,jsx}"
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
"~(.*)$": "<rootDir>/src/$1",
|
||||
"#(.*)$": "<rootDir>/safe-contracts/build/contracts/$1",
|
||||
"^react-native$": "react-native-web"
|
||||
},
|
||||
"setupFiles": [
|
||||
"<rootDir>/config/webpack.config.test.js",
|
||||
"<rootDir>/config/polyfills.js",
|
||||
"<rootDir>/config/jest/LocalStorageMock.js",
|
||||
"<rootDir>/config/jest/Web3Mock.js"
|
||||
],
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>/config/jest/jest.setup.js"
|
||||
],
|
||||
"testEnvironment": "node",
|
||||
"testMatch": [
|
||||
"<rootDir>/src/**/__tests__/**/*.js?(x)",
|
||||
"<rootDir>/src/**/?(*.)(spec|test).js?(x)"
|
||||
],
|
||||
"testURL": "http://localhost:8000",
|
||||
"transform": {
|
||||
"^.+\\.(js|jsx)$": "<rootDir>/node_modules/babel-jest",
|
||||
"^.+\\.(css|scss)$": "<rootDir>/config/jest/cssTransform.js",
|
||||
"^(?!.*\\.(js|jsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
|
||||
},
|
||||
"transformIgnorePatterns": [
|
||||
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$",
|
||||
"[/\\\\]flow-typed[/\\\\].+\\.(js|jsx)$"
|
||||
],
|
||||
"verbose": true
|
||||
},
|
||||
"dependencies": {
|
||||
"@gnosis.pm/util-contracts": "2.0.1",
|
||||
"@gnosis.pm/safe-contracts": "^1.0.0",
|
||||
"@material-ui/core": "4.0.0",
|
||||
"@material-ui/icons": "4.0.0",
|
||||
"@gnosis.pm/util-contracts": "2.0.1",
|
||||
"@material-ui/core": "4.0.1",
|
||||
"@material-ui/icons": "4.0.1",
|
||||
"@welldone-software/why-did-you-render": "^3.0.9",
|
||||
"axios": "^0.18.0",
|
||||
"bignumber.js": "^8.1.1",
|
||||
"bignumber.js": "9.0.0",
|
||||
"connected-react-router": "^6.3.1",
|
||||
"final-form": "4.13.0",
|
||||
"history": "^4.7.2",
|
||||
|
@ -146,18 +111,19 @@
|
|||
"ethereumjs-abi": "^0.6.7",
|
||||
"extract-text-webpack-plugin": "^4.0.0-beta.0",
|
||||
"file-loader": "^3.0.1",
|
||||
"flow-bin": "0.98.1",
|
||||
"flow-bin": "0.99.0",
|
||||
"fs-extra": "8.0.1",
|
||||
"html-loader": "^0.5.5",
|
||||
"html-webpack-plugin": "^3.0.4",
|
||||
"jest": "24.8.0",
|
||||
"json-loader": "^0.5.7",
|
||||
"mini-css-extract-plugin": "0.6.0",
|
||||
"mini-css-extract-plugin": "0.7.0",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"postcss-mixins": "^6.2.0",
|
||||
"postcss-simple-vars": "^5.0.2",
|
||||
"pre-commit": "^1.2.2",
|
||||
"prettier-eslint-cli": "^4.7.1",
|
||||
"react-testing-library": "^7.0.1",
|
||||
"run-with-testrpc": "0.3.1",
|
||||
"storybook-host": "^5.0.3",
|
||||
"storybook-router": "^0.3.3",
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
process.env.NODE_ENV = 'test';
|
||||
process.env.PUBLIC_URL = '';
|
||||
// @flow
|
||||
process.env.NODE_ENV = 'test'
|
||||
process.env.PUBLIC_URL = ''
|
||||
|
||||
// Load environment variables from .env file. Suppress warnings using silent
|
||||
// if this file is missing. dotenv will never modify any environment variables
|
||||
// that have already been set.
|
||||
// https://github.com/motdotla/dotenv
|
||||
require('dotenv').config({silent: true});
|
||||
require('dotenv').config({ silent: true })
|
||||
|
||||
const jest = require('jest');
|
||||
const argv = process.argv.slice(2);
|
||||
const jest = require('jest')
|
||||
|
||||
const argv = process.argv.slice(2)
|
||||
|
||||
// Watch unless on CI or in coverage mode
|
||||
if (!process.env.CI && argv.indexOf('--coverage') < 0) {
|
||||
argv.push('--watch');
|
||||
argv.push('--watch')
|
||||
}
|
||||
|
||||
jest.run(argv);
|
||||
jest.run(argv)
|
||||
|
|
|
@ -21,6 +21,7 @@ type Props = {
|
|||
onReset?: () => void,
|
||||
initialValues?: Object,
|
||||
disabledWhenValidating?: boolean,
|
||||
testId?: string,
|
||||
}
|
||||
|
||||
type State = {
|
||||
|
@ -145,7 +146,7 @@ class GnoStepper extends React.PureComponent<Props, State> {
|
|||
|
||||
render() {
|
||||
const {
|
||||
steps, children, classes, disabledWhenValidating = false,
|
||||
steps, children, classes, disabledWhenValidating = false, testId,
|
||||
} = this.props
|
||||
const { page, values } = this.state
|
||||
const activePage = this.getActivePageFrom(children)
|
||||
|
@ -154,7 +155,7 @@ class GnoStepper extends React.PureComponent<Props, State> {
|
|||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<GnoForm onSubmit={this.handleSubmit} initialValues={values} validation={this.validate}>
|
||||
<GnoForm onSubmit={this.handleSubmit} initialValues={values} validation={this.validate} testId={testId}>
|
||||
{(submitting: boolean, validating: boolean, ...rest: any) => {
|
||||
const disabled = disabledWhenValidating ? submitting || validating : submitting
|
||||
const controls = (
|
||||
|
|
|
@ -16,6 +16,7 @@ type Props = {
|
|||
validation?: (values: Object) => Object | Promise<Object>,
|
||||
initialValues?: Object,
|
||||
formMutators?: Object,
|
||||
testId?: string,
|
||||
}
|
||||
|
||||
const stylesBasedOn = (padding: number): $Shape<CSSStyleDeclaration> => ({
|
||||
|
@ -25,7 +26,7 @@ const stylesBasedOn = (padding: number): $Shape<CSSStyleDeclaration> => ({
|
|||
})
|
||||
|
||||
const GnoForm = ({
|
||||
onSubmit, validation, initialValues, children, padding = 0, formMutators,
|
||||
onSubmit, validation, initialValues, children, padding = 0, formMutators, testId = '',
|
||||
}: Props) => (
|
||||
<Form
|
||||
validate={validation}
|
||||
|
@ -33,7 +34,7 @@ const GnoForm = ({
|
|||
initialValues={initialValues}
|
||||
mutators={formMutators}
|
||||
render={({ handleSubmit, ...rest }) => (
|
||||
<form onSubmit={handleSubmit} style={stylesBasedOn(padding)}>
|
||||
<form onSubmit={handleSubmit} style={stylesBasedOn(padding)} data-testid={testId}>
|
||||
{children(rest.submitting, rest.validating, rest, rest.form.mutators)}
|
||||
</form>
|
||||
)}
|
||||
|
|
|
@ -49,7 +49,7 @@ const createMasterCopies = async () => {
|
|||
proxyFactoryMaster = await ProxyFactory.new({ from: userAccount, gas: '5000000' })
|
||||
|
||||
const GnosisSafe = getGnosisSafeContract(web3)
|
||||
safeMaster = await GnosisSafe.new({ from: userAccount, gas: '6000000' })
|
||||
safeMaster = await GnosisSafe.new({ from: userAccount, gas: '7000000' })
|
||||
}
|
||||
|
||||
export const initContracts = ensureOnce(process.env.NODE_ENV === 'test' ? createMasterCopies : instanciateMasterCopies)
|
||||
|
|
|
@ -37,9 +37,11 @@ const styles = () => ({
|
|||
export const SAFE_INSTANCE_ERROR = 'Address given is not a safe instance'
|
||||
export const SAFE_MASTERCOPY_ERROR = 'Mastercopy used by this safe is not the same'
|
||||
|
||||
// In case of an error here, it will be swallowed by final-form
|
||||
// So if you're experiencing any strang behaviours like freeze or hanging
|
||||
// Don't mind to check if everything is OK inside this function :)
|
||||
export const safeFieldsValidation = async (values: Object) => {
|
||||
const errors = {}
|
||||
|
||||
const web3 = getWeb3()
|
||||
const safeAddress = values[FIELD_LOAD_ADDRESS]
|
||||
if (!safeAddress || mustBeEthereumAddress(safeAddress) !== undefined) {
|
||||
|
@ -51,27 +53,21 @@ export const safeFieldsValidation = async (values: Object) => {
|
|||
|
||||
const code = await web3.eth.getCode(safeAddress)
|
||||
const codeWithoutMetadata = code.substring(0, code.lastIndexOf(metaData))
|
||||
|
||||
const proxyCode = SafeProxy.deployedBytecode
|
||||
const proxyCodeWithoutMetadata = proxyCode.substring(0, proxyCode.lastIndexOf(metaData))
|
||||
|
||||
const safeInstance = codeWithoutMetadata === proxyCodeWithoutMetadata
|
||||
if (!safeInstance) {
|
||||
errors[FIELD_LOAD_ADDRESS] = SAFE_INSTANCE_ERROR
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
// check mastercopy
|
||||
const proxy = contract(SafeProxy)
|
||||
proxy.setProvider(web3.currentProvider)
|
||||
const proxyInstance = await proxy.at(safeAddress)
|
||||
const proxyImplementation = await proxyInstance.implementation()
|
||||
|
||||
const proxyAddressFromStorage = await web3.eth.getStorageAt(safeAddress, 0)
|
||||
const checksummedProxyAddress = web3.utils.toChecksumAddress(proxyAddressFromStorage)
|
||||
const safeMaster = await getSafeMasterContract()
|
||||
const masterCopy = safeMaster.address
|
||||
|
||||
const sameMasterCopy = proxyImplementation === masterCopy
|
||||
const sameMasterCopy = checksummedProxyAddress === masterCopy
|
||||
if (!sameMasterCopy) {
|
||||
errors[FIELD_LOAD_ADDRESS] = SAFE_MASTERCOPY_ERROR
|
||||
}
|
||||
|
|
|
@ -12,9 +12,7 @@ import { history } from '~/store'
|
|||
import { secondary } from '~/theme/variables'
|
||||
import { type SelectorProps } from '~/routes/load/container/selector'
|
||||
|
||||
const getSteps = () => [
|
||||
'Details', 'Review',
|
||||
]
|
||||
const getSteps = () => ['Details', 'Review']
|
||||
|
||||
type Props = SelectorProps & {
|
||||
onLoadSafeSubmit: (values: Object) => Promise<void>,
|
||||
|
@ -37,30 +35,24 @@ const Layout = ({
|
|||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{ provider
|
||||
? (
|
||||
<Block>
|
||||
<Row align="center">
|
||||
<IconButton onClick={back} style={iconStyle} disableRipple>
|
||||
<ChevronLeft />
|
||||
</IconButton>
|
||||
<Heading tag="h2">Load existing Safe</Heading>
|
||||
</Row>
|
||||
<Stepper
|
||||
onSubmit={onLoadSafeSubmit}
|
||||
steps={steps}
|
||||
>
|
||||
<Stepper.Page validate={safeFieldsValidation}>
|
||||
{ DetailsForm }
|
||||
</Stepper.Page>
|
||||
<Stepper.Page network={network} userAddress={userAddress}>
|
||||
{ ReviewInformation }
|
||||
</Stepper.Page>
|
||||
</Stepper>
|
||||
</Block>
|
||||
)
|
||||
: <div>No metamask detected</div>
|
||||
}
|
||||
{provider ? (
|
||||
<Block>
|
||||
<Row align="center">
|
||||
<IconButton onClick={back} style={iconStyle} disableRipple>
|
||||
<ChevronLeft />
|
||||
</IconButton>
|
||||
<Heading tag="h2">Load existing Safe</Heading>
|
||||
</Row>
|
||||
<Stepper onSubmit={onLoadSafeSubmit} steps={steps} testId="load-safe-form">
|
||||
<Stepper.Page validate={safeFieldsValidation}>{DetailsForm}</Stepper.Page>
|
||||
<Stepper.Page network={network} userAddress={userAddress}>
|
||||
{ReviewInformation}
|
||||
</Stepper.Page>
|
||||
</Stepper>
|
||||
</Block>
|
||||
) : (
|
||||
<div>No metamask detected</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -14,9 +14,7 @@ import { getOwnerNameBy, getOwnerAddressBy, FIELD_CONFIRMATIONS } from '~/routes
|
|||
import { history } from '~/store'
|
||||
import { secondary } from '~/theme/variables'
|
||||
|
||||
const getSteps = () => [
|
||||
'Start', 'Owners', 'Confirmations', 'Review',
|
||||
]
|
||||
const getSteps = () => ['Start', 'Owners', 'Confirmations', 'Review']
|
||||
|
||||
const initialValuesFrom = (userAccount: string) => ({
|
||||
[getOwnerNameBy(0)]: 'My Metamask (me)',
|
||||
|
@ -49,37 +47,24 @@ const Layout = ({
|
|||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{ provider
|
||||
? (
|
||||
<Block>
|
||||
<Row align="center">
|
||||
<IconButton onClick={back} style={iconStyle} disableRipple>
|
||||
<ChevronLeft />
|
||||
</IconButton>
|
||||
<Heading tag="h2">Create New Safe</Heading>
|
||||
</Row>
|
||||
<Stepper
|
||||
onSubmit={onCallSafeContractSubmit}
|
||||
steps={steps}
|
||||
initialValues={initialValues}
|
||||
>
|
||||
<Stepper.Page>
|
||||
{ SafeNameField }
|
||||
</Stepper.Page>
|
||||
<Stepper.Page>
|
||||
{ SafeOwnersFields }
|
||||
</Stepper.Page>
|
||||
<Stepper.Page validate={safeFieldsValidation}>
|
||||
{ SafeThresholdField }
|
||||
</Stepper.Page>
|
||||
<Stepper.Page network={network}>
|
||||
{ Review }
|
||||
</Stepper.Page>
|
||||
</Stepper>
|
||||
</Block>
|
||||
)
|
||||
: <div>No metamask detected</div>
|
||||
}
|
||||
{provider ? (
|
||||
<Block>
|
||||
<Row align="center">
|
||||
<IconButton onClick={back} style={iconStyle} disableRipple>
|
||||
<ChevronLeft />
|
||||
</IconButton>
|
||||
<Heading tag="h2">Create New Safe</Heading>
|
||||
</Row>
|
||||
<Stepper onSubmit={onCallSafeContractSubmit} steps={steps} initialValues={initialValues} testId="create-safe-form">
|
||||
<Stepper.Page>{SafeNameField}</Stepper.Page>
|
||||
<Stepper.Page>{SafeOwnersFields}</Stepper.Page>
|
||||
<Stepper.Page validate={safeFieldsValidation}>{SafeThresholdField}</Stepper.Page>
|
||||
<Stepper.Page network={network}>{Review}</Stepper.Page>
|
||||
</Stepper>
|
||||
</Block>
|
||||
) : (
|
||||
<div>No metamask detected</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,11 @@ import { withStyles } from '@material-ui/core/styles'
|
|||
import Field from '~/components/forms/Field'
|
||||
import TextField from '~/components/forms/TextField'
|
||||
import {
|
||||
required, composeValidators, uniqueAddress, mustBeEthereumAddress, noErrorsOn,
|
||||
required,
|
||||
composeValidators,
|
||||
uniqueAddress,
|
||||
mustBeEthereumAddress,
|
||||
noErrorsOn,
|
||||
} from '~/components/forms/validator'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Button from '~/components/layout/Button'
|
||||
|
@ -105,7 +109,6 @@ class SafeOwners extends React.Component<Props, State> {
|
|||
const initialValues = calculateValuesAfterRemoving(index, numOwners, values)
|
||||
updateInitialProps(initialValues)
|
||||
|
||||
|
||||
this.setState(state => ({
|
||||
numOwners: state.numOwners - 1,
|
||||
}))
|
||||
|
@ -135,11 +138,11 @@ class SafeOwners extends React.Component<Props, State> {
|
|||
</Row>
|
||||
<Hairline />
|
||||
<Block margin="md" padding="md">
|
||||
{ [...Array(Number(numOwners))].map((x, index) => {
|
||||
{[...Array(Number(numOwners))].map((x, index) => {
|
||||
const addressName = getOwnerAddressBy(index)
|
||||
|
||||
return (
|
||||
<Row key={`owner${(index)}`} className={classes.owner}>
|
||||
<Row key={`owner${index}`} className={classes.owner}>
|
||||
<Col xs={4}>
|
||||
<Field
|
||||
className={classes.name}
|
||||
|
@ -155,13 +158,15 @@ class SafeOwners extends React.Component<Props, State> {
|
|||
<Field
|
||||
name={addressName}
|
||||
component={TextField}
|
||||
inputAdornment={noErrorsOn(addressName, errors) && {
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<CheckCircle className={classes.check} />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
inputAdornment={
|
||||
noErrorsOn(addressName, errors) && {
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<CheckCircle className={classes.check} />
|
||||
</InputAdornment>
|
||||
),
|
||||
}
|
||||
}
|
||||
type="text"
|
||||
validate={getAddressValidators(otherAccounts, index)}
|
||||
placeholder="Owner Address*"
|
||||
|
@ -169,17 +174,17 @@ class SafeOwners extends React.Component<Props, State> {
|
|||
/>
|
||||
</Col>
|
||||
<Col xs={1} center="xs" middle="xs" className={classes.remove}>
|
||||
{ index > 0
|
||||
&& <Img src={trash} height={20} alt="Delete" onClick={this.onRemoveRow(index)} />
|
||||
}
|
||||
{index > 0 && <Img src={trash} height={20} alt="Delete" onClick={this.onRemoveRow(index)} />}
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
}) }
|
||||
})}
|
||||
</Block>
|
||||
<Row align="center" grow className={classes.add} margin="xl">
|
||||
<Button color="secondary" onClick={this.onAddOwner}>
|
||||
<Paragraph weight="bold" size="md" noMargin>{ADD_OWNER_BUTTON}</Paragraph>
|
||||
<Button color="secondary" onClick={this.onAddOwner} data-testid="add-owner-btn">
|
||||
<Paragraph weight="bold" size="md" noMargin>
|
||||
{ADD_OWNER_BUTTON}
|
||||
</Paragraph>
|
||||
</Button>
|
||||
</Row>
|
||||
</React.Fragment>
|
||||
|
@ -202,5 +207,4 @@ const SafeOwnersPage = ({ updateInitialProps }: Object) => (controls: React$Node
|
|||
</React.Fragment>
|
||||
)
|
||||
|
||||
|
||||
export default SafeOwnersPage
|
||||
|
|
|
@ -4,7 +4,9 @@ import { withStyles } from '@material-ui/core/styles'
|
|||
import MenuItem from '@material-ui/core/MenuItem'
|
||||
import Field from '~/components/forms/Field'
|
||||
import SelectField from '~/components/forms/SelectField'
|
||||
import { composeValidators, minValue, mustBeInteger, required } from '~/components/forms/validator'
|
||||
import {
|
||||
composeValidators, minValue, mustBeInteger, required,
|
||||
} from '~/components/forms/validator'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Row from '~/components/layout/Row'
|
||||
import Col from '~/components/layout/Col'
|
||||
|
@ -52,22 +54,23 @@ const SafeThreshold = ({ classes, values }: Props) => {
|
|||
<Field
|
||||
name={FIELD_CONFIRMATIONS}
|
||||
component={SelectField}
|
||||
validate={composeValidators(
|
||||
required,
|
||||
mustBeInteger,
|
||||
minValue(1),
|
||||
)}
|
||||
validate={composeValidators(required, mustBeInteger, minValue(1))}
|
||||
data-testid="threshold-select-input"
|
||||
>
|
||||
{
|
||||
[...Array(Number(numOwners))].map((x, index) => (
|
||||
<MenuItem key={`selectOwner${index}`} value={`${index + 1}`}>{index + 1}</MenuItem>
|
||||
))
|
||||
}
|
||||
{[...Array(Number(numOwners))].map((x, index) => (
|
||||
<MenuItem key={`selectOwner${index}`} value={`${index + 1}`}>
|
||||
{index + 1}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Field>
|
||||
</Col>
|
||||
<Col xs={10}>
|
||||
<Paragraph size="lg" color="primary" noMargin className={classes.owners}>
|
||||
out of {numOwners} owner(s)
|
||||
out of
|
||||
{' '}
|
||||
{numOwners}
|
||||
{' '}
|
||||
owner(s)
|
||||
</Paragraph>
|
||||
</Col>
|
||||
</Row>
|
||||
|
@ -85,5 +88,4 @@ const SafeOwnersPage = () => (controls: React$Node, { values }: Object) => (
|
|||
</React.Fragment>
|
||||
)
|
||||
|
||||
|
||||
export default SafeOwnersPage
|
||||
|
|
|
@ -1,74 +1,74 @@
|
|||
// @flow
|
||||
import { List, Map } from 'immutable'
|
||||
import axios from 'axios'
|
||||
import type { Dispatch as ReduxDispatch } from 'redux'
|
||||
import { type GlobalState } from '~/store/index'
|
||||
import { makeOwner } from '~/routes/safe/store/models/owner'
|
||||
import { makeTransaction, type Transaction } from '~/routes/safe/store/models/transaction'
|
||||
import { makeConfirmation } from '~/routes/safe/store/models/confirmation'
|
||||
import { loadSafeSubjects } from '~/utils/storage/transactions'
|
||||
import { buildTxServiceUrlFrom, type TxServiceType } from '~/logic/safe/safeTxHistory'
|
||||
import { getOwners } from '~/logic/safe/utils'
|
||||
import { EMPTY_DATA } from '~/logic/wallets/ethTransactions'
|
||||
import addTransactions from './addTransactions'
|
||||
// // @flow
|
||||
// import { List, Map } from 'immutable'
|
||||
// import axios from 'axios'
|
||||
// import type { Dispatch as ReduxDispatch } from 'redux'
|
||||
// import { type GlobalState } from '~/store/index'
|
||||
// import { makeOwner } from '~/routes/safe/store/models/owner'
|
||||
// import { makeTransaction, type Transaction } from '~/routes/safe/store/models/transaction'
|
||||
// import { makeConfirmation } from '~/routes/safe/store/models/confirmation'
|
||||
// import { loadSafeSubjects } from '~/utils/storage/transactions'
|
||||
// import { buildTxServiceUrlFrom, type TxServiceType } from '~/logic/safe/safeTxHistory'
|
||||
// import { getOwners } from '~/logic/safe/utils'
|
||||
// import { EMPTY_DATA } from '~/logic/wallets/ethTransactions'
|
||||
// import addTransactions from './addTransactions'
|
||||
|
||||
type ConfirmationServiceModel = {
|
||||
owner: string,
|
||||
submissionDate: Date,
|
||||
type: string,
|
||||
transactionHash: string,
|
||||
}
|
||||
// type ConfirmationServiceModel = {
|
||||
// owner: string,
|
||||
// submissionDate: Date,
|
||||
// type: string,
|
||||
// transactionHash: string,
|
||||
// }
|
||||
|
||||
type TxServiceModel = {
|
||||
to: string,
|
||||
value: number,
|
||||
data: string,
|
||||
operation: number,
|
||||
nonce: number,
|
||||
submissionDate: Date,
|
||||
executionDate: Date,
|
||||
confirmations: ConfirmationServiceModel[],
|
||||
isExecuted: boolean,
|
||||
}
|
||||
// type TxServiceModel = {
|
||||
// to: string,
|
||||
// value: number,
|
||||
// data: string,
|
||||
// operation: number,
|
||||
// nonce: number,
|
||||
// submissionDate: Date,
|
||||
// executionDate: Date,
|
||||
// confirmations: ConfirmationServiceModel[],
|
||||
// isExecuted: boolean,
|
||||
// }
|
||||
|
||||
const buildTransactionFrom = (safeAddress: string, tx: TxServiceModel, safeSubjects: Map<string, string>) => {
|
||||
const name = safeSubjects.get(String(tx.nonce)) || 'Unknown'
|
||||
const storedOwners = getOwners(safeAddress)
|
||||
const confirmations = List(
|
||||
tx.confirmations.map((conf: ConfirmationServiceModel) => {
|
||||
const ownerName = storedOwners.get(conf.owner.toLowerCase()) || 'UNKNOWN'
|
||||
// const buildTransactionFrom = (safeAddress: string, tx: TxServiceModel, safeSubjects: Map<string, string>) => {
|
||||
// const name = safeSubjects.get(String(tx.nonce)) || 'Unknown'
|
||||
// const storedOwners = getOwners(safeAddress)
|
||||
// const confirmations = List(
|
||||
// tx.confirmations.map((conf: ConfirmationServiceModel) => {
|
||||
// const ownerName = storedOwners.get(conf.owner.toLowerCase()) || 'UNKNOWN'
|
||||
|
||||
return makeConfirmation({
|
||||
owner: makeOwner({ address: conf.owner, name: ownerName }),
|
||||
type: ((conf.type.toLowerCase(): any): TxServiceType),
|
||||
hash: conf.transactionHash,
|
||||
})
|
||||
}),
|
||||
)
|
||||
// return makeConfirmation({
|
||||
// owner: makeOwner({ address: conf.owner, name: ownerName }),
|
||||
// type: ((conf.type.toLowerCase(): any): TxServiceType),
|
||||
// hash: conf.transactionHash,
|
||||
// })
|
||||
// }),
|
||||
// )
|
||||
|
||||
return makeTransaction({
|
||||
name,
|
||||
nonce: tx.nonce,
|
||||
value: Number(tx.value),
|
||||
confirmations,
|
||||
destination: tx.to,
|
||||
data: tx.data ? tx.data : EMPTY_DATA,
|
||||
isExecuted: tx.isExecuted,
|
||||
})
|
||||
}
|
||||
// return makeTransaction({
|
||||
// name,
|
||||
// nonce: tx.nonce,
|
||||
// value: Number(tx.value),
|
||||
// confirmations,
|
||||
// destination: tx.to,
|
||||
// data: tx.data ? tx.data : EMPTY_DATA,
|
||||
// isExecuted: tx.isExecuted,
|
||||
// })
|
||||
// }
|
||||
|
||||
export const loadSafeTransactions = async (safeAddress: string) => {
|
||||
const url = buildTxServiceUrlFrom(safeAddress)
|
||||
const response = await axios.get(url)
|
||||
const transactions: TxServiceModel[] = response.data.results
|
||||
const safeSubjects = loadSafeSubjects(safeAddress)
|
||||
const txsRecord = transactions.map((tx: TxServiceModel) => buildTransactionFrom(safeAddress, tx, safeSubjects))
|
||||
// export const loadSafeTransactions = async (safeAddress: string) => {
|
||||
// const url = buildTxServiceUrlFrom(safeAddress)
|
||||
// const response = await axios.get(url)
|
||||
// const transactions: TxServiceModel[] = response.data.results
|
||||
// const safeSubjects = loadSafeSubjects(safeAddress)
|
||||
// const txsRecord = transactions.map((tx: TxServiceModel) => buildTransactionFrom(safeAddress, tx, safeSubjects))
|
||||
|
||||
return Map().set(safeAddress, List(txsRecord))
|
||||
}
|
||||
// return Map().set(safeAddress, List(txsRecord))
|
||||
// }
|
||||
|
||||
export default (safeAddress: string) => async (dispatch: ReduxDispatch<GlobalState>) => {
|
||||
const transactions: Map<string, List<Transaction>> = await loadSafeTransactions(safeAddress)
|
||||
// export default (safeAddress: string) => async (dispatch: ReduxDispatch<GlobalState>) => {
|
||||
// const transactions: Map<string, List<Transaction>> = await loadSafeTransactions(safeAddress)
|
||||
|
||||
return dispatch(addTransactions(transactions))
|
||||
}
|
||||
// return dispatch(addTransactions(transactions))
|
||||
// }
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import { type Store } from 'redux'
|
||||
import TestUtils from 'react-dom/test-utils'
|
||||
import Select from '@material-ui/core/Select'
|
||||
import { render, fireEvent, cleanup } from 'react-testing-library'
|
||||
import { Provider } from 'react-redux'
|
||||
import { ConnectedRouter } from 'connected-react-router'
|
||||
import { ADD_OWNER_BUTTON } from '~/routes/open/components/SafeOwnersForm'
|
||||
|
@ -15,12 +14,29 @@ import { makeProvider } from '~/logic/wallets/store/model/provider'
|
|||
import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts'
|
||||
import { whenSafeDeployed } from './builder/safe.dom.utils'
|
||||
|
||||
const fillOpenSafeForm = async (localStore: Store<GlobalState>) => {
|
||||
afterEach(cleanup)
|
||||
|
||||
// https://github.com/testing-library/react-testing-library/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
|
||||
})
|
||||
|
||||
const renderOpenSafeForm = async (localStore: Store<GlobalState>) => {
|
||||
const provider = await getProviderInfo()
|
||||
const walletRecord = makeProvider(provider)
|
||||
localStore.dispatch(addProvider(walletRecord))
|
||||
|
||||
return TestUtils.renderIntoDocument(
|
||||
return render(
|
||||
<Provider store={localStore}>
|
||||
<ConnectedRouter history={history}>
|
||||
<Open />
|
||||
|
@ -29,53 +45,54 @@ const fillOpenSafeForm = async (localStore: Store<GlobalState>) => {
|
|||
)
|
||||
}
|
||||
|
||||
const deploySafe = async (safe: React$Component<{}>, threshold: number, numOwners: number) => {
|
||||
const deploySafe = async (createSafeForm: any, threshold: number, numOwners: number) => {
|
||||
const web3 = getWeb3()
|
||||
const accounts = await web3.eth.getAccounts()
|
||||
|
||||
expect(threshold).toBeLessThanOrEqual(numOwners)
|
||||
const form = TestUtils.findRenderedDOMComponentWithTag(safe, 'form')
|
||||
const form = createSafeForm.getByTestId('create-safe-form')
|
||||
|
||||
// Fill Safe's name
|
||||
const inputs = TestUtils.scryRenderedDOMComponentsWithTag(safe, 'input')
|
||||
expect(inputs.length).toBe(1)
|
||||
const fieldName = inputs[0]
|
||||
TestUtils.Simulate.change(fieldName, { target: { value: 'Adolfo Safe' } })
|
||||
TestUtils.Simulate.submit(form)
|
||||
const nameInput: HTMLInputElement = createSafeForm.getByPlaceholderText('Name of the new Safe')
|
||||
|
||||
fireEvent.change(nameInput, { target: { value: 'Adolfo Safe' } })
|
||||
fireEvent.submit(form)
|
||||
await sleep(400)
|
||||
|
||||
// Fill owners
|
||||
const addedUpfront = 1
|
||||
const buttons = TestUtils.scryRenderedDOMComponentsWithTag(safe, 'button')
|
||||
const addOwnerButton = buttons[1]
|
||||
const addOwnerButton = createSafeForm.getByTestId('add-owner-btn')
|
||||
|
||||
expect(addOwnerButton.getElementsByTagName('span')[0].textContent).toEqual(ADD_OWNER_BUTTON)
|
||||
for (let i = addedUpfront; i < numOwners; i += 1) {
|
||||
TestUtils.Simulate.click(addOwnerButton)
|
||||
fireEvent.click(addOwnerButton)
|
||||
}
|
||||
|
||||
const ownerInputs = TestUtils.scryRenderedDOMComponentsWithTag(safe, 'input')
|
||||
expect(ownerInputs.length).toBe(numOwners * 2)
|
||||
const ownerNameInputs = createSafeForm.getAllByPlaceholderText('Owner Name*')
|
||||
const ownerAddressInputs = createSafeForm.getAllByPlaceholderText('Owner Address*')
|
||||
expect(ownerNameInputs.length).toBe(numOwners)
|
||||
expect(ownerAddressInputs.length).toBe(numOwners)
|
||||
|
||||
for (let i = addedUpfront; i < numOwners; i += 1) {
|
||||
const nameIndex = i * 2
|
||||
const addressIndex = i * 2 + 1
|
||||
const ownerName = ownerInputs[nameIndex]
|
||||
const account = ownerInputs[addressIndex]
|
||||
const ownerNameInput = ownerNameInputs[i]
|
||||
const ownerAddressInput = ownerAddressInputs[i]
|
||||
|
||||
TestUtils.Simulate.change(ownerName, { target: { value: `Adolfo ${i + 1} Eth Account` } })
|
||||
TestUtils.Simulate.change(account, { target: { value: accounts[i] } })
|
||||
fireEvent.change(ownerNameInput, { target: { value: `Owner ${i + 1}` } })
|
||||
fireEvent.change(ownerAddressInput, { target: { value: accounts[i] } })
|
||||
}
|
||||
TestUtils.Simulate.submit(form)
|
||||
fireEvent.submit(form)
|
||||
await sleep(400)
|
||||
|
||||
// Fill Threshold
|
||||
const muiSelectFields = TestUtils.scryRenderedComponentsWithType(safe, Select)
|
||||
expect(muiSelectFields.length).toEqual(1)
|
||||
muiSelectFields[0].props.onChange(`${threshold}`)
|
||||
TestUtils.Simulate.submit(form)
|
||||
const thresholdSelect = createSafeForm.getByRole('button')
|
||||
fireEvent.click(thresholdSelect)
|
||||
const thresholdOptions = createSafeForm.getAllByRole('option')
|
||||
fireEvent.click(thresholdOptions[numOwners - 1])
|
||||
fireEvent.submit(form)
|
||||
await sleep(400)
|
||||
|
||||
// Submit
|
||||
TestUtils.Simulate.submit(form)
|
||||
fireEvent.submit(form)
|
||||
await sleep(400)
|
||||
|
||||
// giving some time to the component for updating its state with safe
|
||||
|
@ -84,14 +101,14 @@ const deploySafe = async (safe: React$Component<{}>, threshold: number, numOwner
|
|||
}
|
||||
|
||||
const aDeployedSafe = async (specificStore: Store<GlobalState>, threshold?: number = 1, numOwners?: number = 1) => {
|
||||
const safe: React$Component<{}> = await fillOpenSafeForm(specificStore)
|
||||
const safe: React$Component<{}> = await renderOpenSafeForm(specificStore)
|
||||
const safeAddress = await deploySafe(safe, threshold, numOwners)
|
||||
|
||||
return safeAddress
|
||||
}
|
||||
|
||||
describe('DOM > Feature > CREATE a safe', () => {
|
||||
it('fills correctly the safe form with 4 owners and 4 threshold', async () => {
|
||||
it('fills correctly the safe form with 4 owners and 4 threshold and creates a safe', async () => {
|
||||
const owners = 4
|
||||
const threshold = 4
|
||||
const store = aNewStore()
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import { type Store } from 'redux'
|
||||
import TestUtils from 'react-dom/test-utils'
|
||||
import { Provider } from 'react-redux'
|
||||
import { render, fireEvent, cleanup } from 'react-testing-library'
|
||||
import { ConnectedRouter } from 'connected-react-router'
|
||||
import Load from '~/routes/load/container/Load'
|
||||
import { aNewStore, history, type GlobalState } from '~/store'
|
||||
|
@ -13,12 +13,30 @@ import { makeProvider } from '~/logic/wallets/store/model/provider'
|
|||
import { aMinedSafe } from './builder/safe.redux.builder'
|
||||
import { whenSafeDeployed } from './builder/safe.dom.utils'
|
||||
|
||||
const travelToLoadRoute = async (localStore: Store<GlobalState>) => {
|
||||
afterEach(cleanup)
|
||||
|
||||
// https://github.com/testing-library/react-testing-library/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
|
||||
})
|
||||
|
||||
|
||||
const renderLoadSafe = async (localStore: Store<GlobalState>) => {
|
||||
const provider = await getProviderInfo()
|
||||
const walletRecord = makeProvider(provider)
|
||||
localStore.dispatch(addProvider(walletRecord))
|
||||
|
||||
return TestUtils.renderIntoDocument(
|
||||
return render(
|
||||
<Provider store={localStore}>
|
||||
<ConnectedRouter history={history}>
|
||||
<Load />
|
||||
|
@ -31,26 +49,22 @@ describe('DOM > Feature > LOAD a safe', () => {
|
|||
it('load correctly a created safe', async () => {
|
||||
const store = aNewStore()
|
||||
const address = await aMinedSafe(store)
|
||||
const LoadDom = await travelToLoadRoute(store)
|
||||
|
||||
const form = TestUtils.findRenderedDOMComponentWithTag(LoadDom, 'form')
|
||||
const inputs = TestUtils.scryRenderedDOMComponentsWithTag(LoadDom, 'input')
|
||||
const LoadSafePage = await renderLoadSafe(store)
|
||||
const form = LoadSafePage.getByTestId('load-safe-form')
|
||||
const safeNameInput = LoadSafePage.getByPlaceholderText('Name of the Safe')
|
||||
const safeAddressInput = LoadSafePage.getByPlaceholderText('Safe Address*')
|
||||
|
||||
// Fill Safe's name
|
||||
const fieldName = inputs[0]
|
||||
TestUtils.Simulate.change(fieldName, { target: { value: 'Adolfo Safe' } })
|
||||
const fieldAddress = inputs[1]
|
||||
TestUtils.Simulate.change(fieldAddress, { target: { value: address } })
|
||||
fireEvent.change(safeNameInput, { target: { value: 'A Safe To Load' } })
|
||||
fireEvent.change(safeAddressInput, { target: { value: address } })
|
||||
await sleep(400)
|
||||
|
||||
// Click next
|
||||
TestUtils.Simulate.submit(form)
|
||||
fireEvent.submit(form)
|
||||
await sleep(400)
|
||||
|
||||
// Submit
|
||||
TestUtils.Simulate.submit(form)
|
||||
await sleep(400)
|
||||
|
||||
fireEvent.submit(form)
|
||||
const deployedAddress = await whenSafeDeployed()
|
||||
expect(deployedAddress).toBe(address)
|
||||
})
|
||||
|
|
|
@ -2,17 +2,9 @@
|
|||
import * as React from 'react'
|
||||
|
||||
type WrapperProps = {
|
||||
children: React$Node
|
||||
children: React$Node,
|
||||
}
|
||||
|
||||
class Wrapper extends React.PureComponent<WrapperProps> {
|
||||
render() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{ this.props.children }
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
const Wrapper = ({ children }: WrapperProps) => <React.Fragment>{children}</React.Fragment>
|
||||
|
||||
export default Wrapper
|
||||
|
|
156
yarn.lock
156
yarn.lock
|
@ -1837,16 +1837,16 @@
|
|||
"@types/istanbul-reports" "^1.1.1"
|
||||
"@types/yargs" "^12.0.9"
|
||||
|
||||
"@material-ui/core@4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-4.0.0.tgz#f8bead508cc727188e5d819ae937aa928d83857b"
|
||||
integrity sha512-mLEGTuzgUALRKFI3hkRcS0gi/cB3XV0JA4F5PT3rGUt7Dc4liu8/IGiHF7iQh+p337FMk8vkEMxMVdYd9JXKMQ==
|
||||
"@material-ui/core@4.0.1":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-4.0.1.tgz#7fe3bcb1a89b01376b181358a5ded58291da0016"
|
||||
integrity sha512-cw2Qs3BLem8FOrp/knfjJkwJXG4dZO/HGyWwZV71UWiqDIOF3plHZ7duCbOMIWwKgSUJ85k9omlSHUTT63E/pw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.2.0"
|
||||
"@material-ui/styles" "^4.0.0"
|
||||
"@material-ui/system" "^4.0.0"
|
||||
"@material-ui/types" "^4.0.0"
|
||||
"@material-ui/utils" "^4.0.0"
|
||||
"@material-ui/styles" "^4.0.1"
|
||||
"@material-ui/system" "^4.0.1"
|
||||
"@material-ui/types" "^4.0.1"
|
||||
"@material-ui/utils" "^4.0.1"
|
||||
"@types/react-transition-group" "^2.0.16"
|
||||
clsx "^1.0.2"
|
||||
convert-css-length "^1.0.2"
|
||||
|
@ -1862,22 +1862,22 @@
|
|||
react-transition-group "^4.0.0"
|
||||
warning "^4.0.1"
|
||||
|
||||
"@material-ui/icons@4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/icons/-/icons-4.0.0.tgz#1dec886098663e08dc80f38c5c06d70b21dfc4e7"
|
||||
integrity sha512-hXoKnVLmVer+kic84ypoyG3Amym3a8q3pvDg4KYjeKW9fxGru7x/IkelBJODQL0jO+nAPz1+9RNpFWC75v35dg==
|
||||
"@material-ui/icons@4.0.1":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/icons/-/icons-4.0.1.tgz#f63990f731c6206c82023c96d510bd7bdda44250"
|
||||
integrity sha512-03zUfksGXXbaWX2piB1LCmC28eydlT8ah8MbYT4n4mgiX9BTL4HH50lkFn9JIJJSk2oO5kRy4FvpXRGRBI+oxw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.2.0"
|
||||
|
||||
"@material-ui/styles@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.0.0.tgz#789461e3e2b484a26206db7656c78b3248904f3b"
|
||||
integrity sha512-TUpmXlyZDVOl6E2//+UzsZxgi2E+2L753QY02nNkbAC6PPx8FUBqvnjYSGqX0V/BjTJ/fD4CkoS6ZpY3lHf+Gg==
|
||||
"@material-ui/styles@^4.0.1":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.0.1.tgz#67e880d490f010c9f2956c572b07d218bfa255d7"
|
||||
integrity sha512-SywkWzBzXvm9dUY2rtmzTc/FTlKGctVYGb8hzPZyHU3OI4X9jQH4YnR/OiqTwg4jNpFnASJX5rW1rEUJM+ZnhA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.2.0"
|
||||
"@emotion/hash" "^0.7.1"
|
||||
"@material-ui/types" "^4.0.0"
|
||||
"@material-ui/utils" "^4.0.0"
|
||||
"@material-ui/types" "^4.0.1"
|
||||
"@material-ui/utils" "^4.0.1"
|
||||
clsx "^1.0.2"
|
||||
deepmerge "^3.0.0"
|
||||
hoist-non-react-statics "^3.2.1"
|
||||
|
@ -1892,25 +1892,25 @@
|
|||
prop-types "^15.7.2"
|
||||
warning "^4.0.1"
|
||||
|
||||
"@material-ui/system@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/system/-/system-4.0.0.tgz#bb9a03aa3cf0405c2159c2408b7b8b20b959b119"
|
||||
integrity sha512-SIsqIwjix98Mqw9LVAmRqTs10E4S/SP5n5mlBlhHVHI+2XG2c+MaCPzOF2Zxq0KdqOMgTb7/aevR3mG9UmODxg==
|
||||
"@material-ui/system@^4.0.1":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/system/-/system-4.0.1.tgz#d78969f3dd9eb6baf82ded0ef183368d8befb00b"
|
||||
integrity sha512-NlMF4jZk1xx7taUOT+QhrJw7v7uUi9Ae+G8C8fowGgP5x04whxOuSuSmN9a8u2j7dc8XqahR0OJeA6Xch8ymog==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.2.0"
|
||||
deepmerge "^3.0.0"
|
||||
prop-types "^15.7.2"
|
||||
warning "^4.0.1"
|
||||
|
||||
"@material-ui/types@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/types/-/types-4.0.0.tgz#6804123b1c5d56db232fc54fe745c5b1c3ef7306"
|
||||
integrity sha512-wuiQMo8nSljZR1oWh57UQYssdtFqaU+Cbhr16uLohzzTllpCAK4LkH0slnH3n+5vCa2dgOdNlZTrmsIDDwvRJQ==
|
||||
"@material-ui/types@^4.0.1":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/types/-/types-4.0.1.tgz#a05fe801a10604d99e593303df9e843868008d4f"
|
||||
integrity sha512-FGhogU9l4s+ycMcC3hhOAvu5hcWa5TVSCCGUf4NOUF904ythroWSAvcCHn92NjftXZ8WZqmtPjL1K/d90Pq/3Q==
|
||||
|
||||
"@material-ui/utils@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/utils/-/utils-4.0.0.tgz#cfe8da2328afea8fbc5599d8e7abd64e25828bc3"
|
||||
integrity sha512-gjz52hO1hkIbKPMng1diQybVgtfgCptOCrulUs4emSCHHKUoR1zfT+IUrjgOaKIpYZNOgS/CI7KDMp689+FzeQ==
|
||||
"@material-ui/utils@^4.0.1":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/utils/-/utils-4.0.1.tgz#ea3ab6fe4eea9588713813cff6eca33bda3519f6"
|
||||
integrity sha512-mWRcMQIrqsXGze73tx3hNfB1NUu+BL/oIQI7TImyuhsia1EQXw3bPVBjgwTzqM6MqfXw6eh1fR45Di+WN5hASA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.2.0"
|
||||
prop-types "^15.7.2"
|
||||
|
@ -1991,6 +1991,11 @@
|
|||
dependencies:
|
||||
uuid "^3.1.0"
|
||||
|
||||
"@sheerun/mutationobserver-shim@^0.3.2":
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz#8013f2af54a2b7d735f71560ff360d3a8176a87b"
|
||||
integrity sha512-vTCdPp/T/Q3oSqwHmZ5Kpa9oI7iLtGl3RQaA/NyLHikvcrPxACkkKVr/XzkSPJWXHRhKGzVvb0urJsbMlRxi1Q==
|
||||
|
||||
"@storybook/addon-actions@5.0.11":
|
||||
version "5.0.11"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-5.0.11.tgz#7ca6d6ce9400b9b97f2699935edade88905767c3"
|
||||
|
@ -4402,16 +4407,16 @@ big.js@^5.2.2:
|
|||
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
|
||||
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
|
||||
|
||||
bignumber.js@9.0.0:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.0.tgz#805880f84a329b5eac6e7cb6f8274b6d82bdf075"
|
||||
integrity sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==
|
||||
|
||||
bignumber.js@^7.2.1:
|
||||
version "7.2.1"
|
||||
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f"
|
||||
integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==
|
||||
|
||||
bignumber.js@^8.1.1:
|
||||
version "8.1.1"
|
||||
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-8.1.1.tgz#4b072ae5aea9c20f6730e4e5d529df1271c4d885"
|
||||
integrity sha512-QD46ppGintwPGuL1KqmwhR0O+N2cZUg8JG/VzwI2e28sM9TqHjQB10lI4QAaMHVbLzwVLLAwEglpKPViWX+5NQ==
|
||||
|
||||
"bignumber.js@git+https://github.com/debris/bignumber.js#master":
|
||||
version "2.0.7"
|
||||
resolved "git+https://github.com/debris/bignumber.js#c7a38de919ed75e6fb6ba38051986e294b328df9"
|
||||
|
@ -6554,6 +6559,16 @@ dom-serializer@0, dom-serializer@~0.1.0:
|
|||
domelementtype "^1.3.0"
|
||||
entities "^1.1.1"
|
||||
|
||||
dom-testing-library@^4.1.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/dom-testing-library/-/dom-testing-library-4.1.1.tgz#615af61bee06db51bd8ecea60c113eba7cb49dda"
|
||||
integrity sha512-PUsG7aY5BJxzulDrOtkksqudRRypcVQF6d4RGAyj9xNwallOFqrNLOyg2QW2mCpFaNVPELX8hBX/wbHQtOto/A==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.4.3"
|
||||
"@sheerun/mutationobserver-shim" "^0.3.2"
|
||||
pretty-format "^24.7.0"
|
||||
wait-for-expect "^1.1.1"
|
||||
|
||||
dom-walk@^0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018"
|
||||
|
@ -8155,10 +8170,10 @@ flatted@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.0.tgz#55122b6536ea496b4b44893ee2608141d10d9916"
|
||||
integrity sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==
|
||||
|
||||
flow-bin@0.98.1:
|
||||
version "0.98.1"
|
||||
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.98.1.tgz#a8d781621c91703df69928acc83c9777e2fcbb49"
|
||||
integrity sha512-y1YzQgbFUX4EG6h2EO8PhyJeS0VxNgER8XsTwU8IXw4KozfneSmGVgw8y3TwAOza7rVhTlHEoli1xNuNW1rhPw==
|
||||
flow-bin@0.99.0:
|
||||
version "0.99.0"
|
||||
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.99.0.tgz#df035be2493825ddce0b2f26d1273747465b538c"
|
||||
integrity sha512-PjTzcOwte2mq+aP+HFCQZw/AojltOnOdtZC9iPzkWJJdPH7nYSKkZhC4dFgP24BuXsJH6yZBZ48gEKsX04UegQ==
|
||||
|
||||
flush-write-stream@^1.0.0:
|
||||
version "1.1.1"
|
||||
|
@ -11628,13 +11643,13 @@ min-document@^2.19.0:
|
|||
dependencies:
|
||||
dom-walk "^0.1.0"
|
||||
|
||||
mini-css-extract-plugin@0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.6.0.tgz#a3f13372d6fcde912f3ee4cd039665704801e3b9"
|
||||
integrity sha512-79q5P7YGI6rdnVyIAV4NXpBQJFWdkzJxCim3Kog4078fM0piAaFlwocqbejdWtLW1cEzCexPrh6EdyFsPgVdAw==
|
||||
mini-css-extract-plugin@0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.7.0.tgz#5ba8290fbb4179a43dd27cca444ba150bee743a0"
|
||||
integrity sha512-RQIw6+7utTYn8DBGsf/LpRgZCJMpZt+kuawJ/fju0KiOL6nAaTBNmCJwS7HtwSCXfS47gCkmtBFS7HdsquhdxQ==
|
||||
dependencies:
|
||||
loader-utils "^1.1.0"
|
||||
normalize-url "^2.0.1"
|
||||
normalize-url "1.9.1"
|
||||
schema-utils "^1.0.0"
|
||||
webpack-sources "^1.1.0"
|
||||
|
||||
|
@ -12095,14 +12110,15 @@ normalize-scroll-left@^0.1.2:
|
|||
resolved "https://registry.yarnpkg.com/normalize-scroll-left/-/normalize-scroll-left-0.1.2.tgz#6b79691ba79eb5fb107fa5edfbdc06b55caee2aa"
|
||||
integrity sha512-F9YMRls0zCF6BFIE2YnXDRpHPpfd91nOIaNdDgrx5YMoPLo8Wqj+6jNXHQsYBavJeXP4ww8HCt0xQAKc5qk2Fg==
|
||||
|
||||
normalize-url@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6"
|
||||
integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==
|
||||
normalize-url@1.9.1:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c"
|
||||
integrity sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=
|
||||
dependencies:
|
||||
prepend-http "^2.0.0"
|
||||
query-string "^5.0.1"
|
||||
sort-keys "^2.0.0"
|
||||
object-assign "^4.0.1"
|
||||
prepend-http "^1.0.0"
|
||||
query-string "^4.1.0"
|
||||
sort-keys "^1.0.0"
|
||||
|
||||
normalize-url@^3.0.0:
|
||||
version "3.3.0"
|
||||
|
@ -13270,16 +13286,11 @@ prelude-ls@~1.1.2:
|
|||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
|
||||
integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
|
||||
|
||||
prepend-http@^1.0.1:
|
||||
prepend-http@^1.0.0, prepend-http@^1.0.1:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
|
||||
integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=
|
||||
|
||||
prepend-http@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
|
||||
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
|
||||
|
||||
preserve@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
|
||||
|
@ -13349,7 +13360,7 @@ pretty-format@^23.0.1:
|
|||
ansi-regex "^3.0.0"
|
||||
ansi-styles "^3.2.0"
|
||||
|
||||
pretty-format@^24.8.0:
|
||||
pretty-format@^24.7.0, pretty-format@^24.8.0:
|
||||
version "24.8.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.8.0.tgz#8dae7044f58db7cb8be245383b565a963e3c27f2"
|
||||
integrity sha512-P952T7dkrDEplsR+TuY7q3VXDae5Sr7zmQb12JU/NDQa/3CH7/QW0yvqLcGN6jL+zQFKaoJcPc+yJxMTGmosqw==
|
||||
|
@ -13623,6 +13634,14 @@ qs@6.7.0, qs@^6.5.2:
|
|||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
|
||||
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
|
||||
|
||||
query-string@^4.1.0:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb"
|
||||
integrity sha1-u7aTucqRXCMlFbIosaArYJBD2+s=
|
||||
dependencies:
|
||||
object-assign "^4.1.0"
|
||||
strict-uri-encode "^1.0.0"
|
||||
|
||||
query-string@^5.0.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb"
|
||||
|
@ -14042,6 +14061,14 @@ react-syntax-highlighter@^8.0.1:
|
|||
prismjs "^1.8.4"
|
||||
refractor "^2.4.1"
|
||||
|
||||
react-testing-library@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-testing-library/-/react-testing-library-7.0.1.tgz#0cf113bb53a78599f018378f6854e91a52dbf205"
|
||||
integrity sha512-doQkM3/xPcIm22x9jgTkGxU8xqXg4iWvM1WwbbQ7CI5/EMk3DhloYBwMyk+Ywtta3dIAIh9sC7llXoKovf3L+w==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.4.3"
|
||||
dom-testing-library "^4.1.0"
|
||||
|
||||
react-textarea-autosize@^7.0.4:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-7.1.0.tgz#3132cb77e65d94417558d37c0bfe415a5afd3445"
|
||||
|
@ -15459,10 +15486,10 @@ solidity-sha3@^0.4.1:
|
|||
left-pad "^1.1.1"
|
||||
web3 "^0.16.0"
|
||||
|
||||
sort-keys@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128"
|
||||
integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=
|
||||
sort-keys@^1.0.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad"
|
||||
integrity sha1-RBttTTRnmPG05J6JIK37oOVD+a0=
|
||||
dependencies:
|
||||
is-plain-obj "^1.0.0"
|
||||
|
||||
|
@ -17515,6 +17542,11 @@ w3c-hr-time@^1.0.1:
|
|||
dependencies:
|
||||
browser-process-hrtime "^0.1.2"
|
||||
|
||||
wait-for-expect@^1.1.1:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-1.2.0.tgz#fdab6a26e87d2039101db88bff3d8158e5c3e13f"
|
||||
integrity sha512-EJhKpA+5UHixduMBEGhTFuLuVgQBKWxkFbefOdj2bbk2/OpA5Opsc4aUTGmF+qJ+v3kTGxDRNYwKaT4j6g5n8Q==
|
||||
|
||||
walker@^1.0.7, walker@~1.0.5:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"
|
||||
|
|
Loading…
Reference in New Issue