// (C) 2007-2020 GoodData Corporation
import get = require("lodash/get");
import set = require("lodash/set");
import noop = require("lodash/noop");
import {
    escapeCategories,
    formatOverlapping,
    formatOverlappingForParentAttribute,
    getCustomizedConfiguration,
    percentageDataLabelFormatter,
    getTooltipPositionInViewPort,
    getTooltipPositionInChartContainer,
    TOOLTIP_VIEWPORT_MARGIN_TOP,
    TOOLTIP_PADDING,
} from "../customConfiguration";
import { ISeriesDataItem, IPointData, IChartOptions } from "../../../../../interfaces/Config";
import { VisualizationTypes } from "../../../../../constants/visualizationTypes";
import { immutableSet } from "../../../utils/common";
import {
    supportedStackingAttributesChartTypes,
    supportedTooltipFollowPointerChartTypes,
} from "../../chartOptionsBuilder";
import { AFM } from "@gooddata/typings";
import { IDrillConfig } from "../../../../../interfaces/DrillEvents";

function getData(dataValues: ISeriesDataItem[]) {
    return {
        series: [
            {
                color: "rgb(0, 0, 0)",
                name: "<b>aaa</b>",
                data: dataValues,
            },
        ],
    };
}

const chartOptions = {
    type: VisualizationTypes.LINE,
    yAxes: [{ title: "atitle" }],
    xAxes: [{ title: "xtitle" }],
    data: getData([
        {
            name: "<b>bbb</b>",
            y: 10,
        },
        null,
    ]),
};

describe("getCustomizedConfiguration", () => {
    it("should escape series names", () => {
        const result = getCustomizedConfiguration(chartOptions);
        expect(result.series[0].name).toEqual("&lt;b&gt;aaa&lt;/b&gt;");
    });

    it("should escape data items in series", () => {
        const result = getCustomizedConfiguration(chartOptions);
        expect(result.series[0].data[0].name).toEqual("&lt;b&gt;bbb&lt;/b&gt;");
    });

    it('should handle "%" format on axis and use label formatter', () => {
        const chartOptionsWithFormat = immutableSet(chartOptions, "yAxes[0].format", "0.00 %");
        const resultWithoutFormat = getCustomizedConfiguration(chartOptions);
        const resultWithFormat = getCustomizedConfiguration(chartOptionsWithFormat);

        expect(resultWithoutFormat.yAxis[0].labels.formatter).toBeUndefined();
        expect(resultWithFormat.yAxis[0].labels.formatter).toBeDefined();
    });

    it("should set formatter for xAxis labels to prevent overlapping for bar chart with 90 rotation", () => {
        const result = getCustomizedConfiguration({
            ...chartOptions,
            type: "bar",
            xAxisProps: {
                rotation: "90",
            },
        });

        expect(result.xAxis[0].labels.formatter).toBe(formatOverlapping);
    });

    it("should set formatter for xAxis labels to prevent overlapping for stacking bar chart with 90 rotation", () => {
        const result = getCustomizedConfiguration({
            ...chartOptions,
            isViewByTwoAttributes: true,
            type: "bar",
            xAxisProps: {
                rotation: "90",
            },
        });

        expect(result.xAxis[0].labels.formatter).toBe(formatOverlappingForParentAttribute);
    });

    it("shouldn't set formatter for xAxis by default", () => {
        const result = getCustomizedConfiguration(chartOptions);

        expect(result.xAxis[0].labels.formatter).toBeUndefined();
    });

    it("should set connectNulls for stacked Area chart", () => {
        const result = getCustomizedConfiguration({
            ...chartOptions,
            type: VisualizationTypes.AREA,
            stacking: "normal",
        });

        expect(result.plotOptions.series.connectNulls).toBeTruthy();
    });

    it("should NOT set connectNulls for NON stacked Area chart", () => {
        const result = getCustomizedConfiguration({
            ...chartOptions,
            type: VisualizationTypes.AREA,
            stacking: null,
        });

        expect(result.plotOptions.series).toEqual({});
    });

    it("should NOT set connectNulls for stacked Line chart", () => {
        const result = getCustomizedConfiguration({
            ...chartOptions,
            stacking: "normal",
        });

        expect(result.plotOptions.series.connectNulls).toBeUndefined();
    });

    describe("getAxesConfiguration", () => {
        it("should set Y axis configuration from properties", () => {
            const result = getCustomizedConfiguration({
                ...chartOptions,
                yAxisProps: {
                    min: 20,
                    max: 30,
                    labelsEnabled: false,
                    visible: false,
                },
            });

            const expectedResult = {
                ...result.yAxis[0],
                min: 20,
                max: 30,
                labels: {
                    ...result.yAxis[0].labels,
                    enabled: false,
                },
                title: {
                    ...result.yAxis[0].title,
                    enabled: false,
                    text: "",
                },
            };
            expect(result.yAxis[0]).toEqual(expectedResult);
        });

        it("should set X axis configurations from properties", () => {
            const result = getCustomizedConfiguration({
                ...chartOptions,
                xAxisProps: {
                    visible: false,
                    labelsEnabled: false,
                    rotation: "60",
                },
            });

            const expectedResult = {
                ...result.xAxis[0],
                title: {
                    ...result.xAxis[0].title,
                    enabled: false,
                    text: "",
                },
                labels: {
                    ...result.xAxis[0].labels,
                    enabled: false,
                    rotation: -60,
                },
            };

            expect(result.xAxis[0]).toEqual(expectedResult);
        });

        it("should set X axis configurations with style", () => {
            const result = getCustomizedConfiguration(chartOptions);
            expect(result.xAxis[0].title.style).toEqual({
                color: "#6d7680",
                font: '14px Avenir, "Helvetica Neue", Arial, sans-serif',
                textOverflow: "ellipsis",
            });
        });

        it("should enable axis label for scatter plot when x and y are not set", () => {
            const result = getCustomizedConfiguration({
                ...chartOptions,
                type: VisualizationTypes.SCATTER,
            });

            const expectedXAxisResult = {
                ...result.xAxis[0],
                labels: {
                    ...result.xAxis[0].labels,
                    enabled: true,
                },
            };
            const expectedYAxisResult = {
                ...result.yAxis[0],
                labels: {
                    ...result.yAxis[0].labels,
                    enabled: true,
                },
            };

            expect(result.xAxis[0]).toEqual(expectedXAxisResult);
            expect(result.yAxis[0]).toEqual(expectedYAxisResult);
        });

        it("should disable xAxis labels when x axis is disabled in scatter", () => {
            const result = getCustomizedConfiguration({
                ...chartOptions,
                xAxisProps: {
                    visible: false,
                },
                type: VisualizationTypes.SCATTER,
            });

            const expectedXAxisResult = {
                ...result.xAxis[0],
                labels: {
                    ...result.xAxis[0].labels,
                    enabled: false,
                },
            };
            const expectedYAxisResult = {
                ...result.yAxis[0],
                labels: {
                    ...result.yAxis[0].labels,
                    enabled: true,
                },
            };

            expect(result.xAxis[0]).toEqual(expectedXAxisResult);
            expect(result.yAxis[0]).toEqual(expectedYAxisResult);
        });

        it("should disable labels when labels are disabled in bubble", () => {
            const result = getCustomizedConfiguration({
                ...chartOptions,
                xAxisProps: {
                    labelsEnabled: false,
                },
                type: VisualizationTypes.BUBBLE,
            });

            const expectedXAxisResult = {
                ...result.xAxis[0],
                labels: {
                    ...result.xAxis[0].labels,
                    enabled: false,
                },
            };
            const expectedYAxisResult = {
                ...result.yAxis[0],
                labels: {
                    ...result.yAxis[0].labels,
                    enabled: true,
                },
            };

            expect(result.xAxis[0]).toEqual(expectedXAxisResult);
            expect(result.yAxis[0]).toEqual(expectedYAxisResult);
        });

        it("should enable labels for heatmap when categories are not empty", () => {
            const result = getCustomizedConfiguration({
                ...chartOptions,
                data: {
                    ...chartOptions.data,
                    categories: ["c1", "c2"],
                },
                type: VisualizationTypes.HEATMAP,
            });

            const expectedXAxisResult = {
                ...result.xAxis[0],
                labels: {
                    ...result.xAxis[0].labels,
                    enabled: true,
                },
            };
            const expectedYAxisResult = {
                ...result.yAxis[0],
                labels: {
                    ...result.yAxis[0].labels,
                    enabled: true,
                },
            };

            expect(result.xAxis[0]).toEqual(expectedXAxisResult);
            expect(result.yAxis[0]).toEqual(expectedYAxisResult);
        });

        it("should disable lables for heatmap when categories are empty", () => {
            const result = getCustomizedConfiguration({
                ...chartOptions,
                data: {
                    ...chartOptions.data,
                    categories: [],
                },
                type: VisualizationTypes.HEATMAP,
            });

            const expectedXAxisResult = {
                ...result.xAxis[0],
                labels: {
                    ...result.xAxis[0].labels,
                    enabled: false,
                },
            };
            const expectedYAxisResult = {
                ...result.yAxis[0],
                labels: {
                    ...result.yAxis[0].labels,
                    enabled: false,
                },
            };

            expect(result.xAxis[0]).toEqual(expectedXAxisResult);
            expect(result.yAxis[0]).toEqual(expectedYAxisResult);
        });

        it("should set extremes for y axis when x axis scale changed", () => {
            const result = getCustomizedConfiguration({
                ...chartOptions,
                xAxisProps: {
                    min: 20,
                    max: 30,
                },
            });

            const expectedPlotOptions = {
                getExtremesFromAll: true,
            };
            expect(result.plotOptions.series).toEqual(expectedPlotOptions);
        });

        it("should set axis line width to 1 in scatter plot when axis is enabled", () => {
            const result = getCustomizedConfiguration({
                ...chartOptions,
                yAxisProps: {
                    visible: true,
                },
                xAxisProps: {
                    visible: true,
                },
                type: VisualizationTypes.SCATTER,
            });

            expect(result.xAxis[0].lineWidth).toEqual(1);
            expect(result.yAxis[0].lineWidth).toEqual(1);
        });

        it("should not set axis line when in column and axis is enabled", () => {
            const result = getCustomizedConfiguration({
                ...chartOptions,
                yAxisProps: {
                    visible: true,
                },
                xAxisProps: {
                    visible: true,
                },
                type: VisualizationTypes.COLUMN,
            });

            expect(result.xAxis[0].lineWidth).toBeUndefined();
            expect(result.yAxis[0].lineWidth).toBeUndefined();
        });

        it("should set axis line width to 0 when axis is disabled", () => {
            const result = getCustomizedConfiguration({
                ...chartOptions,
                yAxisProps: {
                    visible: false,
                },
                xAxisProps: {
                    visible: false,
                },
            });

            expect(result.xAxis[0].lineWidth).toEqual(0);
            expect(result.yAxis[0].lineWidth).toEqual(0);
        });
    });

    describe("gridline configuration", () => {
        it("should set gridline width to 0 when grid is disabled", () => {
            const result = getCustomizedConfiguration({ ...chartOptions, grid: { enabled: false } });
            expect(result.yAxis[0].gridLineWidth).toEqual(0);
        });

        it("should set gridline width on xAxis on 1 for Scatterplot when enabled", () => {
            const customConfig = { grid: { enabled: true }, type: VisualizationTypes.SCATTER };
            const result = getCustomizedConfiguration({ ...chartOptions, ...customConfig });
            expect(result.xAxis[0].gridLineWidth).toEqual(1);
        });

        it("should set gridline width on xAxis on 1 for Bubblechart when enabled", () => {
            const customConfig = { grid: { enabled: true }, type: VisualizationTypes.BUBBLE };
            const result = getCustomizedConfiguration({ ...chartOptions, ...customConfig });
            expect(result.xAxis[0].gridLineWidth).toEqual(1);
        });

        it("should set gridline width on xAxis on 0 for Scatterplot when disabled", () => {
            const customConfig = { grid: { enabled: false }, type: VisualizationTypes.SCATTER };
            const result = getCustomizedConfiguration({ ...chartOptions, ...customConfig });
            expect(result.xAxis[0].gridLineWidth).toEqual(0);
        });

        it("should set gridline width on xAxis on 0 for Bubblechart when disabled", () => {
            const customConfig = { grid: { enabled: false }, type: VisualizationTypes.BUBBLE };
            const result = getCustomizedConfiguration({ ...chartOptions, ...customConfig });
            expect(result.xAxis[0].gridLineWidth).toEqual(0);
        });
    });

    describe("labels configuration", () => {
        it("should set two levels labels for multi-level treemap", () => {
            const result = getCustomizedConfiguration({
                ...chartOptions,
                type: VisualizationTypes.TREEMAP,
                stacking: "normal",
            });

            const treemapConfig = result.plotOptions.treemap;
            expect(treemapConfig.levels.length).toEqual(2);
        });

        it("should set one level labels for single-level treemap", () => {
            const result = getCustomizedConfiguration({
                ...chartOptions,
                type: VisualizationTypes.TREEMAP,
                stacking: null,
            });

            const treemapConfig = result.plotOptions.treemap;
            expect(treemapConfig.levels.length).toEqual(1);
        });

        it("should set global HCH dataLabels config according user config for treemap", () => {
            const result = getCustomizedConfiguration(
                {
                    ...chartOptions,
                    type: VisualizationTypes.TREEMAP,
                    stacking: null,
                },
                {
                    dataLabels: {
                        visible: true,
                    },
                },
            );

            const treemapConfig = result.plotOptions.treemap;
            expect(treemapConfig.dataLabels).toEqual({
                allowOverlap: true,
                enabled: true,
            });
        });

        describe("bubble dataLabels formatter", () => {
            function setMinMax(
                obj: any,
                xAxisMin: number,
                xAxisMax: number,
                yAxisMin: number,
                yAxisMax: number,
            ) {
                set(obj, "series.xAxis.min", xAxisMin);
                set(obj, "series.xAxis.max", xAxisMax);
                set(obj, "series.yAxis.min", yAxisMin);
                set(obj, "series.yAxis.max", yAxisMax);
            }

            function setPoint(obj: any, x: number, y: number, z: number) {
                set(obj, "x", x);
                set(obj, "y", y);
                set(obj, "point.z", z);
            }

            it("should draw label when bubble is inside chart area", () => {
                const result = getCustomizedConfiguration(
                    {
                        ...chartOptions,
                        type: VisualizationTypes.BUBBLE,
                    },
                    {
                        dataLabels: {
                            visible: true,
                        },
                    },
                );

                setMinMax(result, 0, 10, 0, 10);
                setPoint(result, 5, 10, 5);

                const bubbleFormatter = get(result, "plotOptions.bubble.dataLabels.formatter", noop).bind(
                    result,
                );

                expect(bubbleFormatter()).toEqual("5");
            });

            it("should not draw dataLabel when label is outside chart area", () => {
                const result = getCustomizedConfiguration(
                    {
                        ...chartOptions,
                        type: VisualizationTypes.BUBBLE,
                    },
                    {
                        dataLabels: {
                            visible: true,
                        },
                    },
                );

                setMinMax(result, 0, 10, 0, 10);
                setPoint(result, 5, 11, 5);

                const bubbleFormatter = get(result, "plotOptions.bubble.dataLabels.formatter", noop).bind(
                    result,
                );

                expect(bubbleFormatter()).toEqual(null);
            });

            it("should show data label when min and max are not defined", () => {
                const result = getCustomizedConfiguration(
                    {
                        ...chartOptions,
                        type: VisualizationTypes.BUBBLE,
                    },
                    {
                        dataLabels: {
                            visible: true,
                        },
                    },
                );

                setPoint(result, 5, 11, 5);

                const bubbleFormatter = get(result, "plotOptions.bubble.dataLabels.formatter", noop).bind(
                    result,
                );

                expect(bubbleFormatter()).toEqual("5");
            });
        });
    });

    describe("tooltip followPointer", () => {
        // convert [bar, column, combo] to [ [bar] , [column] , [combo] ]
        const CHART_TYPES = supportedTooltipFollowPointerChartTypes.map(
            (chartType: string): string[] => [chartType],
        );

        it.each(CHART_TYPES)(
            "should follow pointer for %s chart when data max is above axis max",
            (chartType: string) => {
                const result = getCustomizedConfiguration({
                    ...chartOptions,
                    actions: { tooltip: true },
                    data: getData([{ y: 100 }, { y: 101 }]),
                    type: chartType,
                    yAxisProps: {
                        max: 50,
                    },
                });

                expect(result.tooltip.followPointer).toBeTruthy();
            },
        );

        it.each(CHART_TYPES)(
            "should not follow pointer for %s chart when data max is below axis max",
            (chartType: string) => {
                const result = getCustomizedConfiguration({
                    ...chartOptions,
                    actions: { tooltip: true },
                    data: getData([{ y: 0 }, { y: 1 }]),
                    type: chartType,
                    yAxisProps: {
                        max: 50,
                    },
                });

                expect(result.tooltip.followPointer).toBeFalsy();
            },
        );

        it("should follow pointer for pie chart should be false by default", () => {
            const result = getCustomizedConfiguration({
                ...chartOptions,
                actions: { tooltip: true },
                data: getData([{ y: 100 }, { y: 101 }]),
                type: VisualizationTypes.PIE,
                yAxisProps: {
                    max: 50,
                },
            });

            expect(result.tooltip.followPointer).toBeFalsy();
        });
    });

    describe("tooltip position", () => {
        it("should be positioned by absolute position", () => {
            const highchartContext: any = {
                chart: {
                    plotLeft: 0,
                    plotTop: 0,
                    container: {
                        getBoundingClientRect: jest.fn().mockReturnValue({
                            top: 10,
                            left: 20,
                        }),
                    },
                },
            };
            const mockDataPoint: IPointData = {
                negative: false,
                plotX: 50,
                plotY: 50,
                h: 10,
            };
            // array: [chartType, stacking, labelWidth, labelHeight, point]
            let mockChartParameters = ["bar", false, 10, 10, mockDataPoint];

            // use .apply function here to mock for tooltip callback from highchart
            let relativePosition = getTooltipPositionInChartContainer.apply(
                highchartContext,
                mockChartParameters,
            );
            expect(relativePosition).toEqual({ x: 45, y: 35 });

            let position = getTooltipPositionInViewPort.apply(highchartContext, mockChartParameters);
            // offset: pageOffset + containerPosition - highchart off set
            // bar
            expect(position).toEqual({
                x: 15, // 0 + 20 - 5
                y: 2 + relativePosition.y, // 0 + 10 -8 + 35
            });

            // line
            mockChartParameters = ["line", false, 10, 10, mockDataPoint];
            relativePosition = getTooltipPositionInChartContainer.apply(
                highchartContext,
                mockChartParameters,
            );
            expect(relativePosition).toEqual({ x: 45, y: 26 });
            position = getTooltipPositionInViewPort.apply(highchartContext, mockChartParameters);
            expect(position).toEqual({
                x: 4, // 0 + 20 - 16
                y: -6 + relativePosition.y, // 0 + 10 - 16 + 26
            });
        });

        it("should limit top position of tooltip to keep tooltip in viewport", () => {
            const highchartContext: any = {
                chart: {
                    plotLeft: 0,
                    plotTop: 0,
                    container: {
                        getBoundingClientRect: jest.fn().mockReturnValue({
                            top: 0,
                            left: 0,
                        }),
                    },
                },
            };
            const mockDataPoint: IPointData = {
                negative: false,
                plotX: 0,
                plotY: 0,
                h: 10,
            };
            // array: [chartType, stacking, labelWidth, labelHeight, point]
            const mockChartParameters = ["bar", false, 100, 100, mockDataPoint];

            // use .apply function here to mock for tooltip callback from highchart
            const position = getTooltipPositionInViewPort.apply(highchartContext, mockChartParameters);

            expect(position).toMatchObject({
                y: TOOLTIP_VIEWPORT_MARGIN_TOP - TOOLTIP_PADDING,
            });
        });
    });

    describe("format data labels", () => {
        const getDataLabelPoint = (opposite = false, axisNumber = 1) => ({
            y: 1000,
            percentage: 55.55,
            series: {
                chart: {
                    yAxis: Array(axisNumber).fill({}),
                },
                yAxis: {
                    opposite,
                },
            },
            point: {
                format: "#,##0.00",
            },
        });

        it("should return number for not supported chart", () => {
            const chartOptions = { type: VisualizationTypes.LINE, yAxes: [{}] };
            const configuration = getCustomizedConfiguration(chartOptions);
            const formatter = get(configuration, "plotOptions.bar.dataLabels.formatter", noop);
            const dataLabelPoint = getDataLabelPoint();
            const dataLabel = formatter.call(dataLabelPoint);
            expect(dataLabel).toBe("1,000.00");
        });

        it.each([
            ["should return number for single axis chart without 'Stack to 100%'", 1],
            ["should return number for dual axis chart without 'Stack to 100%'", 2],
        ])("%s", (_description: string, axisNumber: number) => {
            const chartOptions = { type: VisualizationTypes.COLUMN, yAxes: Array(axisNumber).fill({}) };
            const configuration = getCustomizedConfiguration(chartOptions);
            const formatter = get(configuration, "plotOptions.bar.dataLabels.formatter", noop);
            const dataLabelPoint = getDataLabelPoint(false, axisNumber);
            const dataLabel = formatter.call(dataLabelPoint);
            expect(dataLabel).toBe("1,000.00");
        });

        it.each([
            ["should return percentage for left single axis chart with 'Stack to 100%'", false, 1, "55.55%"],
            ["should return percentage for right single axis chart with 'Stack to 100%'", true, 1, "55.55%"],
            [
                "should return percentage for primary axis for dual chart with 'Stack to 100%'",
                false,
                2,
                "55.55%",
            ],
            [
                "should return number for secondary axis for dual chart with 'Stack to 100%'",
                true,
                2,
                "1,000.00",
            ],
        ])("%s", (_description: string, opposite: boolean, axisNumber: number, expectation: string) => {
            const chartOptions = { type: VisualizationTypes.COLUMN, yAxes: Array(axisNumber).fill({}) };
            const config = { stackMeasuresToPercent: true };
            const configuration = getCustomizedConfiguration(chartOptions, config);
            const formatter = get(configuration, "plotOptions.bar.dataLabels.formatter", noop);
            const dataLabelPoint = getDataLabelPoint(opposite, axisNumber);
            const dataLabel = formatter.call(dataLabelPoint);
            expect(dataLabel).toBe(expectation);
        });

        describe("percentage data label formatter", () => {
            it("should return null with empty configuration", () => {
                const result = percentageDataLabelFormatter.call({});
                expect(result).toBeNull();
            });

            it("should return default data labels format with undefined percentage", () => {
                const getDataLabelPoint = {
                    y: 1000,
                    series: {
                        chart: {
                            yAxis: [{}],
                        },
                        yAxis: {
                            opposite: true,
                        },
                    },
                    point: {
                        format: "#,##0.00",
                    },
                };
                const result = percentageDataLabelFormatter.call(getDataLabelPoint);
                expect(result).toEqual("1,000.00");
            });

            it("should format data labels to percentage for single axis chart implicitly", () => {
                const dataLabel = {
                    percentage: 55.55,
                };
                const result = percentageDataLabelFormatter.call(dataLabel);
                expect(result).toBe("55.55%");
            });

            it.each([["left", false], ["right", true]])(
                "should format data labels to percentage for %s single axis chart",
                (_: string, opposite: boolean) => {
                    const dataLabel = {
                        percentage: 55.55,
                        series: {
                            chart: {
                                yAxis: [{}],
                            },
                            yAxis: {
                                opposite,
                            },
                        },
                    };
                    const result = percentageDataLabelFormatter.call(dataLabel);
                    expect(result).toBe("55.55%");
                },
            );

            it.each([["", "primary", false, "55.55%"], [" not", "secondary", true, "123"]])(
                "should %s format data labels to percentage for dual axis chart on %s axis",
                (_negation: string, _axis: string, opposite: boolean, expected: string) => {
                    const dataLabel = {
                        percentage: 55.55,
                        y: 123,
                        series: {
                            chart: {
                                yAxis: [{}, {}],
                            },
                            yAxis: {
                                opposite,
                            },
                        },
                    };
                    const result = percentageDataLabelFormatter.call(dataLabel);
                    expect(result).toBe(expected);
                },
            );
        });
    });

    describe("get X axis with drill config", () => {
        const chartTypes = supportedStackingAttributesChartTypes.map((chartType: string) => [chartType]);

        const afm: AFM.IAfm = {
            attributes: [],
            measures: [],
            filters: [],
        };
        const drillConfig: IDrillConfig = {
            afm,
            onFiredDrillEvent: () => false,
        };

        it.each(chartTypes)('should set "drillConfig" to xAxis to %s chart', (chartType: string) => {
            const result = getCustomizedConfiguration(
                { type: chartType, data: { series: [] } },
                {},
                drillConfig,
            );
            expect(result.xAxis[0].drillConfig).toEqual(drillConfig);
        });

        it('should not set "drillConfig" to unsupported chart type', () => {
            const result = getCustomizedConfiguration({ type: VisualizationTypes.LINE }, {}, drillConfig);
            expect(result.xAxis[0].drillConfig).toBeFalsy();
        });
    });

    describe("get target cursor for bullet chart", () => {
        const chartOptionsWithDrillableTarget: IChartOptions = {
            type: "bullet",
            data: {
                series: [
                    {
                        data: [],
                        isDrillable: false,
                    },
                    {
                        data: [],
                        isDrillable: true,
                    },
                    {
                        type: "bullet",
                        data: [],
                        isDrillable: true,
                    },
                ],
            },
        };
        const chartOptionsWithNonDrillableTarget: IChartOptions = {
            type: "bullet",
            data: {
                series: [
                    {
                        data: [],
                        isDrillable: false,
                    },
                    {
                        data: [],
                        isDrillable: true,
                    },
                    {
                        type: "bullet",
                        data: [],
                        isDrillable: false,
                    },
                ],
            },
        };

        it("should set the target cursor to pointer if the target is drillable", () => {
            const result = getCustomizedConfiguration(chartOptionsWithDrillableTarget);
            expect(result.plotOptions.bullet.cursor).toBe("pointer");
        });

        it("should not set the target cursor to pointer if the target is not drillable", () => {
            const result = getCustomizedConfiguration(chartOptionsWithNonDrillableTarget);
            expect(result.plotOptions.bullet).toBe(undefined);
        });
    });
});

describe("escapeCategories", () => {
    it("should escape string categories", () => {
        const categories = escapeCategories(["cat1", "<cat2/>", "<cat3></cat3>"]);
        expect(categories).toEqual(["cat1", "&lt;cat2/&gt;", "&lt;cat3&gt;&lt;/cat3&gt;"]);
    });

    it("should escape object categories", () => {
        const categories = escapeCategories([
            {
                name: "Status",
                categories: ["cat1", "<cat2/>", "<cat3></cat3>"],
            },
            {
                name: "<span>Sales</span>",
                categories: ["<div>sale1</div>", "<sale2/>", "<sale3></sale3>"],
            },
        ]);
        expect(categories).toEqual([
            {
                name: "Status",
                categories: ["cat1", "&lt;cat2/&gt;", "&lt;cat3&gt;&lt;/cat3&gt;"],
            },
            {
                name: "&lt;span&gt;Sales&lt;/span&gt;",
                categories: ["&lt;div&gt;sale1&lt;/div&gt;", "&lt;sale2/&gt;", "&lt;sale3&gt;&lt;/sale3&gt;"],
            },
        ]);
    });
});
