From d0619b024fb092182e77e16c6742e157c89b2dc9 Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Tue, 23 Oct 2018 20:06:36 -0230 Subject: [PATCH] Update tests, plus some lint fixes, for gas-price-chart --- .../gas-price-chart.component.js | 27 +-- .../gas-price-chart/gas-price-chart.utils.js | 29 ++- .../tests/gas-price-chart.component.test.js | 201 +++++++++++++++--- 3 files changed, 204 insertions(+), 53 deletions(-) diff --git a/ui/app/components/gas-customization/gas-price-chart/gas-price-chart.component.js b/ui/app/components/gas-customization/gas-price-chart/gas-price-chart.component.js index 69f8b3a91..993dd0a2b 100644 --- a/ui/app/components/gas-customization/gas-price-chart/gas-price-chart.component.js +++ b/ui/app/components/gas-customization/gas-price-chart/gas-price-chart.component.js @@ -1,18 +1,13 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import * as d3 from 'd3' -import c3 from 'c3' import { - appendOrUpdateCircle, generateChart, - generateDataUIObj, - getAdjacentGasPrices, getCoordinateData, - getNewXandTimeEstimate, handleChartUpdate, hideDataUI, - setSelectedCircle, setTickPosition, + handleMouseMove, } from './gas-price-chart.utils.js' export default class GasPriceChart extends Component { @@ -37,7 +32,7 @@ export default class GasPriceChart extends Component { estimatedTimesMax, updateCustomGasPrice, }) { - const chart = generateChart(gasPrices, estimatedTimes, gasPricesMax, estimatedTimesMax) + const chart = generateChart(gasPrices, estimatedTimes, gasPricesMax, estimatedTimesMax) setTimeout(function () { setTickPosition('y', 0, -5, 8) @@ -61,7 +56,7 @@ export default class GasPriceChart extends Component { const { x: chartXStart, width: chartWidth } = getCoordinateData('.c3-areas-data1') - handleChartUpdate ({ + handleChartUpdate({ chart, gasPrices, newPrice: currentPrice, @@ -69,24 +64,14 @@ export default class GasPriceChart extends Component { }) d3.select('.c3-chart').on('mousemove', function () { - const { currentPosValue, newTimeEstimate } = getNewXandTimeEstimate({ + handleMouseMove({ xMousePos: d3.event.clientX, chartXStart, chartWidth, gasPrices, estimatedTimes, + chart, }) - - if (currentPosValue === null && newTimeEstimate === null) { - hideDataUI(chart, '#overlayed-circle') - } - - const indexOfNewCircle = estimatedTimes.length + 1 - const dataUIObj = generateDataUIObj(currentPosValue, indexOfNewCircle, newTimeEstimate) - - chart.internal.overlayPoint(dataUIObj, indexOfNewCircle) - chart.internal.showTooltip([dataUIObj], d3.select('.c3-areas-data1')._groups[0]) - chart.internal.showXGridFocus([dataUIObj]) }) }, 0) @@ -97,7 +82,7 @@ export default class GasPriceChart extends Component { const { gasPrices, currentPrice: newPrice } = this.props if (prevProps.currentPrice !== newPrice) { - handleChartUpdate ({ + handleChartUpdate({ chart: this.chart, gasPrices, newPrice, diff --git a/ui/app/components/gas-customization/gas-price-chart/gas-price-chart.utils.js b/ui/app/components/gas-customization/gas-price-chart/gas-price-chart.utils.js index 43bbfd9ab..ffd8056b0 100644 --- a/ui/app/components/gas-customization/gas-price-chart/gas-price-chart.utils.js +++ b/ui/app/components/gas-customization/gas-price-chart/gas-price-chart.utils.js @@ -1,6 +1,27 @@ import * as d3 from 'd3' import c3 from 'c3' +export function handleMouseMove ({ xMousePos, chartXStart, chartWidth, gasPrices, estimatedTimes, chart }) { + const { currentPosValue, newTimeEstimate } = getNewXandTimeEstimate({ + xMousePos, + chartXStart, + chartWidth, + gasPrices, + estimatedTimes, + }) + + if (currentPosValue === null && newTimeEstimate === null) { + hideDataUI(chart, '#overlayed-circle') + } + + const indexOfNewCircle = estimatedTimes.length + 1 + const dataUIObj = generateDataUIObj(currentPosValue, indexOfNewCircle, newTimeEstimate) + + chart.internal.overlayPoint(dataUIObj, indexOfNewCircle) + chart.internal.showTooltip([dataUIObj], d3.select('.c3-areas-data1')._groups[0]) + chart.internal.showXGridFocus([dataUIObj]) +} + export function getCoordinateData (selector) { return d3.select(selector).node().getBoundingClientRect() } @@ -37,7 +58,7 @@ export function handleChartUpdate ({ chart, gasPrices, newPrice, cssId }) { } } -export function getAdjacentGasPrices({ gasPrices, priceToPosition }) { +export function getAdjacentGasPrices ({ gasPrices, priceToPosition }) { const closestLowerValueIndex = gasPrices.findIndex((e, i, a) => e <= priceToPosition && a[i + 1] >= priceToPosition) const closestHigherValueIndex = gasPrices.findIndex((e, i, a) => e > priceToPosition) return { @@ -69,14 +90,14 @@ export function getNewXandTimeEstimate ({ xMousePos, chartXStart, chartWidth, ga closestHigherValue, } = getAdjacentGasPrices({ gasPrices, priceToPosition: currentPosValue }) - return !closestHigherValue || !closestLowerValue + return !closestHigherValue || !closestLowerValue ? { currentPosValue: null, newTimeEstimate: null, } : { currentPosValue, - newTimeEstimate: extrapolateY ({ + newTimeEstimate: extrapolateY({ higherY: estimatedTimes[closestHigherValueIndex], lowerY: estimatedTimes[closestLowerValueIndex], higherX: closestHigherValue, @@ -144,7 +165,7 @@ export function setSelectedCircle ({ const { x: higherX, y: higherY } = getCoordinateData(`.c3-circle-${closestHigherValueIndex}`) const currentX = lowerX + (higherX - lowerX) * (newPrice - closestLowerValue) / (closestHigherValue - closestLowerValue) - const newTimeEstimate = extrapolateY ({ higherY, lowerY, higherX, lowerX, xForExtrapolation: currentX }) + const newTimeEstimate = extrapolateY({ higherY, lowerY, higherX, lowerX, xForExtrapolation: currentX }) chart.internal.selectPoint( generateDataUIObj(currentX, numberOfValues, newTimeEstimate), diff --git a/ui/app/components/gas-customization/gas-price-chart/tests/gas-price-chart.component.test.js b/ui/app/components/gas-customization/gas-price-chart/tests/gas-price-chart.component.test.js index 48b8a6525..158813edb 100644 --- a/ui/app/components/gas-customization/gas-price-chart/tests/gas-price-chart.component.test.js +++ b/ui/app/components/gas-customization/gas-price-chart/tests/gas-price-chart.component.test.js @@ -5,36 +5,59 @@ import sinon from 'sinon' import shallow from '../../../../../lib/shallow-with-context' import * as d3 from 'd3' -const mockSelectReturn = { - ...d3.select('div'), - node: () => ({ - getBoundingClientRect: () => ({ x: 123, y: 321, width: 400 }), - }), +function timeout (time) { + return new Promise((resolve, reject) => { + setTimeout(resolve, time) + }) +} + +const propsMethodSpies = { + updateCustomGasPrice: sinon.spy(), +} + +const selectReturnSpies = { empty: sinon.spy(), remove: sinon.spy(), style: sinon.spy(), select: d3.select, attr: sinon.spy(), on: sinon.spy(), + datum: sinon.stub().returns({ x: 'mockX' }), +} + +const mockSelectReturn = { + ...d3.select('div'), + node: () => ({ + getBoundingClientRect: () => ({ x: 123, y: 321, width: 400 }), + }), + ...selectReturnSpies, +} + +const gasPriceChartUtilsSpies = { + appendOrUpdateCircle: sinon.spy(), + generateChart: sinon.stub().returns({ mockChart: true }), + generateDataUIObj: sinon.spy(), + getAdjacentGasPrices: sinon.spy(), + getCoordinateData: sinon.stub().returns({ x: 'mockCoordinateX', width: 'mockWidth' }), + getNewXandTimeEstimate: sinon.spy(), + handleChartUpdate: sinon.spy(), + hideDataUI: sinon.spy(), + setSelectedCircle: sinon.spy(), + setTickPosition: sinon.spy(), + handleMouseMove: sinon.spy(), +} + +const testProps = { + gasPrices: [1.5, 2.5, 4, 8], + estimatedTimes: [100, 80, 40, 10], + gasPricesMax: 9, + estimatedTimesMax: 100, + currentPrice: 6, + updateCustomGasPrice: propsMethodSpies.updateCustomGasPrice, } const GasPriceChart = proxyquire('../gas-price-chart.component.js', { - 'c3': { - generate: function ({ data: { columns } }) { - return { - internal: { - showTooltip: () => {}, - showXGridFocus: () => {}, - hideXGridFocus: () => {}, - data: { - xs: { - [columns[1][0]]: columns[1].slice(1), - }, - }, - }, - } - }, - }, + './gas-price-chart.utils.js': gasPriceChartUtilsSpies, 'd3': { ...d3, select: function (...args) { @@ -43,20 +66,19 @@ const GasPriceChart = proxyquire('../gas-price-chart.component.js', { ? mockSelectReturn : result }, + event: { + clientX: 'mockClientX', + }, }, }).default +sinon.spy(GasPriceChart.prototype, 'renderChart') + describe('GasPriceChart Component', function () { let wrapper beforeEach(() => { - wrapper = shallow() + wrapper = shallow() }) describe('render()', () => { @@ -70,4 +92,127 @@ describe('GasPriceChart Component', function () { }) }) + describe('componentDidMount', () => { + it('should call this.renderChart with the components props', () => { + assert(GasPriceChart.prototype.renderChart.callCount, 1) + wrapper.instance().componentDidMount() + assert(GasPriceChart.prototype.renderChart.callCount, 2) + assert.deepEqual(GasPriceChart.prototype.renderChart.getCall(1).args, [{...testProps}]) + }) + }) + + describe('componentDidUpdate', () => { + it('should call handleChartUpdate if props.currentPrice has changed', () => { + gasPriceChartUtilsSpies.handleChartUpdate.resetHistory() + wrapper.instance().componentDidUpdate({ currentPrice: 7 }) + assert.equal(gasPriceChartUtilsSpies.handleChartUpdate.callCount, 1) + }) + + it('should call handleChartUpdate with the correct props', () => { + gasPriceChartUtilsSpies.handleChartUpdate.resetHistory() + wrapper.instance().componentDidUpdate({ currentPrice: 7 }) + assert.deepEqual(gasPriceChartUtilsSpies.handleChartUpdate.getCall(0).args, [{ + chart: { mockChart: true }, + gasPrices: [1.5, 2.5, 4, 8], + newPrice: 6, + cssId: '#set-circle', + }]) + }) + + it('should not call handleChartUpdate if props.currentPrice has not changed', () => { + gasPriceChartUtilsSpies.handleChartUpdate.resetHistory() + wrapper.instance().componentDidUpdate({ currentPrice: 6 }) + assert.equal(gasPriceChartUtilsSpies.handleChartUpdate.callCount, 0) + }) + }) + + describe('renderChart', () => { + it('should call setTickPosition 4 times, with the expected props', async () => { + await timeout(0) + gasPriceChartUtilsSpies.setTickPosition.resetHistory() + assert.equal(gasPriceChartUtilsSpies.setTickPosition.callCount, 0) + wrapper.instance().renderChart(testProps) + await timeout(0) + assert.equal(gasPriceChartUtilsSpies.setTickPosition.callCount, 4) + assert.deepEqual(gasPriceChartUtilsSpies.setTickPosition.getCall(0).args, ['y', 0, -5, 8]) + assert.deepEqual(gasPriceChartUtilsSpies.setTickPosition.getCall(1).args, ['y', 1, -3, -5]) + assert.deepEqual(gasPriceChartUtilsSpies.setTickPosition.getCall(2).args, ['x', 0, 3, 15]) + assert.deepEqual(gasPriceChartUtilsSpies.setTickPosition.getCall(3).args, ['x', 1, 3, -8]) + }) + + it('should call handleChartUpdate with the correct props', async () => { + await timeout(0) + gasPriceChartUtilsSpies.handleChartUpdate.resetHistory() + wrapper.instance().renderChart(testProps) + await timeout(0) + assert.deepEqual(gasPriceChartUtilsSpies.handleChartUpdate.getCall(0).args, [{ + chart: { mockChart: true }, + gasPrices: [1.5, 2.5, 4, 8], + newPrice: 6, + cssId: '#set-circle', + }]) + }) + + it('should add three events to the chart', async () => { + await timeout(0) + selectReturnSpies.on.resetHistory() + assert.equal(selectReturnSpies.on.callCount, 0) + wrapper.instance().renderChart(testProps) + await timeout(0) + assert.equal(selectReturnSpies.on.callCount, 3) + + const firstOnEventArgs = selectReturnSpies.on.getCall(0).args + assert.equal(firstOnEventArgs[0], 'mouseout') + const secondOnEventArgs = selectReturnSpies.on.getCall(1).args + assert.equal(secondOnEventArgs[0], 'click') + const thirdOnEventArgs = selectReturnSpies.on.getCall(2).args + assert.equal(thirdOnEventArgs[0], 'mousemove') + }) + + it('should hide the data UI on mouseout', async () => { + await timeout(0) + selectReturnSpies.on.resetHistory() + wrapper.instance().renderChart(testProps) + gasPriceChartUtilsSpies.hideDataUI.resetHistory() + await timeout(0) + const mouseoutEventArgs = selectReturnSpies.on.getCall(0).args + assert.equal(gasPriceChartUtilsSpies.hideDataUI.callCount, 0) + mouseoutEventArgs[1]() + assert.equal(gasPriceChartUtilsSpies.hideDataUI.callCount, 1) + assert.deepEqual(gasPriceChartUtilsSpies.hideDataUI.getCall(0).args, [{ mockChart: true }, '#overlayed-circle']) + }) + + it('should updateCustomGasPrice on click', async () => { + await timeout(0) + selectReturnSpies.on.resetHistory() + wrapper.instance().renderChart(testProps) + propsMethodSpies.updateCustomGasPrice.resetHistory() + await timeout(0) + const mouseoutEventArgs = selectReturnSpies.on.getCall(1).args + assert.equal(propsMethodSpies.updateCustomGasPrice.callCount, 0) + mouseoutEventArgs[1]() + assert.equal(propsMethodSpies.updateCustomGasPrice.callCount, 1) + assert.equal(propsMethodSpies.updateCustomGasPrice.getCall(0).args[0], 'mockX') + }) + + it('should handle mousemove', async () => { + await timeout(0) + selectReturnSpies.on.resetHistory() + wrapper.instance().renderChart(testProps) + gasPriceChartUtilsSpies.handleMouseMove.resetHistory() + await timeout(0) + const mouseoutEventArgs = selectReturnSpies.on.getCall(2).args + assert.equal(gasPriceChartUtilsSpies.handleMouseMove.callCount, 0) + mouseoutEventArgs[1]() + assert.equal(gasPriceChartUtilsSpies.handleMouseMove.callCount, 1) + assert.deepEqual(gasPriceChartUtilsSpies.handleMouseMove.getCall(0).args, [{ + xMousePos: 'mockClientX', + chartXStart: 'mockCoordinateX', + chartWidth: 'mockWidth', + gasPrices: testProps.gasPrices, + estimatedTimes: testProps.estimatedTimes, + chart: { mockChart: true }, + }]) + }) + }) })