import { CellErrorType, CommandResult, Model } from "../../../../src";
import { deepCopy } from "../../../../src/helpers/misc";
import { ClipboardStore } from "../../../../src/stores/clipboard_store";
import { ViewportsStore } from "../../../../src/stores/viewports_store";
import {
  GaugeChartDefinition,
  GaugeChartRuntime,
  SectionRule,
} from "../../../../src/types/chart/gauge_chart";
import { GENERAL_CHART_CREATION_CONTEXT } from "../../../test_helpers/chart_helpers";
import {
  activateSheet,
  addColumns,
  copy,
  createChartDefinitionFromContext,
  createGaugeChart,
  createNamedRange,
  createSheet,
  deleteFigure,
  deleteSheet,
  duplicateSheet,
  paste,
  redo,
  renameSheet,
  selectFigure,
  setCellContent,
  setFormat,
  undo,
  updateChart,
  updateNamedRange,
} from "../../../test_helpers/commands_helpers";
import { makeStoreWithModel } from "../../../test_helpers/stores";

let model: Model;

const lowerColor = "#6aa84f";
const middleColor = "#f1c232";
const upperColor = "#cc0000";

const defaultSectionRule: SectionRule = {
  rangeMin: "0",
  rangeMax: "100",
  colors: {
    lowerColor,
    middleColor,
    upperColor,
  },
  lowerInflectionPoint: {
    type: "number",
    value: "33",
    operator: "<=",
  },
  upperInflectionPoint: {
    type: "number",
    value: "66",
    operator: "<=",
  },
};

const randomSectionRule: SectionRule = {
  rangeMin: "-200",
  rangeMax: "500",
  colors: {
    lowerColor: "#111111",
    middleColor: "#999999",
    upperColor: "#dddddd",
  },
  lowerInflectionPoint: {
    type: "percentage",
    value: "50",
    operator: "<=",
  },
  upperInflectionPoint: {
    type: "number",
    value: "70",
    operator: "<=",
  },
};

beforeEach(() => {
  model = new Model();
  makeStoreWithModel(model, ClipboardStore);
});

describe("datasource tests", function () {
  test("create a gauge chart", () => {
    createGaugeChart(
      model,
      {
        dataRange: "B8",
        title: { text: "Title" },
        sectionRule: randomSectionRule,
      },
      "chartId"
    );
    expect(model.getters.getChartDefinition("chartId") as GaugeChartDefinition).toMatchObject({
      dataRange: "B8",
      type: "gauge",
      title: { text: "Title" },
      sectionRule: randomSectionRule,
    });
    expect(model.getters.getChartRuntime("chartId") as GaugeChartRuntime).toMatchSnapshot();
  });

  test("create empty gauge chart", () => {
    createGaugeChart(model, { dataRange: "A1" }, "chartId");
    expect(model.getters.getChartDefinition("chartId") as GaugeChartDefinition).toMatchObject({
      type: "gauge",
      dataRange: "A1",
      title: { text: "" },
      sectionRule: defaultSectionRule,
    });
    expect(model.getters.getChartRuntime("chartId") as GaugeChartRuntime).toMatchSnapshot();
  });

  test("create gauge from creation context", () => {
    const definition = createChartDefinitionFromContext("gauge", GENERAL_CHART_CREATION_CONTEXT);
    expect(definition).toEqual({
      type: "gauge",
      background: "#123456",
      title: { text: "hello there" },
      dataRange: "Sheet1!B1:B4",
      sectionRule: expect.any(Object),
      humanize: false,
      annotationText: "This is an annotation text",
      annotationLink: "https://www.odoo.com",
    });
  });

  describe("Gauge ranges are adapted", () => {
    beforeEach(() => {
      createSheet(model, { sheetId: "Sheet2", name: "Sheet2" });
      createGaugeChart(
        model,
        {
          dataRange: "Sheet1!B1:B4",
          sectionRule: {
            ...randomSectionRule,
            rangeMin: "=A1+5",
            rangeMax: "=C8",
            lowerInflectionPoint: { operator: "<", type: "percentage", value: "=Sheet2!A2" },
            upperInflectionPoint: { operator: "<", type: "number", value: "=SUM(Sheet1!B1:C4)" },
          },
        },
        "chartId"
      );
    });

    test("ranges in gauge definition change automatically", () => {
      addColumns(model, "before", "A", 2);
      const chart = model.getters.getChartDefinition("chartId") as GaugeChartDefinition;
      expect(chart.dataRange).toStrictEqual("Sheet1!D1:D4");
      expect(chart.sectionRule).toMatchObject({
        rangeMin: "=C1+5",
        rangeMax: "=E8",
        lowerInflectionPoint: { operator: "<", type: "percentage", value: "=Sheet2!A2" },
        upperInflectionPoint: { operator: "<", type: "number", value: "=SUM(Sheet1!D1:E4)" },
      });
    });

    test("copying a gauge chart in another sheet keep the ranges referencing to the same sheet", () => {
      const figureId = model.getters.getFigureIdFromChartId("chartId")!;
      selectFigure(model, figureId);
      copy(model);

      activateSheet(model, "Sheet2");
      paste(model, "A1");

      const copiedChartId = model.getters.getChartIds("Sheet2")[0];
      const chart = model.getters.getChartDefinition(copiedChartId) as GaugeChartDefinition;
      expect(chart.dataRange).toStrictEqual("Sheet1!B1:B4");
      expect(chart.sectionRule).toMatchObject({
        rangeMin: "=Sheet1!A1+5",
        rangeMax: "=Sheet1!C8",
        lowerInflectionPoint: { operator: "<", type: "percentage", value: "=Sheet2!A2" },
        upperInflectionPoint: { operator: "<", type: "number", value: "=SUM(Sheet1!B1:C4)" },
      });
    });

    test("gauge ranges are adapted when duplicating a sheet", () => {
      duplicateSheet(model, "Sheet1", "Sheet3");
      const duplicatedChartId = model.getters.getChartIds("Sheet3")[0];
      const chart = model.getters.getChartDefinition(duplicatedChartId) as GaugeChartDefinition;
      expect(chart.dataRange).toStrictEqual("'Copy of Sheet1'!B1:B4");
      expect(chart.sectionRule).toMatchObject({
        rangeMin: "=A1+5",
        rangeMax: "=C8",
        lowerInflectionPoint: { type: "percentage", value: "=Sheet2!A2" },
        upperInflectionPoint: { type: "number", value: "=SUM('Copy of Sheet1'!B1:C4)" },
      });
    });

    test("gauge range are adapted when renaming the sheet", () => {
      renameSheet(model, "Sheet2", "Boom");
      let chart = model.getters.getChartDefinition("chartId") as GaugeChartDefinition;
      expect(chart.sectionRule).toMatchObject({
        lowerInflectionPoint: { operator: "<", type: "percentage", value: "=Boom!A2" },
      });

      renameSheet(model, "Sheet1", "Magic");
      chart = model.getters.getChartDefinition("chartId") as GaugeChartDefinition;
      expect(chart.dataRange).toStrictEqual("Magic!B1:B4");
      expect(chart.sectionRule).toMatchObject({
        rangeMin: "=A1+5",
        rangeMax: "=C8",
        lowerInflectionPoint: { operator: "<", type: "percentage", value: "=Boom!A2" },
        upperInflectionPoint: { operator: "<", type: "number", value: "=SUM(Magic!B1:C4)" },
      });
    });

    test("gauge formulas are adapted when renaming a named range", () => {
      createNamedRange(model, "MyNamedRange", "A8");
      updateChart(model, "chartId", {
        sectionRule: {
          ...randomSectionRule,
          lowerInflectionPoint: { operator: "<", type: "number", value: "=MyNamedRange" },
        },
      });

      updateNamedRange(model, "MyNamedRange", "BoomNamedRange", "A8");
      const chart = model.getters.getChartDefinition("chartId") as GaugeChartDefinition;
      expect(chart.sectionRule).toMatchObject({
        lowerInflectionPoint: { operator: "<", type: "number", value: "=BoomNamedRange" },
      });
    });
  });

  test("can delete an imported gauge chart", () => {
    createGaugeChart(model, { dataRange: "B7:B8" }, "chartId", undefined, { figureId: "figureId" });
    const exportedData = model.exportData();
    const newModel = new Model(exportedData);
    const { store: viewStore } = makeStoreWithModel(newModel, ViewportsStore);
    expect(viewStore.visibleFigures).toHaveLength(1);
    expect(newModel.getters.getChartRuntime("chartId") as GaugeChartRuntime).toBeTruthy();
    deleteFigure(newModel, "figureId");
    expect(viewStore.visibleFigures).toHaveLength(0);
    expect(() => newModel.getters.getChartRuntime("chartId")).toThrow();
  });

  test("update gauge chart", () => {
    createGaugeChart(model, { dataRange: "B7:B8" }, "chartId");
    updateChart(model, "chartId", {
      dataRange: "A7",
      title: { text: "hello1" },
      sectionRule: randomSectionRule,
    });
    expect(model.getters.getChartDefinition("chartId") as GaugeChartDefinition).toMatchObject({
      dataRange: "A7",
      title: { text: "hello1" },
      sectionRule: randomSectionRule,
    });
  });

  test("Can use formulas as gauge chart values", () => {
    setCellContent(model, "A1", "42");
    setCellContent(model, "A2", "150");

    createGaugeChart(
      model,
      {
        dataRange: "A1",
        sectionRule: {
          rangeMin: "=0",
          rangeMax: "=A2 - 20",
          lowerInflectionPoint: {
            operator: "<=",
            value: "=SUM(10, 12)",
            type: "number",
          },
          upperInflectionPoint: {
            operator: "<=",
            value: "=DIVIDE(100, 2)",
            type: "percentage",
          },
          colors: {
            lowerColor: "#6aa84f",
            middleColor: "#f1c232",
            upperColor: "#cc0000",
          },
        },
      },
      "1"
    );
    const runtime = model.getters.getChartRuntime("1") as GaugeChartRuntime;

    expect(runtime.minValue).toMatchObject({ value: 0, label: "0" });
    expect(runtime.maxValue).toMatchObject({ value: 130, label: "130" });
    expect(runtime.inflectionValues).toMatchObject([
      { value: 22, label: "22", operator: "<=" },
      { value: 65, label: "65", operator: "<=" }, // 50% of 130
    ]);
  });

  test("create gauge chart with invalid ranges", () => {
    const result = createGaugeChart(model, { dataRange: "this is invalid" }, "chartId");
    expect(result).toBeCancelledBecause(CommandResult.InvalidGaugeDataRange);
  });

  describe("create gauge chart with invalid section rule", () => {
    let sectionRule: SectionRule;
    let model: Model;
    beforeEach(() => {
      sectionRule = deepCopy(defaultSectionRule);
      model = new Model();
    });

    test("empty rangeMin", async () => {
      sectionRule = { ...sectionRule, rangeMin: "" };
      const result = createGaugeChart(model, { dataRange: "A1", sectionRule }, "chartId");
      expect(result).toBeCancelledBecause(CommandResult.EmptyGaugeRangeMin);
    });

    test("NaN rangeMin", async () => {
      sectionRule = { ...sectionRule, rangeMin: "I'm not a number" };
      const result = createGaugeChart(model, { dataRange: "A1", sectionRule }, "chartId");
      expect(result).toBeCancelledBecause(CommandResult.GaugeRangeMinNaN);
    });

    test("Invalid rangeMin formula value", () => {
      sectionRule = { ...sectionRule, rangeMin: '=CONCAT("hello", "there")' };
      const result = createGaugeChart(model, { dataRange: "A1", sectionRule }, "1");
      expect(result).toBeSuccessfullyDispatched();
      expect(model.getters.getChartRuntime("1")).toMatchObject({
        minValue: { value: 0, label: "" },
        maxValue: { value: 100, label: "" },
        gaugeValue: { value: 0, label: CellErrorType.GenericError },
        inflectionValues: [],
        colors: [],
      });
    });

    test("empty rangeMax", async () => {
      sectionRule = { ...sectionRule, rangeMax: "" };
      const result = createGaugeChart(model, { dataRange: "A1", sectionRule }, "chartId");
      expect(result).toBeCancelledBecause(CommandResult.EmptyGaugeRangeMax);
    });

    test("NaN rangeMax", async () => {
      sectionRule = { ...sectionRule, rangeMax: "I'm not a number" };
      const result = createGaugeChart(model, { dataRange: "A1", sectionRule }, "chartId");
      expect(result).toBeCancelledBecause(CommandResult.GaugeRangeMaxNaN);
    });

    test("Invalid rangeMin formula value", () => {
      sectionRule = { ...sectionRule, rangeMax: "=)))(((invalid formula)))" };
      const result = createGaugeChart(model, { dataRange: "A1", sectionRule }, "1");
      expect(result).toBeSuccessfullyDispatched();
      expect(model.getters.getChartRuntime("1")).toMatchObject({
        minValue: { value: 0, label: "" },
        maxValue: { value: 100, label: "" },
        gaugeValue: { value: 0, label: CellErrorType.GenericError },
        inflectionValues: [],
        colors: [],
      });
    });

    test("NaN LowerInflectionPoint", async () => {
      sectionRule = {
        ...sectionRule,
        lowerInflectionPoint: { ...sectionRule.lowerInflectionPoint, value: "I'm not a number" },
      };
      const result = createGaugeChart(model, { dataRange: "A1", sectionRule }, "chartId");
      expect(result).toBeCancelledBecause(CommandResult.GaugeLowerInflectionPointNaN);
    });

    test("Invalid formula LowerInflectionPoint", () => {
      sectionRule = {
        ...sectionRule,
        lowerInflectionPoint: { ...sectionRule.lowerInflectionPoint, value: '=TRIM("hello")' },
      };
      const result = createGaugeChart(model, { dataRange: "A1", sectionRule }, "1");
      expect(result).toBeSuccessfullyDispatched();
      const runtime = model.getters.getChartRuntime("1") as GaugeChartRuntime;
      expect(runtime.inflectionValues).toHaveLength(1); // only the upper inflection point is valid and kept
    });

    test("NaN UpperInflectionPoint", async () => {
      sectionRule = {
        ...sectionRule,
        upperInflectionPoint: {
          ...sectionRule.upperInflectionPoint,
          value: "I'm not a number",
        },
      };
      const result = createGaugeChart(model, { dataRange: "A1", sectionRule }, "chartId");
      expect(result).toBeCancelledBecause(CommandResult.GaugeUpperInflectionPointNaN);
    });

    test("Invalid formula UpperInflectionPoint", () => {
      sectionRule = {
        ...sectionRule,
        upperInflectionPoint: {
          ...sectionRule.upperInflectionPoint,
          value: '=CONCAT("hello", " there")',
        },
      };
      const result = createGaugeChart(model, { dataRange: "A1", sectionRule }, "1");
      expect(result).toBeSuccessfullyDispatched();
      const runtime = model.getters.getChartRuntime("1") as GaugeChartRuntime;
      expect(runtime.inflectionValues).toHaveLength(1); // only the lower inflection point is valid and kept
    });

    test("accepts sectionRule formulas returning a 1x1 matrix", () => {
      setCellContent(model, "A1", "10");

      sectionRule = {
        ...sectionRule,
        rangeMin: '=IF(TRUE, A1, "something else")',
        rangeMax: '=IF(TRUE, A1, "something else")',
      };
      const result = createGaugeChart(model, { dataRange: "A1", sectionRule }, "1");
      expect(result).toBeSuccessfullyDispatched();

      const runtime = model.getters.getChartRuntime("1") as GaugeChartRuntime;
      expect(runtime.minValue).toMatchObject({ value: 10 });
      expect(runtime.maxValue).toMatchObject({ value: 10 });
    });
  });

  test("Gauge Chart is deleted on sheet deletion", () => {
    createSheet(model, { sheetId: "sheet2", position: 1 });
    createGaugeChart(model, { dataRange: "Sheet1!B1:B4" }, "chartId", "sheet2");
    expect(model.getters.getChartRuntime("chartId") as GaugeChartRuntime).not.toBeUndefined();
    deleteSheet(model, "sheet2");
    expect(() => model.getters.getChartRuntime("chartId")).toThrow();
  });

  test("Gauge chart is copied on sheet duplication", () => {
    const firstSheetId = model.getters.getActiveSheetId();
    const secondSheetId = "sheet2";
    createGaugeChart(
      model,
      {
        title: { text: "test" },
        dataRange: "B1:B4",
        sectionRule: randomSectionRule,
      },
      firstSheetId
    );
    const figure = model.getters.getFigures(firstSheetId)[0];
    duplicateSheet(model, firstSheetId, secondSheetId);

    expect(model.getters.getFigures(secondSheetId)).toHaveLength(1);
    const duplicatedFigure = model.getters.getFigures(secondSheetId)[0];
    const duplicatedChartId = model.getters.getChartIds(secondSheetId)[0];
    const duplicatedChart = model.getters
      .getChart(duplicatedChartId)
      ?.getDefinition() as GaugeChartDefinition;

    expect(duplicatedChart.title.text).toEqual("test");
    expect(duplicatedChart.dataRange).toEqual("B1:B4");

    expect(duplicatedFigure).toMatchObject({ ...figure, id: expect.any(String) });
    expect(duplicatedFigure.id).not.toBe(figure?.id);
    // duplicated chart is not deleted if original sheet is deleted
    deleteSheet(model, firstSheetId);
    expect(model.getters.getSheetIds()).toHaveLength(1);
    expect(model.getters.getFigures(secondSheetId)).toEqual([duplicatedFigure]);
  });
});

test("create a gauge chart with data from another sheet", () => {
  model = new Model();
  createSheet(model, { sheetId: "42", activate: true });
  createGaugeChart(model, { dataRange: "Sheet1!B1" }, "chartId");
  const chart = model.getters.getChartDefinition("chartId") as GaugeChartDefinition;
  expect(chart.dataRange).toEqual("Sheet1!B1");
});

describe("undo/redo", () => {
  test("undo/redo gauge chart creation", () => {
    const before = model.exportData();
    createGaugeChart(model, { dataRange: "Sheet1!B1:B4" });
    const after = model.exportData();
    undo(model);
    expect(model).toExport(before);
    redo(model);
    expect(model).toExport(after);
  });

  test("undo/redo gauge chart data rebuild the chart runtime", () => {
    createGaugeChart(model, { dataRange: "Sheet1!A2" }, "27");
    setCellContent(model, "A2", "99");
    let gaugeValue = (model.getters.getChartRuntime("27") as GaugeChartRuntime).gaugeValue;
    expect(gaugeValue?.value).toBe(99);
    setCellContent(model, "A2", "12");
    gaugeValue = (model.getters.getChartRuntime("27") as GaugeChartRuntime).gaugeValue;
    expect(gaugeValue?.value).toBe(12);
    undo(model);
    gaugeValue = (model.getters.getChartRuntime("27") as GaugeChartRuntime).gaugeValue;
    expect(gaugeValue?.value).toBe(99);
    redo(model);
    gaugeValue = (model.getters.getChartRuntime("27") as GaugeChartRuntime).gaugeValue;
    expect(gaugeValue?.value).toBe(12);
  });
});

describe("Chart design configuration", () => {
  let defaultChart: GaugeChartDefinition;
  let model: Model;

  beforeEach(() => {
    model = new Model();
    defaultChart = {
      background: "#ffffff",
      dataRange: "A1",
      title: { text: "My chart" },
      type: "gauge",
      sectionRule: deepCopy(defaultSectionRule),
    };
  });

  test("dataRange with a zero value", () => {
    setCellContent(model, "A1", "0");
    createGaugeChart(model, defaultChart, "chartId");
    const gaugeValue = (model.getters.getChartRuntime("chartId") as GaugeChartRuntime).gaugeValue;
    expect(gaugeValue?.value).toBe(0);
  });

  test("empty/NaN dataRange have undefined gauge value", () => {
    createGaugeChart(model, defaultChart, "chartId");
    let gaugeValue = (model.getters.getChartRuntime("chartId") as GaugeChartRuntime).gaugeValue;
    expect(gaugeValue).toBe(undefined);

    setCellContent(model, "A1", "I'm not a number");
    gaugeValue = (model.getters.getChartRuntime("chartId") as GaugeChartRuntime).gaugeValue;
    expect(gaugeValue).toBe(undefined);
  });

  test("empty dataRange --> gauge value is undefined", () => {
    createGaugeChart(model, defaultChart, "chartId");
    const gaugeValue = (model.getters.getChartRuntime("chartId") as GaugeChartRuntime).gaugeValue;
    expect(gaugeValue).toBe(undefined);
  });

  test("NaN dataRange --> gauge value is undefined", () => {
    setCellContent(model, "A1", "bla bla bla");
    createGaugeChart(model, defaultChart, "chartId");
    const gaugeValue = (model.getters.getChartRuntime("chartId") as GaugeChartRuntime).gaugeValue;
    expect(gaugeValue).toBe(undefined);
  });

  test("rangeMin and rangeMax are sorted in the runtime", async () => {
    const sectionRule = { ...defaultChart.sectionRule, rangeMin: "66", rangeMax: "33" };

    createGaugeChart(model, { sectionRule }, "chartId");
    const runtime = model.getters.getChartRuntime("chartId") as GaugeChartRuntime;
    expect(runtime.minValue).toMatchObject({ value: 33 });
    expect(runtime.maxValue).toMatchObject({ value: 66 });
  });

  test("Inflection point are sorted in the runtime", () => {
    const chart = {
      ...defaultChart,
      sectionRule: {
        ...defaultChart.sectionRule,
        lowerInflectionPoint: {
          ...defaultChart.sectionRule.lowerInflectionPoint,
          value: "66",
        },
        upperInflectionPoint: {
          ...defaultChart.sectionRule.upperInflectionPoint,
          value: "33",
        },
        colors: {
          lowerColor: "#f1c232",
          middleColor: "#cc0000",
          upperColor: "#6aa84f",
        },
      },
    };
    createGaugeChart(model, chart, "chartId");
    const runtime = model.getters.getChartRuntime("chartId") as GaugeChartRuntime;
    expect(runtime.colors).toEqual(["#cc0000", "#f1c232", "#6aa84f"]);
    expect(runtime.inflectionValues).toMatchObject([{ value: 33 }, { value: 66 }]);
  });

  test("Duplicated inflection points are filtered in the runtime", () => {
    const chart = {
      ...defaultChart,
      sectionRule: {
        ...defaultChart.sectionRule,
        lowerInflectionPoint: {
          ...defaultChart.sectionRule.lowerInflectionPoint,
          value: "66",
        },
        upperInflectionPoint: {
          ...defaultChart.sectionRule.upperInflectionPoint,
          value: "66",
        },
        colors: {
          lowerColor: "#6aa84f",
          middleColor: "#f1c232",
          upperColor: "#cc0000",
        },
      },
    };
    createGaugeChart(model, chart, "chartId");
    const runtime = model.getters.getChartRuntime("chartId") as GaugeChartRuntime;
    expect(runtime.colors).toEqual(["#6aa84f", "#cc0000"]);
    expect(runtime.inflectionValues).toMatchObject([{ value: 66 }]);
  });

  test("displayed values respect dataRange format", () => {
    setCellContent(model, "A1", "42");
    setFormat(model, "A1", "[$$]0.00");
    createGaugeChart(model, defaultChart, "chartId");
    const chart = model.getters.getChartRuntime("chartId") as GaugeChartRuntime;
    expect(chart.gaugeValue?.label).toBe("$42.00");
    expect(chart.inflectionValues[0].label).toBe("$33.00");
    expect(chart.inflectionValues[1].label).toBe("$66.00");
    expect(chart.minValue.label).toBe("$0.00");
    expect(chart.maxValue.label).toBe("$100.00");
  });

  test("data configuration include inflection point values", () => {
    defaultChart = {
      ...defaultChart,
      sectionRule: {
        ...defaultChart.sectionRule,
        rangeMax: "62",
        lowerInflectionPoint: {
          ...defaultChart.sectionRule.lowerInflectionPoint,
          value: "22",
        },
        upperInflectionPoint: {
          ...defaultChart.sectionRule.upperInflectionPoint,
          value: "42",
        },
      },
    };
    createGaugeChart(model, defaultChart, "chartId");
    const chart = model.getters.getChartRuntime("chartId") as GaugeChartRuntime;
    expect(chart.inflectionValues).toEqual([
      { value: 22, label: "22", operator: "<=" },
      { value: 42, label: "42", operator: "<=" },
    ]);
    expect(chart.colors).toStrictEqual([lowerColor, middleColor, upperColor]);
  });

  test("don't take into account inflection point if empty", () => {
    defaultChart = {
      ...defaultChart,
      sectionRule: {
        ...defaultChart.sectionRule,
        rangeMax: "100",
        lowerInflectionPoint: {
          ...defaultChart.sectionRule.lowerInflectionPoint,
          value: "",
        },
        upperInflectionPoint: {
          ...defaultChart.sectionRule.upperInflectionPoint,
          value: "66",
        },
      },
    };
    createGaugeChart(model, defaultChart, "chartId");
    let chart = model.getters.getChartRuntime("chartId") as GaugeChartRuntime;
    expect(chart.inflectionValues).toMatchObject([{ value: 66 }]);
    expect(chart.colors).toStrictEqual([middleColor, upperColor]);

    defaultChart = {
      ...defaultChart,
      sectionRule: {
        ...defaultChart.sectionRule,
        rangeMax: "100",
        lowerInflectionPoint: {
          ...defaultChart.sectionRule.lowerInflectionPoint,
          value: "33",
        },
        upperInflectionPoint: {
          ...defaultChart.sectionRule.upperInflectionPoint,
          value: "",
        },
      },
    };
    createGaugeChart(model, defaultChart, "chart2");
    chart = model.getters.getChartRuntime("chart2") as GaugeChartRuntime;
    expect(chart.inflectionValues).toMatchObject([{ value: 33 }]);
    expect(chart.colors).toStrictEqual([lowerColor, upperColor]);
  });

  test("scale inflection point value between rangeMin and rangeMax", () => {
    defaultChart = {
      ...defaultChart,
      sectionRule: {
        ...defaultChart.sectionRule,
        rangeMin: "100",
        rangeMax: "200",
        lowerInflectionPoint: {
          ...defaultChart.sectionRule.lowerInflectionPoint,
          value: "90",
        },
        upperInflectionPoint: {
          ...defaultChart.sectionRule.upperInflectionPoint,
          value: "200",
        },
      },
    };
    createGaugeChart(model, defaultChart, "chartId");
    const chart = model.getters.getChartRuntime("chartId") as GaugeChartRuntime;
    expect(chart.inflectionValues).toMatchObject([{ value: 100 }, { value: 200 }]);
    expect(chart.colors).toStrictEqual([lowerColor, middleColor, upperColor]);
  });

  test("Can have lesser than or lesser or equal than operators in thresholds", () => {
    defaultChart = {
      ...defaultChart,
      sectionRule: {
        ...defaultChart.sectionRule,
        lowerInflectionPoint: { type: "number", value: "10", operator: "<" },
        upperInflectionPoint: { type: "number", value: "30", operator: "<=" },
      },
    };
    createGaugeChart(model, defaultChart, "chartId");
    const chart = model.getters.getChartRuntime("chartId") as GaugeChartRuntime;
    expect(chart.inflectionValues).toMatchObject([
      { value: 10, operator: "<" },
      { value: 30, operator: "<=" },
    ]);
    expect(chart.colors).toStrictEqual([lowerColor, middleColor, upperColor]);
  });
});
