diff options
Diffstat (limited to 'ui/app/ducks/tests/gas-duck.test.js')
-rw-r--r-- | ui/app/ducks/tests/gas-duck.test.js | 600 |
1 files changed, 600 insertions, 0 deletions
diff --git a/ui/app/ducks/tests/gas-duck.test.js b/ui/app/ducks/tests/gas-duck.test.js new file mode 100644 index 000000000..3637d8f29 --- /dev/null +++ b/ui/app/ducks/tests/gas-duck.test.js @@ -0,0 +1,600 @@ +import assert from 'assert' +import sinon from 'sinon' +import proxyquire from 'proxyquire' + + +const GasDuck = proxyquire('../gas.duck.js', { + '../../lib/local-storage-helpers': { + loadLocalStorageData: sinon.spy(), + saveLocalStorageData: sinon.spy(), + }, +}) + +const { + basicGasEstimatesLoadingStarted, + basicGasEstimatesLoadingFinished, + setBasicGasEstimateData, + setCustomGasPrice, + setCustomGasLimit, + setCustomGasTotal, + setCustomGasErrors, + resetCustomGasState, + fetchBasicGasAndTimeEstimates, + fetchBasicGasEstimates, + gasEstimatesLoadingStarted, + gasEstimatesLoadingFinished, + setPricesAndTimeEstimates, + fetchGasEstimates, + setApiEstimatesLastRetrieved, +} = GasDuck +const GasReducer = GasDuck.default + +describe('Gas Duck', () => { + let tempFetch + let tempDateNow + const mockEthGasApiResponse = { + average: 20, + avgWait: 'mockAvgWait', + block_time: 'mockBlock_time', + blockNum: 'mockBlockNum', + fast: 30, + fastest: 40, + fastestWait: 'mockFastestWait', + fastWait: 'mockFastWait', + safeLow: 10, + safeLowWait: 'mockSafeLowWait', + speed: 'mockSpeed', + standard: 20, + } + const mockPredictTableResponse = [ + { expectedTime: 400, expectedWait: 40, gasprice: 0.25, somethingElse: 'foobar' }, + { expectedTime: 200, expectedWait: 20, gasprice: 0.5, somethingElse: 'foobar' }, + { expectedTime: 100, expectedWait: 10, gasprice: 1, somethingElse: 'foobar' }, + { expectedTime: 75, expectedWait: 7.5, gasprice: 1.5, somethingElse: 'foobar' }, + { expectedTime: 50, expectedWait: 5, gasprice: 2, somethingElse: 'foobar' }, + { expectedTime: 35, expectedWait: 4.5, gasprice: 3, somethingElse: 'foobar' }, + { expectedTime: 34, expectedWait: 4.4, gasprice: 3.1, somethingElse: 'foobar' }, + { expectedTime: 25, expectedWait: 4.2, gasprice: 3.5, somethingElse: 'foobar' }, + { expectedTime: 20, expectedWait: 4, gasprice: 4, somethingElse: 'foobar' }, + { expectedTime: 19, expectedWait: 3.9, gasprice: 4.1, somethingElse: 'foobar' }, + { expectedTime: 15, expectedWait: 3, gasprice: 7, somethingElse: 'foobar' }, + { expectedTime: 14, expectedWait: 2.9, gasprice: 7.1, somethingElse: 'foobar' }, + { expectedTime: 12, expectedWait: 2.5, gasprice: 8, somethingElse: 'foobar' }, + { expectedTime: 10, expectedWait: 2, gasprice: 10, somethingElse: 'foobar' }, + { expectedTime: 9, expectedWait: 1.9, gasprice: 10.1, somethingElse: 'foobar' }, + { expectedTime: 5, expectedWait: 1, gasprice: 15, somethingElse: 'foobar' }, + { expectedTime: 4, expectedWait: 0.9, gasprice: 15.1, somethingElse: 'foobar' }, + { expectedTime: 2, expectedWait: 0.8, gasprice: 17, somethingElse: 'foobar' }, + { expectedTime: 1.1, expectedWait: 0.6, gasprice: 19.9, somethingElse: 'foobar' }, + { expectedTime: 1, expectedWait: 0.5, gasprice: 20, somethingElse: 'foobar' }, + ] + const fetchStub = sinon.stub().callsFake((url) => new Promise(resolve => { + const dataToResolve = url.match(/ethgasAPI|gasexpress/) + ? mockEthGasApiResponse + : mockPredictTableResponse + resolve({ + json: () => new Promise(resolve => resolve(dataToResolve)), + }) + })) + + beforeEach(() => { + tempFetch = global.fetch + tempDateNow = global.Date.now + global.fetch = fetchStub + global.Date.now = () => 2000000 + }) + + afterEach(() => { + fetchStub.resetHistory() + global.fetch = tempFetch + global.Date.now = tempDateNow + }) + + const mockState = { + gas: { + mockProp: 123, + }, + } + const initState = { + customData: { + price: null, + limit: '0x5208', + }, + basicEstimates: { + average: null, + fastestWait: null, + fastWait: null, + fast: null, + safeLowWait: null, + blockNum: null, + avgWait: null, + blockTime: null, + speed: null, + fastest: null, + safeLow: null, + }, + basicEstimateIsLoading: true, + errors: {}, + gasEstimatesLoading: true, + priceAndTimeEstimates: [], + priceAndTimeEstimatesLastRetrieved: 0, + basicPriceAndTimeEstimates: [], + basicPriceAndTimeEstimatesLastRetrieved: 0, + basicPriceEstimatesLastRetrieved: 0, + } + const BASIC_GAS_ESTIMATE_LOADING_FINISHED = 'metamask/gas/BASIC_GAS_ESTIMATE_LOADING_FINISHED' + const BASIC_GAS_ESTIMATE_LOADING_STARTED = 'metamask/gas/BASIC_GAS_ESTIMATE_LOADING_STARTED' + const GAS_ESTIMATE_LOADING_FINISHED = 'metamask/gas/GAS_ESTIMATE_LOADING_FINISHED' + const GAS_ESTIMATE_LOADING_STARTED = 'metamask/gas/GAS_ESTIMATE_LOADING_STARTED' + const RESET_CUSTOM_GAS_STATE = 'metamask/gas/RESET_CUSTOM_GAS_STATE' + const SET_BASIC_GAS_ESTIMATE_DATA = 'metamask/gas/SET_BASIC_GAS_ESTIMATE_DATA' + const SET_CUSTOM_GAS_ERRORS = 'metamask/gas/SET_CUSTOM_GAS_ERRORS' + const SET_CUSTOM_GAS_LIMIT = 'metamask/gas/SET_CUSTOM_GAS_LIMIT' + const SET_CUSTOM_GAS_PRICE = 'metamask/gas/SET_CUSTOM_GAS_PRICE' + const SET_CUSTOM_GAS_TOTAL = 'metamask/gas/SET_CUSTOM_GAS_TOTAL' + const SET_PRICE_AND_TIME_ESTIMATES = 'metamask/gas/SET_PRICE_AND_TIME_ESTIMATES' + const SET_API_ESTIMATES_LAST_RETRIEVED = 'metamask/gas/SET_API_ESTIMATES_LAST_RETRIEVED' + const SET_BASIC_API_ESTIMATES_LAST_RETRIEVED = 'metamask/gas/SET_BASIC_API_ESTIMATES_LAST_RETRIEVED' + const SET_BASIC_PRICE_ESTIMATES_LAST_RETRIEVED = 'metamask/gas/SET_BASIC_PRICE_ESTIMATES_LAST_RETRIEVED' + + describe('GasReducer()', () => { + it('should initialize state', () => { + assert.deepEqual( + GasReducer({}), + initState + ) + }) + + it('should return state unchanged if it does not match a dispatched actions type', () => { + assert.deepEqual( + GasReducer(mockState, { + type: 'someOtherAction', + value: 'someValue', + }), + Object.assign({}, mockState.gas) + ) + }) + + it('should set basicEstimateIsLoading to true when receiving a BASIC_GAS_ESTIMATE_LOADING_STARTED action', () => { + assert.deepEqual( + GasReducer(mockState, { + type: BASIC_GAS_ESTIMATE_LOADING_STARTED, + }), + Object.assign({basicEstimateIsLoading: true}, mockState.gas) + ) + }) + + it('should set basicEstimateIsLoading to false when receiving a BASIC_GAS_ESTIMATE_LOADING_FINISHED action', () => { + assert.deepEqual( + GasReducer(mockState, { + type: BASIC_GAS_ESTIMATE_LOADING_FINISHED, + }), + Object.assign({basicEstimateIsLoading: false}, mockState.gas) + ) + }) + + it('should set gasEstimatesLoading to true when receiving a GAS_ESTIMATE_LOADING_STARTED action', () => { + assert.deepEqual( + GasReducer(mockState, { + type: GAS_ESTIMATE_LOADING_STARTED, + }), + Object.assign({gasEstimatesLoading: true}, mockState.gas) + ) + }) + + it('should set gasEstimatesLoading to false when receiving a GAS_ESTIMATE_LOADING_FINISHED action', () => { + assert.deepEqual( + GasReducer(mockState, { + type: GAS_ESTIMATE_LOADING_FINISHED, + }), + Object.assign({gasEstimatesLoading: false}, mockState.gas) + ) + }) + + it('should return a new object (and not just modify the existing state object)', () => { + assert.deepEqual(GasReducer(mockState), mockState.gas) + assert.notEqual(GasReducer(mockState), mockState.gas) + }) + + it('should set basicEstimates when receiving a SET_BASIC_GAS_ESTIMATE_DATA action', () => { + assert.deepEqual( + GasReducer(mockState, { + type: SET_BASIC_GAS_ESTIMATE_DATA, + value: { someProp: 'someData123' }, + }), + Object.assign({basicEstimates: {someProp: 'someData123'} }, mockState.gas) + ) + }) + + it('should set priceAndTimeEstimates when receiving a SET_PRICE_AND_TIME_ESTIMATES action', () => { + assert.deepEqual( + GasReducer(mockState, { + type: SET_PRICE_AND_TIME_ESTIMATES, + value: { someProp: 'someData123' }, + }), + Object.assign({priceAndTimeEstimates: {someProp: 'someData123'} }, mockState.gas) + ) + }) + + it('should set customData.price when receiving a SET_CUSTOM_GAS_PRICE action', () => { + assert.deepEqual( + GasReducer(mockState, { + type: SET_CUSTOM_GAS_PRICE, + value: 4321, + }), + Object.assign({customData: {price: 4321} }, mockState.gas) + ) + }) + + it('should set customData.limit when receiving a SET_CUSTOM_GAS_LIMIT action', () => { + assert.deepEqual( + GasReducer(mockState, { + type: SET_CUSTOM_GAS_LIMIT, + value: 9876, + }), + Object.assign({customData: {limit: 9876} }, mockState.gas) + ) + }) + + it('should set customData.total when receiving a SET_CUSTOM_GAS_TOTAL action', () => { + assert.deepEqual( + GasReducer(mockState, { + type: SET_CUSTOM_GAS_TOTAL, + value: 10000, + }), + Object.assign({customData: {total: 10000} }, mockState.gas) + ) + }) + + it('should set priceAndTimeEstimatesLastRetrieved when receiving a SET_API_ESTIMATES_LAST_RETRIEVED action', () => { + assert.deepEqual( + GasReducer(mockState, { + type: SET_API_ESTIMATES_LAST_RETRIEVED, + value: 1500000000000, + }), + Object.assign({ priceAndTimeEstimatesLastRetrieved: 1500000000000 }, mockState.gas) + ) + }) + + it('should set priceAndTimeEstimatesLastRetrieved when receiving a SET_BASIC_API_ESTIMATES_LAST_RETRIEVED action', () => { + assert.deepEqual( + GasReducer(mockState, { + type: SET_BASIC_API_ESTIMATES_LAST_RETRIEVED, + value: 1700000000000, + }), + Object.assign({ basicPriceAndTimeEstimatesLastRetrieved: 1700000000000 }, mockState.gas) + ) + }) + + it('should set errors when receiving a SET_CUSTOM_GAS_ERRORS action', () => { + assert.deepEqual( + GasReducer(mockState, { + type: SET_CUSTOM_GAS_ERRORS, + value: { someError: 'error_error' }, + }), + Object.assign({errors: {someError: 'error_error'} }, mockState.gas) + ) + }) + + it('should return the initial state in response to a RESET_CUSTOM_GAS_STATE action', () => { + assert.deepEqual( + GasReducer(mockState, { + type: RESET_CUSTOM_GAS_STATE, + }), + Object.assign({}, initState) + ) + }) + }) + + describe('basicGasEstimatesLoadingStarted', () => { + it('should create the correct action', () => { + assert.deepEqual( + basicGasEstimatesLoadingStarted(), + { type: BASIC_GAS_ESTIMATE_LOADING_STARTED } + ) + }) + }) + + describe('basicGasEstimatesLoadingFinished', () => { + it('should create the correct action', () => { + assert.deepEqual( + basicGasEstimatesLoadingFinished(), + { type: BASIC_GAS_ESTIMATE_LOADING_FINISHED } + ) + }) + }) + + describe('fetchBasicGasEstimates', () => { + const mockDistpatch = sinon.spy() + it('should call fetch with the expected params', async () => { + await fetchBasicGasEstimates()(mockDistpatch, () => ({ gas: Object.assign( + {}, + initState, + { basicPriceAEstimatesLastRetrieved: 1000000 } + ) })) + assert.deepEqual( + mockDistpatch.getCall(0).args, + [{ type: BASIC_GAS_ESTIMATE_LOADING_STARTED} ] + ) + assert.deepEqual( + global.fetch.getCall(0).args, + [ + 'https://dev.blockscale.net/api/gasexpress.json', + { + 'headers': {}, + 'referrer': 'https://dev.blockscale.net/api/', + 'referrerPolicy': 'no-referrer-when-downgrade', + 'body': null, + 'method': 'GET', + 'mode': 'cors', + }, + ] + ) + + assert.deepEqual( + mockDistpatch.getCall(1).args, + [{ type: SET_BASIC_PRICE_ESTIMATES_LAST_RETRIEVED, value: 2000000 } ] + ) + + assert.deepEqual( + mockDistpatch.getCall(2).args, + [{ + type: SET_BASIC_GAS_ESTIMATE_DATA, + value: { + average: 20, + blockTime: 'mockBlock_time', + blockNum: 'mockBlockNum', + fast: 30, + fastest: 40, + safeLow: 10, + }, + }] + ) + assert.deepEqual( + mockDistpatch.getCall(3).args, + [{ type: BASIC_GAS_ESTIMATE_LOADING_FINISHED }] + ) + }) + }) + + describe('fetchBasicGasAndTimeEstimates', () => { + const mockDistpatch = sinon.spy() + it('should call fetch with the expected params', async () => { + await fetchBasicGasAndTimeEstimates()(mockDistpatch, () => ({ gas: Object.assign( + {}, + initState, + { basicPriceAndTimeEstimatesLastRetrieved: 1000000 } + ) })) + assert.deepEqual( + mockDistpatch.getCall(0).args, + [{ type: BASIC_GAS_ESTIMATE_LOADING_STARTED} ] + ) + assert.deepEqual( + global.fetch.getCall(0).args, + [ + 'https://ethgasstation.info/json/ethgasAPI.json', + { + 'headers': {}, + 'referrer': 'http://ethgasstation.info/json/', + 'referrerPolicy': 'no-referrer-when-downgrade', + 'body': null, + 'method': 'GET', + 'mode': 'cors', + }, + ] + ) + + assert.deepEqual( + mockDistpatch.getCall(1).args, + [{ type: SET_BASIC_API_ESTIMATES_LAST_RETRIEVED, value: 2000000 } ] + ) + + assert.deepEqual( + mockDistpatch.getCall(2).args, + [{ + type: SET_BASIC_GAS_ESTIMATE_DATA, + value: { + average: 2, + avgWait: 'mockAvgWait', + blockTime: 'mockBlock_time', + blockNum: 'mockBlockNum', + fast: 3, + fastest: 4, + fastestWait: 'mockFastestWait', + fastWait: 'mockFastWait', + safeLow: 1, + safeLowWait: 'mockSafeLowWait', + speed: 'mockSpeed', + }, + }] + ) + assert.deepEqual( + mockDistpatch.getCall(3).args, + [{ type: BASIC_GAS_ESTIMATE_LOADING_FINISHED }] + ) + }) + }) + + describe('fetchGasEstimates', () => { + const mockDistpatch = sinon.spy() + + beforeEach(() => { + mockDistpatch.resetHistory() + }) + + it('should call fetch with the expected params', async () => { + global.fetch.resetHistory() + await fetchGasEstimates(5)(mockDistpatch, () => ({ gas: Object.assign( + {}, + initState, + { priceAndTimeEstimatesLastRetrieved: 1000000 } + ) })) + assert.deepEqual( + mockDistpatch.getCall(0).args, + [{ type: GAS_ESTIMATE_LOADING_STARTED} ] + ) + assert.deepEqual( + global.fetch.getCall(0).args, + [ + 'https://ethgasstation.info/json/predictTable.json', + { + 'headers': {}, + 'referrer': 'http://ethgasstation.info/json/', + 'referrerPolicy': 'no-referrer-when-downgrade', + 'body': null, + 'method': 'GET', + 'mode': 'cors', + }, + ] + ) + + assert.deepEqual( + mockDistpatch.getCall(1).args, + [{ type: SET_API_ESTIMATES_LAST_RETRIEVED, value: 2000000 }] + ) + + const { type: thirdDispatchCallType, value: priceAndTimeEstimateResult } = mockDistpatch.getCall(2).args[0] + assert.equal(thirdDispatchCallType, SET_PRICE_AND_TIME_ESTIMATES) + assert(priceAndTimeEstimateResult.length < mockPredictTableResponse.length * 3 - 2) + assert(!priceAndTimeEstimateResult.find(d => d.expectedTime > 100)) + assert(!priceAndTimeEstimateResult.find((d, i, a) => a[a + 1] && d.expectedTime > a[a + 1].expectedTime)) + assert(!priceAndTimeEstimateResult.find((d, i, a) => a[a + 1] && d.gasprice > a[a + 1].gasprice)) + + assert.deepEqual( + mockDistpatch.getCall(3).args, + [{ type: GAS_ESTIMATE_LOADING_FINISHED }] + ) + }) + + it('should not call fetch if the estimates were retrieved < 75000 ms ago', async () => { + global.fetch.resetHistory() + await fetchGasEstimates(5)(mockDistpatch, () => ({ gas: Object.assign( + {}, + initState, + { + priceAndTimeEstimatesLastRetrieved: Date.now(), + priceAndTimeEstimates: [{ + expectedTime: '10', + expectedWait: 2, + gasprice: 50, + }], + } + ) })) + assert.deepEqual( + mockDistpatch.getCall(0).args, + [{ type: GAS_ESTIMATE_LOADING_STARTED} ] + ) + assert.equal(global.fetch.callCount, 0) + + assert.deepEqual( + mockDistpatch.getCall(1).args, + [{ + type: SET_PRICE_AND_TIME_ESTIMATES, + value: [ + { + expectedTime: '10', + expectedWait: 2, + gasprice: 50, + }, + ], + + }] + ) + assert.deepEqual( + mockDistpatch.getCall(2).args, + [{ type: GAS_ESTIMATE_LOADING_FINISHED }] + ) + }) + }) + + describe('gasEstimatesLoadingStarted', () => { + it('should create the correct action', () => { + assert.deepEqual( + gasEstimatesLoadingStarted(), + { type: GAS_ESTIMATE_LOADING_STARTED } + ) + }) + }) + + describe('gasEstimatesLoadingFinished', () => { + it('should create the correct action', () => { + assert.deepEqual( + gasEstimatesLoadingFinished(), + { type: GAS_ESTIMATE_LOADING_FINISHED } + ) + }) + }) + + describe('setPricesAndTimeEstimates', () => { + it('should create the correct action', () => { + assert.deepEqual( + setPricesAndTimeEstimates('mockPricesAndTimeEstimates'), + { type: SET_PRICE_AND_TIME_ESTIMATES, value: 'mockPricesAndTimeEstimates' } + ) + }) + }) + + describe('setBasicGasEstimateData', () => { + it('should create the correct action', () => { + assert.deepEqual( + setBasicGasEstimateData('mockBasicEstimatData'), + { type: SET_BASIC_GAS_ESTIMATE_DATA, value: 'mockBasicEstimatData' } + ) + }) + }) + + describe('setCustomGasPrice', () => { + it('should create the correct action', () => { + assert.deepEqual( + setCustomGasPrice('mockCustomGasPrice'), + { type: SET_CUSTOM_GAS_PRICE, value: 'mockCustomGasPrice' } + ) + }) + }) + + describe('setCustomGasLimit', () => { + it('should create the correct action', () => { + assert.deepEqual( + setCustomGasLimit('mockCustomGasLimit'), + { type: SET_CUSTOM_GAS_LIMIT, value: 'mockCustomGasLimit' } + ) + }) + }) + + describe('setCustomGasTotal', () => { + it('should create the correct action', () => { + assert.deepEqual( + setCustomGasTotal('mockCustomGasTotal'), + { type: SET_CUSTOM_GAS_TOTAL, value: 'mockCustomGasTotal' } + ) + }) + }) + + describe('setCustomGasErrors', () => { + it('should create the correct action', () => { + assert.deepEqual( + setCustomGasErrors('mockErrorObject'), + { type: SET_CUSTOM_GAS_ERRORS, value: 'mockErrorObject' } + ) + }) + }) + + describe('setApiEstimatesLastRetrieved', () => { + it('should create the correct action', () => { + assert.deepEqual( + setApiEstimatesLastRetrieved(1234), + { type: SET_API_ESTIMATES_LAST_RETRIEVED, value: 1234 } + ) + }) + }) + + describe('resetCustomGasState', () => { + it('should create the correct action', () => { + assert.deepEqual( + resetCustomGasState(), + { type: RESET_CUSTOM_GAS_STATE } + ) + }) + }) + +}) |