Refactored components used to render charts for visits

This commit is contained in:
Alejandro Celaya
2021-09-18 19:05:28 +02:00
parent 27c4bd792b
commit 3c23016028
17 changed files with 350 additions and 320 deletions

View File

@@ -0,0 +1,46 @@
import { ReactNode } from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import { Card, CardBody, CardHeader, CardFooter } from 'reactstrap';
import { ChartCard } from '../../../src/visits/charts/ChartCard';
describe('<ChartCard />', () => {
let wrapper: ShallowWrapper;
const createWrapper = (title: Function | string = '', footer?: ReactNode) => {
wrapper = shallow(<ChartCard title={title} footer={footer} />);
return wrapper;
};
afterEach(() => wrapper?.unmount());
it('renders expected components', () => {
const wrapper = createWrapper();
const card = wrapper.find(Card);
const header = wrapper.find(CardHeader);
const body = wrapper.find(CardBody);
const footer = wrapper.find(CardFooter);
expect(card).toHaveLength(1);
expect(header).toHaveLength(1);
expect(body).toHaveLength(1);
expect(footer).toHaveLength(0);
});
it.each([
[ 'the title', 'the title' ],
[ () => 'the title from func', 'the title from func' ],
])('properly renders title by parsing provided value', (title, expectedTitle) => {
const wrapper = createWrapper(title);
const header = wrapper.find(CardHeader);
expect(header.html()).toContain(expectedTitle);
});
it('renders footer only when provided', () => {
const wrapper = createWrapper('', 'the footer');
const footer = wrapper.find(CardFooter);
expect(footer).toHaveLength(1);
expect(footer.html()).toContain('the footer');
});
});

View File

@@ -0,0 +1,47 @@
import { shallow, ShallowWrapper } from 'enzyme';
import { Doughnut } from 'react-chartjs-2';
import { keys, values } from 'ramda';
import { DoughnutChart } from '../../../src/visits/charts/DoughnutChart';
describe.skip('<DoughnutChart />', () => {
let wrapper: ShallowWrapper;
const stats = {
foo: 123,
bar: 456,
};
afterEach(() => wrapper?.unmount());
it('renders Doughnut with expected props', () => {
wrapper = shallow(<DoughnutChart stats={stats} />);
const doughnut = wrapper.find(Doughnut);
const cols = wrapper.find('.col-sm-12');
expect(doughnut).toHaveLength(1);
const { labels, datasets } = doughnut.prop('data');
const [{ data, backgroundColor, borderColor }] = datasets;
const { plugins, scales } = doughnut.prop('options') ?? {};
expect(labels).toEqual(keys(stats));
expect(data).toEqual(values(stats));
expect(datasets).toHaveLength(1);
expect(backgroundColor).toEqual([
'#97BBCD',
'#F7464A',
'#46BFBD',
'#FDB45C',
'#949FB1',
'#57A773',
'#414066',
'#08B2E3',
'#B6C454',
'#DCDCDC',
'#463730',
]);
expect(borderColor).toEqual('white');
expect(plugins.legend).toEqual({ display: false });
expect(scales).toBeUndefined();
expect(cols).toHaveLength(2);
});
});

View File

@@ -0,0 +1,33 @@
import { shallow } from 'enzyme';
import { Mock } from 'ts-mockery';
import { Chart, ChartDataset } from 'chart.js';
import { DoughnutChartLegend } from '../../../src/visits/charts/DoughnutChartLegend';
describe('<DoughnutChartLegend />', () => {
const labels = [ 'foo', 'bar', 'baz', 'foo2', 'bar2' ];
const colors = [ 'foo_color', 'bar_color', 'baz_color' ];
const defaultColor = 'red';
const datasets = [ Mock.of<ChartDataset>({ backgroundColor: colors }) ];
const chart = Mock.of<Chart>({
config: {
data: { labels, datasets },
options: { defaultColor } as any,
},
});
test('renders the expected amount of items with expected colors and labels', () => {
const wrapper = shallow(<DoughnutChartLegend chart={chart} />);
const items = wrapper.find('li');
expect.assertions(labels.length * 2 + 1);
expect(items).toHaveLength(labels.length);
labels.forEach((label, index) => {
const item = items.at(index);
expect(item.find('.doughnut-chart-legend__item-color').prop('style')).toEqual({
backgroundColor: colors[index] ?? defaultColor,
});
expect(item.find('.doughnut-chart-legend__item-text').text()).toEqual(label);
});
});
});

View File

@@ -0,0 +1,60 @@
import { shallow, ShallowWrapper } from 'enzyme';
import { Bar } from 'react-chartjs-2';
import { prettify } from '../../../src/utils/helpers/numbers';
import { MAIN_COLOR, MAIN_COLOR_ALPHA } from '../../../src/utils/theme';
import { HorizontalBarChart } from '../../../src/visits/charts/HorizontalBarChart';
describe.skip('<HorizontalBarChart />', () => {
let wrapper: ShallowWrapper;
const stats = {
foo: 123,
bar: 456,
};
afterEach(() => wrapper?.unmount());
it('renders Bar with expected properties', () => {
wrapper = shallow(<HorizontalBarChart stats={stats} />);
const horizontal = wrapper.find(Bar);
const cols = wrapper.find('.col-sm-12');
expect(horizontal).toHaveLength(1);
const { datasets: [{ backgroundColor, borderColor }] } = horizontal.prop('data');
const { plugins, scales } = horizontal.prop('options') ?? {};
expect(backgroundColor).toEqual(MAIN_COLOR_ALPHA);
expect(borderColor).toEqual(MAIN_COLOR);
expect(plugins.legend).toEqual({ display: false });
expect(scales).toEqual({
x: {
beginAtZero: true,
stacked: true,
ticks: {
precision: 0,
callback: prettify,
},
},
y: { stacked: true },
});
expect(cols).toHaveLength(1);
});
it.each([
[{ foo: 23 }, [ 100, 456 ], [ 23, 0 ]],
[{ foo: 50 }, [ 73, 456 ], [ 50, 0 ]],
[{ bar: 45 }, [ 123, 411 ], [ 0, 45 ]],
[{ bar: 20, foo: 13 }, [ 110, 436 ], [ 13, 20 ]],
[ undefined, [ 123, 456 ], undefined ],
])('splits highlighted data from regular data', (highlightedStats, expectedData, expectedHighlightedData) => {
wrapper = shallow(<HorizontalBarChart stats={stats} highlightedStats={highlightedStats} />);
const horizontal = wrapper.find(Bar);
const { datasets: [{ data, label }, highlightedData ] } = horizontal.prop('data');
expect(label).toEqual('Visits');
expect(data).toEqual(expectedData);
expectedHighlightedData && expect(highlightedData.data).toEqual(expectedHighlightedData);
!expectedHighlightedData && expect(highlightedData).toBeUndefined();
});
});

View File

@@ -0,0 +1,118 @@
import { shallow, ShallowWrapper } from 'enzyme';
import { range } from 'ramda';
import SortingDropdown from '../../../src/utils/SortingDropdown';
import PaginationDropdown from '../../../src/utils/PaginationDropdown';
import { OrderDir, rangeOf } from '../../../src/utils/utils';
import { Stats } from '../../../src/visits/types';
import { SortableBarChartCard } from '../../../src/visits/charts/SortableBarChartCard';
import { HorizontalBarChart } from '../../../src/visits/charts/HorizontalBarChart';
describe('<SortableBarChartCard />', () => {
let wrapper: ShallowWrapper;
const sortingItems = {
name: 'Name',
amount: 'Amount',
};
const stats = {
Foo: 100,
Bar: 50,
};
const createWrapper = (withPagination = false, extraStats = {}) => {
wrapper = shallow(
<SortableBarChartCard
title="Foo"
stats={{ ...stats, ...extraStats }}
sortingItems={sortingItems}
withPagination={withPagination}
/>,
);
return wrapper;
};
afterEach(() => wrapper?.unmount());
it('renders stats unchanged when no ordering is set', () => {
const wrapper = createWrapper();
const chart = wrapper.find(HorizontalBarChart);
expect(chart.prop('stats')).toEqual(stats);
});
describe('renders properly ordered stats when ordering is set', () => {
let assert: (sortName: string, sortDir: OrderDir, keys: string[], values: number[], done: Function) => void;
beforeEach(() => {
const wrapper = createWrapper();
const dropdown = wrapper.renderProp('title' as never)().find(SortingDropdown);
assert = (sortName: string, sortDir: OrderDir, keys: string[], values: number[], done: Function) => {
dropdown.prop('onChange')(sortName, sortDir);
setImmediate(() => {
const stats = wrapper.find(HorizontalBarChart).prop('stats');
expect(Object.keys(stats)).toEqual(keys);
expect(Object.values(stats)).toEqual(values);
done();
});
};
});
it('name - ASC', (done) => assert('name', 'ASC', [ 'Bar', 'Foo' ], [ 50, 100 ], done));
it('name - DESC', (done) => assert('name', 'DESC', [ 'Foo', 'Bar' ], [ 100, 50 ], done));
it('value - ASC', (done) => assert('value', 'ASC', [ 'Bar', 'Foo' ], [ 50, 100 ], done));
it('value - DESC', (done) => assert('value', 'DESC', [ 'Foo', 'Bar' ], [ 100, 50 ], done));
});
describe('renders properly paginated stats when pagination is set', () => {
let assert: (itemsPerPage: number, expectedStats: string[], done: Function) => void;
beforeEach(() => {
const wrapper = createWrapper(true, range(1, 159).reduce<Stats>((accum, value) => {
accum[`key_${value}`] = value;
return accum;
}, {}));
const dropdown = wrapper.renderProp('title' as never)().find(PaginationDropdown);
assert = (itemsPerPage: number, expectedStats: string[], done: Function) => {
dropdown.prop('setValue')(itemsPerPage);
setImmediate(() => {
const stats = wrapper.find(HorizontalBarChart).prop('stats');
expect(Object.keys(stats)).toEqual(expectedStats);
done();
});
};
});
const buildExpected = (size: number): string[] => [ 'Foo', 'Bar', ...rangeOf(size - 2, (i) => `key_${i}`) ];
it('50 items per page', (done) => assert(50, buildExpected(50), done));
it('100 items per page', (done) => assert(100, buildExpected(100), done));
it('200 items per page', (done) => assert(200, buildExpected(160), done));
it('500 items per page', (done) => assert(500, buildExpected(160), done));
});
it('renders extra header content', () => {
const wrapper = shallow(
<span>
<SortableBarChartCard
title="Foo"
stats={stats}
sortingItems={sortingItems}
extraHeaderContent={() => (
<span>
<span className="foo-span">Foo</span>
<span className="bar-span">Bar</span>
</span>
)}
/>
</span>,
).find(SortableBarChartCard);
const header = wrapper.renderProp('extraHeaderContent')();
expect(header.find('.foo-span')).toHaveLength(1);
expect(header.find('.bar-span')).toHaveLength(1);
});
});