safe creation test wip

This commit is contained in:
mmv 2019-05-29 18:11:44 +04:00
parent 4583e85782
commit 267f271364
10 changed files with 193 additions and 163 deletions

View File

@ -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"]
}
}
}

49
babel.config.js Normal file
View File

@ -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'],
},
},
}

View File

@ -1,12 +1,13 @@
// @flow
// This is a custom Jest transformer turning style imports into empty objects. // This is a custom Jest transformer turning style imports into empty objects.
// http://facebook.github.io/jest/docs/tutorial-webpack.html // http://facebook.github.io/jest/docs/tutorial-webpack.html
module.exports = { module.exports = {
process() { process() {
return 'module.exports = {};'; return 'module.exports = {};'
}, },
getCacheKey(fileData, filename) { getCacheKey(fileData, filename) {
// The output is always the same. // The output is always the same.
return 'cssTransform'; return 'cssTransform'
}, },
}; }

View File

@ -1,10 +1,11 @@
const path = require('path'); // @flow
const path = require('path')
// This is a custom Jest transformer turning file imports into filenames. // This is a custom Jest transformer turning file imports into filenames.
// http://facebook.github.io/jest/docs/tutorial-webpack.html // http://facebook.github.io/jest/docs/tutorial-webpack.html
module.exports = { module.exports = {
process(src, filename) { process(src, filename) {
return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';'; return `module.exports = ${JSON.stringify(path.basename(filename))};`
}, },
}; }

28
jest.config.js Normal file
View File

@ -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,
}

View File

@ -29,41 +29,6 @@
"pre-commit": [ "pre-commit": [
"precommit" "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": { "dependencies": {
"@gnosis.pm/safe-contracts": "^1.0.0", "@gnosis.pm/safe-contracts": "^1.0.0",
"@gnosis.pm/util-contracts": "2.0.1", "@gnosis.pm/util-contracts": "2.0.1",
@ -158,6 +123,7 @@
"postcss-simple-vars": "^5.0.2", "postcss-simple-vars": "^5.0.2",
"pre-commit": "^1.2.2", "pre-commit": "^1.2.2",
"prettier-eslint-cli": "^4.7.1", "prettier-eslint-cli": "^4.7.1",
"react-testing-library": "^7.0.1",
"run-with-testrpc": "0.3.1", "run-with-testrpc": "0.3.1",
"storybook-host": "^5.0.3", "storybook-host": "^5.0.3",
"storybook-router": "^0.3.3", "storybook-router": "^0.3.3",

View File

@ -1,18 +1,20 @@
process.env.NODE_ENV = 'test'; // @flow
process.env.PUBLIC_URL = ''; process.env.NODE_ENV = 'test'
process.env.PUBLIC_URL = ''
// Load environment variables from .env file. Suppress warnings using silent // Load environment variables from .env file. Suppress warnings using silent
// if this file is missing. dotenv will never modify any environment variables // if this file is missing. dotenv will never modify any environment variables
// that have already been set. // that have already been set.
// https://github.com/motdotla/dotenv // https://github.com/motdotla/dotenv
require('dotenv').config({silent: true}); require('dotenv').config({ silent: true })
const jest = require('jest'); const jest = require('jest')
const argv = process.argv.slice(2);
const argv = process.argv.slice(2)
// Watch unless on CI or in coverage mode // Watch unless on CI or in coverage mode
if (!process.env.CI && argv.indexOf('--coverage') < 0) { if (!process.env.CI && argv.indexOf('--coverage') < 0) {
argv.push('--watch'); argv.push('--watch')
} }
jest.run(argv); jest.run(argv)

View File

@ -1,74 +1,74 @@
// @flow // // @flow
import { List, Map } from 'immutable' // import { List, Map } from 'immutable'
import axios from 'axios' // import axios from 'axios'
import type { Dispatch as ReduxDispatch } from 'redux' // import type { Dispatch as ReduxDispatch } from 'redux'
import { type GlobalState } from '~/store/index' // import { type GlobalState } from '~/store/index'
import { makeOwner } from '~/routes/safe/store/models/owner' // import { makeOwner } from '~/routes/safe/store/models/owner'
import { makeTransaction, type Transaction } from '~/routes/safe/store/models/transaction' // import { makeTransaction, type Transaction } from '~/routes/safe/store/models/transaction'
import { makeConfirmation } from '~/routes/safe/store/models/confirmation' // import { makeConfirmation } from '~/routes/safe/store/models/confirmation'
import { loadSafeSubjects } from '~/utils/storage/transactions' // import { loadSafeSubjects } from '~/utils/storage/transactions'
import { buildTxServiceUrlFrom, type TxServiceType } from '~/logic/safe/safeTxHistory' // import { buildTxServiceUrlFrom, type TxServiceType } from '~/logic/safe/safeTxHistory'
import { getOwners } from '~/logic/safe/utils' // import { getOwners } from '~/logic/safe/utils'
import { EMPTY_DATA } from '~/logic/wallets/ethTransactions' // import { EMPTY_DATA } from '~/logic/wallets/ethTransactions'
import addTransactions from './addTransactions' // import addTransactions from './addTransactions'
type ConfirmationServiceModel = { // type ConfirmationServiceModel = {
owner: string, // owner: string,
submissionDate: Date, // submissionDate: Date,
type: string, // type: string,
transactionHash: string, // transactionHash: string,
} // }
type TxServiceModel = { // type TxServiceModel = {
to: string, // to: string,
value: number, // value: number,
data: string, // data: string,
operation: number, // operation: number,
nonce: number, // nonce: number,
submissionDate: Date, // submissionDate: Date,
executionDate: Date, // executionDate: Date,
confirmations: ConfirmationServiceModel[], // confirmations: ConfirmationServiceModel[],
isExecuted: boolean, // isExecuted: boolean,
} // }
const buildTransactionFrom = (safeAddress: string, tx: TxServiceModel, safeSubjects: Map<string, string>) => { // const buildTransactionFrom = (safeAddress: string, tx: TxServiceModel, safeSubjects: Map<string, string>) => {
const name = safeSubjects.get(String(tx.nonce)) || 'Unknown' // const name = safeSubjects.get(String(tx.nonce)) || 'Unknown'
const storedOwners = getOwners(safeAddress) // const storedOwners = getOwners(safeAddress)
const confirmations = List( // const confirmations = List(
tx.confirmations.map((conf: ConfirmationServiceModel) => { // tx.confirmations.map((conf: ConfirmationServiceModel) => {
const ownerName = storedOwners.get(conf.owner.toLowerCase()) || 'UNKNOWN' // const ownerName = storedOwners.get(conf.owner.toLowerCase()) || 'UNKNOWN'
return makeConfirmation({ // return makeConfirmation({
owner: makeOwner({ address: conf.owner, name: ownerName }), // owner: makeOwner({ address: conf.owner, name: ownerName }),
type: ((conf.type.toLowerCase(): any): TxServiceType), // type: ((conf.type.toLowerCase(): any): TxServiceType),
hash: conf.transactionHash, // hash: conf.transactionHash,
}) // })
}), // }),
) // )
return makeTransaction({ // return makeTransaction({
name, // name,
nonce: tx.nonce, // nonce: tx.nonce,
value: Number(tx.value), // value: Number(tx.value),
confirmations, // confirmations,
destination: tx.to, // destination: tx.to,
data: tx.data ? tx.data : EMPTY_DATA, // data: tx.data ? tx.data : EMPTY_DATA,
isExecuted: tx.isExecuted, // isExecuted: tx.isExecuted,
}) // })
} // }
export const loadSafeTransactions = async (safeAddress: string) => { // export const loadSafeTransactions = async (safeAddress: string) => {
const url = buildTxServiceUrlFrom(safeAddress) // const url = buildTxServiceUrlFrom(safeAddress)
const response = await axios.get(url) // const response = await axios.get(url)
const transactions: TxServiceModel[] = response.data.results // const transactions: TxServiceModel[] = response.data.results
const safeSubjects = loadSafeSubjects(safeAddress) // const safeSubjects = loadSafeSubjects(safeAddress)
const txsRecord = transactions.map((tx: TxServiceModel) => buildTransactionFrom(safeAddress, tx, safeSubjects)) // 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>) => { // export default (safeAddress: string) => async (dispatch: ReduxDispatch<GlobalState>) => {
const transactions: Map<string, List<Transaction>> = await loadSafeTransactions(safeAddress) // const transactions: Map<string, List<Transaction>> = await loadSafeTransactions(safeAddress)
return dispatch(addTransactions(transactions)) // return dispatch(addTransactions(transactions))
} // }

View File

@ -2,6 +2,7 @@
import * as React from 'react' import * as React from 'react'
import { type Store } from 'redux' import { type Store } from 'redux'
import TestUtils from 'react-dom/test-utils' import TestUtils from 'react-dom/test-utils'
import { render, fireEvent, cleanup } from 'react-testing-library'
import Select from '@material-ui/core/Select' import Select from '@material-ui/core/Select'
import { Provider } from 'react-redux' import { Provider } from 'react-redux'
import { ConnectedRouter } from 'connected-react-router' import { ConnectedRouter } from 'connected-react-router'
@ -15,12 +16,14 @@ import { makeProvider } from '~/logic/wallets/store/model/provider'
import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts' import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts'
import { whenSafeDeployed } from './builder/safe.dom.utils' import { whenSafeDeployed } from './builder/safe.dom.utils'
afterEach(cleanup)
const fillOpenSafeForm = async (localStore: Store<GlobalState>) => { const fillOpenSafeForm = async (localStore: Store<GlobalState>) => {
const provider = await getProviderInfo() const provider = await getProviderInfo()
const walletRecord = makeProvider(provider) const walletRecord = makeProvider(provider)
localStore.dispatch(addProvider(walletRecord)) localStore.dispatch(addProvider(walletRecord))
return TestUtils.renderIntoDocument( return render(
<Provider store={localStore}> <Provider store={localStore}>
<ConnectedRouter history={history}> <ConnectedRouter history={history}>
<Open /> <Open />

View File

@ -1991,6 +1991,11 @@
dependencies: dependencies:
uuid "^3.1.0" 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": "@storybook/addon-actions@5.0.11":
version "5.0.11" version "5.0.11"
resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-5.0.11.tgz#7ca6d6ce9400b9b97f2699935edade88905767c3" resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-5.0.11.tgz#7ca6d6ce9400b9b97f2699935edade88905767c3"
@ -6554,6 +6559,16 @@ dom-serializer@0, dom-serializer@~0.1.0:
domelementtype "^1.3.0" domelementtype "^1.3.0"
entities "^1.1.1" 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: dom-walk@^0.1.0:
version "0.1.1" version "0.1.1"
resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018"
@ -13345,7 +13360,7 @@ pretty-format@^23.0.1:
ansi-regex "^3.0.0" ansi-regex "^3.0.0"
ansi-styles "^3.2.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" version "24.8.0"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.8.0.tgz#8dae7044f58db7cb8be245383b565a963e3c27f2" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.8.0.tgz#8dae7044f58db7cb8be245383b565a963e3c27f2"
integrity sha512-P952T7dkrDEplsR+TuY7q3VXDae5Sr7zmQb12JU/NDQa/3CH7/QW0yvqLcGN6jL+zQFKaoJcPc+yJxMTGmosqw== integrity sha512-P952T7dkrDEplsR+TuY7q3VXDae5Sr7zmQb12JU/NDQa/3CH7/QW0yvqLcGN6jL+zQFKaoJcPc+yJxMTGmosqw==
@ -14046,6 +14061,14 @@ react-syntax-highlighter@^8.0.1:
prismjs "^1.8.4" prismjs "^1.8.4"
refractor "^2.4.1" 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: react-textarea-autosize@^7.0.4:
version "7.1.0" version "7.1.0"
resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-7.1.0.tgz#3132cb77e65d94417558d37c0bfe415a5afd3445" resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-7.1.0.tgz#3132cb77e65d94417558d37c0bfe415a5afd3445"
@ -17519,6 +17542,11 @@ w3c-hr-time@^1.0.1:
dependencies: dependencies:
browser-process-hrtime "^0.1.2" 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: walker@^1.0.7, walker@~1.0.5:
version "1.0.7" version "1.0.7"
resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"