javascript - 如何模拟 Highchart 上的点击

标签 javascript reactjs highcharts jestjs

这是我的 DashboardPage 组件:

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);

这是简单的 Highchart 组件:

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}
        />
    );
});

我想在 React Highcharts 中使用 Jest 测试对plotOptions 的点击。这是 Jest 生成的 HTML 页面:

  <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>
  • 我正在考虑获取 g 元素并单击它

    render(<BrowserRouter><DashboardPage store={store} /></BrowserRouter>);
            const company = screen.getAllByRole('g');
    

    但是我得到了:

    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
    
  • 我正在尝试按类名获取元素:

    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);
    
    });
    

    但是最后一个 console.log(company) 我得到一个空的 HTML 集合:

    HTMLCollection {}
    

    我还尝试删除 async/await 模式,但没有成功。

    我尝试使用:

    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]);
    

    测试本身有效,但覆盖范围提示单击 e 中缺少箭头功能

    point: {
        events: {
            click: (e) => {
                props.setCompanyIdInUsersStore(e.target.point.idCompany);
                return navigate(PATH_USERS);
            }
        }
    }
    

最佳答案

覆盖率统计数据是正确的,因为 Highcharts 使用 SVG 和其他不支持 DOM 模拟点击事件的组件(路径、矩形等)渲染元素。如果您用 mouseOver 替换 click,您的测试覆盖率将达到 100%,因为它是受支持的。 Highcharts 团队可能会进行一些自定义实现来实现点击处理程序,我们需要利用它来解决您的用例。 @ppotaczek 指出了正确的做法。

代码修改:

  • 假设 DashboardPage 组件接受可选属性 onAfterChartCreated。
  • 它将该属性作为回调值传递给 Chart 组件。

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}
        />
    );
});

回调是 HighCharts react 包装器上的一个 Prop ,它在渲染图表时执行并返回图表的实例。您可以引用他们的文档以获取更多信息或查看 How do i access highcharts api after component renders?

现在我们修改 DashboardPage 组件的测试用例如下:

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} />);

所以,我们在测试中只使用回调,以提高覆盖率。这应该会将您的 DashboardPage 组件覆盖率提高到 100%。

关于javascript - 如何模拟 Highchart 上的点击,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72576640/

相关文章:

javascript - PutImageData() 不绘制 ImageData

java - 使用 GWT 在 Donut Highchart 上单击事件

reactjs - nextjs 中的环境变量

Highcharts 认为所有日期均为 1970-01-01,尽管它们是有效的纪元时间

javascript - 如何从当前导航器系列的 Highcharts 中获取摘要?

html 元素数组上的 Javascript unshift()

javascript - 在 phantomjs 中解析发布数据

javascript - 从 Google Visualization 的 ColumnChart 中手动选择一个条形图

reactjs - 具有默认类型的通用组件

javascript - 使用 extract-text-webpack-plugin 和在 HTML header 中链接合并的 CSS 文件有什么区别?