Reputation: 5172
This is my DashboardPage component:
export const DashboardPage = (props) => {
const [mounted, setMounted] = useState(false);
const { index } = props;
let navigate = useNavigate();
useEffect(() => {
if (mounted === false) {
setMounted(true);
index();
}
}, [mounted, index]);
const optionNUsersPerCompany = {
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false,
type: 'pie'
},
title: {
text: 'Utenti per azienda'
},
tooltip: {
pointFormat: '{series.name}: <b>{point.percentage:.1f}%</b>'
},
accessibility: {
point: {
valueSuffix: '%'
}
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: true,
format: '<b>{point.name}</b>: {point.percentage:.1f} %'
},
}
},
series: [{
name: 'Utenti',
colorByPoint: true,
data: getDataUsersPerCompany(props.items),
point: {
events: {
click: (e) => {
console.log(e);
props.setCompanyIdInUsersStore(e.target.point.idCompany);
return navigate(PATH_USERS);
}
}
}
}]
}
return (
<Chart options={optionNUsersPerCompany} />
)
}
const mapDispatchToProps = (dispatch) => {
return {
index: () => dispatch(statsActions.index()),
setCompanyIdInUsersStore: (id) => dispatch(companiesActions.setCompanyIdInUsersStore(id)),
};
}
const mapStateToProps = (state) => {
return {
items: state.stats.items,
}
};
export default connect(mapStateToProps, mapDispatchToProps)(DashboardPage);
And this is the simple Highchart Component:
import React, { useState } from "react";
import HighchartsReact from "highcharts-react-official";
import Highcharts from 'highcharts';
export default React.memo(function Chart(props) {
const [currentOptions, setCurrentOptions] = useState({});
/**
* Questo controllo serve a non re-draware per due volte di seguito i containers
*/
if (JSON.stringify(props.options) !== JSON.stringify(currentOptions)) {
setCurrentOptions(props.options);
}
return (
<HighchartsReact
highcharts={Highcharts}
options={currentOptions}
immutable={true}
/>
);
});
I want to test the click on plotOptions with Jest in React Highcharts. This is the resulting HTML page from Jest:
<div>
<div
data-highcharts-chart="1"
style="overflow: hidden;"
>
<div
class="highcharts-container "
dir="ltr"
id="highcharts-r3a94y8-149"
style="position: relative; overflow: hidden; width: 600px; height: 400px; text-align: left; line-height: normal; z-index: 0; user-select: none; outline: none;"
>
<svg
class="highcharts-root"
height="400"
style="font-family: \"Lucida Grande\", \"Lucida Sans Unicode\", Arial, Helvetica, sans-serif; font-size: 12px;"
version="1.1"
viewBox="0 0 600 400"
width="600"
xmlns="http://www.w3.org/2000/svg"
>
<desc>
Created with Highcharts 10.1.0
</desc>
<defs>
<clippath
id="highcharts-r3a94y8-151-"
>
<rect
fill="none"
height="375"
width="580"
x="0"
y="0"
/>
</clippath>
</defs>
<rect
class="highcharts-background"
fill="#ffffff"
height="400"
rx="0"
ry="0"
width="600"
x="0"
y="0"
/>
<rect
class="highcharts-plot-background"
fill="none"
height="375"
width="580"
x="10"
y="10"
/>
<rect
class="highcharts-plot-border"
data-z-index="1"
fill="none"
height="375"
width="580"
x="10"
y="10"
/>
<g
class="highcharts-series-group"
data-z-index="3"
>
<g
class="highcharts-series highcharts-series-0 highcharts-pie-series highcharts-tracker"
data-z-index="0.1"
opacity="1"
style="cursor: pointer;"
transform="translate(10,10) scale(1 1)"
>
<path
class="highcharts-point highcharts-color-0"
d="M 290 175 A 0 0 0 0 1 290 175 L 290 175 A 0 0 0 0 0 290 175 Z"
fill="#7cb5ec"
opacity="1"
stroke="#ffffff"
stroke-linejoin="round"
stroke-width="1"
transform="translate(0,0)"
/>
</g>
<g
class="highcharts-markers highcharts-series-0 highcharts-pie-series"
data-z-index="0.1"
opacity="1"
transform="translate(10,10) scale(1 1)"
/>
</g>
<text
class="highcharts-title"
data-z-index="4"
style="color: rgb(51, 51, 51); font-size: 18px; fill: #333333;"
text-anchor="middle"
x="300"
y="24"
>
Utenti per azienda
</text>
<text
class="highcharts-subtitle"
data-z-index="4"
style="color: rgb(102, 102, 102); fill: #666666;"
text-anchor="middle"
x="300"
y="10"
/>
<text
class="highcharts-caption"
data-z-index="4"
style="color: rgb(102, 102, 102); fill: #666666;"
text-anchor="start"
x="10"
y="397"
/>
<g
class="highcharts-data-labels highcharts-series-0 highcharts-pie-series highcharts-tracker"
data-z-index="6"
opacity="0"
style="cursor: pointer;"
transform="translate(10,10) scale(1 1)"
>
<path
class="highcharts-data-label-connector highcharts-color-0"
d="M 295 370 C 290 370 290 352 290 346 L 290 340"
fill="none"
stroke="#7cb5ec"
stroke-width="1"
/>
<g
class="highcharts-label highcharts-data-label highcharts-data-label-color-0"
data-z-index="1"
style="cursor: pointer;"
transform="translate(300,360)"
>
<text
data-z-index="1"
style="color: rgb(0, 0, 0); font-size: 11px; font-weight: bold; fill: #000000;"
x="5"
y="16"
>
<tspan
style="font-weight: bold;"
>
acme
</tspan>
: 100.0 %
</text>
</g>
</g>
<g
class="highcharts-legend highcharts-no-tooltip"
data-z-index="7"
>
<rect
class="highcharts-legend-box"
fill="none"
height="8"
rx="0"
ry="0"
visibility="hidden"
width="8"
x="0"
y="0"
/>
<g
data-z-index="1"
>
<g />
</g>
</g>
<text
class="highcharts-credits"
data-z-index="8"
style="cursor: pointer; color: rgb(153, 153, 153); font-size: 9px; fill: #999999;"
text-anchor="end"
x="590"
y="395"
>
Highcharts.com
</text>
</svg>
</div>
</div>
</div>
I'm thinking of get the g
element and click on it
render(<BrowserRouter><DashboardPage store={store} /></BrowserRouter>);
const company = screen.getAllByRole('g');
but I got:
TestingLibraryElementError: Unable to find an accessible element with the role "g"
There are no accessible roles. But there might be some inaccessible roles. If you wish to access them, then set the `hidden` option to `true`. Learn more about this here: https://testing-library.com/docs/dom-testing-library/api-queries#byrole
I'm trying to get element by class name:
it('Can handle handleOnUserClick on Chart', async () => {
let initialState = {
stats: {
items: {
nUsersPerCompany: [
{
company: "demolitori",
qty: 5,
id: 16
}
]
}
},
setCompanyIdInUsersStore: () => {},
};
let store = mockStore(initialState);
const { container } = render(<BrowserRouter><DashboardPage store={store} /></BrowserRouter>);
// This console.log outputs the previous HTML code
console.log(prettyDOM(container));
const company = await container.getElementsByClassName("highcharts-label");
console.log(company);
fireEvent.click(company);
});
But with last console.log(company)
I get an empty HTML collection:
HTMLCollection {}
I tried also to remove the async
/await
mode without luck.
I tried to use:
const {container} = render(<BrowserRouter><DashboardPage store={store} /></BrowserRouter>);
console.log(prettyDOM(container))
const path = container.getElementsByClassName("highcharts-color-0");
console.log(prettyDOM(path[0]))
fireEvent.click(path[0]);
The test itself works, but coverage complain with missing the arrow function in click e
point: {
events: {
click: (e) => {
props.setCompanyIdInUsersStore(e.target.point.idCompany);
return navigate(PATH_USERS);
}
}
}
Upvotes: 2
Views: 1671
Reputation: 261
The coverage statistic is correct, as Highcharts render the elements using SVG and other components (path, rect etc.) that do not support click events simulation by DOM. If you replace click with mouseOver, your test coverage would hit 100%, as it is supported. Highcharts team might be doing some custom implementation to achieve click handlers and we need to utilize that to solve your use case. @ppotaczek pointed out the correct approach.
Code modifications:
import { useState, useEffect, useRef } from "react";
import {useNavigate} from 'react-router-dom';
import Chart from "./Chart";
import {connect} from 'react-redux';
export const DashboardPage = (props) => {
// previous code
return (
<Chart options={optionNUsersPerCompany} callback={props.onAfterChartCreated} />
)
}
// follow up code
export default connect(mapStateToProps, mapDispatchToProps)(DashboardPage);
import React, { useState } from "react";
import HighchartsReact from "highcharts-react-official";
import Highcharts from 'highcharts';
export default React.memo(function Chart(props) {
const [currentOptions, setCurrentOptions] = useState({});
/**
* Questo controllo serve a non re-draware per due volte di seguito i containers
*/
if (JSON.stringify(props.options) !== JSON.stringify(currentOptions)) {
setCurrentOptions(props.options);
}
return (
<HighchartsReact
highcharts={Highcharts}
options={currentOptions}
immutable={true}
callback={props.callback}
/>
);
});
callback is a prop on HighCharts react wrapper, that executes when chart is rendered and returns an instance of the chart. You could refer to their docs for more info or check out How do i access highcharts api after component renders?
Now we modify the test case for DashboardPage component as follows:
const afterChartCreatedCallback = (chart) => {
// We can now trigger click on any data point using Highcharts API
chart.series[0].data[0].firePointEvent('click');
}
render(<DashboardPage onAfterChartCreated={afterChartCreatedCallback} />);
So, we only use the callback in the test, to improve the coverage. This should increase your DashboardPage component coverage to 100%.
Upvotes: 4