Skip to content
This repository has been archived by the owner on Nov 10, 2023. It is now read-only.

#110 Sorting out tests: Safe creation and Safe loading, fix the validator for safe address #113

Merged
merged 6 commits into from May 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
48 changes: 0 additions & 48 deletions .babelrc

This file was deleted.

49 changes: 49 additions & 0 deletions babel.config.js
@@ -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'],
},
},
}
7 changes: 4 additions & 3 deletions config/jest/cssTransform.js
@@ -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'
},
};
}
7 changes: 4 additions & 3 deletions config/jest/fileTransform.js
@@ -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))};`
},
};
}
28 changes: 28 additions & 0 deletions jest.config.js
@@ -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 changes: 7 additions & 41 deletions package.json
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
16 changes: 9 additions & 7 deletions scripts/test.js
@@ -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)
5 changes: 3 additions & 2 deletions src/components/Stepper/index.jsx
Expand Up @@ -21,6 +21,7 @@ type Props = {
onReset?: () => void,
initialValues?: Object,
disabledWhenValidating?: boolean,
testId?: string,
}

type State = {
Expand Down Expand Up @@ -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)
Expand All @@ -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 = (
Expand Down
5 changes: 3 additions & 2 deletions src/components/forms/GnoForm/index.jsx
Expand Up @@ -16,6 +16,7 @@ type Props = {
validation?: (values: Object) => Object | Promise<Object>,
initialValues?: Object,
formMutators?: Object,
testId?: string,
}

const stylesBasedOn = (padding: number): $Shape<CSSStyleDeclaration> => ({
Expand All @@ -25,15 +26,15 @@ 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}
onSubmit={onSubmit}
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>
)}
Expand Down
2 changes: 1 addition & 1 deletion src/logic/contracts/safeContracts.js
Expand Up @@ -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)
Expand Down
16 changes: 6 additions & 10 deletions src/routes/load/components/DetailsForm/index.jsx
Expand Up @@ -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) {
Expand All @@ -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
}
Expand Down