Odoo18-Base/addons/spreadsheet/static/tests/charts/model/odoo_chart_plugin.test.js
2025-01-06 10:57:38 +07:00

916 lines
34 KiB
JavaScript

import { animationFrame } from "@odoo/hoot-mock";
import { describe, expect, test } from "@odoo/hoot";
import { OdooBarChart } from "@spreadsheet/chart/odoo_chart/odoo_bar_chart";
import { OdooChart } from "@spreadsheet/chart/odoo_chart/odoo_chart";
import { OdooLineChart } from "@spreadsheet/chart/odoo_chart/odoo_line_chart";
import {
createSpreadsheetWithChart,
insertChartInSpreadsheet,
} from "@spreadsheet/../tests/helpers/chart";
import { insertListInSpreadsheet } from "@spreadsheet/../tests/helpers/list";
import { createModelWithDataSource } from "@spreadsheet/../tests/helpers/model";
import { addGlobalFilter } from "@spreadsheet/../tests/helpers/commands";
import { THIS_YEAR_GLOBAL_FILTER } from "@spreadsheet/../tests/helpers/global_filter";
import { mockService, makeServerError } from "@web/../tests/web_test_helpers";
import * as spreadsheet from "@odoo/o-spreadsheet";
import { user } from "@web/core/user";
import {
getBasicServerData,
defineSpreadsheetActions,
defineSpreadsheetModels,
} from "@spreadsheet/../tests/helpers/data";
import { waitForDataLoaded } from "@spreadsheet/helpers/model";
const { toZone } = spreadsheet.helpers;
const fr_FR = {
name: "French",
code: "fr_FR",
thousandsSeparator: " ",
decimalSeparator: ",",
dateFormat: "dd/mm/yyyy",
timeFormat: "hh:mm:ss",
formulaArgSeparator: ";",
};
describe.current.tags("headless");
defineSpreadsheetModels();
defineSpreadsheetActions();
test("Can add an Odoo Bar chart", async () => {
const { model } = await createSpreadsheetWithChart({ type: "odoo_bar" });
const sheetId = model.getters.getActiveSheetId();
expect(model.getters.getChartIds(sheetId).length).toBe(1);
const chartId = model.getters.getChartIds(sheetId)[0];
const chart = model.getters.getChart(chartId);
expect(chart instanceof OdooBarChart).toBe(true);
expect(chart.getDefinitionForExcel()).toBe(undefined);
expect(model.getters.getChartRuntime(chartId).chartJsConfig.type).toBe("bar");
});
test("Can add an Odoo Line chart", async () => {
const { model } = await createSpreadsheetWithChart({ type: "odoo_line" });
const sheetId = model.getters.getActiveSheetId();
expect(model.getters.getChartIds(sheetId).length).toBe(1);
const chartId = model.getters.getChartIds(sheetId)[0];
const chart = model.getters.getChart(chartId);
expect(chart instanceof OdooLineChart).toBe(true);
expect(chart.getDefinitionForExcel()).toBe(undefined);
expect(model.getters.getChartRuntime(chartId).chartJsConfig.type).toBe("line");
});
test("Can add an Odoo Pie chart", async () => {
const { model } = await createSpreadsheetWithChart({ type: "odoo_pie" });
const sheetId = model.getters.getActiveSheetId();
expect(model.getters.getChartIds(sheetId).length).toBe(1);
const chartId = model.getters.getChartIds(sheetId)[0];
const chart = model.getters.getChart(chartId);
expect(chart instanceof OdooChart).toBe(true);
expect(chart.getDefinitionForExcel()).toBe(undefined);
expect(model.getters.getChartRuntime(chartId).chartJsConfig.type).toBe("pie");
});
test("A data source is added after a chart creation", async () => {
const { model } = await createSpreadsheetWithChart();
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
expect(model.getters.getChartDataSource(chartId)).not.toBe(undefined);
});
test("Odoo bar chart runtime loads the data", async () => {
const { model } = await createSpreadsheetWithChart({
type: "odoo_bar",
mockRPC: async function (route, args) {
if (args.method === "web_read_group") {
expect.step("web_read_group");
}
},
});
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
// it should not be loaded eagerly
expect.verifySteps([]);
expect(model.getters.getChartRuntime(chartId).chartJsConfig.data).toEqual({
datasets: [],
labels: [],
});
await animationFrame();
expect(model.getters.getChartRuntime(chartId).chartJsConfig.data).toEqual({
datasets: [
{
backgroundColor: "#4EA7F2",
borderColor: "#FFFFFF",
borderWidth: 1,
data: [1, 3],
label: "Count",
},
],
labels: ["false", "true"],
});
// it should have loaded the data
expect.verifySteps(["web_read_group"]);
});
test("Odoo pie chart runtime loads the data", async () => {
const { model } = await createSpreadsheetWithChart({
type: "odoo_pie",
mockRPC: async function (route, args) {
if (args.method === "web_read_group") {
expect.step("web_read_group");
}
},
});
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
// it should not be loaded eagerly
expect.verifySteps([]);
expect(model.getters.getChartRuntime(chartId).chartJsConfig.data).toEqual({
datasets: [],
labels: [],
});
await animationFrame();
expect(model.getters.getChartRuntime(chartId).chartJsConfig.data).toEqual({
datasets: [
{
backgroundColor: ["#4EA7F2", "#EA6175", "#43C5B1"],
borderColor: "#FFFFFF",
data: [1, 3],
hoverOffset: 30,
label: "",
},
],
labels: ["false", "true"],
});
// it should have loaded the data
expect.verifySteps(["web_read_group"]);
});
test("Odoo line chart runtime loads the data", async () => {
const { model } = await createSpreadsheetWithChart({
type: "odoo_line",
mockRPC: async function (route, args) {
if (args.method === "web_read_group") {
expect.step("web_read_group");
}
},
});
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
// it should not be loaded eagerly
expect.verifySteps([]);
expect(model.getters.getChartRuntime(chartId).chartJsConfig.data).toEqual({
datasets: [],
labels: [],
});
await animationFrame();
expect(model.getters.getChartRuntime(chartId).chartJsConfig.data).toEqual({
datasets: [
{
backgroundColor: "#4EA7F2",
borderColor: "#4EA7F2",
data: [1, 3],
label: "Count",
lineTension: 0,
fill: false,
pointBackgroundColor: "#4EA7F2",
},
],
labels: ["false", "true"],
});
// it should have loaded the data
expect.verifySteps(["web_read_group"]);
});
test("Area charts are supported", async () => {
const { model } = await createSpreadsheetWithChart({ type: "odoo_line" });
await waitForDataLoaded(model);
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
const definition = model.getters.getChartDefinition(chartId);
model.dispatch("UPDATE_CHART", {
definition: { ...definition, fillArea: true, stacked: false },
id: chartId,
sheetId,
});
let runtime = model.getters.getChartRuntime(chartId).chartJsConfig;
expect(runtime.options.scales.x.stacked).toBe(undefined);
expect(runtime.options.scales.y.stacked).toBe(undefined);
expect(runtime.data.datasets[0].fill).toBe("origin");
model.dispatch("UPDATE_CHART", {
definition: { ...definition, fillArea: true, stacked: true },
id: chartId,
sheetId,
});
runtime = model.getters.getChartRuntime(chartId).chartJsConfig;
expect(runtime.options.scales.x.stacked).toBe(undefined);
expect(runtime.options.scales.y.stacked).toBe(true);
expect(runtime.data.datasets[0].fill).toBe("origin");
});
test("Data reloaded strictly upon domain update", async () => {
const { model } = await createSpreadsheetWithChart({
type: "odoo_line",
mockRPC: async function (route, args) {
if (args.method === "web_read_group") {
expect.step("web_read_group");
}
},
});
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
const definition = model.getters.getChartDefinition(chartId);
// force runtime computation
model.getters.getChartRuntime(chartId);
await animationFrame();
// it should have loaded the data
expect.verifySteps(["web_read_group"]);
model.dispatch("UPDATE_CHART", {
definition: {
...definition,
searchParams: { ...definition.searchParams, domain: [["1", "=", "1"]] },
},
id: chartId,
sheetId,
});
// force runtime computation
model.getters.getChartRuntime(chartId);
await animationFrame();
// it should have loaded the data with a new domain
expect.verifySteps(["web_read_group"]);
const newDefinition = model.getters.getChartDefinition(chartId);
model.dispatch("UPDATE_CHART", {
definition: {
...newDefinition,
background: "#00FF00",
},
id: chartId,
sheetId,
});
// force runtime computation
model.getters.getChartRuntime(chartId);
await animationFrame();
// it should have not have loaded the data since the domain was unchanged
expect.verifySteps([]);
});
test("Can import/export an Odoo chart", async () => {
const model = await createModelWithDataSource();
insertChartInSpreadsheet(model, "odoo_line");
const data = model.exportData();
const figures = data.sheets[0].figures;
expect(figures.length).toBe(1);
const figure = figures[0];
expect(figure.tag).toBe("chart");
expect(figure.data.type).toBe("odoo_line");
const m1 = await createModelWithDataSource({ spreadsheetData: data });
const sheetId = m1.getters.getActiveSheetId();
expect(m1.getters.getChartIds(sheetId).length).toBe(1);
const chartId = m1.getters.getChartIds(sheetId)[0];
expect(model.getters.getChartDataSource(chartId)).not.toBe(undefined);
expect(m1.getters.getChartRuntime(chartId).chartJsConfig.type).toBe("line");
});
test("can import (export) contextual domain", async function () {
const chartId = "1";
const uid = user.userId;
const spreadsheetData = {
sheets: [
{
figures: [
{
id: chartId,
x: 10,
y: 10,
width: 536,
height: 335,
tag: "chart",
data: {
type: "odoo_line",
title: { text: "Partners" },
legendPosition: "top",
searchParams: {
domain: '[("foo", "=", uid)]',
groupBy: [],
orderBy: [],
},
metaData: {
groupBy: ["foo"],
measure: "__count",
resModel: "partner",
},
},
},
],
},
],
};
const model = await createModelWithDataSource({
spreadsheetData,
mockRPC: function (route, args) {
if (args.method === "web_read_group") {
expect(args.kwargs.domain).toEqual([["foo", "=", uid]]);
expect.step("web_read_group");
}
},
});
model.getters.getChartRuntime(chartId).chartJsConfig.data; // force loading the chart data
await animationFrame();
expect(model.exportData().sheets[0].figures[0].data.searchParams.domain).toBe(
'[("foo", "=", uid)]',
{ message: "the domain is exported with the dynamic parts" }
);
expect.verifySteps(["web_read_group"]);
});
test("Can undo/redo an Odoo chart creation", async () => {
const model = await createModelWithDataSource();
insertChartInSpreadsheet(model, "odoo_line");
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
expect(model.getters.getChartDataSource(chartId)).not.toBe(undefined);
model.dispatch("REQUEST_UNDO");
expect(model.getters.getChartIds(sheetId).length).toBe(0);
model.dispatch("REQUEST_REDO");
expect(model.getters.getChartDataSource(chartId)).not.toBe(undefined);
expect(model.getters.getChartIds(sheetId).length).toBe(1);
});
test("charts with no legend", async () => {
const { model } = await createSpreadsheetWithChart({ type: "odoo_pie" });
insertChartInSpreadsheet(model, "odoo_bar");
insertChartInSpreadsheet(model, "odoo_line");
const sheetId = model.getters.getActiveSheetId();
const [pieChartId, barChartId, lineChartId] = model.getters.getChartIds(sheetId);
const pie = model.getters.getChartDefinition(pieChartId);
const bar = model.getters.getChartDefinition(barChartId);
const line = model.getters.getChartDefinition(lineChartId);
expect(
model.getters.getChartRuntime(pieChartId).chartJsConfig.options.plugins.legend.display
).toBe(true);
expect(
model.getters.getChartRuntime(barChartId).chartJsConfig.options.plugins.legend.display
).toBe(true);
expect(
model.getters.getChartRuntime(lineChartId).chartJsConfig.options.plugins.legend.display
).toBe(true);
model.dispatch("UPDATE_CHART", {
definition: {
...pie,
legendPosition: "none",
},
id: pieChartId,
sheetId,
});
model.dispatch("UPDATE_CHART", {
definition: {
...bar,
legendPosition: "none",
},
id: barChartId,
sheetId,
});
model.dispatch("UPDATE_CHART", {
definition: {
...line,
legendPosition: "none",
},
id: lineChartId,
sheetId,
});
expect(
model.getters.getChartRuntime(pieChartId).chartJsConfig.options.plugins.legend.display
).toBe(false);
expect(
model.getters.getChartRuntime(barChartId).chartJsConfig.options.plugins.legend.display
).toBe(false);
expect(
model.getters.getChartRuntime(lineChartId).chartJsConfig.options.plugins.legend.display
).toBe(false);
});
test("Bar chart with stacked attribute is supported", async () => {
const { model } = await createSpreadsheetWithChart({ type: "odoo_bar" });
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
const definition = model.getters.getChartDefinition(chartId);
model.dispatch("UPDATE_CHART", {
definition: {
...definition,
stacked: true,
},
id: chartId,
sheetId,
});
expect(model.getters.getChartRuntime(chartId).chartJsConfig.options.scales.x.stacked).toBe(
true
);
expect(model.getters.getChartRuntime(chartId).chartJsConfig.options.scales.y.stacked).toBe(
true
);
model.dispatch("UPDATE_CHART", {
definition: {
...definition,
stacked: false,
},
id: chartId,
sheetId,
});
expect(model.getters.getChartRuntime(chartId).chartJsConfig.options.scales.x.stacked).toBe(
undefined
);
expect(model.getters.getChartRuntime(chartId).chartJsConfig.options.scales.y.stacked).toBe(
undefined
);
});
test("Can copy/paste Odoo chart", async () => {
const { model } = await createSpreadsheetWithChart({ type: "odoo_pie" });
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
model.dispatch("SELECT_FIGURE", { id: chartId });
model.dispatch("COPY");
model.dispatch("PASTE", { target: [toZone("A1")] });
const chartIds = model.getters.getChartIds(sheetId);
expect(chartIds.length).toBe(2);
expect(model.getters.getChart(chartIds[1]) instanceof OdooChart).toBe(true);
expect(JSON.stringify(model.getters.getChartRuntime(chartIds[1]))).toBe(
JSON.stringify(model.getters.getChartRuntime(chartId))
);
expect(model.getters.getChart(chartId).dataSource).not.toBe(
model.getters.getChart(chartIds[1]).dataSource,
{ message: "The datasource is also duplicated" }
);
});
test("Can cut/paste Odoo chart", async () => {
const { model } = await createSpreadsheetWithChart({ type: "odoo_pie" });
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
const chartRuntime = model.getters.getChartRuntime(chartId);
model.dispatch("SELECT_FIGURE", { id: chartId });
model.dispatch("CUT");
model.dispatch("PASTE", { target: [toZone("A1")] });
const chartIds = model.getters.getChartIds(sheetId);
expect(chartIds.length).toBe(1);
expect(chartIds[0]).not.toBe(chartId);
expect(model.getters.getChart(chartIds[0]) instanceof OdooChart).toBe(true);
expect(JSON.stringify(model.getters.getChartRuntime(chartIds[0]))).toBe(
JSON.stringify(chartRuntime)
);
});
test("Duplicating a sheet correctly duplicates Odoo chart", async () => {
const { model } = await createSpreadsheetWithChart({ type: "odoo_bar" });
const sheetId = model.getters.getActiveSheetId();
const secondSheetId = "secondSheetId";
const chartId = model.getters.getChartIds(sheetId)[0];
model.dispatch("DUPLICATE_SHEET", { sheetId, sheetIdTo: secondSheetId });
const chartIds = model.getters.getChartIds(secondSheetId);
expect(chartIds.length).toBe(1);
expect(model.getters.getChart(chartIds[0]) instanceof OdooChart).toBe(true);
expect(JSON.stringify(model.getters.getChartRuntime(chartIds[0]))).toBe(
JSON.stringify(model.getters.getChartRuntime(chartId))
);
expect(model.getters.getChart(chartId).dataSource).not.toBe(
model.getters.getChart(chartIds[0]).dataSource,
{ message: "The datasource is also duplicated" }
);
});
test("Line chart with stacked attribute is supported", async () => {
const { model } = await createSpreadsheetWithChart({ type: "odoo_line" });
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
const definition = model.getters.getChartDefinition(chartId);
model.dispatch("UPDATE_CHART", {
definition: {
...definition,
stacked: true,
},
id: chartId,
sheetId,
});
expect(model.getters.getChartRuntime(chartId).chartJsConfig.options.scales.x.stacked).toBe(
undefined
);
expect(model.getters.getChartRuntime(chartId).chartJsConfig.options.scales.y.stacked).toBe(
true
);
model.dispatch("UPDATE_CHART", {
definition: {
...definition,
stacked: false,
},
id: chartId,
sheetId,
});
expect(model.getters.getChartRuntime(chartId).chartJsConfig.options.scales.x.stacked).toBe(
undefined
);
expect(model.getters.getChartRuntime(chartId).chartJsConfig.options.scales.y.stacked).toBe(
undefined
);
});
test("Load odoo chart spreadsheet with models that cannot be accessed", async function () {
let hasAccessRights = true;
const { model } = await createSpreadsheetWithChart({
mockRPC: async function (route, args) {
if (args.model === "partner" && args.method === "web_read_group" && !hasAccessRights) {
throw makeServerError({ description: "ya done!" });
}
},
});
const chartId = model.getters.getFigures(model.getters.getActiveSheetId())[0].id;
const chartDataSource = model.getters.getChartDataSource(chartId);
await waitForDataLoaded(model);
const data = chartDataSource.getData();
expect(data.datasets.length).toBe(1);
expect(data.labels.length).toBe(2);
hasAccessRights = false;
chartDataSource.load({ reload: true });
await waitForDataLoaded(model);
expect(chartDataSource.getData()).toEqual({ datasets: [], labels: [] });
});
test("Line chart to support cumulative data", async () => {
const { model } = await createSpreadsheetWithChart({ type: "odoo_line" });
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
const definition = model.getters.getChartDefinition(chartId);
await waitForDataLoaded(model);
expect(model.getters.getChartRuntime(chartId).chartJsConfig.data.datasets[0].data).toEqual([
1, 3,
]);
model.dispatch("UPDATE_CHART", {
definition: {
...definition,
cumulative: true,
},
id: chartId,
sheetId,
});
expect(model.getters.getChartRuntime(chartId).chartJsConfig.data.datasets[0].data).toEqual([
1, 4,
]);
model.dispatch("UPDATE_CHART", {
definition: {
...definition,
cumulative: false,
},
id: chartId,
sheetId,
});
expect(model.getters.getChartRuntime(chartId).chartJsConfig.data.datasets[0].data).toEqual([
1, 3,
]);
});
test("cumulative line chart with past data before domain period", async () => {
const serverData = getBasicServerData();
serverData.models.partner.records = [
{ date: "2020-01-01", probability: 10 },
{ date: "2021-01-01", probability: 2 },
{ date: "2022-01-01", probability: 3 },
{ date: "2022-03-01", probability: 4 },
{ date: "2022-06-01", probability: 5 },
];
const { model } = await createSpreadsheetWithChart({
type: "odoo_line",
serverData,
definition: {
type: "odoo_line",
metaData: {
groupBy: ["date"],
measure: "probability",
order: null,
resModel: "partner",
},
searchParams: {
comparison: null,
context: {},
domain: [
["date", ">=", "2022-01-01"],
["date", "<=", "2022-12-31"],
],
groupBy: [],
orderBy: [],
},
cumulative: true,
title: { text: "Partners" },
dataSourceId: "42",
id: "42",
},
});
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
await waitForDataLoaded(model);
expect(model.getters.getChartRuntime(chartId).chartJsConfig.data.datasets[0].data).toEqual([
15, 19, 24,
]);
});
test("Can insert odoo chart from a different model", async () => {
const model = await createModelWithDataSource();
insertListInSpreadsheet(model, { model: "product", columns: ["name"] });
await addGlobalFilter(model, THIS_YEAR_GLOBAL_FILTER);
const sheetId = model.getters.getActiveSheetId();
expect(model.getters.getChartIds(sheetId).length).toBe(0);
insertChartInSpreadsheet(model);
expect(model.getters.getChartIds(sheetId).length).toBe(1);
});
test("Odoo chart legend color changes with background color update", async () => {
const { model } = await createSpreadsheetWithChart({ type: "odoo_bar" });
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
const definition = model.getters.getChartDefinition(chartId);
expect(
model.getters.getChartRuntime(chartId).chartJsConfig.options.plugins.legend.labels.color
).toBe("#000000");
model.dispatch("UPDATE_CHART", {
definition: {
...definition,
background: "#000000",
},
id: chartId,
sheetId,
});
expect(
model.getters.getChartRuntime(chartId).chartJsConfig.options.plugins.legend.labels.color
).toBe("#FFFFFF");
});
test("Remove odoo chart when sheet is deleted", async () => {
const { model } = await createSpreadsheetWithChart({ type: "odoo_line" });
const sheetId = model.getters.getActiveSheetId();
model.dispatch("CREATE_SHEET", {
sheetId: model.uuidGenerator.uuidv4(),
position: model.getters.getSheetIds().length,
});
expect(model.getters.getOdooChartIds().length).toBe(1);
model.dispatch("DELETE_SHEET", { sheetId });
expect(model.getters.getOdooChartIds().length).toBe(0);
});
test("Odoo chart datasource display name has a default when the chart title is empty", async () => {
const { model } = await createSpreadsheetWithChart({ type: "odoo_line" });
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
const definition = model.getters.getChartDefinition(chartId);
expect(model.getters.getOdooChartDisplayName(chartId)).toBe("(#1) Partners");
model.dispatch("UPDATE_CHART", {
definition: {
...definition,
title: { text: "" },
},
id: chartId,
sheetId,
});
expect(model.getters.getOdooChartDisplayName(chartId)).toBe("(#1) Odoo Line Chart");
});
test("See records when clicking on a bar chart bar", async () => {
const action = {
domain: [
["date", ">=", "2022-01-01"],
["date", "<", "2022-02-01"],
"&",
["date", ">=", "2022-01-01"],
["date", "<=", "2022-12-31"],
],
name: "January 2022 / Probability",
res_model: "partner",
target: "current",
type: "ir.actions.act_window",
views: [
[false, "list"],
[false, "form"],
],
};
const fakeActionService = {
doAction: async (request, options = {}) => {
if (request.type === "ir.actions.act_window") {
expect.step("do-action");
expect(request).toEqual(action);
}
},
loadAction(actionRequest) {
expect.step("load-action");
expect(actionRequest).toBe("test.my_action");
return action;
},
};
mockService("action", fakeActionService);
const serverData = getBasicServerData();
serverData.models.partner.records = [
{ date: "2020-01-01", probability: 10 },
{ date: "2021-01-01", probability: 2 },
{ date: "2022-01-01", probability: 3 },
{ date: "2022-03-01", probability: 4 },
{ date: "2022-06-01", probability: 5 },
];
const { model } = await createSpreadsheetWithChart({
type: "odoo_bar",
serverData,
definition: {
type: "odoo_bar",
metaData: {
groupBy: ["date"],
measure: "probability",
order: null,
resModel: "partner",
},
searchParams: {
comparison: null,
context: {},
domain: [
["date", ">=", "2022-01-01"],
["date", "<=", "2022-12-31"],
],
groupBy: [],
orderBy: [],
},
actionXmlId: "test.my_action",
cumulative: true,
title: { text: "Partners" },
dataSourceId: "42",
id: "42",
},
});
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
await waitForDataLoaded(model);
const runtime = model.getters.getChartRuntime(chartId);
expect.verifySteps([]);
await runtime.chartJsConfig.options.onClick(undefined, [{ datasetIndex: 0, index: 0 }]);
await animationFrame();
expect.verifySteps(["load-action", "do-action"]);
});
test("See records when clicking on a pie chart slice", async () => {
const fakeActionService = {
doAction: async (request, options = {}) => {
if (request.type === "ir.actions.act_window") {
expect.step("do-action");
expect(request).toEqual({
domain: [
["date", ">=", "2022-01-01"],
["date", "<", "2022-02-01"],
"&",
["date", ">=", "2022-01-01"],
["date", "<=", "2022-12-31"],
],
name: "January 2022",
res_model: "partner",
target: "current",
type: "ir.actions.act_window",
views: [
[false, "list"],
[false, "form"],
],
});
}
},
};
mockService("action", fakeActionService);
const serverData = getBasicServerData();
serverData.models.partner.records = [
{ date: "2020-01-01", probability: 10 },
{ date: "2021-01-01", probability: 2 },
{ date: "2022-01-01", probability: 3 },
{ date: "2022-03-01", probability: 4 },
{ date: "2022-06-01", probability: 5 },
];
const { model } = await createSpreadsheetWithChart({
type: "odoo_pie",
serverData,
definition: {
type: "odoo_pie",
metaData: {
groupBy: ["date"],
measure: "probability",
resModel: "partner",
},
searchParams: {
context: {},
domain: [
["date", ">=", "2022-01-01"],
["date", "<=", "2022-12-31"],
],
groupBy: [],
orderBy: [],
},
cumulative: true,
title: { text: "Partners" },
dataSourceId: "42",
id: "42",
},
});
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
await waitForDataLoaded(model);
const runtime = model.getters.getChartRuntime(chartId);
expect.verifySteps([]);
await runtime.chartJsConfig.options.onClick(undefined, [{ datasetIndex: 0, index: 0 }]);
await animationFrame();
expect.verifySteps(["do-action"]);
});
test("import/export action xml id", async () => {
const { model } = await createSpreadsheetWithChart({
type: "odoo_bar",
definition: {
type: "odoo_bar",
metaData: {
groupBy: [],
measure: "probability",
resModel: "partner",
},
searchParams: {
domain: [],
groupBy: [],
orderBy: [],
},
actionXmlId: "test.my_action",
cumulative: true,
title: { text: "Partners" },
dataSourceId: "42",
id: "42",
},
});
const exported = model.exportData();
expect(exported.sheets[0].figures[0].data.actionXmlId).toBe("test.my_action");
const model2 = await createModelWithDataSource({ spreadsheetData: exported });
const sheetId = model2.getters.getActiveSheetId();
const chartId = model2.getters.getChartIds(sheetId)[0];
expect(model2.getters.getChartDefinition(chartId).actionXmlId).toBe("test.my_action");
});
test("Show values is taken into account in the runtime", async () => {
const { model } = await createSpreadsheetWithChart({ type: "odoo_bar" });
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
const definition = model.getters.getChartDefinition(chartId);
model.dispatch("UPDATE_CHART", {
definition: {
...definition,
showValues: true,
},
id: chartId,
sheetId,
});
const runtime = model.getters.getChartRuntime(chartId);
expect(runtime.chartJsConfig.options.plugins.chartShowValuesPlugin.showValues).toBe(true);
});
test("Displays correct thousand separator for positive value in Odoo Bar chart Y-axis", async () => {
const { model } = await createSpreadsheetWithChart({ type: "odoo_bar" });
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
const runtime = model.getters.getChartRuntime(chartId);
expect(runtime.chartJsConfig.options.scales.y?.ticks.callback(60000000)).toBe("60,000,000");
expect(runtime.chartJsConfig.options.scales.y?.ticks.callback(-60000000)).toBe("-60,000,000");
});
test("Thousand separator in Odoo Bar chart Y-axis is locale-dependent", async () => {
const { model } = await createSpreadsheetWithChart({ type: "odoo_bar" });
model.dispatch("UPDATE_LOCALE", { locale: fr_FR });
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
const runtime = model.getters.getChartRuntime(chartId);
expect(runtime.chartJsConfig.options.scales.y?.ticks.callback(60000000)).toBe("60 000 000");
expect(runtime.chartJsConfig.options.scales.y?.ticks.callback(-60000000)).toBe("-60 000 000");
});
test("Chart data source is recreated when chart type is updated", async () => {
const { model } = await createSpreadsheetWithChart({ type: "odoo_bar" });
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
const chartDataSource = model.getters.getChartDataSource(chartId);
model.dispatch("UPDATE_CHART", {
definition: {
...model.getters.getChartDefinition(chartId),
type: "odoo_line",
},
id: chartId,
sheetId,
});
expect(chartDataSource !== model.getters.getChartDataSource(chartId)).toBe(true, {
message: "The data source should have been recreated",
});
});