Files
enterprise-addons/spreadsheet_edition/static/tests/pivot/pivot_side_panel.test.js
T

825 lines
34 KiB
JavaScript

import { defineSpreadsheetModels, Partner } from "@spreadsheet/../tests/helpers/data";
import { describe, expect, getFixture, test, beforeEach } from "@odoo/hoot";
import { runAllTimers } from "@odoo/hoot-dom";
import { animationFrame } from "@odoo/hoot-mock";
import { registries, stores } from "@odoo/o-spreadsheet";
import { createSpreadsheetWithPivot } from "@spreadsheet/../tests/helpers/pivot";
import { addGlobalFilter, updatePivot } from "@spreadsheet/../tests/helpers/commands";
import {
contains,
fields,
makeServerError,
mountWithCleanup,
} from "@web/../tests/web_test_helpers";
import { Component, onMounted, onWillUnmount, xml } from "@odoo/owl";
import { PivotDetailsSidePanel } from "@spreadsheet_edition/bundle/pivot/side_panels/pivot_details_side_panel";
const { useStore, useStoreProvider, ModelStore, SidePanelStore } = stores;
defineSpreadsheetModels();
describe.current.tags("desktop");
const { coreViewsPluginRegistry, pivotRegistry } = registries;
let target;
beforeEach(() => {
target = getFixture();
});
class Parent extends Component {
// We need to wrap the component in a div with `o-spreadsheet` to have a correct portal target for spreadsheet popovers
static template = xml/* xml */ `
<div class="o-spreadsheet">
<PivotDetailsSidePanel onCloseSidePanel="props.onCloseSidePanel" pivotId="props.pivotId"/>
</div>
`;
static components = { PivotDetailsSidePanel };
static props = {
model: Object,
pivotId: String,
onCloseSidePanel: Function,
};
setup() {
const stores = useStoreProvider();
stores.inject(ModelStore, this.props.model);
const sidePanelStore = useStore(SidePanelStore);
sidePanelStore.open("PivotSidePanel", this.props);
onMounted(() => {
this.props.model.on("update", this, () => this.render(true));
stores.on("store-updated", this, this.render.bind(this, true));
});
onWillUnmount(() => {
this.props.model.off("update", this);
stores.off("store-updated", this);
});
}
}
async function openSidePanel(model, env, pivotId, onCloseSidePanel = () => {}) {
env.notifyUser = env.notifyUser || (() => {});
env.openSidePanel = env.openSidePanel || (() => {});
await mountWithCleanup(Parent, { env, props: { model, pivotId, onCloseSidePanel } });
}
async function replaceSidePanel(model, env, pivotId, onCloseSidePanel = () => {}) {
env.notifyUser = env.notifyUser || (() => {});
env.replaceSidePanel = env.replaceSidePanel || (() => {});
await mountWithCleanup(Parent, { env, props: { model, pivotId, onCloseSidePanel } });
}
function getFieldItem(name, fixture) {
return fixture.querySelector(
`.o_popover_field_selector .o_model_field_selector_popover_item[data-name="${name}"]`
);
}
test("Open pivot properties", async function () {
const { pivotId, env, model } = await createSpreadsheetWithPivot();
await openSidePanel(model, env, pivotId);
expect(".o_spreadsheet_pivot_side_panel").toHaveCount(1);
});
test("Pivot properties panel shows ascending sorting", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot();
updatePivot(model, pivotId, {
sortedColumn: {
domain: [{ value: 1, field: "foo", type: "integer" }],
measure: "probability:avg",
order: "asc",
},
});
await openSidePanel(model, env, pivotId);
expect(".o-pivot-sort").toHaveCount(1);
expect(".o-pivot-sort").toHaveText(/ascending/);
const pivotSortingNodes = target.querySelectorAll(".o-sort-card");
expect(pivotSortingNodes[0].textContent).toBe("Foo = 1");
expect(pivotSortingNodes[1].textContent).toBe("Measure = Probability");
});
test("Pivot properties panel shows descending sorting", async function () {
const { model, pivotId, env } = await createSpreadsheetWithPivot();
updatePivot(model, pivotId, {
sortedColumn: {
domain: [{ value: 1, field: "foo", type: "integer" }],
measure: "probability:avg",
order: "desc",
},
});
await openSidePanel(model, env, pivotId);
expect(".o-pivot-sort").toHaveCount(1);
expect(".o-pivot-sort").toHaveText(/descending/);
const pivotSortingNodes = target.querySelectorAll(".o-sort-card");
expect(pivotSortingNodes[0].textContent).toBe("Foo = 1");
expect(pivotSortingNodes[1].textContent).toBe("Measure = Probability");
});
test("Removing the measure removes the sortedColumn", async function () {
const { model, pivotId, env } = await createSpreadsheetWithPivot();
updatePivot(model, pivotId, {
sortedColumn: {
domain: [{ value: 1, field: "foo", type: "integer" }],
measure: "probability:avg",
order: "asc",
},
});
await openSidePanel(model, env, pivotId);
expect(model.getters.getPivot(pivotId).definition.sortedColumn).not.toBe(undefined);
await contains(".pivot-measure .fa-trash").click();
expect(model.getters.getPivot(pivotId).definition.sortedColumn).toBe(undefined);
});
test("Removing a column removes the sortedColumn", async function () {
const { model, pivotId, env } = await createSpreadsheetWithPivot();
updatePivot(model, pivotId, {
sortedColumn: {
domain: [{ value: 1, field: "foo", type: "integer" }],
measure: "probability:avg",
order: "asc",
},
});
await openSidePanel(model, env, pivotId);
expect(model.getters.getPivot(pivotId).definition.sortedColumn).not.toBe(undefined);
await contains(".pivot-dimension .fa-trash").click();
expect(model.getters.getPivot(pivotId).definition.sortedColumn).toBe(undefined);
});
test("Open pivot properties properties with non-loaded field", async function () {
const PivotUIPlugin = coreViewsPluginRegistry.get("pivot_ui");
const { model, pivotId, env } = await createSpreadsheetWithPivot();
const pivotPlugin = model["handlers"].find((handler) => handler instanceof PivotUIPlugin);
const dataSource = Object.values(pivotPlugin.pivots)[0];
// remove all loading promises and the model to simulate the data source is not loaded
dataSource._loadPromise = undefined;
dataSource._createModelPromise = undefined;
dataSource._model = undefined;
await openSidePanel(model, env, pivotId);
expect(".o_spreadsheet_pivot_side_panel").toHaveCount(1);
});
test("Update the pivot title from the side panel", async function () {
const { model, pivotId, env } = await createSpreadsheetWithPivot();
await openSidePanel(model, env, pivotId);
const target = await contains(".os-input");
await target.click();
await target.edit("new name");
expect(model.getters.getPivotName(pivotId)).toBe("new name");
});
test("Duplicate the pivot from the side panel", async function () {
const { model, pivotId, env } = await createSpreadsheetWithPivot();
env.openSidePanel = (name, props) => {
expect.step(name);
expect.step(props.pivotId);
};
await openSidePanel(model, env, pivotId);
expect(model.getters.getPivotIds().length).toBe(1);
expect(".os-pivot-title").toHaveValue("Partner Pivot");
await contains(".os-cog-wheel-menu-icon").click();
await contains(".o-popover .fa-clone").click();
expect(model.getters.getPivotIds().length).toBe(2);
const duplicatedPivotId = model.getters.getPivotIds()[1];
expect.verifySteps(["PivotSidePanel", duplicatedPivotId]);
expect(model.getters.getPivotCoreDefinition(duplicatedPivotId).name).toBe(
"Partner Pivot (copy)"
);
});
test("A warning is displayed in the side panel if the pivot is unused", async function () {
const { model, pivotId, env } = await createSpreadsheetWithPivot();
await openSidePanel(model, env, pivotId);
expect(".o-validation-warning").toHaveCount(0);
model.dispatch("CREATE_SHEET", { sheetId: "sh2", name: "Sheet2" });
const activeSheetId = model.getters.getActiveSheetId();
model.dispatch("ACTIVATE_SHEET", { sheetIdFrom: activeSheetId, sheetIdTo: "sh2" });
model.dispatch("DELETE_SHEET", { sheetId: activeSheetId });
await animationFrame();
expect(".o-validation-warning").toHaveCount(1);
model.dispatch("REQUEST_UNDO");
await animationFrame();
expect(".o-validation-warning").toHaveCount(0);
});
test("An error is displayed in the side panel if the pivot has invalid model", async function () {
const { model, pivotId, env } = await createSpreadsheetWithPivot({
mockRPC: async function (route, { model, method, kwargs }) {
if (model === "unknown" && method === "fields_get") {
throw makeServerError({ code: 404 });
}
},
});
const pivot = model.getters.getPivotCoreDefinition(pivotId);
env.model.dispatch("UPDATE_PIVOT", {
pivotId,
pivot: {
...pivot,
model: "unknown",
},
});
await openSidePanel(model, env, pivotId);
expect(".o-validation-error").toHaveCount(1);
});
test("can drag a column dimension to row", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot();
await openSidePanel(model, env, pivotId);
await contains(".pivot-defer-update input").click();
expect("pivot-defer-update").toHaveCount(0, {
message: "defer updates is not displayed by default",
});
expect(".pivot-defer-update .btn").toHaveCount(0, {
message: "it should not show the update/discard buttons",
});
let definition = JSON.parse(JSON.stringify(model.getters.getPivotCoreDefinition(pivotId)));
expect(definition.columns).toEqual([{ fieldName: "foo" }]);
expect(definition.rows).toEqual([{ fieldName: "bar" }]);
await contains(".pivot-dimensions div:nth-child(2)").dragAndDrop(
".pivot-dimensions div:nth-child(4)",
{ position: "bottom" }
);
await animationFrame();
expect(".pivot-defer-update .fa-undo").toHaveCount(1);
expect(".pivot-defer-update .sp_apply_update").toHaveCount(1);
// TODO use a snapshot
definition = JSON.parse(JSON.stringify(model.getters.getPivotCoreDefinition(pivotId)));
// update is not applied until the user clicks on the save button
expect(definition.columns).toEqual([{ fieldName: "foo" }]);
expect(definition.rows).toEqual([{ fieldName: "bar" }]);
await contains(".pivot-defer-update .o-button-link").click();
expect(".pivot-defer-update .btn").toHaveCount(0, {
message: "it should not show the update/discard buttons",
});
definition = JSON.parse(JSON.stringify(model.getters.getPivotCoreDefinition(pivotId)));
expect(definition.columns).toEqual([]);
expect(definition.rows).toEqual([{ fieldName: "bar" }, { fieldName: "foo" }]);
});
test("updates are applied immediately after defer update checkbox has been unchecked", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot();
await openSidePanel(model, env, pivotId);
await contains(".pivot-dimensions div:nth-child(2)").dragAndDrop(
".pivot-dimensions div:nth-child(4)",
{ position: "bottom" }
);
await animationFrame();
expect(".pivot-defer-update .btn").toHaveCount(0, {
message: "it should not show the update/discard buttons",
});
const definition = JSON.parse(JSON.stringify(model.getters.getPivotCoreDefinition(pivotId)));
expect(definition.columns).toEqual([]);
expect(definition.rows).toEqual([{ fieldName: "bar" }, { fieldName: "foo" }]);
});
test("remove pivot dimension", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot();
await openSidePanel(model, env, pivotId);
await contains(".pivot-defer-update input").click();
await contains(".pivot-dimensions .fa-trash").click();
await animationFrame();
await contains(".pivot-defer-update .o-button-link").click();
const definition = JSON.parse(JSON.stringify(model.getters.getPivotCoreDefinition(pivotId)));
expect(definition.columns).toEqual([]);
expect(definition.rows).toEqual([{ fieldName: "bar" }]);
});
test("remove pivot date time dimension", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot({
arch: /*xml*/ `
<pivot>
<field name="date" type="row" interval="year"/>
<field name="date" type="row" interval="month"/>
<field name="probability" type="measure"/>
</pivot>
`,
});
await openSidePanel(model, env, pivotId);
await contains(".pivot-defer-update input").click();
await contains(".pivot-dimensions .fa-trash").click();
await animationFrame();
await contains(".pivot-defer-update .o-button-link").click();
const definition = JSON.parse(JSON.stringify(model.getters.getPivotCoreDefinition(pivotId)));
expect(definition.rows).toEqual([{ fieldName: "date", granularity: "month" }]);
});
test("add column dimension", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot({
arch: /*xml*/ `
<pivot>
<field name="probability" type="measure"/>
</pivot>
`,
});
await openSidePanel(model, env, pivotId);
await contains(".add-dimension.o-button").click();
await contains(
".o_popover_field_selector .o_model_field_selector_popover_item[data-name='bar'] button"
).click();
await contains(".pivot-defer-update .o-checkbox").click();
const definition = JSON.parse(JSON.stringify(model.getters.getPivotCoreDefinition(pivotId)));
expect(definition.columns).toEqual([{ fieldName: "bar", order: "asc" }]);
expect(definition.rows).toEqual([]);
});
test("add row dimension", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot({
arch: /*xml*/ `
<pivot>
<field name="probability" type="measure"/>
</pivot>
`,
});
await openSidePanel(model, env, pivotId);
await contains(".add-dimension.o-button:eq(1)").click();
await contains(
".o_popover_field_selector .o_model_field_selector_popover_item[data-name='bar'] button"
).click();
await contains(".pivot-defer-update .o-checkbox").click();
const definition = JSON.parse(JSON.stringify(model.getters.getPivotCoreDefinition(pivotId)));
expect(definition.columns).toEqual([]);
expect(definition.rows).toEqual([{ fieldName: "bar", order: "asc" }]);
});
test("add column dimension on a related model", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot({
arch: /*xml*/ `
<pivot>
<field name="probability" type="measure"/>
</pivot>
`,
});
await openSidePanel(model, env, pivotId);
await contains(".add-dimension.o-button").click();
await contains(
".o_popover_field_selector .o_model_field_selector_popover_item[data-name='product_id'] .o_model_field_selector_popover_item_relation"
).click();
await contains(
".o_popover_field_selector .o_model_field_selector_popover_item[data-name='active'] button"
).click();
expect(".fa-exclamation-triangle").toHaveCount(0);
expect(".pivot-dimension:first .o-fw-bold").toHaveText("Product > Active");
await contains(".pivot-defer-update .o-checkbox").click();
const definition = JSON.parse(JSON.stringify(model.getters.getPivotCoreDefinition(pivotId)));
expect(definition.columns).toEqual([{ fieldName: "product_id.active", order: "asc" }]);
expect(definition.rows).toEqual([]);
expect(".pivot-dimension:first .o-fw-bold").toHaveText("Product > Active");
});
test("add row dimension on a related model", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot({
arch: /*xml*/ `
<pivot>
<field name="probability" type="measure"/>
</pivot>
`,
});
await openSidePanel(model, env, pivotId);
await contains(".add-dimension.o-button:eq(1)").click();
await contains(
".o_popover_field_selector .o_model_field_selector_popover_item[data-name='product_id'] .o_model_field_selector_popover_item_relation"
).click();
await contains(
".o_popover_field_selector .o_model_field_selector_popover_item[data-name='active'] button"
).click();
expect(".fa-exclamation-triangle").toHaveCount(0);
expect(".pivot-dimension:first .o-fw-bold").toHaveText("Product > Active");
await contains(".pivot-defer-update .o-checkbox").click();
const definition = JSON.parse(JSON.stringify(model.getters.getPivotCoreDefinition(pivotId)));
expect(definition.columns).toEqual([]);
expect(definition.rows).toEqual([{ fieldName: "product_id.active", order: "asc" }]);
expect(".pivot-dimension:first .o-fw-bold").toHaveText("Product > Active");
});
test("single non-relation dimension is filtered out after selection", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot();
await openSidePanel(model, env, pivotId);
const fixture = getFixture();
await contains(".add-dimension.o-button").click();
getFieldItem("id", fixture).querySelector("button").click();
await contains(".pivot-defer-update .o-checkbox").click();
await contains(".add-dimension.o-button").click();
expect(getFieldItem("id", fixture)).toBe(null);
});
test("date/datetime field can be re-selected because of different granularities", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot();
await openSidePanel(model, env, pivotId);
const fixture = getFixture();
await contains(".add-dimension.o-button").click();
getFieldItem("date", fixture).querySelector("button").click();
await contains(".pivot-defer-update .o-checkbox").click();
await contains(".add-dimension.o-button").click();
const item = getFieldItem("date", fixture);
expect(item).not.toBe(null);
expect(item.getAttribute("data-tooltip")).toBe(null);
expect(item.querySelector(".o_model_field_selector_popover_item_name").disabled).toBe(false);
});
test("date/datetime field is filtered out once every granularity is used", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot();
const dateGranularities = pivotRegistry.get("ODOO").dateGranularities;
updatePivot(model, pivotId, {
rows: dateGranularities.map((granularity) => ({ fieldName: "date", granularity })),
});
await openSidePanel(model, env, pivotId);
const fixture = getFixture();
await contains(".add-dimension.o-button").click();
expect(getFieldItem("date", fixture)).toBe(null);
});
test("relation field cannot be re-selected but subfields stay accessible", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot();
await openSidePanel(model, env, pivotId);
const fixture = getFixture();
await contains(".add-dimension.o-button").click();
getFieldItem("product_id", fixture).querySelector("button").click();
await contains(".pivot-defer-update .o-checkbox").click();
await contains(".add-dimension.o-button").click();
const item = getFieldItem("product_id", fixture);
expect(item.getAttribute("data-tooltip")).toBe("Pivot contains duplicate groupbys");
expect(item.querySelector(".o_model_field_selector_popover_item_name").disabled).toBe(true);
expect(item.querySelector(".o_model_field_selector_popover_item_relation").disabled).toBe(
false
);
});
test("Cannot follow relation of a non store field", async function () {
Partner._fields.product_id = fields.Many2one({
string: "Product",
relation: "product",
store: false,
});
const { model, env, pivotId } = await createSpreadsheetWithPivot({
arch: /*xml*/ `
<pivot>
<field name="probability" type="measure"/>
</pivot>
`,
});
await openSidePanel(model, env, pivotId);
await contains(".add-dimension.o-button:eq(1)").click();
expect(
".o_popover_field_selector .o_model_field_selector_popover_item[data-name='product_id'] .o_model_field_selector_popover_item_relation"
).toHaveCount(0);
});
test("Cannot follow relation of a m2m field", async function () {
Partner._fields.product_ids = fields.Many2many({
relation: "product",
store: true,
searchable: true,
string: "Product",
});
const { model, env, pivotId } = await createSpreadsheetWithPivot({
arch: /*xml*/ `
<pivot>
<field name="probability" type="measure"/>
</pivot>
`,
});
await openSidePanel(model, env, pivotId);
await contains(".add-dimension.o-button:eq(1)").click();
expect(
".o_popover_field_selector .o_model_field_selector_popover_item[data-name='product_ids'] .o_model_field_selector_popover_item_relation"
).toHaveCount(0);
});
test("select dimensions with arrow keys", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot({
arch: /*xml*/ `
<pivot>
<field name="probability" type="measure"/>
</pivot>
`,
});
const fixture = getFixture();
await openSidePanel(model, env, pivotId);
await contains(".add-dimension.o-button").click();
let options = [
...fixture.querySelectorAll(
".o_popover_field_selector .o_model_field_selector_popover_item"
),
];
expect(options[0].className.includes("active")).toBe(true);
expect(options[1].className.includes("active")).toBe(false);
await contains(".o_model_field_selector_popover_search input").press("ArrowDown");
options = [
...fixture.querySelectorAll(
".o_popover_field_selector .o_model_field_selector_popover_item"
),
];
expect(options[0].className.includes("active")).toBe(false);
expect(options[1].className.includes("active")).toBe(true);
await contains(".o_model_field_selector_popover_search input").press("ArrowUp");
options = [
...fixture.querySelectorAll(
".o_popover_field_selector .o_model_field_selector_popover_item"
),
];
expect(options[0].className.includes("active")).toBe(true);
expect(options[1].className.includes("active")).toBe(false);
await contains(".o_model_field_selector_popover_search input").press("Enter");
await contains(".pivot-defer-update .o-checkbox").click();
const definition = JSON.parse(JSON.stringify(model.getters.getPivotCoreDefinition(pivotId)));
expect(definition.columns).toEqual([{ fieldName: "bar", order: "asc" }]);
expect(definition.rows).toEqual([]);
});
test("escape key closes the autocomplete popover", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot();
await openSidePanel(model, env, pivotId);
await contains(".add-dimension.o-button").click();
expect(".o_model_field_selector_popover_search input").toHaveCount(1);
await contains(".o_model_field_selector_popover_search input").press("Escape");
expect(".o_model_field_selector_popover_search input").toHaveCount(0);
});
test("add pivot dimension input autofocus", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot();
await openSidePanel(model, env, pivotId);
await contains(".add-dimension.o-button").click();
expect(".o_model_field_selector_popover_search input").toBeFocused();
await contains(".o_model_field_selector_popover_search input").press("Escape");
await contains(".add-dimension.o-button").click();
expect(".o_model_field_selector_popover_search input").toBeFocused();
});
test("add and search dimension", async function () {
const foo = fields.Integer({
string: "Foo",
store: true,
searchable: true,
aggregator: "sum",
groupable: true,
});
const foobar = fields.Char({
string: "FooBar",
store: true,
sortable: true,
groupable: true,
searchable: true,
});
Partner._fields.foobar = foobar;
Partner._fields.foo = foo;
Partner._records = [{ id: 1, foo: 12, bar: true, foobar: "foobar", probability: 10 }];
const { model, env, pivotId } = await createSpreadsheetWithPivot({
arch: /*xml*/ `
<pivot>
<field name="probability" type="measure"/>
</pivot>
`,
});
await openSidePanel(model, env, pivotId);
await contains(".pivot-defer-update input").click();
await contains(".add-dimension.o-button").click();
await contains(".o_model_field_selector_popover_search input").edit("fooba", {
confirm: false,
});
await runAllTimers();
await animationFrame();
await contains(".o_model_field_selector_popover_search input").press("Enter");
expect(model.getters.getPivotCoreDefinition(pivotId).columns).toEqual([]);
expect(model.getters.getPivotCoreDefinition(pivotId).rows).toEqual([]);
await contains(".pivot-defer-update input").click();
expect(
JSON.parse(JSON.stringify(model.getters.getPivotCoreDefinition(pivotId))).columns
).toEqual([{ fieldName: "foobar", order: "asc" }]);
expect(model.getters.getPivotCoreDefinition(pivotId).rows).toEqual([]);
});
test("remove pivot measure", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot();
await openSidePanel(model, env, pivotId);
await contains(".pivot-defer-update input").click();
await contains(".pivot-dimensions .fa-trash:last").click();
await animationFrame();
await contains(".pivot-defer-update .o-button-link").click();
const definition = JSON.parse(JSON.stringify(model.getters.getPivotCoreDefinition(pivotId)));
expect(definition.columns).toEqual([{ fieldName: "foo" }]);
expect(definition.rows).toEqual([{ fieldName: "bar" }]);
expect(definition.measures).toEqual([]);
});
test("add measure", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot();
await openSidePanel(model, env, pivotId);
const fixture = getFixture();
await contains(fixture.querySelectorAll(".add-dimension.o-button")[2]).click();
await contains(fixture.querySelectorAll(".o-popover .o-autocomplete-value")[0]).click();
await contains(".pivot-defer-update .o-checkbox").click();
const definition = JSON.parse(JSON.stringify(model.getters.getPivotCoreDefinition(pivotId)));
expect(definition.columns).toEqual([{ fieldName: "foo" }]);
expect(definition.rows).toEqual([{ fieldName: "bar" }]);
expect(definition.measures).toEqual([
{ id: "probability:avg", fieldName: "probability", aggregator: "avg" },
{ id: "__count:sum", fieldName: "__count", aggregator: "sum" },
]);
});
test("change measure aggregator", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot({
arch: /*xml*/ `
<pivot>
<field name="probability" type="measure"/>
</pivot>
`,
});
await openSidePanel(model, env, pivotId);
const fixture = getFixture();
await contains(".pivot-defer-update input").click();
expect(fixture.querySelector(".pivot-measure select")).toHaveValue("avg");
await contains(".pivot-measure select").select("min");
expect(".pivot-measure select option:checked").toHaveText("Minimum");
await contains(".pivot-defer-update .o-button-link").click();
expect(fixture.querySelector(".pivot-measure select")).toHaveValue("min");
const definition = model.getters.getPivotCoreDefinition(pivotId);
expect(definition.measures).toEqual([
{
id: "probability:min",
fieldName: "probability",
aggregator: "min",
userDefinedName: undefined,
computedBy: undefined,
format: undefined,
isHidden: undefined,
display: undefined,
},
]);
});
test("pivot with a reference field measure", async function () {
const currency_reference = fields.Reference({
string: "Currency reference",
store: true,
sortable: true,
groupable: true,
searchable: true,
aggregator: "count_distinct",
selection: [["res.currency", "Currency"]],
});
Partner._fields.currency_reference = currency_reference;
Partner._records = [{ id: 1, currency_reference: "res.currency,1" }];
const { model, env, pivotId } = await createSpreadsheetWithPivot({
arch: /*xml*/ `
<pivot>
<field name="currency_reference" type="measure"/>
</pivot>
`,
});
await openSidePanel(model, env, pivotId);
const fixture = getFixture();
expect(fixture.querySelector(".pivot-measure select")).toHaveValue("count_distinct");
});
test("change dimension order", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot({
arch: /*xml*/ `
<pivot>
<field name="foo" type="row"/>
<field name="probability" type="measure"/>
</pivot>
`,
});
await openSidePanel(model, env, pivotId);
await contains(".pivot-defer-update input").click();
const fixture = getFixture();
expect(fixture.querySelector(".pivot-dimensions select")).toHaveValue("");
await contains(".pivot-dimensions select").select("desc");
expect(fixture.querySelector(".pivot-dimensions select")).toHaveValue("desc");
await contains(".pivot-defer-update .o-button-link").click();
let definition = JSON.parse(JSON.stringify(model.getters.getPivotCoreDefinition(pivotId)));
expect(definition.rows).toEqual([{ fieldName: "foo", order: "desc" }]);
// reset to automatic
await contains(".pivot-dimensions select").select("");
await contains(".pivot-defer-update .o-button-link").click();
definition = JSON.parse(JSON.stringify(model.getters.getPivotCoreDefinition(pivotId)));
expect(definition.rows).toEqual([{ fieldName: "foo" }]);
});
test("change date dimension granularity", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot({
arch: /*xml*/ `
<pivot>
<field name="date" interval="day" type="row"/>
<field name="probability" type="measure"/>
</pivot>
`,
});
await openSidePanel(model, env, pivotId);
const fixture = getFixture();
await contains(".pivot-defer-update input").click();
expect(fixture.querySelectorAll(".pivot-dimensions select")[0]).toHaveValue("day");
await contains(fixture.querySelectorAll(".pivot-dimensions select")[0]).select("week");
expect(fixture.querySelectorAll(".pivot-dimensions select")[0]).toHaveValue("week");
await contains(".pivot-defer-update .o-button-link").click();
const definition = JSON.parse(JSON.stringify(model.getters.getPivotCoreDefinition(pivotId)));
expect(definition.rows).toEqual([{ fieldName: "date", granularity: "week" }]);
});
test("pivot with twice the same date field with different granularity", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot({
arch: /*xml*/ `
<pivot>
<field name="date" interval="year" type="row"/>
<field name="date" interval="day" type="row"/>
<field name="probability" type="measure"/>
</pivot>
`,
});
await openSidePanel(model, env, pivotId);
const fixture = getFixture();
const firstDateGroup = fixture.querySelectorAll(".pivot-dimensions select")[0];
const secondDateGroup = fixture.querySelectorAll(".pivot-dimensions select")[2];
expect(firstDateGroup).toHaveValue("year");
expect(secondDateGroup).toHaveValue("day");
expect(firstDateGroup).toHaveText(
"Year\nQuarter\nQuarter & Year\nMonth\nMonth & Year\nWeek\nWeek & Year\nDay of Month\nDay of Week"
);
expect(secondDateGroup).toHaveText(
"Quarter\nQuarter & Year\nMonth\nMonth & Year\nWeek\nWeek & Year\nDay of Month\nDay\nDay of Week"
);
});
test("Can change measure display as from the side panel", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot();
env.replaceSidePanel = (name, props) => {
expect.step(name);
};
await replaceSidePanel(model, env, pivotId);
await contains(".pivot-measure .fa-cog").click();
expect.verifySteps(["PivotMeasureDisplayPanel"]);
});
test("display pivot related filters panel", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot();
await openSidePanel(model, env, pivotId);
await addGlobalFilter(
model,
{ id: "42", type: "relation", label: "Filter" },
{
pivot: {
[pivotId]: {
chain: "product_id",
type: "many2one",
},
},
}
);
await addGlobalFilter(model, { id: "43", type: "relation", label: "Filter 2" });
expect(".o_side_panel_collapsible_title:contains(Matching 1 / 2 filters)").toHaveCount(1);
});
test("PivotModelFieldSelectorPopover in debug mode for column", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot({
arch: /*xml*/ `
<pivot>
<field name="probability" type="measure"/>
</pivot>
`,
});
env.debug = true;
await openSidePanel(model, env, pivotId);
await contains(".add-dimension.o-button:eq(0)").click();
expect(
"li.o_model_field_selector_popover_item div.o_model_field_selector_popover_item_title"
).toHaveCount(12);
});
test("PivotModelFieldSelectorPopover in debug mode for row", async function () {
const { model, env, pivotId } = await createSpreadsheetWithPivot({
arch: /*xml*/ `
<pivot>
<field name="probability" type="measure"/>
</pivot>
`,
});
env.debug = true;
await openSidePanel(model, env, pivotId);
await contains(".add-dimension.o-button:eq(1)").click();
expect(
"li.o_model_field_selector_popover_item div.o_model_field_selector_popover_item_title"
).toHaveCount(12);
});