Odoo18-Base/addons/web/static/tests/views/graph/graph_test_helpers.js
2025-01-06 10:57:38 +07:00

213 lines
6.2 KiB
JavaScript

import { before, expect } from "@odoo/hoot";
import { queryAllTexts, queryOne } from "@odoo/hoot-dom";
import { contains, findComponent, preloadBundle } from "@web/../tests/web_test_helpers";
import { ensureArray } from "@web/core/utils/arrays";
import { patch } from "@web/core/utils/patch";
import { GraphController } from "@web/views/graph/graph_controller";
import { GraphRenderer } from "@web/views/graph/graph_renderer";
/**
* @typedef {"bar" | "line" | "pie"} GraphMode
*
* @typedef {import("@web/views/view").View} GraphView
*/
/**
* @param {GraphView} view
* @param {string | Iterable<string>} keys
* @param {Record<string, any> | Iterable<Record<string, any>>} expectedDatasets
*/
export const checkDatasets = (view, keys, expectedDatasets) => {
keys = ensureArray(keys);
const datasets = getChart(view).data.datasets;
const actualValues = [];
for (const dataset of datasets) {
const partialDataset = {};
for (const key of keys) {
partialDataset[key] = dataset[key];
}
actualValues.push(partialDataset);
}
expect(actualValues).toEqual(ensureArray(expectedDatasets));
};
/**
* @param {GraphView} view
* @param {GraphMode} mode
*/
export const checkModeIs = (view, mode) => {
expect(getGraphModelMetaData(view).mode).toBe(mode);
expect(getChart(view).config.type).toBe(mode);
expect(getModeButton(mode)).toHaveClass("active");
};
/**
* @param {GraphView} view
* @param {{ lines: { label: string, value: string }[], title?: string }} expectedTooltip
* @param {number} index
* @param {number} datasetIndex
*/
export const checkTooltip = (view, { title, lines }, index, datasetIndex = null) => {
// If the tooltip options are changed, this helper should change: we construct the dataPoints
// similarly to Chart.js according to the values set for the tooltips options 'mode' and 'intersect'.
const chart = getChart(view);
const { datasets } = chart.data;
const dataPoints = [];
for (let i = 0; i < datasets.length; i++) {
const dataset = datasets[i];
const raw = dataset.data[index];
if (raw !== undefined && (datasetIndex === null || datasetIndex === i)) {
dataPoints.push({
datasetIndex: i,
dataIndex: index,
raw,
});
}
}
chart.config.options.plugins.tooltip.external({
tooltip: { opacity: 1, x: 1, y: 1, dataPoints },
});
const lineLabels = [];
const lineValues = [];
for (const line of lines) {
lineLabels.push(line.label);
lineValues.push(String(line.value));
}
expect(`.o_graph_custom_tooltip`).toHaveCount(1);
expect(`table thead tr th.o_measure`).toHaveText(title || "Count");
expect(queryAllTexts(`table tbody tr td small.o_label`)).toEqual(lineLabels);
expect(queryAllTexts(`table tbody tr td.o_value`)).toEqual(lineValues);
};
/**
* @param {"asc" | "desc"} direction
*/
export const clickSort = (direction) => contains(`.fa-sort-amount-${direction}`).click();
/**
* @param {GraphView} view
*/
export const getChart = (view) => getGraphRenderer(view).chart;
/**
* @param {GraphView} view
*/
export const getGraphModelMetaData = (view) => getGraphModel(view).metaData;
/**
* @param {GraphMode} mode
*/
export const getModeButton = (mode) => queryOne`.o_graph_button[data-mode=${mode}]`;
/**
* @param {GraphView} view
*/
export const getScaleY = (view) => getChart(view).config.options.scales.y;
/**
* @param {GraphView} view
*/
export const getYAxisLabel = (view) => getChart(view).config.options.scales.y.title.text;
/**
* @param {GraphView} view
* @param {string | Iterable<string>} expectedLabels
*/
export function checkLabels(view, expectedLabels) {
expect(getChart(view).data.labels.map(String)).toEqual(ensureArray(expectedLabels));
}
/**
* @param {GraphView} view
* @param {string | Iterable<string>} expectedLabels
*/
export function checkYTicks(view, expectedLabels) {
const labels = getChart(view).scales.y.ticks.map((l) => l.label);
expect(labels).toEqual(expectedLabels);
}
/**
* @param {GraphView} view
* @param {string | Iterable<string>} expectedLabels
*/
export function checkLegend(view, expectedLabels) {
const chart = getChart(view);
const labels = chart.config.options.plugins.legend.labels
.generateLabels(chart)
.map((o) => o.text);
const expectedLabelsList = ensureArray(expectedLabels);
expect(labels).toEqual(expectedLabelsList, {
message: `Legend should be matching: ${expectedLabelsList
.map((label) => `"${label}"`)
.join(", ")}`,
});
}
/**
* @param {GraphView} view
*/
export async function clickOnDataset(view) {
const chart = getChart(view);
const point = chart.getDatasetMeta(0).data[0].getCenterPoint();
return contains(chart.canvas).click({ position: point, relative: true });
}
/**
* @param {GraphView} view
*/
export function getGraphController(view) {
return findComponent(view, (c) => c instanceof GraphController);
}
/**
* @param {GraphView} view
*/
export function getGraphModel(view) {
return getGraphController(view).model;
}
/**
* @param {GraphView} view
* @returns {GraphRenderer}
*/
export function getGraphRenderer(view) {
return findComponent(view, (c) => c instanceof GraphRenderer);
}
/**
* @param {GraphMode} mode
*/
export function selectMode(mode) {
return contains(getModeButton(mode)).click();
}
/**
* @param {GraphView} view
* @param {string} text
*/
export async function clickOnLegend(view, text) {
const chart = getChart(view);
const index = chart.legend.legendItems.findIndex((e) => e.text === text);
const { left, top, width, height } = chart.legend.legendHitBoxes[index];
const point = {
x: left + width / 2,
y: top + height / 2,
};
return contains(chart.canvas).click({ position: point, relative: true });
}
/**
* Helper to call at the start of a test suite using the Chart.js lib.
*
* It will:
* - pre-load the Chart.js lib before tests are run;
* - disable all animations in the lib.
*/
export function setupChartJsForTests() {
preloadBundle("web.chartjs_lib");
before(() => patch(Chart.defaults, { animation: false }));
}