Skip to content

Commit

Permalink
Updated testcases for 100% test coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
ReshmaJoshy committed Jan 17, 2024
1 parent de1211f commit a0a51e4
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 66 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@


Try tweaking a waterfall chart using this codesandbox link <a href="https://codesandbox.io/s/waterfall-chart-example-uvr8jd">here</a>
Try tweaking a waterfall chart using this codesandbox link <a href="https://codesandbox.io/p/sandbox/react-water-fall-chart-nxkyrt">here</a>



Expand Down Expand Up @@ -58,9 +58,9 @@ The dataPoints prop is an array of dataPoint with the following keys:



- `label` - a string to represent each transaction
- `label` - a string to represent each dataPoint

- `value` - a number that specifies the transaction quantity
- `value` - a number that specifies the dataPoint quantity

An example for dataPoint array is shown below:

Expand Down
153 changes: 96 additions & 57 deletions src/tests/waterfallChart.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { render, fireEvent, prettyDOM, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import WaterFallChart from '../waterfall-chart';
import { wait } from '@testing-library/user-event/dist/utils';

// Mock data for dataPoints
const dataPoints = [
Expand All @@ -24,27 +25,40 @@ const props = {
summaryXLabel: 'summary',
styles: {
positiveBar: { fill: 'blue' },
negativeBar: { fill: 'red' },
negativeBar: { fill: 'red' }
},
onChartClick: mockOnMouseClick,
onMouseEnter: mockOnMouseEnter,
onMouseLeave: mockOnMouseLeave
}
};

const DEFAULT_OFFSET_HEIGHT = 1000;
const DEFAULT_OFFSET_WIDTH = 1500;
Object.defineProperties(window.HTMLElement.prototype, {
offsetHeight: {
get() {
return parseFloat(this.style.height) || DEFAULT_OFFSET_HEIGHT;
}
},
offsetWidth: {
get() {
return parseFloat(this.style.width) || DEFAULT_OFFSET_WIDTH;
}
}
});

describe('WaterFallChart component', () => {
it('renders the chart with correct bars and labels', () => {
// Render the WaterFallChart component with dataPoints as props
const { container, getByText } = render(<WaterFallChart
{...props}
/>);
const { container, getByText } = render(<WaterFallChart {...props} />);

// Assert that the chart bars are rendered with correct heights
const positiveBar = container.querySelector('#chartBar-0');
const negativeBar = container.querySelector('#chartBar-1');
const summaryBar = container.querySelector('#summaryBar');
if (positiveBar) expect(positiveBar).toHaveAttribute('height', '50');
if (negativeBar) expect(negativeBar).toHaveAttribute('height', '25');
if (summaryBar) expect(summaryBar).toHaveAttribute('height', '75');
expect(positiveBar).toHaveAttribute('height', '300');
expect(negativeBar).toHaveAttribute('height', '150');
expect(summaryBar).toHaveAttribute('height', '750');

// Assert that the x-axis and y-axis lines are rendered
const xAxisLine = container.querySelector('#xAxisLine');
Expand All @@ -59,9 +73,8 @@ describe('WaterFallChart component', () => {

it('does not render the chart with correct bars and labels when data points is empty', () => {
// Render the WaterFallChart component with dataPoints as props
const { container, getByText } = render(<WaterFallChart
dataPoints={[]}
/>);

const { container, getByText } = render(<WaterFallChart dataPoints={[]} />);

// Assert that the chart bars are rendered with correct heights
const positiveBar = container.querySelector('#chartBar-0');
Expand All @@ -86,17 +99,13 @@ describe('WaterFallChart component', () => {
// Mock callback function

// Render the WaterFallChart component with dataPoints and onChartClick callback as props
const { container } = render(
<WaterFallChart {...props} />
);
const { container } = render(<WaterFallChart {...props} />);

// Click on a chart bar
const barZero = container.querySelector('#chartBar-0');
if (barZero) {
fireEvent.click(barZero);
// Assert that the mock callback function is called with the correct chart element
expect(mockOnMouseClick).toHaveBeenCalledTimes(1);
}
fireEvent.click(barZero as Element);
// Assert that the mock callback function is called with the correct chart element
expect(mockOnMouseClick).toHaveBeenCalledTimes(1);
});

it('calls onMouseEnter and onMouseLeavecallback when a bar is hovered', () => {
Expand All @@ -106,22 +115,21 @@ describe('WaterFallChart component', () => {

// Render the WaterFallChart component with dataPoints and onChartClick callback as props
const { container } = render(
<WaterFallChart dataPoints={dataPoints}
<WaterFallChart
dataPoints={dataPoints}
onMouseEnter={(e: any, chartElement) => mockOnMounseEnter(e, chartElement)}
onMouseLeave={(e: any, chartElement) => mockOnMounseLeave(e, chartElement)}
/>
);

// Click on a chart bar
const barZero = container.querySelector('#chartBar-0');
if (barZero) {
fireEvent.mouseEnter(barZero);
// Assert that the mock callback function is called with the correct chart element
expect(mockOnMounseEnter).toHaveBeenCalledTimes(1);
fireEvent.mouseLeave(barZero);
// Assert that the mock callback function is called with the correct chart element
expect(mockOnMounseLeave).toHaveBeenCalledTimes(1);
}
fireEvent.mouseEnter(barZero as Element);
// Assert that the mock callback function is called with the correct chart element
expect(mockOnMounseEnter).toHaveBeenCalledTimes(1);
fireEvent.mouseLeave(barZero as Element);
// Assert that the mock callback function is called with the correct chart element
expect(mockOnMounseLeave).toHaveBeenCalledTimes(1);
});

it('calls onMouseEnter and onMouseLeave callback when a bar is hovered', () => {
Expand All @@ -140,14 +148,12 @@ describe('WaterFallChart component', () => {

// Simulate mouse events
const barZero = container.querySelector('#chartBar-0');
if (barZero) {
fireEvent.mouseEnter(barZero);
fireEvent.mouseLeave(barZero);
fireEvent.mouseEnter(barZero as Element);
fireEvent.mouseLeave(barZero as Element);

// Assert that the mock callback functions are called
expect(mockOnMouseEnter).toHaveBeenCalledTimes(1);
expect(mockOnMouseLeave).toHaveBeenCalledTimes(1);
}
// Assert that the mock callback functions are called
expect(mockOnMouseEnter).toHaveBeenCalledTimes(1);
expect(mockOnMouseLeave).toHaveBeenCalledTimes(1);
});

it('does not render bridge lines when showBridgeLines prop is set to false', () => {
Expand Down Expand Up @@ -177,25 +183,23 @@ describe('WaterFallChart component', () => {
expect(summaryBar).toBeNull();
});

it('sets barWidth based on calculateBarWidth when initialBarWidth is 0 or not defined', () => {
it('sets barWidth based on calculateBarWidth when initialBarWidth is 0 or not defined', async () => {
// Render the WaterFallChart component with initialBarWidth as 0 or undefined
const { container } = render(<WaterFallChart dataPoints={dataPoints} barWidth={0} />);
const barZero = container.querySelector('#chartBar-0');

if (barZero) {
expect(barZero).toHaveAttribute('width', '100');
}
fireEvent(window, new Event('resize'));
await waitFor(() => {
const barZero = container.querySelector('#chartBar-0');
expect(barZero).toHaveAttribute('width', '187.5');
});
});

it('handles resize event and recalculates barWidth when initialBarWidth is 0', () => {
// Render the WaterFallChart component with initialBarWidth as 0
const { container } = render(<WaterFallChart dataPoints={dataPoints} barWidth={0} />);
const barZero = container.querySelector('#chartBar-0');

if (barZero) {
fireEvent(window, new Event('resize'));
expect(barZero).toHaveAttribute('width', '100');
}
fireEvent(window, new Event('resize'));
expect(barZero).toHaveAttribute('width', '187.5');
});

it('handles resize event and recalculates barWidth when initialBarWidth is less than or equal to 0', () => {
Expand All @@ -205,7 +209,7 @@ describe('WaterFallChart component', () => {

if (barZero) {
fireEvent(window, new Event('resize'));
expect(barZero).toHaveAttribute('width', '100');
expect(barZero).toHaveAttribute('width', '187.5');
}
});

Expand All @@ -214,26 +218,61 @@ describe('WaterFallChart component', () => {
const { container } = render(<WaterFallChart dataPoints={dataPoints} barWidth={50} />);
const barZero = container.querySelector('#chartBar-0');

if (barZero) {
expect(barZero).toHaveAttribute('width', '50');
}
expect(barZero).toHaveAttribute('width', '50');
});

it('calls onChartClick callback when clicking summary bar', () => {
// Mock callback function
const mockOnClick = jest.fn();

// Render the WaterFallChart component with onChartClick callback as props
const { container } = render(<WaterFallChart dataPoints={dataPoints} onChartClick={(e) => mockOnClick(e)} />);

// Click on the summary bar
const summaryBar = container.querySelector('#summaryBar');
fireEvent.click(summaryBar as Element);
expect(mockOnClick).toHaveBeenCalledTimes(1);
});
it('calls onMouseEnter and onMouseLeavecallback when summary bar is hovered', () => {
// Mock callback function
const mockOnMounseEnter = jest.fn();
const mockOnMounseLeave = jest.fn();

// Render the WaterFallChart component with dataPoints and onChartClick callback as props
const { container } = render(
<WaterFallChart dataPoints={dataPoints} onChartClick={(e) => mockOnClick(e)} />
<WaterFallChart
dataPoints={dataPoints}
onMouseEnter={(e: any, chartElement) => mockOnMounseEnter(e, chartElement)}
onMouseLeave={(e: any, chartElement) => mockOnMounseLeave(e, chartElement)}
/>
);

// Click on the summary bar
// Click on summary bar
const summaryBar = container.querySelector('#summaryBar');
if (summaryBar) {
fireEvent.click(summaryBar);
expect(mockOnClick).toHaveBeenCalledTimes(1);
}
fireEvent.mouseEnter(summaryBar as Element);
// Assert that the mock callback function is called with the correct chart element
expect(mockOnMounseEnter).toHaveBeenCalledTimes(1);
fireEvent.mouseLeave(summaryBar as Element);
// Assert that the mock callback function is called with the correct chart element
expect(mockOnMounseLeave).toHaveBeenCalledTimes(1);
});
});

it('Set barWidth as zero if window width is zero', () => {
Object.defineProperties(window.HTMLElement.prototype, {
offsetHeight: {
get() {
return 0.1;
}
},
offsetWidth: {
get() {
return 0;
}
}
});
const { container } = render(<WaterFallChart dataPoints={dataPoints} barWidth={0}/>);
const barZero = container.querySelector('#chartBar-0');

expect(barZero).toHaveAttribute('width', '0');
});
});
3 changes: 1 addition & 2 deletions src/waterfall-chart/WaterFallChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ const WaterFallChart: FC<IWaterfallGraphProps> = (props) => {
window.addEventListener('resize', onWrapperDimensionsChange);

return () => window.removeEventListener('resize', onWrapperDimensionsChange);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [initialBarWidth]);
}, [initialBarWidth, calculateBarWidth]);

const summaryValue = Math.abs(chartElements[chartElements?.length - 1]?.cumulativeSum);
const summaryBarHeight = Math.abs((summaryValue / yAxisScale) * yAxisPixelsPerUnit);
Expand Down
2 changes: 1 addition & 1 deletion src/waterfall-chart/useWaterFallChart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const useWaterfallChart = (
yAxisPixelsPerUnit: number,
showFinalSummary: boolean
): IUseWaterfallChartReturnType => {
if (chartHeight <= 0) {
if (chartHeight <= 0 || dataPoints?.length === 0) {
return {
chartElements: [],
yValueForZeroLine: 0,
Expand Down
4 changes: 1 addition & 3 deletions src/waterfall-chart/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ export function getSmallestCumulativeSum(arr: Array<IDataPoint>): number {
}
}

if (minSum > 0) return 0; // if chart never goes below zero then smallest value should be zero
return minSum;
return minSum > 0 ? 0 : minSum; // if chart never goes below zero then smallest value should be zero
}

export function roundMinVal(minVal: number, range: number): number {
Expand Down Expand Up @@ -69,7 +68,6 @@ export function getIntervalAndYPoints(
}

export function checkIfScaleSufficient(scale: number, maxLabelsCount: number, valueRange: number): boolean {
if (maxLabelsCount === 0) return true; // to stop the while loop from checking for sufficient scale with zero maxLabelsCount
if (scale * maxLabelsCount >= valueRange) return true;
return false;
}

0 comments on commit a0a51e4

Please sign in to comment.