diff --git a/.flowconfig b/.flowconfig index 9a632b96..6897a28b 100644 --- a/.flowconfig +++ b/.flowconfig @@ -9,7 +9,7 @@ .*/node_modules/react-router-redux/.* .*/node_modules/oboe/test/.* .*/_old/.* -.*/public/solidity/.* +.*/public/.* [libs] ./src/flowtype/npm/redux_v3.x.x.js diff --git a/jest.config.js b/jest.config.js index d7e1c86a..d9c12451 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,11 +1,19 @@ module.exports = { rootDir: './src', + automock: false, + coverageDirectory: 'coverage/', collectCoverage: true, testURL: 'http://localhost', modulePathIgnorePatterns: [ 'node_modules', + 'utils/windowUtils.js', + 'utils/promiseUtils.js', + 'utils/networkUtils.js', ], collectCoverageFrom: [ 'utils/**.js', ], + setupFiles: [ + './support/setupJest.js', + ], }; diff --git a/package.json b/package.json index cca0a91d..27532f4c 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,10 @@ "ethereumjs-units": "^0.2.0", "ethereumjs-util": "^5.1.4", "git-revision-webpack-plugin": "^3.0.3", + "flow-webpack-plugin": "^1.2.0", "hdkey": "^0.8.0", "html-webpack-plugin": "^3.2.0", + "jest-fetch-mock": "^1.6.5", "npm-run-all": "^4.1.3", "prop-types": "^15.6.2", "raf": "^3.4.0", diff --git a/src/reducers/ModalReducer.js b/src/reducers/ModalReducer.js index f6e09a51..bd86b456 100644 --- a/src/reducers/ModalReducer.js +++ b/src/reducers/ModalReducer.js @@ -57,6 +57,7 @@ export default function modal(state: State = initialState, action: Action): Stat if (state.opened && action.device.path === state.device.path && action.device.status === 'occupied') { return initialState; } + return state; case DEVICE.DISCONNECT: diff --git a/src/support/setupJest.js b/src/support/setupJest.js new file mode 100644 index 00000000..8e88457e --- /dev/null +++ b/src/support/setupJest.js @@ -0,0 +1 @@ +global.fetch = require('jest-fetch-mock'); \ No newline at end of file diff --git a/src/utils/__tests__/__snapshots__/device.test.js.snap b/src/utils/__tests__/__snapshots__/device.test.js.snap index 70364320..8ce5ffe6 100644 --- a/src/utils/__tests__/__snapshots__/device.test.js.snap +++ b/src/utils/__tests__/__snapshots__/device.test.js.snap @@ -55,3 +55,13 @@ exports[`device utils get version 4`] = `"1"`; exports[`device utils get version 5`] = `"1"`; exports[`device utils get version 6`] = `"T"`; + +exports[`device utils isDisabled 1`] = `false`; + +exports[`device utils isDisabled 2`] = `true`; + +exports[`device utils isWebUSB 1`] = `true`; + +exports[`device utils isWebUSB 2`] = `false`; + +exports[`device utils isWebUSB 3`] = `true`; diff --git a/src/utils/__tests__/__snapshots__/ethUtils.test.js.snap b/src/utils/__tests__/__snapshots__/ethUtils.test.js.snap index 0ffd930d..f5b70d96 100644 --- a/src/utils/__tests__/__snapshots__/ethUtils.test.js.snap +++ b/src/utils/__tests__/__snapshots__/ethUtils.test.js.snap @@ -1,5 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`eth utils calcGasPrice 1`] = `"89090990901"`; + exports[`eth utils decimalToHex 1`] = `"0"`; exports[`eth utils decimalToHex 2`] = `"1"`; @@ -8,4 +10,36 @@ exports[`eth utils decimalToHex 3`] = `"2"`; exports[`eth utils decimalToHex 4`] = `"64"`; -exports[`eth utils decimalToHex 5`] = `"3e7"`; +exports[`eth utils decimalToHex 5`] = `"2540be3ff"`; + +exports[`eth utils hexToDecimal 1`] = `"9999999999"`; + +exports[`eth utils hexToDecimal 2`] = `"100"`; + +exports[`eth utils hexToDecimal 3`] = `"2"`; + +exports[`eth utils hexToDecimal 4`] = `"1"`; + +exports[`eth utils hexToDecimal 5`] = `"0"`; + +exports[`eth utils hexToDecimal 6`] = `"null"`; + +exports[`eth utils padLeftEven 1`] = `"02540be3ff"`; + +exports[`eth utils sanitizeHex 1`] = `"0x02540be3ff"`; + +exports[`eth utils sanitizeHex 2`] = `"0x01"`; + +exports[`eth utils sanitizeHex 3`] = `"0x02"`; + +exports[`eth utils sanitizeHex 4`] = `"0x0100"`; + +exports[`eth utils sanitizeHex 5`] = `null`; + +exports[`eth utils sanitizeHex 6`] = `""`; + +exports[`eth utils strip 1`] = `""`; + +exports[`eth utils strip 2`] = `"02540be3ff"`; + +exports[`eth utils strip 3`] = `"02540be3ff"`; diff --git a/src/utils/__tests__/__snapshots__/formatUtils.test.js.snap b/src/utils/__tests__/__snapshots__/formatUtils.test.js.snap new file mode 100644 index 00000000..448a338a --- /dev/null +++ b/src/utils/__tests__/__snapshots__/formatUtils.test.js.snap @@ -0,0 +1,45 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`format utils btckb2satoshib 1`] = `0`; + +exports[`format utils btckb2satoshib 2`] = `100000`; + +exports[`format utils btckb2satoshib 3`] = `200000`; + +exports[`format utils btckb2satoshib 4`] = `10000000`; + +exports[`format utils btckb2satoshib 5`] = `99900000`; + +exports[`format utils formatAmount 1`] = `"0 btc"`; + +exports[`format utils formatAmount 2`] = `"10 mBTC"`; + +exports[`format utils formatAmount 3`] = `"0.000005 mBTC"`; + +exports[`format utils formatAmount 4`] = `"1e-8 eth"`; + +exports[`format utils formatAmount 5`] = `"0.00099999 tau"`; + +exports[`format utils formatTime 1`] = `"No time estimate"`; + +exports[`format utils formatTime 2`] = `"1 minutes"`; + +exports[`format utils formatTime 3`] = `"2 minutes"`; + +exports[`format utils formatTime 4`] = `"1 hour 40 minutes"`; + +exports[`format utils formatTime 5`] = `"16 hours 39 minutes"`; + +exports[`format utils formatTime 6`] = `"45 minutes"`; + +exports[`format utils hexToString 1`] = `"test"`; + +exports[`format utils hexToString 2`] = `"0001"`; + +exports[`format utils hexToString 3`] = `"test99999"`; + +exports[`format utils stringToHex 1`] = `"0074006500730074"`; + +exports[`format utils stringToHex 2`] = `"0030003000300031"`; + +exports[`format utils stringToHex 3`] = `"007400650073007400390039003900390039"`; diff --git a/src/utils/__tests__/device.test.js b/src/utils/__tests__/device.test.js index 6bfdc4c6..e135a151 100644 --- a/src/utils/__tests__/device.test.js +++ b/src/utils/__tests__/device.test.js @@ -38,6 +38,29 @@ describe('device utils', () => { }); }); + it('isWebUSB', () => { + const data = [ + { transport: { version: ['webusb'] } }, + { transport: { version: ['aaaaaa'] } }, + { transport: { version: ['webusb', 'test'] } }, + ]; + + data.forEach((item) => { + expect(dUtils.isWebUSB(item.transport)).toMatchSnapshot(); + }); + }); + + it('isDisabled', () => { + const data = [ + { selectedDevice: { features: null }, devices: [1, 2, 3], transport: { version: ['webusb', 'test'] } }, + { selectedDevice: { features: null }, devices: [], transport: { version: ['test'] } }, + ]; + + data.forEach((item) => { + expect(dUtils.isDisabled(item.selectedDevice, item.devices, item.transport)).toMatchSnapshot(); + }); + }); + it('get version', () => { const deviceMock = [ { }, diff --git a/src/utils/__tests__/ethUtils.test.js b/src/utils/__tests__/ethUtils.test.js index 3ff6e2ea..07bd1250 100644 --- a/src/utils/__tests__/ethUtils.test.js +++ b/src/utils/__tests__/ethUtils.test.js @@ -1,11 +1,53 @@ +import BigNumber from 'bignumber.js'; import * as ethUtils from '../ethUtils'; describe('eth utils', () => { it('decimalToHex', () => { - const input = [0, 1, 2, 100, 999]; + const input = [0, 1, 2, 100, 9999999999]; input.forEach((entry) => { expect(ethUtils.decimalToHex(entry)).toMatchSnapshot(); }); }); + + it('hexToDecimal', () => { + const input = ['2540be3ff', '64', '2', '1', '0', '']; + + input.forEach((entry) => { + expect(ethUtils.hexToDecimal(entry)).toMatchSnapshot(); + }); + }); + + it('padLeftEven', () => { + const input = ['2540be3ff']; + + input.forEach((entry) => { + expect(ethUtils.padLeftEven(entry)).toMatchSnapshot(); + }); + }); + + it('sanitizeHex', () => { + const input = ['0x2540be3ff', '1', '2', '100', 999, '']; + + input.forEach((entry) => { + expect(ethUtils.sanitizeHex(entry)).toMatchSnapshot(); + }); + }); + + + it('strip', () => { + const input = ['0x', '0x2540be3ff', '2540be3ff']; + + input.forEach((entry) => { + expect(ethUtils.strip(entry)).toMatchSnapshot(); + }); + }); + + it('calcGasPrice', () => { + const input = [{ price: new BigNumber(9898998989), limit: '9' }]; + + input.forEach((entry) => { + expect(ethUtils.calcGasPrice(entry.price, entry.limit)).toMatchSnapshot(); + }); + }); }); diff --git a/src/utils/__tests__/formatUtils.test.js b/src/utils/__tests__/formatUtils.test.js new file mode 100644 index 00000000..eb1d026d --- /dev/null +++ b/src/utils/__tests__/formatUtils.test.js @@ -0,0 +1,49 @@ +import * as formatUtils from '../formatUtils'; + +describe('format utils', () => { + it('formatAmount', () => { + const input = [ + { amount: 0, coinInfo: { isBitcoin: true, currencyUnits: 'mbtc', shortcut: 'btc' } }, + { amount: 1000000, coinInfo: { isBitcoin: true, currencyUnits: 'mbtc', shortcut: 'btc' } }, + { amount: 0.5, coinInfo: { isBitcoin: true, currencyUnits: 'mbtc', shortcut: 'btc' } }, + { amount: 1, coinInfo: { isBitcoin: false, shortcut: 'eth' } }, + { amount: 99999, coinInfo: { isBitcoin: false, shortcut: 'tau' } }, + ]; + + input.forEach((entry) => { + expect(formatUtils.formatAmount(entry.amount, entry.coinInfo, entry.coinInfo.currencyUnits)).toMatchSnapshot(); + }); + }); + + it('formatTime', () => { + const input = [0, 1, 2, 100, 999, 45]; + + input.forEach((entry) => { + expect(formatUtils.formatTime(entry)).toMatchSnapshot(); + }); + }); + + it('btckb2satoshib', () => { + const input = [0, 1, 2, 100, 999]; + + input.forEach((entry) => { + expect(formatUtils.btckb2satoshib(entry)).toMatchSnapshot(); + }); + }); + + it('stringToHex', () => { + const input = ['test', '0001', 'test99999']; + + input.forEach((entry) => { + expect(formatUtils.stringToHex(entry)).toMatchSnapshot(); + }); + }); + + it('hexToString', () => { + const input = ['0074006500730074', '0030003000300031', '007400650073007400390039003900390039']; + + input.forEach((entry) => { + expect(formatUtils.hexToString(entry)).toMatchSnapshot(); + }); + }); +}); diff --git a/src/utils/formatUtils.js b/src/utils/formatUtils.js index 982c9afb..986b0ee9 100644 --- a/src/utils/formatUtils.js +++ b/src/utils/formatUtils.js @@ -1,11 +1,9 @@ /* @flow */ - -const currencyUnits: string = 'mbtc2'; - // TODO: chagne currency units +const currencyUnitsConstant: string = 'mbtc2'; -export const formatAmount = (n: number, coinInfo: any): string => { +export const formatAmount = (n: number, coinInfo: any, currencyUnits = currencyUnitsConstant): string => { const amount = (n / 1e8); if (coinInfo.isBitcoin && currencyUnits === 'mbtc' && amount <= 0.1 && n !== 0) { const s = (n / 1e5).toString(); diff --git a/src/utils/networkUtils.js b/src/utils/networkUtils.js index 360168d9..fc29250b 100644 --- a/src/utils/networkUtils.js +++ b/src/utils/networkUtils.js @@ -1,6 +1,5 @@ /* @flow */ - import 'whatwg-fetch'; export const httpRequest = async (url: string, type: string = 'text'): any => { @@ -15,16 +14,6 @@ export const httpRequest = async (url: string, type: string = 'text'): any => { await response.text(); } throw new Error(`${url} ${response.statusText}`); - - - // return fetch(url, { credentials: 'same-origin' }).then((response) => { - // if (response.status === 200) { - - // return response.text().then(result => (json ? JSON.parse(result) : result)); - // } else { - // throw new Error(response.statusText); - // } - // }) }; export const JSONRequest = async (url: string): Promise => { diff --git a/webpack/dev.babel.js b/webpack/dev.babel.js index 42da5afb..f39a2e6e 100644 --- a/webpack/dev.babel.js +++ b/webpack/dev.babel.js @@ -1,6 +1,8 @@ import webpack from 'webpack'; import GitRevisionPlugin from 'git-revision-webpack-plugin'; import HtmlWebpackPlugin from 'html-webpack-plugin'; +import FlowWebpackPlugin from 'flow-webpack-plugin'; + import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; import { @@ -47,6 +49,7 @@ module.exports = { { loader: 'stylelint-custom-processor-loader', options: { + emitWarning: true, configPath: '.stylelintrc', }, }, @@ -93,6 +96,7 @@ module.exports = { new webpack.DefinePlugin({ COMMITHASH: JSON.stringify(gitRevisionPlugin.commithash()), }), + new FlowWebpackPlugin(), new HtmlWebpackPlugin({ chunks: ['index'], template: `${SRC}index.html`, diff --git a/webpack/local.babel.js b/webpack/local.babel.js index 92930de8..8e78c49b 100644 --- a/webpack/local.babel.js +++ b/webpack/local.babel.js @@ -115,7 +115,6 @@ module.exports = { filename: '[name].css', chunkFilename: '[id].css', }), - new HtmlWebpackPlugin({ chunks: ['index'], template: `${SRC}index.html`, diff --git a/webpack/production.babel.js b/webpack/production.babel.js index 3761cec3..95932ddd 100644 --- a/webpack/production.babel.js +++ b/webpack/production.babel.js @@ -3,6 +3,7 @@ import webpack from 'webpack'; import GitRevisionPlugin from 'git-revision-webpack-plugin'; import HtmlWebpackPlugin from 'html-webpack-plugin'; import CopyWebpackPlugin from 'copy-webpack-plugin'; +import FlowWebpackPlugin from 'flow-webpack-plugin'; import { SRC, BUILD, PUBLIC } from './constants'; const gitRevisionPlugin = new GitRevisionPlugin(); @@ -66,6 +67,7 @@ module.exports = { new webpack.DefinePlugin({ COMMITHASH: JSON.stringify(gitRevisionPlugin.commithash()), }), + new FlowWebpackPlugin(), new HtmlWebpackPlugin({ chunks: ['index'], template: `${SRC}index.html`, diff --git a/yarn.lock b/yarn.lock index af3c54fa..2d274b77 100644 --- a/yarn.lock +++ b/yarn.lock @@ -255,6 +255,10 @@ version "0.7.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" +"@types/jest@^23.0.0": + version "23.3.2" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.2.tgz#07b90f6adf75d42c34230c026a2529e56c249dbb" + "@webassemblyjs/ast@1.5.13": version "1.5.13" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.5.13.tgz#81155a570bd5803a30ec31436bc2c9c0ede38f25" @@ -4306,6 +4310,10 @@ flow-parser@^0.*: version "0.72.0" resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.72.0.tgz#6c8041e76ac7d0be1a71ce29c00cd1435fb6013c" +flow-webpack-plugin@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/flow-webpack-plugin/-/flow-webpack-plugin-1.2.0.tgz#1958821d16135028e391cad5ee2f3a4fa78197ec" + flush-write-stream@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.2.tgz#c81b90d8746766f1a609a46809946c45dd8ae417" @@ -5593,7 +5601,7 @@ isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" -isomorphic-fetch@^2.1.1: +isomorphic-fetch@^2.1.1, isomorphic-fetch@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" dependencies: @@ -5785,6 +5793,14 @@ jest-environment-node@^23.4.0: jest-mock "^23.2.0" jest-util "^23.4.0" +jest-fetch-mock@^1.6.5: + version "1.6.5" + resolved "https://registry.yarnpkg.com/jest-fetch-mock/-/jest-fetch-mock-1.6.5.tgz#178fa1a937ef6f61fb8e8483b6d4602b17e0d96d" + dependencies: + "@types/jest" "^23.0.0" + isomorphic-fetch "^2.2.1" + promise-polyfill "^7.1.1" + jest-get-type@^22.1.0: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.4.3.tgz#e3a8504d8479342dd4420236b322869f18900ce4" @@ -8034,6 +8050,10 @@ promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" +promise-polyfill@^7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-7.1.2.tgz#ab05301d8c28536301622d69227632269a70ca3b" + promise@^7.1.1: version "7.3.1" resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"