import { describe, expect, test } from "@odoo/hoot"; import { registries } from "@odoo/o-spreadsheet"; import { createSpreadsheetWithChart } from "@spreadsheet/../tests/helpers/chart"; import { addGlobalFilter, setCellContent, setCellFormat, setCellStyle, setGlobalFilterValue, } from "@spreadsheet/../tests/helpers/commands"; import { defineSpreadsheetModels } from "@spreadsheet/../tests/helpers/data"; import { getCell, getEvaluatedCell } from "@spreadsheet/../tests/helpers/getters"; import { THIS_YEAR_GLOBAL_FILTER } from "@spreadsheet/../tests/helpers/global_filter"; import { createModelWithDataSource } from "@spreadsheet/../tests/helpers/model"; import { createSpreadsheetWithPivot } from "@spreadsheet/../tests/helpers/pivot"; import { freezeOdooData } from "@spreadsheet/helpers/model"; import { OdooPivot, OdooPivotRuntimeDefinition } from "@spreadsheet/pivot/odoo_pivot"; const { pivotRegistry } = registries; import { getMenuServerData } from "@spreadsheet/../tests/links/menu_data_utils"; import { createSpreadsheetWithList } from "../helpers/list"; describe.current.tags("headless"); defineSpreadsheetModels(); test("odoo pivot functions are replaced with their value", async function () { const { model } = await createSpreadsheetWithPivot(); expect(getCell(model, "A3").content).toBe('=PIVOT.HEADER(1,"bar",FALSE)'); expect(getCell(model, "C3").content).toBe( '=PIVOT.VALUE(1,"probability:avg","bar",FALSE,"foo",2)' ); expect(getEvaluatedCell(model, "A3").value).toBe("No"); expect(getEvaluatedCell(model, "C3").value).toBe(15); const data = await freezeOdooData(model); const cells = data.sheets[0].cells; expect(cells.A3.content).toBe("No", { message: "the content is replaced with the value" }); expect(cells.C3.content).toBe("15", { message: "the content is replaced with the value" }); expect(data.formats[data.sheets[0].formats.C3]).toBe("#,##0.00"); }); test("Pivot with a type different of ODOO is not converted", async function () { // Add a pivot with a type different of ODOO pivotRegistry.add("NEW_KIND_OF_PIVOT", { ui: OdooPivot, definition: OdooPivotRuntimeDefinition, externalData: true, onIterationEndEvaluation: () => {}, granularities: [], isMeasureCandidate: () => false, isGroupable: () => false, }); const spreadsheetData = { pivots: { 1: { type: "NEW_KIND_OF_PIVOT", name: "Name", model: "partner", measures: ["probability"], formulaId: "1", colGroupBys: ["foo"], rowGroupBys: ["bar"], sortedColumn: null, }, }, }; const model = await createModelWithDataSource({ spreadsheetData }); setCellContent(model, "A1", `=PIVOT.VALUE(1, "probability:avg")`); setCellContent(model, "A2", `=PIVOT.HEADER(1, "measure", "probability:avg")`); const data = await freezeOdooData(model); const cells = data.sheets[0].cells; expect(cells.A1.content).toBe(`=PIVOT.VALUE(1, "probability:avg")`, { message: "the content is not replaced with the value", }); expect(cells.A2.content).toBe(`=PIVOT.HEADER(1, "measure", "probability:avg")`, { message: "the content is not replaced with the value", }); }); test("values are not exported formatted", async function () { const { model } = await createSpreadsheetWithPivot(); expect(getCell(model, "A3").content).toBe('=PIVOT.HEADER(1,"bar",FALSE)'); expect(getCell(model, "C3").content).toBe( '=PIVOT.VALUE(1,"probability:avg","bar",FALSE,"foo",2)' ); setCellFormat(model, "C3", "mmmm yyyy"); setCellContent(model, "C4", "=C3+31"); expect(getEvaluatedCell(model, "C3").value).toBe(15); expect(getEvaluatedCell(model, "C3").formattedValue).toBe("January 1900"); expect(getEvaluatedCell(model, "C4").value).toBe(46); expect(getEvaluatedCell(model, "C4").formattedValue).toBe("February 1900"); const data = await freezeOdooData(model); const sharedModel = await createModelWithDataSource({ spreadsheetData: data }); expect(getEvaluatedCell(sharedModel, "C3").value).toBe(15); expect(getEvaluatedCell(sharedModel, "C3").formattedValue).toBe("January 1900"); expect(getEvaluatedCell(sharedModel, "C4").value).toBe(46); expect(getEvaluatedCell(sharedModel, "C4").formattedValue).toBe("February 1900"); }); test("invalid expression with pivot function", async function () { const { model } = await createSpreadsheetWithPivot(); setCellContent(model, "A1", "=PIVOT.VALUE(1)+"); // invalid expression expect(getEvaluatedCell(model, "A1").value).toBe("#BAD_EXPR"); const data = await freezeOdooData(model); const cells = data.sheets[0].cells; expect(cells.A1.content).toBe("=PIVOT.VALUE(1)+", { message: "the content is left as is when the expression is invalid", }); }); test("odoo pivot functions detection is not case sensitive", async function () { const { model } = await createSpreadsheetWithPivot(); setCellContent(model, "A1", '=pivot.value(1,"probability:avg")'); const data = await freezeOdooData(model); const A1 = data.sheets[0].cells.A1; expect(A1.content).toBe("131", { message: "the content is replaced with the value" }); }); test("computed format is exported", async function () { const { model } = await createSpreadsheetWithPivot({ arch: /* xml */ ` `, }); setCellContent(model, "A1", '=PIVOT.VALUE(1,"pognon:avg")'); expect(getCell(model, "A1").format).toBe(undefined); expect(getEvaluatedCell(model, "A1").format).toBe("#,##0.00[$€]"); const data = await freezeOdooData(model); const formatId = data.sheets[0].formats.A1; const format = data.formats[formatId]; expect(format).toBe("#,##0.00[$€]"); const sharedModel = await createModelWithDataSource({ spreadsheetData: data }); expect(getCell(sharedModel, "A1").format).toBe("#,##0.00[$€]"); }); test("odoo charts are replaced with an image", async function () { const { model } = await createSpreadsheetWithChart({ type: "odoo_bar" }); const data = await freezeOdooData(model); expect(data.sheets[0].figures.length).toBe(1); expect(data.sheets[0].figures[0].tag).toBe("image"); }); test("translation function are replaced with their value", async function () { const model = await createModelWithDataSource(); setCellContent(model, "A1", `=_t("example")`); setCellContent(model, "A2", `=CONCATENATE("for",_t(" example"))`); expect(getEvaluatedCell(model, "A1").value).toBe("example"); expect(getEvaluatedCell(model, "A2").value).toBe("for example"); const data = await freezeOdooData(model); const cells = data.sheets[0].cells; expect(cells.A1.content).toBe("example", { message: "the content is replaced with the value", }); expect(cells.A2.content).toBe("for example", { message: "the content is replaced with the value even when translation function is nested", }); }); test("a new sheet is added for global filters", async function () { const model = await createModelWithDataSource(); await addGlobalFilter(model, THIS_YEAR_GLOBAL_FILTER); const data = await freezeOdooData(model); expect(data.sheets.length).toBe(2); expect(data.sheets[1].name).toBe("Active Filters"); expect(data.sheets[1].cells.A2.content).toBe("This Year"); }); test("global filters and their display value are exported", async function () { const model = await createModelWithDataSource(); await addGlobalFilter(model, THIS_YEAR_GLOBAL_FILTER); const data = await freezeOdooData(model); expect(data.globalFilters.length).toBe(1); expect(data.globalFilters[0].label).toBe("This Year"); expect(data.globalFilters[0].value).toBe(new Date().getFullYear().toString()); }); test("from/to global filters are exported", async function () { const model = await createModelWithDataSource(); await addGlobalFilter(model, { id: "42", type: "date", label: "Date Filter", rangeType: "from_to", }); await setGlobalFilterValue(model, { id: "42", value: { from: "2020-01-01", to: "2021-01-01", }, }); const data = await freezeOdooData(model); const filterSheet = data.sheets[1]; expect(filterSheet.cells.B2.content).toBe("43831"); expect(filterSheet.cells.C2.content).toBe("44197"); expect(filterSheet.formats.B2).toBe(1); expect(filterSheet.formats.C2).toBe(1); expect(data.formats[1]).toBe("m/d/yyyy"); expect(data.globalFilters.length).toBe(1); expect(data.globalFilters[0].label).toBe("Date Filter"); expect(data.globalFilters[0].value).toBe("1/1/2020, 1/1/2021"); }); test("from/to global filter without value is exported", async function () { const model = await createModelWithDataSource(); await addGlobalFilter(model, { id: "42", type: "date", label: "Date Filter", rangeType: "from_to", }); const data = await freezeOdooData(model); const filterSheet = data.sheets[1]; expect(filterSheet.cells.A2.content).toBe("Date Filter"); expect(filterSheet.cells.B2).toEqual({ content: "" }); expect(filterSheet.cells.B2).toEqual({ content: "" }); expect(filterSheet.formats.B2).toBe(1); expect(filterSheet.formats.C2).toBe(1); expect(data.formats[1]).toBe("m/d/yyyy"); expect(data.globalFilters.length).toBe(1); expect(data.globalFilters[0].label).toBe("Date Filter"); expect(data.globalFilters[0].value).toBe(""); }); test("odoo links are replaced with their label", async function () { const view = { name: "an odoo view", viewType: "list", action: { modelName: "partner", views: [[false, "list"]], }, }; const data = { sheets: [ { cells: { A1: { content: "[menu_xml](odoo://ir_menu_xml_id/test_menu)" }, A2: { content: "[menu_id](odoo://ir_menu_id/12)" }, A3: { content: `[odoo_view](odoo://view/${JSON.stringify(view)})` }, A4: { content: "[external_link](https://odoo.com)" }, A5: { content: "[internal_link](o-spreadsheet://Sheet1)" }, }, }, ], }; const model = await createModelWithDataSource({ spreadsheetData: data, serverData: getMenuServerData(), }); const frozenData = await freezeOdooData(model); expect(frozenData.sheets[0].cells.A1.content).toBe("menu_xml"); expect(frozenData.sheets[0].cells.A2.content).toBe("menu_id"); expect(frozenData.sheets[0].cells.A3.content).toBe("odoo_view"); expect(frozenData.sheets[0].cells.A4.content).toBe("[external_link](https://odoo.com)"); expect(frozenData.sheets[0].cells.A5.content).toBe("[internal_link](o-spreadsheet://Sheet1)"); }); test("spilled pivot table", async function () { const { model } = await createSpreadsheetWithPivot({ arch: /* xml */ ` `, }); setCellContent(model, "A10", "=PIVOT(1)"); setCellStyle(model, "B12", { bold: true }); const data = await freezeOdooData(model); const sheet = data.sheets[0]; const cells = sheet.cells; expect(cells.A10.content).toBe("(#1) Partner Pivot"); expect(cells.A11.content).toBe(""); expect(cells.A12.content).toBe("Total"); expect(cells.B10.content).toBe("Total"); expect(cells.B11.content).toBe("Probability"); expect(cells.B12.content).toBe("131"); expect(data.formats[sheet.formats.B12]).toBe("#,##0.00"); expect(data.pivots).toEqual({}); expect(sheet.styles).toEqual({ B12: 1 }); expect(data.styles[sheet.styles["B12"]]).toEqual( { bold: true }, { message: "style is preserved" } ); }); test("Lists are purged from the frozen data", async function () { const { model } = await createSpreadsheetWithList(); const data = await freezeOdooData(model); expect(data.lists).toEqual({}); });