5701 lines
197 KiB
JavaScript
5701 lines
197 KiB
JavaScript
/** @odoo-module **/
|
||
|
||
import { registry } from "@web/core/registry";
|
||
import { session } from "@web/session";
|
||
import { makeFakeLocalizationService, makeFakeUserService } from "../helpers/mock_services";
|
||
import {
|
||
click,
|
||
getFixture,
|
||
legacyExtraNextTick,
|
||
makeDeferred,
|
||
mockDownload,
|
||
nextTick,
|
||
patchDate,
|
||
patchWithCleanup,
|
||
triggerEvent,
|
||
triggerEvents,
|
||
mouseEnter,
|
||
} from "../helpers/utils";
|
||
import {
|
||
applyGroup,
|
||
editFavoriteName,
|
||
getFacetTexts,
|
||
removeFacet,
|
||
saveFavorite,
|
||
selectGroup,
|
||
setupControlPanelFavoriteMenuRegistry,
|
||
setupControlPanelServiceRegistry,
|
||
toggleAddCustomGroup,
|
||
toggleComparisonMenu,
|
||
toggleFavoriteMenu,
|
||
toggleFilterMenu,
|
||
toggleGroupByMenu,
|
||
toggleMenu,
|
||
toggleMenuItem,
|
||
toggleMenuItemOption,
|
||
toggleSaveFavorite,
|
||
} from "../search/helpers";
|
||
import { createWebClient, doAction } from "../webclient/helpers";
|
||
import { makeView } from "./helpers";
|
||
import { browser } from "@web/core/browser/browser";
|
||
|
||
const serviceRegistry = registry.category("services");
|
||
|
||
import { markup } from "@odoo/owl";
|
||
|
||
/**
|
||
* Helper function that returns, given a pivot instance, the values of the
|
||
* table, separated by ','.
|
||
*
|
||
* @returns {string}
|
||
*/
|
||
function getCurrentValues(el) {
|
||
return [...el.querySelectorAll(".o_pivot_cell_value div")].map((el) => el.innerText).join();
|
||
}
|
||
|
||
let serverData;
|
||
let target;
|
||
QUnit.module("Views", (hooks) => {
|
||
hooks.beforeEach(() => {
|
||
target = getFixture();
|
||
serverData = {
|
||
models: {
|
||
partner: {
|
||
fields: {
|
||
foo: {
|
||
string: "Foo",
|
||
type: "integer",
|
||
searchable: true,
|
||
group_operator: "sum",
|
||
},
|
||
bar: { string: "bar", type: "boolean", store: true, sortable: true },
|
||
date: { string: "Date", type: "date", store: true, sortable: true },
|
||
product_id: {
|
||
string: "Product",
|
||
type: "many2one",
|
||
relation: "product",
|
||
store: true,
|
||
sortable: true,
|
||
},
|
||
other_product_id: {
|
||
string: "Other Product",
|
||
type: "many2one",
|
||
relation: "product",
|
||
store: true,
|
||
sortable: true,
|
||
},
|
||
non_stored_m2o: {
|
||
string: "Non Stored M2O",
|
||
type: "many2one",
|
||
relation: "product",
|
||
},
|
||
customer: {
|
||
string: "Customer",
|
||
type: "many2one",
|
||
relation: "customer",
|
||
store: true,
|
||
sortable: true,
|
||
},
|
||
computed_field: {
|
||
string: "Computed and not stored",
|
||
type: "integer",
|
||
compute: true,
|
||
group_operator: "sum",
|
||
},
|
||
company_type: {
|
||
string: "Company Type",
|
||
type: "selection",
|
||
selection: [
|
||
["company", "Company"],
|
||
["individual", "individual"],
|
||
],
|
||
searchable: true,
|
||
sortable: true,
|
||
store: true,
|
||
},
|
||
price_nonaggregatable: {
|
||
string: "Price non-aggregatable",
|
||
type: "monetary",
|
||
group_operator: undefined,
|
||
store: true,
|
||
},
|
||
},
|
||
records: [
|
||
{
|
||
id: 1,
|
||
foo: 12,
|
||
bar: true,
|
||
date: "2016-12-14",
|
||
product_id: 37,
|
||
customer: 1,
|
||
computed_field: 19,
|
||
company_type: "company",
|
||
},
|
||
{
|
||
id: 2,
|
||
foo: 1,
|
||
bar: true,
|
||
date: "2016-10-26",
|
||
product_id: 41,
|
||
customer: 2,
|
||
computed_field: 23,
|
||
company_type: "individual",
|
||
},
|
||
{
|
||
id: 3,
|
||
foo: 17,
|
||
bar: true,
|
||
date: "2016-12-15",
|
||
product_id: 41,
|
||
customer: 2,
|
||
computed_field: 26,
|
||
company_type: "company",
|
||
},
|
||
{
|
||
id: 4,
|
||
foo: 2,
|
||
bar: false,
|
||
date: "2016-04-11",
|
||
product_id: 41,
|
||
customer: 1,
|
||
computed_field: 19,
|
||
company_type: "individual",
|
||
},
|
||
],
|
||
},
|
||
product: {
|
||
fields: {
|
||
name: { string: "Product Name", type: "char" },
|
||
},
|
||
records: [
|
||
{
|
||
id: 37,
|
||
display_name: "xphone",
|
||
},
|
||
{
|
||
id: 41,
|
||
display_name: "xpad",
|
||
},
|
||
],
|
||
},
|
||
customer: {
|
||
fields: {
|
||
name: { string: "Customer Name", type: "char" },
|
||
},
|
||
records: [
|
||
{
|
||
id: 1,
|
||
display_name: "First",
|
||
},
|
||
{
|
||
id: 2,
|
||
display_name: "Second",
|
||
},
|
||
],
|
||
},
|
||
},
|
||
};
|
||
setupControlPanelFavoriteMenuRegistry();
|
||
setupControlPanelServiceRegistry();
|
||
serviceRegistry.add("localization", makeFakeLocalizationService());
|
||
serviceRegistry.add("user", makeFakeUserService());
|
||
patchWithCleanup(browser, { setTimeout: (fn) => fn() });
|
||
});
|
||
|
||
QUnit.module("PivotView");
|
||
|
||
QUnit.test("simple pivot rendering", async function (assert) {
|
||
assert.expect(4);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot string="Partners">
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
mockRPC(route, args) {
|
||
if (args.method === "read_group") {
|
||
assert.strictEqual(
|
||
args.kwargs.lazy,
|
||
false,
|
||
"the read_group should be done with the lazy=false option"
|
||
);
|
||
}
|
||
},
|
||
});
|
||
|
||
assert.hasClass(target.querySelector(".o_pivot_view"), "o_view_controller");
|
||
assert.hasClass(target.querySelector("table"), "o_enable_linking");
|
||
assert.containsOnce(
|
||
target,
|
||
"td.o_pivot_cell_value:contains(32)",
|
||
"should contain a pivot cell with the sum of all records"
|
||
);
|
||
});
|
||
|
||
QUnit.test(
|
||
"all measures should be displayed with a pivot_measures context",
|
||
async function (assert) {
|
||
assert.expect(1);
|
||
|
||
serverData.models.partner.fields.bouh = {
|
||
string: "bouh",
|
||
type: "integer",
|
||
group_operator: "sum",
|
||
};
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
context: { pivot_measures: ["foo"] },
|
||
arch: `
|
||
<pivot string="Partners">
|
||
<field name="foo" type="measure"/>
|
||
<field name="bouh" type="measure"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
await click(target.querySelector(".o_cp_bottom_left button.dropdown-toggle"));
|
||
const measures = Array.from(
|
||
target.querySelectorAll(".o_cp_bottom_left .dropdown-menu .dropdown-item")
|
||
).map((e) => e.textContent);
|
||
|
||
assert.deepEqual(measures, ["bouh", "Foo", "Count"]);
|
||
}
|
||
);
|
||
|
||
QUnit.test("pivot rendering with widget", async function (assert) {
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot string="Partners">
|
||
<field name="foo" type="measure" widget="float_time"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
assert.containsOnce(
|
||
target,
|
||
"td.o_pivot_cell_value:contains(32:00)",
|
||
"should contain a pivot cell with the sum of all records"
|
||
);
|
||
});
|
||
|
||
QUnit.test("pivot rendering with string attribute on field", async function (assert) {
|
||
serverData.models.partner.fields.foo = {
|
||
string: "Foo",
|
||
type: "integer",
|
||
store: true,
|
||
group_operator: "sum",
|
||
};
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot string="Partners">
|
||
<field name="foo" string="BAR" type="measure"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
await click(target.querySelector(".o_cp_bottom_left button.dropdown-toggle"));
|
||
assert.strictEqual(
|
||
target.querySelector(".o_cp_bottom_left .dropdown-menu .dropdown-item").innerText,
|
||
"BAR"
|
||
);
|
||
assert.strictEqual(
|
||
target.querySelector(".o_pivot_measure_row").innerText,
|
||
"BAR",
|
||
"the displayed name should be the one set in the string attribute"
|
||
);
|
||
});
|
||
|
||
QUnit.test("Pivot with integer row group by with 0 as header", async function (assert) {
|
||
serverData.models.partner.records[0].foo = 0;
|
||
serverData.models.partner.records[1].foo = 0;
|
||
serverData.models.partner.records[2].foo = 0;
|
||
serverData.models.partner.records[3].foo = 0;
|
||
|
||
const pivot = await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot string="Partners">
|
||
<field name="foo" type="measure"/>
|
||
<field name="foo" type="row"/>
|
||
</pivot>`,
|
||
});
|
||
const { rows } = pivot.model.getTable();
|
||
assert.strictEqual(rows.length, 2);
|
||
assert.strictEqual(rows[0].title, "Total");
|
||
assert.strictEqual(rows[1].title, 0);
|
||
});
|
||
|
||
QUnit.test("Pivot with integer col group by with 0 as header", async function (assert) {
|
||
serverData.models.partner.records[0].foo = 0;
|
||
serverData.models.partner.records[1].foo = 0;
|
||
serverData.models.partner.records[2].foo = 0;
|
||
serverData.models.partner.records[3].foo = 0;
|
||
|
||
const pivot = await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot string="Partners">
|
||
<field name="foo" type="measure"/>
|
||
<field name="foo" type="col"/>
|
||
</pivot>`,
|
||
});
|
||
const { headers } = pivot.model.getTable();
|
||
assert.strictEqual(headers[1][0].title, 0);
|
||
});
|
||
|
||
QUnit.test(
|
||
"pivot rendering with string attribute on non stored field",
|
||
async function (assert) {
|
||
serverData.models.partner.fields.fubar = {
|
||
string: "Fubar",
|
||
type: "integer",
|
||
store: false,
|
||
group_operator: "sum",
|
||
};
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot string="Partners">
|
||
<field name="fubar" string="fubar" type="measure"/>
|
||
</pivot>`,
|
||
});
|
||
assert.strictEqual(
|
||
target.querySelector(".o_pivot_measure_row").innerText,
|
||
"fubar",
|
||
"the displayed name should be the one set in the string attribute"
|
||
);
|
||
}
|
||
);
|
||
|
||
QUnit.test("pivot rendering with invisible attribute on field", async function (assert) {
|
||
// when invisible, a field should neither be an active measure nor be a selectable measure
|
||
Object.assign(serverData.models.partner.fields, {
|
||
foo: { string: "Foo", type: "integer", store: true, group_operator: "sum" },
|
||
foo2: { string: "Foo2", type: "integer", store: true, group_operator: "sum" },
|
||
});
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot string="Partners">
|
||
<field name="foo" type="measure"/>
|
||
<field name="foo2" type="measure" invisible="1"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
// there should be only one displayed measure as the other one is invisible
|
||
assert.containsOnce(target, ".o_pivot_measure_row");
|
||
await click(target.querySelector(".o_cp_bottom_left button.dropdown-toggle"));
|
||
// there should be only one measure besides count, as the other one is invisible
|
||
assert.containsN(target, ".o_cp_bottom_left .dropdown-menu .dropdown-item", 2);
|
||
// the invisible field souldn't be in the groupable fields neither
|
||
await click(target.querySelector(".o_pivot_header_cell_closed"));
|
||
assert.containsNone(target, '.dropdown-menu a[data-field="foo2"]');
|
||
});
|
||
|
||
QUnit.test('pivot view without "string" attribute', async function (assert) {
|
||
assert.expect(1);
|
||
|
||
const pivot = await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
// this is important for export functionality.
|
||
assert.strictEqual(
|
||
pivot.model.metaData.title.toString(),
|
||
pivot.env._t("Untitled"),
|
||
"should have a valid title"
|
||
);
|
||
});
|
||
|
||
QUnit.test("group headers should have a tooltip", async function (assert) {
|
||
assert.expect(2);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id" type="col"/>
|
||
<field name="date" type="row"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
assert.strictEqual(
|
||
target.querySelectorAll("tbody .o_pivot_header_cell_closed")[0].dataset.tooltip,
|
||
"Date"
|
||
);
|
||
assert.strictEqual(
|
||
target.querySelectorAll("thead .o_pivot_header_cell_closed")[1].dataset.tooltip,
|
||
"Product"
|
||
);
|
||
});
|
||
|
||
QUnit.test(
|
||
"pivot view add computed fields explicitly defined as measure",
|
||
async function (assert) {
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="computed_field" type="measure"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
await click(target.querySelector(".o_cp_bottom_left button.dropdown-toggle"));
|
||
assert.containsOnce(
|
||
target,
|
||
".o_cp_bottom_left .dropdown-menu .dropdown-item:contains(Computed and not stored)"
|
||
);
|
||
assert.strictEqual(
|
||
target.querySelector(".o_pivot_measure_row").innerText,
|
||
"Computed and not stored"
|
||
);
|
||
}
|
||
);
|
||
|
||
QUnit.test(
|
||
"pivot view do not add number field without group_operator",
|
||
async function (assert) {
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="price_nonaggregatable"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
await click(target.querySelector(".o_cp_bottom_left button.dropdown-toggle"));
|
||
assert.containsNone(
|
||
target,
|
||
".o_cp_bottom_left .dropdown-menu .dropdown-item:contains(Price non-aggregatable)"
|
||
);
|
||
}
|
||
);
|
||
|
||
QUnit.test("clicking on a cell triggers a doAction", async function (assert) {
|
||
assert.expect(2);
|
||
|
||
serverData.views = {
|
||
"partner,2,form": "<form/>",
|
||
"partner,false,list": "<list/>",
|
||
"partner,5,kanban": "<kanban/>",
|
||
};
|
||
patchWithCleanup(session, {
|
||
user_context: { userContextKey: true },
|
||
});
|
||
const fakeActionService = {
|
||
start() {
|
||
return {
|
||
doAction(action) {
|
||
assert.deepEqual(
|
||
action,
|
||
{
|
||
context: { someKey: true, uid: 7, userContextKey: true },
|
||
domain: [["product_id", "=", 37]],
|
||
name: "Partners",
|
||
res_model: "partner",
|
||
target: "current",
|
||
type: "ir.actions.act_window",
|
||
view_mode: "list",
|
||
views: [
|
||
[false, "list"],
|
||
[2, "form"],
|
||
],
|
||
},
|
||
"should trigger do_action with the correct args"
|
||
);
|
||
return Promise.resolve(true);
|
||
},
|
||
};
|
||
},
|
||
};
|
||
serviceRegistry.add("action", fakeActionService, { force: true });
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot string="Partners">
|
||
<field name="product_id" type="row"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
context: { someKey: true, search_default_test: 3 },
|
||
config: {
|
||
views: [
|
||
[2, "form"],
|
||
[5, "kanban"],
|
||
[false, "list"],
|
||
[false, "pivot"],
|
||
],
|
||
},
|
||
});
|
||
|
||
assert.hasClass(target.querySelector("table"), "o_enable_linking");
|
||
await click(target.querySelectorAll(".o_pivot_cell_value")[1]); // should trigger a do_action
|
||
});
|
||
|
||
QUnit.test("row and column are highlighted when hovering a cell", async function (assert) {
|
||
assert.expect(11);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot string="Partners">
|
||
<field name="foo" type="col"/>
|
||
<field name="product_id" type="row"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
// check row highlighting
|
||
assert.hasClass(
|
||
target.querySelector("table"),
|
||
"table-hover",
|
||
"with className 'table-hover', rows are highlighted (bootstrap)"
|
||
);
|
||
|
||
// check column highlighting
|
||
// hover third measure
|
||
await triggerEvents(target, "th.o_pivot_measure_row:nth-of-type(3)", ["mouseover"]);
|
||
assert.containsN(target, ".o_cell_hover", 3);
|
||
for (var i = 1; i <= 3; i++) {
|
||
assert.hasClass(
|
||
target.querySelector(`tbody tr:nth-of-type(${i}) td:nth-of-type(3)`),
|
||
"o_cell_hover"
|
||
);
|
||
}
|
||
await triggerEvents(target, "th.o_pivot_measure_row:nth-of-type(3)", ["mouseout"]);
|
||
assert.containsNone(target, ".o_cell_hover");
|
||
|
||
// hover second cell, second row
|
||
await triggerEvents(target, "tbody tr:nth-of-type(1) td:nth-of-type(2)", ["mouseover"]);
|
||
assert.containsN(target, ".o_cell_hover", 3);
|
||
for (i = 1; i <= 3; i++) {
|
||
assert.hasClass(
|
||
target.querySelector(`tbody tr:nth-of-type(${i}) td:nth-of-type(2)`),
|
||
"o_cell_hover"
|
||
);
|
||
}
|
||
await triggerEvents(target, "tbody tr:nth-of-type(2) td:nth-of-type(2)", ["mouseout"]);
|
||
assert.containsNone(target, ".o_cell_hover");
|
||
});
|
||
|
||
QUnit.test("columns are highlighted when hovering a measure", async function (assert) {
|
||
assert.expect(15);
|
||
|
||
patchDate(2016, 11, 20, 1, 0, 0);
|
||
|
||
serverData.models.partner.records[0].date = "2016-11-15";
|
||
serverData.models.partner.records[1].date = "2016-12-17";
|
||
serverData.models.partner.records[2].date = "2016-11-22";
|
||
serverData.models.partner.records[3].date = "2016-11-03";
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id" type="row"/>
|
||
<field name="date" type="col"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="date_filter" date="date" domain="[]" default_period='this_month'/>
|
||
</search>`,
|
||
context: { search_default_date_filter: true },
|
||
});
|
||
|
||
await toggleComparisonMenu(target);
|
||
await toggleMenuItem(target, "Date: Previous period");
|
||
|
||
// hover Count in first group
|
||
await triggerEvents(target, "th.o_pivot_measure_row:nth-of-type(1)", ["mouseover"]);
|
||
assert.containsN(target, ".o_cell_hover", 3);
|
||
for (let i = 1; i <= 3; i++) {
|
||
assert.hasClass(
|
||
target.querySelector(`tbody tr:nth-of-type(${i}) td:nth-of-type(1)`),
|
||
"o_cell_hover"
|
||
);
|
||
}
|
||
await triggerEvents(target, "th.o_pivot_measure_row:nth-of-type(1)", ["mouseout"]);
|
||
assert.containsNone(target, ".o_cell_hover");
|
||
|
||
// hover Count in second group
|
||
await triggerEvents(target, "th.o_pivot_measure_row:nth-of-type(2)", ["mouseover"]);
|
||
assert.containsN(target, ".o_cell_hover", 3);
|
||
for (let i = 1; i <= 3; i++) {
|
||
assert.hasClass(
|
||
target.querySelector(`tbody tr:nth-of-type(${i}) td:nth-of-type(4)`),
|
||
"o_cell_hover"
|
||
);
|
||
}
|
||
await triggerEvents(target, "th.o_pivot_measure_row:nth-of-type(2)", ["mouseout"]);
|
||
assert.containsNone(target, ".o_cell_hover");
|
||
|
||
// hover Count in total column
|
||
await triggerEvents(target, "th.o_pivot_measure_row:nth-of-type(3)", ["mouseover"]);
|
||
assert.containsN(target, ".o_cell_hover", 3);
|
||
for (let i = 1; i <= 3; i++) {
|
||
assert.hasClass(
|
||
target.querySelector(`tbody tr:nth-of-type(${i}) td:nth-of-type(7)`),
|
||
"o_cell_hover"
|
||
);
|
||
}
|
||
await triggerEvents(target, "th.o_pivot_measure_row:nth-of-type(3)", ["mouseout"]);
|
||
assert.containsNone(target, ".o_cell_hover");
|
||
});
|
||
|
||
QUnit.test(
|
||
"columns are highlighted when hovering an origin (comparison mode)",
|
||
async function (assert) {
|
||
assert.expect(5);
|
||
|
||
patchDate(2016, 11, 20, 1, 0, 0);
|
||
|
||
serverData.models.partner.records[0].date = "2016-11-15";
|
||
serverData.models.partner.records[1].date = "2016-12-17";
|
||
serverData.models.partner.records[2].date = "2016-11-22";
|
||
serverData.models.partner.records[3].date = "2016-11-03";
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id" type="row"/>
|
||
<field name="date" type="col"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="date_filter" date="date" domain="[]" default_period='this_month'/>
|
||
</search>`,
|
||
context: { search_default_date_filter: true },
|
||
});
|
||
|
||
await toggleComparisonMenu(target);
|
||
await toggleMenuItem(target, "Date: Previous period");
|
||
|
||
// hover the second origin in second group
|
||
await triggerEvents(target, "th.o_pivot_origin_row:nth-of-type(5)", ["mouseover"]);
|
||
assert.containsN(target, ".o_cell_hover", 3);
|
||
for (let i = 1; i <= 3; i++) {
|
||
assert.hasClass(
|
||
target.querySelector(`tbody tr:nth-of-type(${i}) td:nth-of-type(5)`),
|
||
"o_cell_hover"
|
||
);
|
||
}
|
||
await triggerEvents(target, "th.o_pivot_origin_row:nth-of-type(5)", ["mouseout"]);
|
||
assert.containsNone(target, ".o_cell_hover");
|
||
}
|
||
);
|
||
|
||
QUnit.test('pivot view with disable_linking="True"', async function (assert) {
|
||
const fakeActionService = {
|
||
start() {
|
||
return {
|
||
doAction() {
|
||
throw new Error("should not execute an action");
|
||
},
|
||
};
|
||
},
|
||
};
|
||
serviceRegistry.add("action", fakeActionService, { force: true });
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot disable_linking="True">
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
assert.doesNotHaveClass(target.querySelector("table"), "o_enable_linking");
|
||
assert.containsOnce(target, ".o_pivot_cell_value");
|
||
await click(target.querySelector(".o_pivot_cell_value")); // should not trigger a do_action
|
||
});
|
||
|
||
QUnit.test('clicking on the "Total" cell with time range activated', async function (assert) {
|
||
assert.expect(2);
|
||
|
||
patchDate(2016, 11, 20, 1, 0, 0);
|
||
|
||
const fakeActionService = {
|
||
start() {
|
||
return {
|
||
doAction(action) {
|
||
assert.deepEqual(
|
||
action.domain,
|
||
["&", ["date", ">=", "2016-12-01"], ["date", "<=", "2016-12-31"]],
|
||
"should trigger do_action with the correct action domain"
|
||
);
|
||
return Promise.resolve(true);
|
||
},
|
||
};
|
||
},
|
||
};
|
||
serviceRegistry.add("action", fakeActionService, { force: true });
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: "<pivot/>",
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="date_filter" date="date" domain="[]" default_period='this_month'/>
|
||
</search>`,
|
||
context: { search_default_date_filter: true },
|
||
});
|
||
|
||
assert.hasClass(
|
||
target.querySelector("table"),
|
||
"o_enable_linking",
|
||
"root node should have classname 'o_enable_linking'"
|
||
);
|
||
await click(target.querySelector(".o_pivot_cell_value"));
|
||
});
|
||
|
||
QUnit.test(
|
||
'clicking on a fake cell value ("empty group") in comparison mode',
|
||
async function (assert) {
|
||
assert.expect(3);
|
||
|
||
patchDate(2016, 11, 20, 1, 0, 0);
|
||
|
||
serverData.models.partner.records[0].date = "2016-11-15";
|
||
serverData.models.partner.records[1].date = "2016-11-17";
|
||
serverData.models.partner.records[2].date = "2016-11-22";
|
||
serverData.models.partner.records[3].date = "2016-11-03";
|
||
|
||
const expectedDomains = [
|
||
["&", ["date", ">=", "2016-12-01"], ["date", "<=", "2016-12-31"]],
|
||
[[0, "=", 1]],
|
||
];
|
||
const fakeActionService = {
|
||
start() {
|
||
return {
|
||
doAction(action) {
|
||
assert.deepEqual(action.domain, expectedDomains.shift());
|
||
return Promise.resolve(true);
|
||
},
|
||
};
|
||
},
|
||
};
|
||
serviceRegistry.add("action", fakeActionService, { force: true });
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `<pivot><field name="product_id" type="row"/></pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="date_filter" date="date" domain="[]" default_period='this_month'/>
|
||
</search>`,
|
||
context: { search_default_date_filter: true },
|
||
});
|
||
|
||
await toggleComparisonMenu(target);
|
||
await toggleMenuItem(target, "Date: Previous period");
|
||
|
||
assert.hasClass(target.querySelector("table"), "o_enable_linking");
|
||
// here we click on the group corresponding to Total/Total/This Month
|
||
target.querySelectorAll(".o_pivot_cell_value")[1].click(); // should trigger a do_action with appropriate domain
|
||
// here we click on the group corresponding to xphone/Total/This Month
|
||
target.querySelectorAll(".o_pivot_cell_value")[4].click(); // should trigger a do_action with appropriate domain
|
||
}
|
||
);
|
||
|
||
QUnit.test("pivot view grouped by date field", async function (assert) {
|
||
assert.expect(2);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="date" interval="month" type="col"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
mockRPC(route, args) {
|
||
if (args.method === "read_group") {
|
||
const wrongFields = args.kwargs.fields.filter((field) => {
|
||
return !(field.split(":")[0] in serverData.models.partner.fields);
|
||
});
|
||
assert.ok(
|
||
!wrongFields.length,
|
||
"fields given to read_group should exist on the model"
|
||
);
|
||
}
|
||
},
|
||
});
|
||
});
|
||
|
||
QUnit.test("without measures, pivot view uses __count by default", async function (assert) {
|
||
assert.expect(4);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: "<pivot></pivot>",
|
||
mockRPC(route, args) {
|
||
if (args.method === "read_group") {
|
||
assert.deepEqual(
|
||
args.kwargs.fields,
|
||
["__count"],
|
||
"should make a read_group with no valid fields"
|
||
);
|
||
}
|
||
},
|
||
});
|
||
|
||
await click(target.querySelector(".o_cp_bottom_left .dropdown-toggle"));
|
||
assert.containsOnce(target, ".o_cp_bottom_left .dropdown-menu .dropdown-item");
|
||
const measure = target.querySelector(".o_cp_bottom_left .dropdown-menu .dropdown-item");
|
||
assert.strictEqual(measure.innerText, "Count");
|
||
assert.hasClass(measure, "selected", "The count measure should be selected");
|
||
});
|
||
|
||
QUnit.test("pivot view can be reloaded", async function (assert) {
|
||
let readGroupCount = 0;
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: "<pivot></pivot>",
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="some_filter" string="Some Filter" domain="[('foo', '>', 10)]"/>
|
||
</search>`,
|
||
mockRPC(route, args) {
|
||
if (args.method === "read_group") {
|
||
readGroupCount++;
|
||
}
|
||
},
|
||
});
|
||
|
||
assert.containsOnce(
|
||
target,
|
||
"td.o_pivot_cell_value:contains(4)",
|
||
"should contain a pivot cell with the number of all records"
|
||
);
|
||
assert.strictEqual(readGroupCount, 1, "should have done 1 rpc");
|
||
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "Some Filter");
|
||
assert.containsOnce(
|
||
target,
|
||
"td.o_pivot_cell_value:contains(2)",
|
||
"should contain a pivot cell with the number of remaining records"
|
||
);
|
||
assert.strictEqual(readGroupCount, 2, "should have done 2 rpcs");
|
||
});
|
||
|
||
QUnit.test("pivot view grouped by many2one field", async function (assert) {
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id" type="row"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
assert.containsOnce(target, ".o_pivot_header_cell_opened", "should have one opened header");
|
||
assert.containsOnce(
|
||
target,
|
||
".o_pivot_header_cell_closed:contains(xphone)",
|
||
"should display one header with 'xphone'"
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
".o_pivot_header_cell_closed:contains(xpad)",
|
||
"should display one header with 'xpad'"
|
||
);
|
||
});
|
||
|
||
QUnit.test("basic folding/unfolding", async function (assert) {
|
||
assert.expect(7);
|
||
|
||
let rpcCount = 0;
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id" type="row"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
mockRPC(route, args) {
|
||
if (args.method === "read_group") {
|
||
rpcCount++;
|
||
}
|
||
},
|
||
});
|
||
assert.containsN(
|
||
target,
|
||
"tbody tr",
|
||
3,
|
||
"should have 3 rows: 1 for the opened header, and 2 for data"
|
||
);
|
||
|
||
// click on the opened header to close it
|
||
await click(target, ".o_pivot_header_cell_opened");
|
||
|
||
assert.containsOnce(target, "tbody tr", "should have 1 row");
|
||
|
||
// click on closed header to open dropdown
|
||
await click(target, "tbody .o_pivot_header_cell_closed");
|
||
assert.containsOnce(target, ".o_pivot .dropdown-menu");
|
||
assert.strictEqual(
|
||
target.querySelector(".o_pivot .dropdown-menu").innerText.replace(/\s/g, ""),
|
||
"CompanyTypeCustomerDateOtherProductProductbarAddCustomGroup"
|
||
);
|
||
|
||
// open the Date sub dropdown
|
||
await mouseEnter(target, ".o_pivot .dropdown-menu .dropdown-toggle.o_menu_item");
|
||
assert.strictEqual(
|
||
target
|
||
.querySelector(".o_pivot .dropdown-menu .dropdown-menu")
|
||
.innerText.replace(/\s/g, ""),
|
||
"YearQuarterMonthWeekDay"
|
||
);
|
||
|
||
await click(
|
||
target.querySelectorAll(".o_pivot .dropdown-menu .dropdown-menu .dropdown-item")[2]
|
||
);
|
||
|
||
assert.containsN(target, "tbody tr", 4, "should have 4 rows: one for header, 3 for data");
|
||
assert.strictEqual(
|
||
rpcCount,
|
||
3,
|
||
"should have done 3 rpcs (initial load) + open header with different groupbys"
|
||
);
|
||
});
|
||
|
||
QUnit.test("more folding/unfolding", async function (assert) {
|
||
assert.expect(1);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id" type="row"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
// open dropdown to zoom into first row
|
||
await click(target.querySelector("tbody .o_pivot_header_cell_closed"));
|
||
// click on date by day
|
||
await mouseEnter(target.querySelector("tbody .dropdown-menu .dropdown-toggle"));
|
||
await click(target.querySelector("tbody .dropdown-menu .dropdown-menu span:nth-child(5)"));
|
||
|
||
// open dropdown to zoom into second row
|
||
await click(target.querySelectorAll("tbody th.o_pivot_header_cell_closed")[1]);
|
||
|
||
assert.containsN(
|
||
target,
|
||
"tbody tr",
|
||
7,
|
||
"should have 7 rows (1 for total, 1 for xphone, 1 for xpad, 4 for data)"
|
||
);
|
||
});
|
||
|
||
QUnit.test("fold and unfold header group", async function (assert) {
|
||
assert.expect(3);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id" type="col"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
assert.containsN(target, "thead tr", 3);
|
||
|
||
// fold opened col group
|
||
await click(target.querySelector("thead .o_pivot_header_cell_opened"));
|
||
assert.containsN(target, "thead tr", 2);
|
||
|
||
// unfold it
|
||
await click(target.querySelector("thead .o_pivot_header_cell_closed"));
|
||
await click(target.querySelector(".dropdown-menu span:nth-child(5)"));
|
||
assert.containsN(target, "thead tr", 3);
|
||
});
|
||
|
||
QUnit.test("unfold second header group", async function (assert) {
|
||
assert.expect(4);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id" type="col"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
assert.containsN(target, "thead tr", 3);
|
||
let values = ["12", "20", "32"];
|
||
assert.strictEqual(getCurrentValues(target), values.join(","));
|
||
|
||
// unfold it
|
||
await click(target.querySelector("thead .o_pivot_header_cell_closed:last-child"));
|
||
await click(target.querySelector(".dropdown-menu span:nth-child(1)"));
|
||
assert.containsN(target, "thead tr", 4);
|
||
values = ["12", "17", "3", "32"];
|
||
assert.strictEqual(getCurrentValues(target), values.join(","));
|
||
});
|
||
|
||
QUnit.test(
|
||
"pivot renders group dropdown same as search groupby dropdown if group bys are specified in search arch",
|
||
async function (assert) {
|
||
assert.expect(6);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id" type="row"/>
|
||
<field name="bar" type="col"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
// TOASK DAM: <search><field/></search> won´t appear in groupbymenu ?
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="bar" string="bar" context="{'group_by': 'bar'}"/>
|
||
<filter name="foo" string="foo" context="{'group_by': 'foo'}"/>
|
||
<filter name="product_id" string="product" context="{'group_by': 'product_id'}"/>
|
||
</search>`,
|
||
});
|
||
|
||
// open group by dropdown
|
||
await toggleGroupByMenu(target);
|
||
assert.containsN(
|
||
target,
|
||
".o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item",
|
||
3,
|
||
"should have 3 dropdown items in searchview groupby"
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
".o_control_panel .o_cp_bottom_right .dropdown-menu .o_add_custom_group_menu",
|
||
"should have custom group generator in searchview groupby"
|
||
);
|
||
|
||
// click on closed header to open dropdown
|
||
await click(target, "tbody tr:last-child .o_pivot_header_cell_closed");
|
||
assert.containsN(
|
||
target,
|
||
".dropdown-menu > .dropdown-item",
|
||
3,
|
||
"should have 3 dropdown items same as searchview groupby"
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
".dropdown-menu .o_add_custom_group_menu",
|
||
"should have custom group generator same as searchview groupby"
|
||
);
|
||
// check custom groupby selection has groupable fields only
|
||
await mouseEnter(target, ".dropdown-menu .o_add_custom_group_menu .dropdown-toggle");
|
||
assert.containsN(
|
||
target,
|
||
".dropdown-menu .o_add_custom_group_menu .dropdown-menu option",
|
||
6,
|
||
"should have 6 fields in custom groupby"
|
||
);
|
||
const optionDescriptions = [
|
||
...target.querySelectorAll(
|
||
".dropdown-menu .o_add_custom_group_menu .dropdown-menu option"
|
||
),
|
||
].map((option) => option.innerText.trim());
|
||
assert.deepEqual(
|
||
optionDescriptions,
|
||
["Company Type", "Customer", "Date", "Other Product", "Product", "bar"],
|
||
"should only have groupable fields in custom groupby"
|
||
);
|
||
}
|
||
);
|
||
|
||
QUnit.test("pivot group dropdown sync with search groupby dropdown", async function (assert) {
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id" type="row"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="bar" string="bar" context="{'group_by': 'bar'}"/>
|
||
<filter name="product_id" string="product" context="{'group_by': 'product_id'}"/>
|
||
</search>`,
|
||
});
|
||
|
||
// open group by dropdown
|
||
await toggleGroupByMenu(target);
|
||
assert.containsN(
|
||
target,
|
||
".o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item",
|
||
2,
|
||
"should have 2 dropdown items in searchview groupby"
|
||
);
|
||
|
||
// click on closed header to open dropdown
|
||
await click(target, "tbody tr:last-child .o_pivot_header_cell_closed");
|
||
assert.containsN(
|
||
target,
|
||
".dropdown-menu .o_menu_item",
|
||
2,
|
||
"should have 2 dropdown items in pivot groupby"
|
||
);
|
||
|
||
// add a custom group in searchview groupby
|
||
await toggleGroupByMenu(target);
|
||
await toggleAddCustomGroup(target);
|
||
await applyGroup(target);
|
||
assert.containsN(
|
||
target,
|
||
".o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item",
|
||
3,
|
||
"should have 3 dropdown items in searchview groupby now"
|
||
);
|
||
await click(target, "tbody tr:last-child .o_pivot_header_cell_closed");
|
||
assert.containsN(
|
||
target,
|
||
".dropdown-menu .o_menu_item",
|
||
2,
|
||
"should still have 2 dropdown items in pivot groupby"
|
||
);
|
||
|
||
// add a custom group in pivot groupby
|
||
await mouseEnter(target, ".dropdown-menu .o_add_custom_group_menu .dropdown-toggle");
|
||
target.querySelector(".dropdown-menu .o_add_custom_group_menu select").value = "date";
|
||
await triggerEvent(target, ".dropdown-menu .o_add_custom_group_menu select", "change");
|
||
await click(target, ".dropdown-menu .o_add_custom_group_menu .dropdown-menu .btn");
|
||
// click on closed header to open groupby selection dropdown
|
||
await click(target, "tbody tr:last-child .o_pivot_header_cell_closed");
|
||
assert.containsN(
|
||
target,
|
||
".dropdown-menu .o_menu_item",
|
||
3,
|
||
"should have 3 dropdown items in pivot groupby dropdown"
|
||
);
|
||
|
||
// applying custom groupby in pivot groupby dropdown will not update search dropdown
|
||
await toggleGroupByMenu(target);
|
||
assert.containsN(
|
||
target,
|
||
".o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item",
|
||
3,
|
||
"should still have 3 dropdown items in searchview groupby dropdown"
|
||
);
|
||
});
|
||
|
||
QUnit.test(
|
||
"pivot groupby dropdown renders custom search at the end with separator",
|
||
async function (assert) {
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id" type="row"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="bar" string="bar" context="{'group_by': 'bar'}"/>
|
||
<filter name="product_id" string="product" context="{'group_by': 'product_id'}"/>
|
||
</search>`,
|
||
});
|
||
|
||
// open group by dropdown
|
||
await toggleGroupByMenu(target);
|
||
assert.containsN(
|
||
target,
|
||
".o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item",
|
||
2,
|
||
"should have 2 dropdown items in searchview groupby"
|
||
);
|
||
await toggleAddCustomGroup(target);
|
||
await applyGroup(target);
|
||
assert.containsN(
|
||
target,
|
||
".o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item",
|
||
3,
|
||
"should have 3 dropdown items in searchview groupby now"
|
||
);
|
||
|
||
// click on closed header to open dropdown
|
||
await click(target.querySelectorAll("tbody .o_pivot_header_cell_closed")[1]);
|
||
let items = target.querySelectorAll(".o_menu_item");
|
||
assert.deepEqual(
|
||
[...items].map((it) => it.innerText),
|
||
["bar", "product"]
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
"tbody .dropdown-menu .dropdown-divider",
|
||
"pivot groupby menu should only have one separator"
|
||
);
|
||
assert.hasClass(
|
||
items[items.length - 1].nextElementSibling,
|
||
"dropdown-divider",
|
||
"pivot groupby menu separator is placed after all menu items"
|
||
);
|
||
|
||
// add a custom group in pivot groupby
|
||
await mouseEnter(target, ".dropdown-menu .o_add_custom_group_menu .dropdown-toggle");
|
||
target.querySelector(".o_add_custom_group_menu select").value = "customer";
|
||
await triggerEvent(target, ".dropdown-menu .o_add_custom_group_menu select", "change");
|
||
await click(target, ".dropdown-menu .o_add_custom_group_menu .dropdown-menu .btn");
|
||
|
||
await click(target.querySelectorAll("tbody .o_pivot_header_cell_closed")[1]);
|
||
items = target.querySelectorAll(".o_menu_item");
|
||
assert.deepEqual(
|
||
[...items].map((it) => it.innerText),
|
||
["bar", "product", "Customer"]
|
||
);
|
||
assert.containsN(
|
||
target,
|
||
"tbody .dropdown-menu .dropdown-divider",
|
||
2,
|
||
"pivot groupby menu should now have two separators"
|
||
);
|
||
assert.hasClass(
|
||
items[items.length - 1].previousElementSibling,
|
||
"dropdown-divider",
|
||
"last pivot groupby menu item is placed after a separator"
|
||
);
|
||
assert.hasClass(
|
||
items[items.length - 1].nextElementSibling,
|
||
"dropdown-divider",
|
||
"a pivot groupby menu separator is placed after all menu items"
|
||
);
|
||
}
|
||
);
|
||
|
||
QUnit.test(
|
||
"pivot custom groupby: grouping on date field use default interval month",
|
||
async function (assert) {
|
||
assert.expect(1);
|
||
|
||
let checkReadGroup = false;
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id" type="row"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="bar" string="bar" context="{'group_by': 'bar'}"/>
|
||
</search>`,
|
||
mockRPC(route, args) {
|
||
if (args.method === "read_group" && checkReadGroup) {
|
||
assert.deepEqual(
|
||
args.kwargs.groupby,
|
||
["date:month"],
|
||
"should use default month as an interval in read_group"
|
||
);
|
||
checkReadGroup = false;
|
||
}
|
||
},
|
||
});
|
||
|
||
// click on closed header to open dropdown and apply groupby on date field
|
||
await click(target.querySelector("thead .o_pivot_header_cell_closed"));
|
||
await mouseEnter(
|
||
target.querySelector(
|
||
"thead .dropdown-menu .o_add_custom_group_menu .dropdown-toggle "
|
||
)
|
||
);
|
||
|
||
checkReadGroup = true;
|
||
const select = target.querySelector(".o_add_custom_group_menu select");
|
||
select.value = "date";
|
||
select.dispatchEvent(new Event("change"));
|
||
await click(target.querySelector(".o_add_custom_group_menu .btn-primary"));
|
||
}
|
||
);
|
||
|
||
QUnit.test("pivot view without group by specified in search arch", async function (assert) {
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id" type="row"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
// open group by dropdown
|
||
await toggleGroupByMenu(target);
|
||
assert.containsNone(
|
||
target,
|
||
".o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item",
|
||
"should not have any dropdown item in searchview groupby"
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
".o_control_panel .o_cp_bottom_right .dropdown-menu .o_add_custom_group_menu",
|
||
"should have add custom group item in searchview groupby"
|
||
);
|
||
// click on closed header to open dropdown
|
||
await click(target.querySelectorAll("tbody .o_pivot_header_cell_closed")[1]);
|
||
assert.containsN(
|
||
target,
|
||
"tbody .dropdown-menu .o_menu_item",
|
||
6,
|
||
"should have 6 dropdown items i.e. all groupable fields available"
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
".dropdown-menu .o_add_custom_group_menu",
|
||
"should have custom group generator in groupby dropdown"
|
||
);
|
||
});
|
||
|
||
QUnit.test(
|
||
"pivot view do not show custom group selection if there are no groupable fields",
|
||
async function (assert) {
|
||
assert.expect(4);
|
||
|
||
for (const fieldName of [
|
||
"bar",
|
||
"company_type",
|
||
"customer",
|
||
"date",
|
||
"other_product_id",
|
||
]) {
|
||
delete serverData.models.partner.fields[fieldName];
|
||
}
|
||
|
||
// Keep product_id but make it ungroupable
|
||
delete serverData.models.partner.fields.product_id.sortable;
|
||
delete serverData.models.partner.fields.product_id.store;
|
||
|
||
serverData.models.partner.records = [
|
||
{
|
||
id: 1,
|
||
foo: 12,
|
||
product_id: 37,
|
||
computed_field: 19,
|
||
},
|
||
];
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="foo" type="measure"/>
|
||
<field name="product_id" invisible="1"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="product_id" string="product" context="{'group_by': 'product_id'}"/>
|
||
</search>`,
|
||
});
|
||
|
||
// open group by dropdown
|
||
await toggleGroupByMenu(target);
|
||
assert.containsOnce(
|
||
target,
|
||
".o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item",
|
||
"should have 1 dropdown item in searchview groupby"
|
||
);
|
||
assert.containsNone(
|
||
target,
|
||
".o_control_panel .o_cp_bottom_right .dropdown-menu .o_add_custom_group_menu",
|
||
"should not have custom group generator in searchview groupby"
|
||
);
|
||
|
||
// click on closed header to open dropdown
|
||
await click(target.querySelector("tbody .o_pivot_header_cell_closed"));
|
||
assert.containsOnce(
|
||
target,
|
||
"tbody .dropdown-menu .dropdown-item",
|
||
"should have 1 dropdown items"
|
||
);
|
||
assert.containsNone(
|
||
target,
|
||
".dropdown-menu .o_add_custom_group_menu",
|
||
"should not have custom group generator in groupby dropdown"
|
||
);
|
||
}
|
||
);
|
||
|
||
QUnit.test(
|
||
"pivot custom groupby: adding a custom group close the pivot groupby menu",
|
||
async function (assert) {
|
||
assert.expect(3);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id" type="row"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="bar" string="bar" context="{'group_by': 'bar'}"/>
|
||
</search>`,
|
||
});
|
||
|
||
// click on closed header to open dropdown
|
||
await click(target.querySelector("thead .o_pivot_header_cell_closed"));
|
||
assert.containsOnce(target, "thead .dropdown-menu .o_add_custom_group_menu");
|
||
await mouseEnter(
|
||
target.querySelector(
|
||
"thead .dropdown-menu .o_add_custom_group_menu .dropdown-toggle"
|
||
)
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
"thead .dropdown-menu .o_add_custom_group_menu .dropdown-menu"
|
||
);
|
||
|
||
// click on apply button should close dropdown
|
||
await click(
|
||
target.querySelector(
|
||
"thead .dropdown-menu .o_add_custom_group_menu .dropdown-menu .btn-primary"
|
||
)
|
||
);
|
||
assert.containsNone(target, "thead .dropdown-menu");
|
||
}
|
||
);
|
||
|
||
QUnit.test("can toggle extra measure", async function (assert) {
|
||
let rpcCount = 0;
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id" type="row"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
mockRPC() {
|
||
rpcCount++;
|
||
},
|
||
});
|
||
|
||
rpcCount = 0;
|
||
assert.containsN(
|
||
target,
|
||
".o_pivot_cell_value",
|
||
3,
|
||
"should have 3 cells: 1 for the open header, and 2 for data"
|
||
);
|
||
|
||
await click(target.querySelector(".o_cp_bottom_left button.dropdown-toggle"));
|
||
assert.doesNotHaveClass(
|
||
$(target).find(".dropdown-item:contains(Count)"),
|
||
"selected",
|
||
"the __count measure should not be selected"
|
||
);
|
||
await click($(target).find(".o_cp_bottom_left .dropdown-item:contains(Count)")[0]);
|
||
|
||
assert.hasClass(
|
||
$(target).find(".o_cp_bottom_left .dropdown-item:contains(Count)"),
|
||
"selected",
|
||
"the __count measure should be selected"
|
||
);
|
||
assert.containsN(
|
||
target,
|
||
".o_pivot_cell_value",
|
||
6,
|
||
"should have 6 cells: 2 for the open header, and 4 for data"
|
||
);
|
||
assert.strictEqual(rpcCount, 2, "should have done 2 rpcs to reload data");
|
||
|
||
await click($(target).find(".o_cp_bottom_left .dropdown-item:contains(Count)")[0]);
|
||
|
||
assert.doesNotHaveClass(
|
||
$(target).find(".dropdown-item:contains(Count)")[0],
|
||
"selected",
|
||
"the __count measure should not be selected"
|
||
);
|
||
assert.containsN(
|
||
target,
|
||
".o_pivot_cell_value",
|
||
3,
|
||
"should have 3 cells: 1 for the open header, and 2 for data"
|
||
);
|
||
assert.strictEqual(rpcCount, 2, "should not have done any extra rpcs");
|
||
});
|
||
|
||
QUnit.test("no content helper when no active measure", async function (assert) {
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `<pivot/>`,
|
||
});
|
||
|
||
assert.containsNone(target, ".o_view_nocontent");
|
||
assert.containsOnce(target, "table");
|
||
|
||
await click(target.querySelector(".o_cp_bottom_left button.dropdown-toggle"));
|
||
await click($(target).find(".o_cp_bottom_left .dropdown-item:contains(Count)")[0]);
|
||
|
||
assert.containsOnce(target, ".o_view_nocontent");
|
||
assert.containsNone(target, "table");
|
||
});
|
||
|
||
QUnit.test("no content helper when no data", async function (assert) {
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `<pivot/>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="some_filter" string="Some Filter" domain="[('foo', '=', 12345)]"/>
|
||
</search>`,
|
||
});
|
||
|
||
assert.containsNone(target, ".o_view_nocontent");
|
||
assert.containsOnce(target, "table");
|
||
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "Some Filter");
|
||
|
||
assert.containsOnce(target, ".o_view_nocontent");
|
||
assert.containsNone(target, "table");
|
||
});
|
||
|
||
QUnit.test("no content helper when no data, part 2", async function (assert) {
|
||
serverData.models.partner.records = [];
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: "<pivot/>",
|
||
});
|
||
|
||
assert.containsOnce(target, ".o_view_nocontent");
|
||
});
|
||
|
||
QUnit.test("no content helper when no data, part 3", async function (assert) {
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: "<pivot/>",
|
||
searchViewArch: `
|
||
<search>
|
||
<field name="foo"/>
|
||
<filter name="some_filter" string="Some Filter" domain="[('foo', '>', 10)]"/>
|
||
</search>`,
|
||
context: {
|
||
search_default_foo: 12345,
|
||
},
|
||
});
|
||
|
||
assert.containsOnce(target, ".o_searchview .o_searchview_facet");
|
||
assert.containsOnce(target, ".o_view_nocontent");
|
||
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "Some Filter");
|
||
assert.containsN(target, ".o_searchview .o_searchview_facet", 2);
|
||
assert.containsOnce(target, ".o_view_nocontent");
|
||
|
||
await toggleMenuItem(target, "Some Filter");
|
||
assert.containsOnce(target, ".o_searchview .o_searchview_facet");
|
||
assert.containsOnce(target, ".o_view_nocontent");
|
||
|
||
await click(target, ".o_facet_remove");
|
||
assert.containsNone(target, ".o_searchview .o_searchview_facet");
|
||
assert.containsNone(target, ".o_view_nocontent");
|
||
|
||
// tries to open a field selection menu, to make sure it was not
|
||
// removed from the dom.
|
||
await click(target.querySelector("tbody .o_pivot_header_cell_closed"));
|
||
assert.containsOnce(target, "tbody .dropdown-menu");
|
||
});
|
||
|
||
QUnit.test("tries to restore previous state after domain change", async function (assert) {
|
||
assert.expect(7);
|
||
|
||
let rpcCount = 0;
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id" type="row"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="my_filter" string="My Filter" domain="[('foo', '=', 12345)]"/>
|
||
</search>`,
|
||
mockRPC() {
|
||
rpcCount++;
|
||
},
|
||
});
|
||
|
||
assert.containsN(
|
||
target,
|
||
".o_pivot_cell_value",
|
||
3,
|
||
"should have 3 cells: 1 for the open header, and 2 for data"
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
".o_pivot_measure_row:contains(Foo)",
|
||
"should have 1 row for measure Foo"
|
||
);
|
||
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "My Filter");
|
||
assert.containsNone(target, "table");
|
||
|
||
rpcCount = 0;
|
||
await removeFacet(target);
|
||
|
||
assert.containsOnce(target, "table");
|
||
assert.equal(rpcCount, 2, "should have reloaded data");
|
||
assert.containsN(
|
||
target,
|
||
".o_pivot_cell_value",
|
||
3,
|
||
"should still have 3 cells: 1 for the open header, and 2 for data"
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
".o_pivot_measure_row:contains(Foo)",
|
||
"should still have 1 row for measure Foo"
|
||
);
|
||
});
|
||
|
||
QUnit.test("can be grouped with the search view", async function (assert) {
|
||
assert.expect(4);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter string="Product" name="product_id" context="{'group_by':'product_id'}"/>
|
||
</search>`,
|
||
});
|
||
|
||
assert.containsOnce(target, ".o_pivot_cell_value", "should have only 1 cell");
|
||
assert.containsOnce(target, "tbody tr", "should have 1 rows");
|
||
|
||
await toggleGroupByMenu(target);
|
||
await toggleMenuItem(target, "Product");
|
||
|
||
assert.containsN(target, ".o_pivot_cell_value", 3, "should have 3 cells");
|
||
assert.containsN(target, "tbody tr", 3, "should have 3 rows");
|
||
});
|
||
|
||
QUnit.test("can sort data in a column by clicking on header", async function (assert) {
|
||
assert.expect(3);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="foo" type="measure"/>
|
||
<field name="product_id" type="row"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
let values = ["32", "12", "20"];
|
||
assert.strictEqual(
|
||
getCurrentValues(target),
|
||
values.join(","),
|
||
"should have proper values in cells (total, result 1, result 2)"
|
||
);
|
||
|
||
await click(target.querySelector("th.o_pivot_measure_row"));
|
||
|
||
values = ["32", "12", "20"];
|
||
assert.strictEqual(
|
||
getCurrentValues(target),
|
||
values.join(","),
|
||
"should have proper values in cells (total, result 1, result 2)"
|
||
);
|
||
|
||
await click(target.querySelector("th.o_pivot_measure_row"));
|
||
|
||
values = ["32", "20", "12"];
|
||
assert.strictEqual(
|
||
getCurrentValues(target),
|
||
values.join(","),
|
||
"should have proper values in cells (total, result 1, result 2)"
|
||
);
|
||
});
|
||
|
||
QUnit.test("can expand all rows", async function (assert) {
|
||
assert.expect(7);
|
||
|
||
let nbReadGroups = 0;
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="foo" type="measure"/>
|
||
<field name="product_id" type="row"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter string="Date" name="date" context="{'group_by':'date'}"/>
|
||
<filter string="Product" name="product_id" context="{'group_by':'product_id'}"/>
|
||
</search>`,
|
||
mockRPC(route, args) {
|
||
if (args.method === "read_group") {
|
||
nbReadGroups++;
|
||
}
|
||
},
|
||
});
|
||
|
||
assert.strictEqual(nbReadGroups, 2, "should have done 2 read_group RPCS");
|
||
assert.strictEqual(
|
||
getCurrentValues(target),
|
||
"32,12,20",
|
||
"should have proper values in cells (total, result 1, result 2)"
|
||
);
|
||
|
||
// expand on date:days, product
|
||
await toggleGroupByMenu(target);
|
||
await toggleMenuItem(target, "Date");
|
||
await toggleMenuItemOption(target, "Date", "Month");
|
||
nbReadGroups = 0;
|
||
await toggleMenuItem(target, "Product");
|
||
|
||
assert.strictEqual(nbReadGroups, 3, "should have done 3 read_group RPCS");
|
||
assert.containsN(
|
||
target,
|
||
"tbody tr",
|
||
8,
|
||
"should have 7 rows (total + 3 for December and 2 for October and April)"
|
||
);
|
||
|
||
// collapse the first two rows
|
||
await click(target.querySelectorAll("tbody .o_pivot_header_cell_opened")[2]);
|
||
await click(target.querySelectorAll("tbody .o_pivot_header_cell_opened")[1]);
|
||
|
||
assert.containsN(target, "tbody tr", 6, "should have 6 rows now");
|
||
|
||
// expand all
|
||
nbReadGroups = 0;
|
||
await click(target.querySelector(".o_pivot_expand_button"));
|
||
|
||
assert.strictEqual(nbReadGroups, 3, "should have done 3 read_group RPCS");
|
||
assert.containsN(target, "tbody tr", 8, "should have 8 rows again");
|
||
});
|
||
|
||
QUnit.test("expand all with a delay", async function (assert) {
|
||
assert.expect(4);
|
||
|
||
let def;
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="foo" type="measure"/>
|
||
<field name="product_id" type="row"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter string="Date" name="date" context="{'group_by':'date'}"/>
|
||
<filter string="Product" name="product_id" context="{'group_by':'product_id'}"/>
|
||
</search>`,
|
||
mockRPC(route, args) {
|
||
if (args.method === "read_group") {
|
||
return Promise.resolve(def);
|
||
}
|
||
},
|
||
});
|
||
|
||
// expand on date:days, product
|
||
await toggleGroupByMenu(target);
|
||
await toggleMenuItem(target, "Date");
|
||
await toggleMenuItemOption(target, "Date", "Month");
|
||
await toggleMenuItem(target, "Product");
|
||
assert.containsN(
|
||
target,
|
||
"tbody tr",
|
||
8,
|
||
"should have 7 rows (total + 3 for December and 2 for October and April)"
|
||
);
|
||
|
||
// collapse the first two rows
|
||
await click(target.querySelectorAll("tbody .o_pivot_header_cell_opened")[2]);
|
||
await click(target.querySelectorAll("tbody .o_pivot_header_cell_opened")[1]);
|
||
|
||
assert.containsN(target, "tbody tr", 6, "should have 6 rows now");
|
||
|
||
// expand all
|
||
def = makeDeferred();
|
||
await click(target.querySelector(".o_pivot_expand_button"));
|
||
assert.containsN(target, "tbody tr", 6, "should have 6 rows now");
|
||
def.resolve();
|
||
await nextTick();
|
||
assert.containsN(target, "tbody tr", 8, "should have 8 rows again");
|
||
});
|
||
|
||
QUnit.test("can download a file", async function (assert) {
|
||
assert.expect(1);
|
||
|
||
mockDownload(({ url }) => {
|
||
assert.strictEqual(url, "/web/pivot/export_xlsx");
|
||
return Promise.resolve();
|
||
});
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="date" interval="month" type="col"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
await click(target.querySelector(".o_pivot_download"));
|
||
});
|
||
|
||
QUnit.test(
|
||
"download a file with single measure, measure row displayed in table",
|
||
async function (assert) {
|
||
assert.expect(2);
|
||
|
||
const downloadDef = makeDeferred();
|
||
mockDownload(async ({ url, data }) => {
|
||
data = JSON.parse(await data.data.text());
|
||
assert.strictEqual(url, "/web/pivot/export_xlsx");
|
||
assert.strictEqual(
|
||
data.measure_headers.length,
|
||
4,
|
||
"should have measure_headers in data"
|
||
);
|
||
downloadDef.resolve();
|
||
return Promise.resolve();
|
||
});
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="date" interval="month" type="col"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
await click(target.querySelector(".o_pivot_download"));
|
||
await downloadDef;
|
||
}
|
||
);
|
||
|
||
QUnit.test("download button is disabled when there is no data", async function (assert) {
|
||
assert.expect(1);
|
||
|
||
serverData.models.partner.records = [];
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="date" interval="month" type="col"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
assert.ok(
|
||
target.querySelector(".o_pivot_download").disabled,
|
||
"download button should be disabled"
|
||
);
|
||
});
|
||
|
||
QUnit.test("correctly save measures and groupbys to favorite", async function (assert) {
|
||
assert.expect(3);
|
||
|
||
let expectedContext;
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="date" interval="day" type="col"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
mockRPC(route, args) {
|
||
if (args.method === "create_or_replace") {
|
||
assert.deepEqual(args.args[0].context, expectedContext);
|
||
return true;
|
||
}
|
||
},
|
||
});
|
||
|
||
expectedContext = {
|
||
group_by: [],
|
||
pivot_column_groupby: ["date:day"],
|
||
pivot_measures: ["foo"],
|
||
pivot_row_groupby: [],
|
||
};
|
||
await toggleFavoriteMenu(target);
|
||
await toggleSaveFavorite(target);
|
||
await editFavoriteName(target, "Fav1");
|
||
await saveFavorite(target);
|
||
|
||
// expand header on field customer
|
||
await click(target.querySelectorAll("thead .o_pivot_header_cell_closed")[1]);
|
||
await click(target.querySelectorAll("thead .dropdown-menu .dropdown-item")[1]);
|
||
expectedContext = {
|
||
group_by: [],
|
||
pivot_column_groupby: ["date:day", "customer"],
|
||
pivot_measures: ["foo"],
|
||
pivot_row_groupby: [],
|
||
};
|
||
await toggleFavoriteMenu(target);
|
||
await toggleSaveFavorite(target);
|
||
await editFavoriteName(target, "Fav2");
|
||
await saveFavorite(target);
|
||
|
||
// expand row on field product_id
|
||
await click(target.querySelector("tbody .o_pivot_header_cell_closed"));
|
||
await click(target.querySelectorAll("tbody .dropdown-menu .dropdown-item")[4]);
|
||
expectedContext = {
|
||
group_by: [],
|
||
pivot_column_groupby: ["date:day", "customer"],
|
||
pivot_measures: ["foo"],
|
||
pivot_row_groupby: ["product_id"],
|
||
};
|
||
await toggleFavoriteMenu(target);
|
||
await toggleSaveFavorite(target);
|
||
await editFavoriteName(target, "Fav3");
|
||
await saveFavorite(target);
|
||
});
|
||
|
||
QUnit.test("correctly remove pivot_ keys from the context", async function (assert) {
|
||
assert.expect(5);
|
||
|
||
// in this test, we use "foo" as a measure
|
||
serverData.models.partner.fields.foo.store = true;
|
||
serverData.models.partner.fields.amount = {
|
||
string: "Amount",
|
||
type: "float",
|
||
group_operator: "sum",
|
||
};
|
||
|
||
let expectedContext;
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="date" interval="day" type="col"/>
|
||
<field name="amount" type="measure"/>
|
||
</pivot>`,
|
||
context: {
|
||
search_default_initial_context: 1,
|
||
},
|
||
searchViewArch: `
|
||
<search>
|
||
<filter
|
||
name="initial_context"
|
||
string="Initial favorite"
|
||
domain="[]"
|
||
context="{
|
||
'pivot_measures': ['foo'],
|
||
'pivot_column_groupby': ['customer'],
|
||
'pivot_row_groupby': ['product_id'],
|
||
}"
|
||
/>
|
||
</search>`,
|
||
mockRPC(route, args) {
|
||
if (args.method === "create_or_replace") {
|
||
assert.deepEqual(args.args[0].context, expectedContext);
|
||
return true;
|
||
}
|
||
},
|
||
});
|
||
|
||
// Unload the filter
|
||
await removeFacet(target); // remove previous favorite
|
||
expectedContext = {
|
||
group_by: [],
|
||
pivot_column_groupby: ["customer"],
|
||
pivot_measures: ["foo"],
|
||
pivot_row_groupby: ["product_id"],
|
||
};
|
||
await toggleFavoriteMenu(target);
|
||
await toggleSaveFavorite(target);
|
||
await editFavoriteName(target, "1");
|
||
await saveFavorite(target);
|
||
|
||
// Let's get rid of the rows groupBy
|
||
await click(target, "tbody .o_pivot_header_cell_opened");
|
||
expectedContext = {
|
||
group_by: [],
|
||
pivot_column_groupby: ["customer"],
|
||
pivot_measures: ["foo"],
|
||
pivot_row_groupby: [],
|
||
};
|
||
await toggleFavoriteMenu(target);
|
||
await toggleSaveFavorite(target);
|
||
await editFavoriteName(target, "2");
|
||
await saveFavorite(target);
|
||
|
||
// And now, get rid of both col and row groupby
|
||
//await click(target, "tbody .o_pivot_header_cell_opened"); //It was already removed
|
||
await click(target, "thead .o_pivot_header_cell_opened");
|
||
expectedContext = {
|
||
group_by: [],
|
||
pivot_column_groupby: [],
|
||
pivot_measures: ["foo"],
|
||
pivot_row_groupby: [],
|
||
};
|
||
await toggleFavoriteMenu(target);
|
||
await toggleSaveFavorite(target);
|
||
await editFavoriteName(target, "3");
|
||
await saveFavorite(target);
|
||
|
||
// Group row by product_id
|
||
await click(target, "tbody .o_pivot_header_cell_closed");
|
||
await click(target, ".dropdown-menu span:nth-child(5)");
|
||
expectedContext = {
|
||
group_by: [],
|
||
pivot_column_groupby: [],
|
||
pivot_measures: ["foo"],
|
||
pivot_row_groupby: ["product_id"],
|
||
};
|
||
await toggleFavoriteMenu(target);
|
||
await toggleSaveFavorite(target);
|
||
await editFavoriteName(target, "4");
|
||
await saveFavorite(target);
|
||
|
||
// Group column by customer
|
||
await click(target, "thead .o_pivot_header_cell_closed");
|
||
await click(target, ".dropdown-menu span:nth-child(2)");
|
||
expectedContext = {
|
||
group_by: [],
|
||
pivot_column_groupby: ["customer"],
|
||
pivot_measures: ["foo"],
|
||
pivot_row_groupby: ["product_id"],
|
||
};
|
||
await toggleFavoriteMenu(target);
|
||
await toggleSaveFavorite(target);
|
||
await editFavoriteName(target, "5");
|
||
await saveFavorite(target);
|
||
});
|
||
|
||
QUnit.test("Apply two groupby, and remove facet", async function (assert) {
|
||
serverData.views = {
|
||
"partner,false,pivot": `
|
||
<pivot>
|
||
<field name="customer" type="row"/>
|
||
</pivot>`,
|
||
"partner,false,search": `
|
||
<search>
|
||
<filter name="group_by_product" string="Product" domain="[]" context="{'group_by': 'product_id'}"/>
|
||
<filter name="group_by_bar" string="Bar" domain="[]" context="{'group_by': 'bar'}"/>
|
||
</search>`,
|
||
};
|
||
|
||
const webClient = await createWebClient({ serverData });
|
||
|
||
await doAction(webClient, {
|
||
res_model: "partner",
|
||
type: "ir.actions.act_window",
|
||
views: [[false, "pivot"]],
|
||
});
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("tbody .o_pivot_header_cell_closed").textContent,
|
||
"First"
|
||
);
|
||
|
||
// Apply both groupbys
|
||
await toggleGroupByMenu(target);
|
||
await toggleMenuItem(target, "Product");
|
||
assert.strictEqual(
|
||
target.querySelector("tbody .o_pivot_header_cell_closed").textContent,
|
||
"xphone"
|
||
);
|
||
|
||
await toggleMenuItem(target, "Bar");
|
||
assert.strictEqual(
|
||
target.querySelector("tbody .o_pivot_header_cell_closed").textContent,
|
||
"Yes"
|
||
);
|
||
|
||
// remove filter
|
||
await removeFacet(target);
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("tbody .o_pivot_header_cell_closed").textContent,
|
||
"Yes"
|
||
);
|
||
});
|
||
|
||
QUnit.test("Add a group by on the CP when a favorite already exists", async function (assert) {
|
||
serverData.views = {
|
||
"partner,false,pivot": `
|
||
<pivot>
|
||
</pivot>`,
|
||
"partner,false,search": `
|
||
<search>
|
||
<filter name="groubybar" string="Bar" domain="[]" context="{'group_by': 'bar'}"/>
|
||
</search>`,
|
||
};
|
||
|
||
serverData.models.partner.filters = [
|
||
{
|
||
context: "{'pivot_row_groupby': ['date']}",
|
||
domain: "[]",
|
||
id: 7,
|
||
is_default: true,
|
||
name: "My favorite",
|
||
sort: "[]",
|
||
user_id: [2, "Mitchell Admin"],
|
||
},
|
||
];
|
||
|
||
const webClient = await createWebClient({ serverData });
|
||
|
||
await doAction(webClient, {
|
||
res_model: "partner",
|
||
type: "ir.actions.act_window",
|
||
views: [[false, "pivot"]],
|
||
});
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("tbody .o_pivot_header_cell_closed").textContent,
|
||
"April 2016"
|
||
);
|
||
|
||
// Apply BAR groupbys
|
||
await toggleGroupByMenu(target);
|
||
await toggleMenuItem(target, "Bar");
|
||
assert.strictEqual(
|
||
target.querySelector("tbody .o_pivot_header_cell_closed").textContent,
|
||
"No"
|
||
);
|
||
|
||
// remove groupBy
|
||
await toggleMenuItem(target, "Bar");
|
||
assert.strictEqual(
|
||
target.querySelector("tbody .o_pivot_header_cell_closed").textContent,
|
||
"April 2016"
|
||
);
|
||
|
||
// remove all facets
|
||
await removeFacet(target);
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("tbody .o_pivot_header_cell_closed").textContent,
|
||
"April 2016"
|
||
);
|
||
});
|
||
|
||
QUnit.skip("Removing or adding filter shouldn't modify the row group", async function (assert) {
|
||
serverData.views = {
|
||
"partner,false,pivot": `
|
||
<pivot>
|
||
<field name="customer" type="row"/>
|
||
</pivot>`,
|
||
"partner,false,search": `
|
||
<search>
|
||
<filter name="bayou" string="Bayou" domain="[('foo','=', 12)]"/>
|
||
</search>`,
|
||
};
|
||
|
||
const webClient = await createWebClient({ serverData });
|
||
|
||
await doAction(webClient, {
|
||
res_model: "partner",
|
||
type: "ir.actions.act_window",
|
||
views: [[false, "pivot"]],
|
||
context: { search_default_bayou: 1, group_by: ["company_type"] },
|
||
});
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("tbody .o_pivot_header_cell_closed").textContent,
|
||
"Company"
|
||
);
|
||
|
||
// Let's get rid of the rows groupBy
|
||
await click(target, "tbody .o_pivot_header_cell_opened");
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("tbody .o_pivot_header_cell_closed").textContent,
|
||
"Total"
|
||
);
|
||
|
||
// Group row by product_id
|
||
await click(target, "tbody .o_pivot_header_cell_closed");
|
||
await click(target, ".dropdown-menu span:nth-child(5)");
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("tbody .o_pivot_header_cell_closed").textContent,
|
||
"xphone"
|
||
);
|
||
|
||
// remove filter
|
||
await removeFacet(target);
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("tbody .o_pivot_header_cell_closed").textContent,
|
||
"xphone"
|
||
);
|
||
});
|
||
|
||
QUnit.test(
|
||
"Adding a Favorite at anytime should modify the row/column groupby",
|
||
async function (assert) {
|
||
serverData.views = {
|
||
"partner,false,pivot": `
|
||
<pivot>
|
||
<field name="customer" type="row"/>
|
||
<field name="date" interval="month" type="col" />
|
||
</pivot>`,
|
||
"partner,false,search": "<search/>",
|
||
};
|
||
serverData.models["partner"].filters = [
|
||
{
|
||
user_id: [2, "Mitchell Admin"],
|
||
name: "My favorite",
|
||
id: 5,
|
||
context: `{"pivot_row_groupby":["product_id"], "pivot_column_groupby": ["bar"]}`,
|
||
sort: "[]",
|
||
domain: "",
|
||
is_default: false,
|
||
model_id: "partner",
|
||
action_id: false,
|
||
},
|
||
];
|
||
|
||
const webClient = await createWebClient({ serverData });
|
||
|
||
await doAction(webClient, {
|
||
res_model: "partner",
|
||
type: "ir.actions.act_window",
|
||
views: [[false, "pivot"]],
|
||
});
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("tbody .o_pivot_header_cell_closed").textContent,
|
||
"First"
|
||
);
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("thead .o_pivot_header_cell_closed").textContent,
|
||
"April 2016"
|
||
);
|
||
|
||
// activate the unique existing favorite
|
||
await toggleFavoriteMenu(target);
|
||
await toggleMenuItem(target, 0);
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("tbody .o_pivot_header_cell_closed").textContent,
|
||
"xphone"
|
||
);
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("thead .o_pivot_header_cell_closed").textContent,
|
||
"No"
|
||
);
|
||
|
||
// desactivate the unique existing favorite
|
||
await toggleMenuItem(target, 0);
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("tbody .o_pivot_header_cell_closed").textContent,
|
||
"xphone"
|
||
);
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("thead .o_pivot_header_cell_closed").textContent,
|
||
"No"
|
||
);
|
||
|
||
// Let's get rid of the rows and columns groupBy
|
||
await click(target, "tbody .o_pivot_header_cell_opened");
|
||
await click(target, "thead .o_pivot_header_cell_opened");
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("tbody .o_pivot_header_cell_closed").textContent,
|
||
"Total"
|
||
);
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("thead .o_pivot_header_cell_closed").textContent,
|
||
"Total"
|
||
);
|
||
|
||
// activate AGAIN the unique existing favorite
|
||
await toggleFavoriteMenu(target);
|
||
await toggleMenuItem(target, 0);
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("tbody .o_pivot_header_cell_closed").textContent,
|
||
"xphone"
|
||
);
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("thead .o_pivot_header_cell_closed").textContent,
|
||
"No"
|
||
);
|
||
}
|
||
);
|
||
|
||
QUnit.test("Unload Filter, reset display, load another filter", async function (assert) {
|
||
assert.expect(18);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
context: {
|
||
pivot_measures: ["foo"],
|
||
pivot_column_groupby: ["customer"],
|
||
pivot_row_groupby: ["product_id"],
|
||
},
|
||
|
||
searchViewArch: `
|
||
<search>
|
||
<filter
|
||
name="no_context_filter"
|
||
string="My fake favorite"
|
||
domain="[]"
|
||
context="{}"
|
||
/>
|
||
<filter
|
||
name="reset_filter"
|
||
string="My fake favorite 2"
|
||
domain="[]"
|
||
context="{
|
||
'pivot_measures': ['foo'],
|
||
'pivot_column_groupby': ['customer'],
|
||
'pivot_row_groupby': ['product_id'],
|
||
}"
|
||
/>
|
||
</search>`,
|
||
});
|
||
|
||
// Check Columns
|
||
assert.strictEqual(
|
||
$(target).find("thead .o_pivot_header_cell_opened").length,
|
||
1,
|
||
"The column should be grouped"
|
||
);
|
||
assert.strictEqual(
|
||
$(target).find('thead tr:contains("First")').length,
|
||
1,
|
||
'There should be a column "First"'
|
||
);
|
||
assert.strictEqual(
|
||
$(target).find('thead tr:contains("Second")').length,
|
||
1,
|
||
'There should be a column "Second"'
|
||
);
|
||
|
||
// Check Rows
|
||
assert.strictEqual(
|
||
$(target).find("tbody .o_pivot_header_cell_opened").length,
|
||
1,
|
||
"The row should be grouped"
|
||
);
|
||
assert.strictEqual(
|
||
$(target).find('tbody tr:contains("xphone")').length,
|
||
1,
|
||
'There should be a row "xphone"'
|
||
);
|
||
assert.strictEqual(
|
||
$(target).find('tbody tr:contains("xpad")').length,
|
||
1,
|
||
'There should be a row "xpad"'
|
||
);
|
||
|
||
// Equivalent to unload the filter
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "My fake favorite");
|
||
// collapse all headers
|
||
await click(target, ".o_pivot_header_cell_opened:first-child");
|
||
await click(target, ".o_pivot_header_cell_opened");
|
||
|
||
// Check Columns
|
||
assert.strictEqual(
|
||
$(target).find("thead .o_pivot_header_cell_closed").length,
|
||
1,
|
||
"The column should not be grouped"
|
||
);
|
||
assert.strictEqual(
|
||
$(target).find('thead tr:contains("First")').length,
|
||
0,
|
||
'There should not be a column "First"'
|
||
);
|
||
assert.strictEqual(
|
||
$(target).find('thead tr:contains("Second")').length,
|
||
0,
|
||
'There should not be a column "Second"'
|
||
);
|
||
|
||
// Check Rows
|
||
assert.strictEqual(
|
||
$(target).find("tbody .o_pivot_header_cell_closed").length,
|
||
1,
|
||
"The row should not be grouped"
|
||
);
|
||
assert.strictEqual(
|
||
$(target).find('tbody tr:contains("xphone")').length,
|
||
0,
|
||
'There should not be a row "xphone"'
|
||
);
|
||
assert.strictEqual(
|
||
$(target).find('tbody tr:contains("xpad")').length,
|
||
0,
|
||
'There should not be a row "xpad"'
|
||
);
|
||
|
||
// Equivalent to load another filter
|
||
await removeFacet(target); // remove previously saved favorite
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "My fake favorite 2");
|
||
|
||
// Check Columns
|
||
assert.strictEqual(
|
||
$(target).find("thead .o_pivot_header_cell_opened").length,
|
||
1,
|
||
"The column should be grouped"
|
||
);
|
||
assert.strictEqual(
|
||
$(target).find('thead tr:contains("First")').length,
|
||
1,
|
||
'There should be a column "First"'
|
||
);
|
||
assert.strictEqual(
|
||
$(target).find('thead tr:contains("Second")').length,
|
||
1,
|
||
'There should be a column "Second"'
|
||
);
|
||
|
||
// Check Rows
|
||
assert.strictEqual(
|
||
$(target).find("tbody .o_pivot_header_cell_opened").length,
|
||
1,
|
||
"The row should be grouped"
|
||
);
|
||
assert.strictEqual(
|
||
$(target).find('tbody tr:contains("xphone")').length,
|
||
1,
|
||
'There should be a row "xphone"'
|
||
);
|
||
assert.strictEqual(
|
||
$(target).find('tbody tr:contains("xpad")').length,
|
||
1,
|
||
'There should be a row "xpad"'
|
||
);
|
||
});
|
||
|
||
QUnit.test("Reload, group by columns, reload", async function (assert) {
|
||
assert.expect(2);
|
||
|
||
let expectedContext;
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: "<pivot/>",
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="my_filter_1" string="My Filter 1" domain="[('product_id', '=', 37)]"/>
|
||
<filter name="my_filter_2" string="My Filter 2" domain="[('product_id', '=', 41)]"/>
|
||
</search>`,
|
||
mockRPC(route, args) {
|
||
if (args.method === "create_or_replace") {
|
||
assert.deepEqual(args.args[0].context, expectedContext);
|
||
return true;
|
||
}
|
||
},
|
||
});
|
||
|
||
// Set a column groupby
|
||
await click(target.querySelector("thead .o_pivot_header_cell_closed"));
|
||
await click(target.querySelectorAll("thead .dropdown-item")[1]);
|
||
|
||
// Set a domain
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "My Filter 1");
|
||
|
||
// Save to favorites and check that column groupbys were not lost
|
||
expectedContext = {
|
||
group_by: [],
|
||
pivot_column_groupby: ["customer"],
|
||
pivot_measures: ["__count"],
|
||
pivot_row_groupby: [],
|
||
};
|
||
await toggleFavoriteMenu(target);
|
||
await toggleSaveFavorite(target);
|
||
await editFavoriteName(target, "My favorite 1");
|
||
await saveFavorite(target);
|
||
|
||
// Set a column groupby
|
||
await removeFacet(target); // remove previously saved favorite
|
||
await click(target.querySelector("thead .o_pivot_header_cell_closed"));
|
||
await click(target.querySelectorAll("thead .dropdown-menu .dropdown-item")[4]);
|
||
|
||
// Set a domain
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "My Filter 2");
|
||
|
||
expectedContext = {
|
||
group_by: [],
|
||
pivot_column_groupby: ["customer", "product_id"],
|
||
pivot_measures: ["__count"],
|
||
pivot_row_groupby: [],
|
||
};
|
||
await toggleFavoriteMenu(target);
|
||
await toggleSaveFavorite(target);
|
||
await editFavoriteName(target, "My favorite 2");
|
||
await saveFavorite(target);
|
||
});
|
||
|
||
QUnit.test("folded groups remain folded at reload", async function (assert) {
|
||
assert.expect(5);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id" type="row"/>
|
||
<field name="company_type" type="col"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="dummy_filter" string="Dummy Filter" domain="[('id', '>', 0)]"/>
|
||
</search>`,
|
||
});
|
||
|
||
let values = ["29", "3", "32", "12", "12", "17", "3", "20"];
|
||
assert.strictEqual(getCurrentValues(target), values.join(","));
|
||
|
||
// expand a col group
|
||
await click(target.querySelectorAll("thead .o_pivot_header_cell_closed")[1]);
|
||
await click(target.querySelectorAll("thead .dropdown-menu .dropdown-item")[1]);
|
||
|
||
values = ["29", "2", "1", "32", "12", "12", "17", "2", "1", "20"];
|
||
assert.strictEqual(getCurrentValues(target), values.join(","));
|
||
|
||
// expand a row group
|
||
await click(target.querySelectorAll("tbody .o_pivot_header_cell_closed")[1]);
|
||
await click(target.querySelectorAll("tbody .dropdown-menu .dropdown-item")[3]);
|
||
|
||
values = ["29", "2", "1", "32", "12", "12", "17", "2", "1", "20", "17", "2", "1", "20"];
|
||
assert.strictEqual(getCurrentValues(target), values.join(","));
|
||
|
||
// reload (should keep folded groups folded as col/row groupbys didn't change)
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "Dummy Filter");
|
||
|
||
assert.strictEqual(getCurrentValues(target), values.join(","));
|
||
|
||
await click(target.querySelector(".o_pivot_expand_button"));
|
||
|
||
// sanity check of what the table should look like if all groups are
|
||
// expanded, to ensure that the former asserts are pertinent
|
||
values = [
|
||
"12",
|
||
"17",
|
||
"2",
|
||
"1",
|
||
"32",
|
||
"12",
|
||
"12",
|
||
"12",
|
||
"12",
|
||
"17",
|
||
"2",
|
||
"1",
|
||
"20",
|
||
"17",
|
||
"2",
|
||
"1",
|
||
"20",
|
||
];
|
||
assert.strictEqual(getCurrentValues(target), values.join(","));
|
||
});
|
||
|
||
QUnit.test("Empty results keep groupbys", async function (assert) {
|
||
assert.expect(6);
|
||
|
||
const expectedContext = {
|
||
group_by: [],
|
||
pivot_column_groupby: ["customer"],
|
||
pivot_measures: ["__count"],
|
||
pivot_row_groupby: [],
|
||
};
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: "<pivot/>",
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="my_filter_1" string="My Filter 1" domain="[('id', '=', 0)]"/>
|
||
<filter name="my_filter_2" string="My Filter 2" domain="[('product_id', '=', 37)]"/>
|
||
</search>`,
|
||
mockRPC(route, args) {
|
||
if (args.method === "create_or_replace") {
|
||
assert.deepEqual(args.args[0].context, expectedContext);
|
||
return true;
|
||
}
|
||
},
|
||
});
|
||
|
||
// Set a column groupby
|
||
await click(target.querySelector("thead .o_pivot_header_cell_closed"));
|
||
await click(target.querySelectorAll("thead .dropdown-menu .dropdown-item")[1]);
|
||
|
||
assert.containsOnce(target, "table");
|
||
|
||
// Set a domain for empty results
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "My Filter 1");
|
||
assert.containsNone(target, "table");
|
||
|
||
await toggleFavoriteMenu(target);
|
||
await toggleSaveFavorite(target);
|
||
await editFavoriteName(target, "My favorite 1");
|
||
await saveFavorite(target);
|
||
|
||
// Set a domain for not empty results
|
||
await removeFacet(target); // remove previously saved favorite
|
||
assert.containsOnce(target, "table");
|
||
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "My Filter 2");
|
||
assert.containsOnce(target, "table");
|
||
|
||
await toggleFavoriteMenu(target);
|
||
await toggleSaveFavorite(target);
|
||
await editFavoriteName(target, "My favorite 2");
|
||
await saveFavorite(target);
|
||
});
|
||
|
||
QUnit.test("correctly uses pivot_ keys from the context", async function (assert) {
|
||
assert.expect(7);
|
||
|
||
// in this test, we use "foo" as a measure
|
||
serverData.models.partner.fields.foo.store = true;
|
||
serverData.models.partner.fields.amount = {
|
||
string: "Amount",
|
||
type: "float",
|
||
group_operator: "sum",
|
||
};
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="date" interval="day" type="col"/>
|
||
<field name="amount" type="measure"/>
|
||
</pivot>`,
|
||
context: {
|
||
pivot_measures: ["foo"],
|
||
pivot_column_groupby: ["customer"],
|
||
pivot_row_groupby: ["product_id"],
|
||
},
|
||
});
|
||
|
||
assert.containsOnce(
|
||
target,
|
||
"thead .o_pivot_header_cell_opened",
|
||
"column: should have one opened header"
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
"thead .o_pivot_header_cell_closed:contains(First)",
|
||
"column: should display one closed header with 'First'"
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
"thead .o_pivot_header_cell_closed:contains(Second)",
|
||
"column: should display one closed header with 'Second'"
|
||
);
|
||
|
||
assert.containsOnce(
|
||
target,
|
||
"tbody .o_pivot_header_cell_opened",
|
||
"row: should have one opened header"
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
"tbody .o_pivot_header_cell_closed:contains(xphone)",
|
||
"row: should display one closed header with 'xphone'"
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
"tbody .o_pivot_header_cell_closed:contains(xpad)",
|
||
"row: should display one closed header with 'xpad'"
|
||
);
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("tbody tr").querySelectorAll("td")[2].innerText,
|
||
"32",
|
||
"selected measure should be foo, with total 32"
|
||
);
|
||
});
|
||
|
||
QUnit.test("clear table cells data after closeGroup", async function (assert) {
|
||
assert.expect(2);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: "<pivot/>",
|
||
groupBy: ["product_id"],
|
||
});
|
||
|
||
await click(target.querySelector("thead .o_pivot_header_cell_closed"));
|
||
await mouseEnter(target.querySelector("thead .dropdown-menu .dropdown-toggle"));
|
||
await click(
|
||
target.querySelectorAll("thead .dropdown-menu .dropdown-menu .dropdown-item")[3]
|
||
);
|
||
|
||
// close and reopen row groupings after changing value
|
||
serverData.models.partner.records.find((r) => r.product_id === 37).date = "2016-10-27";
|
||
|
||
await click(target.querySelector("tbody .o_pivot_header_cell_opened"));
|
||
await click(target.querySelector("tbody .o_pivot_header_cell_closed"));
|
||
await click(target.querySelectorAll("tbody .dropdown-menu .dropdown-item")[4]);
|
||
assert.strictEqual(target.querySelectorAll(".o_pivot_cell_value")[4].innerText, ""); // xphone December 2016
|
||
|
||
// invert axis, and reopen column groupings
|
||
await click(target.querySelector(".o_cp_bottom_left .o_pivot_flip_button"));
|
||
await click(target.querySelector("thead .o_pivot_header_cell_opened"));
|
||
await click(target.querySelector("thead .o_pivot_header_cell_closed"));
|
||
await click(target.querySelectorAll("thead .dropdown-menu .dropdown-item")[4]);
|
||
assert.strictEqual(target.querySelectorAll(".o_pivot_cell_value")[3].innerText, ""); // December 2016 xphone
|
||
});
|
||
|
||
QUnit.test("correctly group data after flip (1)", async function (assert) {
|
||
assert.expect(4);
|
||
|
||
serverData.views = {
|
||
"partner,false,pivot": "<pivot/>",
|
||
"partner,false,search": `<search><filter name="bayou" string="Bayou" domain="[(1,'=',1)]"/></search>`,
|
||
"partner,false,list": '<tree><field name="foo"/></tree>',
|
||
"partner,false,form": '<form><field name="foo"/></form>',
|
||
};
|
||
|
||
const webClient = await createWebClient({ serverData });
|
||
|
||
await doAction(webClient, {
|
||
res_model: "partner",
|
||
type: "ir.actions.act_window",
|
||
views: [[false, "pivot"]],
|
||
context: { group_by: ["product_id"] },
|
||
});
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("tbody th")].map((e) => e.innerText),
|
||
["Total", "xphone", "xpad"]
|
||
);
|
||
|
||
// flip axis
|
||
await click(target.querySelector(".o_pivot_flip_button"));
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("tbody th")].map((e) => e.innerText),
|
||
["Total"]
|
||
);
|
||
|
||
// select filter "Bayou" in control panel
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "Bayou");
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("tbody th")].map((e) => e.innerText),
|
||
["Total", "xphone", "xpad"]
|
||
);
|
||
|
||
// close row header "Total"
|
||
await click(target.querySelector("tbody .o_pivot_header_cell_opened"));
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("tbody th")].map((e) => e.innerText),
|
||
["Total"]
|
||
);
|
||
});
|
||
|
||
QUnit.test("correctly group data after flip (2)", async function (assert) {
|
||
assert.expect(5);
|
||
|
||
serverData.views = {
|
||
"partner,false,pivot": "<pivot/>",
|
||
"partner,false,search": `<search><filter name="bayou" string="Bayou" domain="[(1,'=',1)]"/></search>`,
|
||
"partner,false,list": '<tree><field name="foo"/></tree>',
|
||
"partner,false,form": '<form><field name="foo"/></form>',
|
||
};
|
||
|
||
const webClient = await createWebClient({ serverData });
|
||
|
||
await doAction(webClient, {
|
||
res_model: "partner",
|
||
type: "ir.actions.act_window",
|
||
views: [[false, "pivot"]],
|
||
context: { group_by: ["product_id"] },
|
||
});
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("tbody th")].map((e) => e.innerText),
|
||
["Total", "xphone", "xpad"]
|
||
);
|
||
|
||
// select filter "Bayou" in control panel
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "Bayou");
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("tbody th")].map((e) => e.innerText),
|
||
["Total", "xphone", "xpad"]
|
||
);
|
||
|
||
// flip axis
|
||
await click(target.querySelector(".o_pivot_flip_button"));
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("tbody th")].map((e) => e.innerText),
|
||
["Total"]
|
||
);
|
||
|
||
// unselect filter "Bayou" in control panel
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "Bayou");
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("tbody th")].map((e) => e.innerText),
|
||
["Total", "xphone", "xpad"]
|
||
);
|
||
|
||
// close row header "Total"
|
||
await click(target.querySelector("tbody .o_pivot_header_cell_opened"));
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("tbody th")].map((e) => e.innerText),
|
||
["Total"]
|
||
);
|
||
});
|
||
|
||
QUnit.test("correctly uses pivot_ keys from the context (at reload)", async function (assert) {
|
||
assert.expect(8);
|
||
|
||
// in this test, we use "foo" as a measure
|
||
serverData.models.partner.fields.foo.store = true;
|
||
serverData.models.partner.fields.amount = {
|
||
string: "Amount",
|
||
type: "float",
|
||
group_operator: "sum",
|
||
};
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="date" interval="day" type="col"/>
|
||
<field name="amount" type="measure"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter
|
||
name="filter_with_context"
|
||
string="My fake favorite"
|
||
domain="[]"
|
||
context="{
|
||
'pivot_measures': ['foo'],
|
||
'pivot_column_groupby': ['customer'],
|
||
'pivot_row_groupby': ['product_id']
|
||
}"
|
||
/>
|
||
</search>`,
|
||
});
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("tbody tr").querySelectorAll("td.o_pivot_cell_value")[4].innerText,
|
||
"0.00",
|
||
"the active measure should be amount"
|
||
);
|
||
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "My fake favorite");
|
||
|
||
assert.containsOnce(
|
||
target,
|
||
"thead .o_pivot_header_cell_opened",
|
||
"column: should have one opened header"
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
"thead .o_pivot_header_cell_closed:contains(First)",
|
||
"column: should display one closed header with 'First'"
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
"thead .o_pivot_header_cell_closed:contains(Second)",
|
||
"column: should display one closed header with 'Second'"
|
||
);
|
||
|
||
assert.containsOnce(
|
||
target,
|
||
"tbody .o_pivot_header_cell_opened",
|
||
"row: should have one opened header"
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
"tbody .o_pivot_header_cell_closed:contains(xphone)",
|
||
"row: should display one closed header with 'xphone'"
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
"tbody .o_pivot_header_cell_closed:contains(xpad)",
|
||
"row: should display one closed header with 'xpad'"
|
||
);
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("tbody tr").querySelectorAll("td")[2].innerText,
|
||
"32",
|
||
"selected measure should be foo, with total 32"
|
||
);
|
||
});
|
||
|
||
QUnit.test("correctly use group_by key from the context", async function (assert) {
|
||
assert.expect(7);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="customer" type="col" />
|
||
<field name="foo" type="measure" />
|
||
</pivot>`,
|
||
groupBy: ["product_id"],
|
||
});
|
||
|
||
assert.containsOnce(
|
||
target,
|
||
"thead .o_pivot_header_cell_opened",
|
||
"column: should have one opened header"
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
"thead .o_pivot_header_cell_closed:contains(First)",
|
||
'column: should display one closed header with "First"'
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
"thead .o_pivot_header_cell_closed:contains(Second)",
|
||
'column: should display one closed header with "Second"'
|
||
);
|
||
|
||
assert.containsOnce(
|
||
target,
|
||
"tbody .o_pivot_header_cell_opened",
|
||
"row: should have one opened header"
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
"tbody .o_pivot_header_cell_closed:contains(xphone)",
|
||
'row: should display one closed header with "xphone"'
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
"tbody .o_pivot_header_cell_closed:contains(xpad)",
|
||
'row: should display one closed header with "xpad"'
|
||
);
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("tbody tr").querySelectorAll("td")[2].innerText,
|
||
"32",
|
||
"selected measure should be foo, with total 32"
|
||
);
|
||
});
|
||
|
||
QUnit.test(
|
||
"correctly uses pivot_row_groupby key with default groupBy from the context",
|
||
async function (assert) {
|
||
assert.expect(6);
|
||
|
||
serverData.models.partner.fields.amount = {
|
||
string: "Amount",
|
||
type: "float",
|
||
group_operator: "sum",
|
||
};
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="customer" type="col"/>
|
||
<field name="date" interval="day" type="row"/>
|
||
</pivot>`,
|
||
groupBy: ["customer"],
|
||
context: {
|
||
pivot_row_groupby: ["product_id"],
|
||
},
|
||
});
|
||
|
||
assert.containsOnce(
|
||
target,
|
||
"thead .o_pivot_header_cell_opened",
|
||
"column: should have one opened header"
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
"thead .o_pivot_header_cell_closed:contains(First)",
|
||
"column: should display one closed header with 'First'"
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
"thead .o_pivot_header_cell_closed:contains(Second)",
|
||
"column: should display one closed header with 'Second'"
|
||
);
|
||
|
||
// With pivot_row_groupby, groupBy customer should replace and eventually display product_id
|
||
assert.containsOnce(
|
||
target,
|
||
"tbody .o_pivot_header_cell_opened",
|
||
"row: should have one opened header"
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
"tbody .o_pivot_header_cell_closed:contains(xphone)",
|
||
"row: should display one closed header with 'xphone'"
|
||
);
|
||
assert.containsOnce(
|
||
target,
|
||
"tbody .o_pivot_header_cell_closed:contains(xpad)",
|
||
"row: should display one closed header with 'xpad'"
|
||
);
|
||
}
|
||
);
|
||
|
||
QUnit.test("pivot still handles __count__ measure", async function (assert) {
|
||
// for retro-compatibility reasons, the pivot view still handles
|
||
// '__count__' measure.
|
||
assert.expect(4);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: "<pivot></pivot>",
|
||
mockRPC(route, args) {
|
||
if (args.method === "read_group") {
|
||
assert.deepEqual(
|
||
args.kwargs.fields,
|
||
["__count"],
|
||
"should make a read_group with field __count"
|
||
);
|
||
}
|
||
},
|
||
context: {
|
||
pivot_measures: ["__count__"],
|
||
},
|
||
});
|
||
|
||
await click(target.querySelector(".o_cp_bottom_left button.dropdown-toggle"));
|
||
assert.containsOnce(target, ".o_cp_bottom_left .dropdown-item");
|
||
assert.strictEqual(
|
||
target.querySelector(".o_cp_bottom_left .dropdown-item").innerText,
|
||
"Count"
|
||
);
|
||
assert.hasClass(target.querySelector(".o_cp_bottom_left .dropdown-item"), "selected");
|
||
});
|
||
|
||
QUnit.test("not use a many2one as a measure by default", async function (assert) {
|
||
assert.expect(3);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id"/>
|
||
<field name="date" interval="month" type="col"/>
|
||
</pivot>`,
|
||
});
|
||
await click(target.querySelector(".o_cp_bottom_left button.dropdown-toggle"));
|
||
assert.containsOnce(target, ".o_cp_bottom_left .dropdown-item");
|
||
assert.strictEqual(
|
||
target.querySelector(".o_cp_bottom_left .dropdown-item").innerText,
|
||
"Count"
|
||
);
|
||
assert.hasClass(target.querySelector(".o_cp_bottom_left .dropdown-item"), "selected");
|
||
});
|
||
|
||
QUnit.test("pivot view with many2one field as a measure", async function (assert) {
|
||
assert.expect(1);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id" type="measure"/>
|
||
<field name="date" interval="month" type="col"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
assert.strictEqual(
|
||
target.querySelector("table tbody tr").innerText.replace(/\s/g, ""),
|
||
"Total1122",
|
||
"should display product_id count as measure"
|
||
);
|
||
});
|
||
|
||
QUnit.test("m2o as measure, drilling down into data", async function (assert) {
|
||
assert.expect(1);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id" type="measure"/>
|
||
</pivot>`,
|
||
});
|
||
await click(target.querySelector("tbody .o_pivot_header_cell_closed"));
|
||
// click on date by month
|
||
await mouseEnter(target.querySelector("tbody .dropdown-menu .dropdown-toggle"));
|
||
await click(
|
||
target.querySelectorAll("tbody .dropdown-menu .dropdown-menu .dropdown-item")[3]
|
||
);
|
||
|
||
assert.strictEqual(
|
||
[...target.querySelectorAll(".o_pivot_cell_value")].map((c) => c.innerText).join(""),
|
||
"2112",
|
||
"should have loaded the proper data"
|
||
);
|
||
});
|
||
|
||
QUnit.test("Row and column groupbys plus a domain", async function (assert) {
|
||
assert.expect(3);
|
||
|
||
const expectedContext = {
|
||
group_by: [],
|
||
pivot_column_groupby: ["customer"],
|
||
pivot_measures: ["foo"],
|
||
pivot_row_groupby: ["product_id"],
|
||
};
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="some_filter" string="Some Filter" domain="[('product_id', '=', 41)]"/>
|
||
</search>`,
|
||
mockRPC(route, args) {
|
||
if (args.method === "create_or_replace") {
|
||
assert.deepEqual(args.args[0].context, expectedContext);
|
||
return true;
|
||
}
|
||
},
|
||
});
|
||
|
||
// Set a column groupby
|
||
await click(target.querySelector("thead .o_pivot_header_cell_closed"));
|
||
await click(target.querySelectorAll("thead .dropdown-item")[1]);
|
||
|
||
// Set a Row groupby
|
||
await click(target.querySelector("tbody .o_pivot_header_cell_closed"));
|
||
await click(target.querySelectorAll("tbody .dropdown-item")[4]);
|
||
|
||
// Add a filter
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "Some Filter");
|
||
|
||
assert.containsOnce(
|
||
target,
|
||
"tbody .o_pivot_header_cell_closed",
|
||
"There should be only one product line because of the domain"
|
||
);
|
||
assert.strictEqual(
|
||
target.querySelector("tbody .o_pivot_header_cell_closed").innerText,
|
||
"xpad",
|
||
"The product should be the right one"
|
||
);
|
||
|
||
// Save current search to favorite
|
||
await toggleFavoriteMenu(target);
|
||
await toggleSaveFavorite(target);
|
||
await editFavoriteName(target, "My favorite");
|
||
await saveFavorite(target);
|
||
});
|
||
|
||
QUnit.test(
|
||
"parallel data loading should discard all but the last one",
|
||
async function (assert) {
|
||
assert.expect(6);
|
||
|
||
let def;
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter string="Product" name="product_id" context="{'group_by':'product_id'}"/>
|
||
<filter string="Customer" name="customer" context="{'group_by':'customer'}"/>
|
||
</search>`,
|
||
mockRPC(route, args) {
|
||
if (args.method === "read_group") {
|
||
return Promise.resolve(def);
|
||
}
|
||
},
|
||
});
|
||
|
||
assert.containsOnce(target, ".o_pivot_cell_value", "should have 1 cell initially");
|
||
assert.containsOnce(target, "tbody tr", "should have 1 row initially");
|
||
|
||
def = makeDeferred();
|
||
await toggleGroupByMenu(target);
|
||
await toggleMenuItem(target, "Product");
|
||
await toggleMenuItem(target, "Customer");
|
||
|
||
assert.containsOnce(target, ".o_pivot_cell_value", "should still have 1 cell");
|
||
assert.containsOnce(target, "tbody tr", "should still have 1 row");
|
||
|
||
await def.resolve();
|
||
await nextTick();
|
||
|
||
assert.containsN(target, ".o_pivot_cell_value", 6, "should have 6 cells");
|
||
assert.containsN(target, "tbody tr", 6, "should have 6 rows");
|
||
}
|
||
);
|
||
|
||
QUnit.test("pivot measures should be alphabetically sorted", async function (assert) {
|
||
assert.expect(1);
|
||
|
||
// It's important to compare capitalized and lowercased words
|
||
// to be sure the sorting is effective with both of them
|
||
serverData.models.partner.fields.bouh = {
|
||
string: "bouh",
|
||
type: "integer",
|
||
group_operator: "sum",
|
||
};
|
||
serverData.models.partner.fields.modd = {
|
||
string: "modd",
|
||
type: "integer",
|
||
group_operator: "sum",
|
||
};
|
||
serverData.models.partner.fields.zip = {
|
||
string: "Zip",
|
||
type: "integer",
|
||
group_operator: "sum",
|
||
};
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="zip" type="measure"/>
|
||
<field name="foo" type="measure"/>
|
||
<field name="bouh" type="measure"/>
|
||
<field name="modd" type="measure"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
await click(target.querySelector(".o_cp_bottom_left button.dropdown-toggle"));
|
||
assert.strictEqual(
|
||
[...target.querySelectorAll(".o_cp_bottom_left .dropdown-item")]
|
||
.map((i) => i.innerText)
|
||
.join(""),
|
||
"bouhFoomoddZipCount"
|
||
);
|
||
});
|
||
|
||
QUnit.test("pivot view should use default order for auto sorting", async function (assert) {
|
||
assert.expect(1);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot default_order="foo asc">
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
assert.hasClass(
|
||
target.querySelector("thead th.o_pivot_measure_row"),
|
||
"o_pivot_sort_order_asc"
|
||
);
|
||
});
|
||
|
||
QUnit.test("pivot view can be flipped", async function (assert) {
|
||
assert.expect(5);
|
||
|
||
var rpcCount = 0;
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id" type="row"/>
|
||
</pivot>`,
|
||
mockRPC() {
|
||
rpcCount++;
|
||
},
|
||
});
|
||
|
||
assert.containsN(
|
||
target,
|
||
"tbody tr",
|
||
3,
|
||
"should have 3 rows: 1 for the open header, and 2 for data"
|
||
);
|
||
let values = ["4", "1", "3"];
|
||
assert.strictEqual(getCurrentValues(target), values.join());
|
||
|
||
rpcCount = 0;
|
||
await click(target.querySelector(".o_pivot_flip_button"));
|
||
|
||
assert.strictEqual(rpcCount, 0, "should not have done any rpc");
|
||
assert.containsOnce(target, "tbody tr", "should have 1 rows: 1 for the main header");
|
||
|
||
values = ["1", "3", "4"];
|
||
assert.strictEqual(getCurrentValues(target), values.join());
|
||
});
|
||
|
||
QUnit.test("rendering of pivot view with comparison", async function (assert) {
|
||
serverData.models.partner.records[0].date = "2016-12-15";
|
||
serverData.models.partner.records[1].date = "2016-12-17";
|
||
serverData.models.partner.records[2].date = "2016-11-22";
|
||
serverData.models.partner.records[3].date = "2016-11-03";
|
||
|
||
patchDate(2016, 11, 20, 1, 0, 0);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="date" interval="month" type="col"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="date_filter" date="date" domain="[]" default_period='last_year'/>
|
||
</search>`,
|
||
context: { search_default_date_filter: 1 },
|
||
mockRPC(route, args) {
|
||
if (args.method === "create_or_replace") {
|
||
assert.deepEqual(args.args[0].context, {
|
||
pivot_measures: ["__count"],
|
||
pivot_column_groupby: [],
|
||
pivot_row_groupby: ["product_id"],
|
||
group_by: [],
|
||
comparison: {
|
||
comparisonId: "previous_period",
|
||
comparisonRange: [
|
||
"&",
|
||
["date", ">=", "2016-11-01"],
|
||
["date", "<=", "2016-11-30"],
|
||
],
|
||
comparisonRangeDescription: "November 2016",
|
||
fieldDescription: "Date",
|
||
fieldName: "date",
|
||
range: [
|
||
"&",
|
||
["date", ">=", "2016-12-01"],
|
||
["date", "<=", "2016-12-31"],
|
||
],
|
||
rangeDescription: "December 2016",
|
||
},
|
||
});
|
||
return true;
|
||
}
|
||
},
|
||
});
|
||
|
||
// with no data
|
||
await toggleComparisonMenu(target);
|
||
await toggleMenuItem(target, "Date: Previous period");
|
||
|
||
assert.containsOnce(target, "p.o_view_nocontent_empty_folder");
|
||
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "Date");
|
||
await toggleMenuItemOption(target, "Date", "December");
|
||
await toggleMenuItemOption(target, "Date", "2016");
|
||
await toggleMenuItemOption(target, "Date", "2015");
|
||
|
||
assert.containsN(
|
||
target,
|
||
".o_pivot thead tr:last th",
|
||
9,
|
||
"last header row should contains 9 cells (3*[December 2016, November 2016, Variation]"
|
||
);
|
||
let values = ["19", "0", "-100%", "0", "13", "100%", "19", "13", "-31.58%"];
|
||
assert.strictEqual(getCurrentValues(target), values.join());
|
||
|
||
// with data, with row groupby
|
||
await click(target.querySelector("tbody .o_pivot_header_cell_closed"));
|
||
await click(target.querySelectorAll("tbody .dropdown-menu .dropdown-item")[4]);
|
||
values = [
|
||
"19",
|
||
"0",
|
||
"-100%",
|
||
"0",
|
||
"13",
|
||
"100%",
|
||
"19",
|
||
"13",
|
||
"-31.58%",
|
||
"19",
|
||
"0",
|
||
"-100%",
|
||
"0",
|
||
"1",
|
||
"100%",
|
||
"19",
|
||
"1",
|
||
"-94.74%",
|
||
"0",
|
||
"12",
|
||
"100%",
|
||
"0",
|
||
"12",
|
||
"100%",
|
||
];
|
||
assert.strictEqual(getCurrentValues(target), values.join());
|
||
|
||
await click(target.querySelector(".o_cp_bottom_left button.dropdown-toggle"));
|
||
|
||
await click(target.querySelectorAll(".o_cp_bottom_left .dropdown-menu .dropdown-item")[0]);
|
||
await click(target.querySelectorAll(".o_cp_bottom_left .dropdown-menu .dropdown-item")[1]);
|
||
values = ["2,0,-100%,0,2,100%,2,2,0%,2,0,-100%,0,1,100%,2,1,-50%,0,1,100%,0,1,100%"];
|
||
assert.strictEqual(getCurrentValues(target), values.join());
|
||
|
||
await click(target.querySelector("thead .o_pivot_header_cell_opened"));
|
||
values = ["2", "2", "0%", "2", "1", "-50%", "0", "1", "100%"];
|
||
assert.strictEqual(getCurrentValues(target), values.join());
|
||
|
||
await toggleFavoriteMenu(target);
|
||
await toggleSaveFavorite(target);
|
||
await editFavoriteName(target, "Fav");
|
||
await saveFavorite(target);
|
||
});
|
||
|
||
QUnit.test("export data in excel with comparison", async function (assert) {
|
||
assert.expect(12);
|
||
|
||
serverData.models.partner.records[0].date = "2016-12-15";
|
||
serverData.models.partner.records[1].date = "2016-12-17";
|
||
serverData.models.partner.records[2].date = "2016-11-22";
|
||
serverData.models.partner.records[3].date = "2016-11-03";
|
||
|
||
patchDate(2016, 11, 20, 1, 0, 0);
|
||
|
||
const downloadDef = makeDeferred();
|
||
mockDownload(async ({ url, data }) => {
|
||
data = JSON.parse(await data.data.text());
|
||
for (const l of data.col_group_headers) {
|
||
const titles = l.map((o) => o.title);
|
||
assert.step(JSON.stringify(titles));
|
||
}
|
||
const measures = data.measure_headers.map((o) => o.title);
|
||
assert.step(JSON.stringify(measures));
|
||
const origins = data.origin_headers.map((o) => o.title);
|
||
assert.step(JSON.stringify(origins));
|
||
assert.step(String(data.measure_count));
|
||
assert.step(String(data.origin_count));
|
||
const valuesLength = data.rows.map((o) => o.values.length);
|
||
assert.step(JSON.stringify(valuesLength));
|
||
assert.strictEqual(
|
||
url,
|
||
"/web/pivot/export_xlsx",
|
||
"should call get_file with correct parameters"
|
||
);
|
||
downloadDef.resolve();
|
||
return Promise.resolve();
|
||
});
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="date" interval="month" type="col"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="date_filter" date="date" domain="[]" default_period='antepenultimate_month'/>
|
||
</search>`,
|
||
context: { search_default_date_filter: 1 },
|
||
});
|
||
|
||
// open comparison menu
|
||
await toggleComparisonMenu(target);
|
||
// compare October 2016 to September 2016
|
||
await toggleMenuItem(target, "Date: Previous period");
|
||
|
||
// With the data above, the time ranges contain no record.
|
||
assert.containsOnce(target, "p.o_view_nocontent_empty_folder", "there should be no data");
|
||
// export data should be impossible since the pivot buttons
|
||
// are deactivated (exception: the 'Measures' button).
|
||
assert.ok(target.querySelector(".o_control_panel button.o_pivot_download").disabled);
|
||
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "Date");
|
||
await toggleMenuItemOption(target, "Date", "December");
|
||
await toggleMenuItemOption(target, "Date", "October");
|
||
assert.notOk(target.querySelector(".o_control_panel button.o_pivot_download").disabled);
|
||
|
||
// With the data above, the time ranges contain some records.
|
||
// export data. Should execute 'get_file'
|
||
await click(target.querySelector(".o_control_panel button.o_pivot_download"));
|
||
await downloadDef;
|
||
|
||
assert.verifySteps([
|
||
// col group headers
|
||
'["Total",""]',
|
||
'["November 2016","December 2016"]',
|
||
// measure headers
|
||
'["Foo","Foo","Foo"]',
|
||
// origin headers
|
||
'["November 2016","December 2016","Variation","November 2016","December 2016"' +
|
||
',"Variation","November 2016","December 2016","Variation"]',
|
||
// number of 'measures'
|
||
"1",
|
||
// number of 'origins'
|
||
"2",
|
||
// rows values length
|
||
"[9]",
|
||
]);
|
||
});
|
||
|
||
QUnit.test("rendering pivot view with comparison and count measure", async function (assert) {
|
||
assert.expect(2);
|
||
|
||
let mockMock = false;
|
||
let nbReadGroup = 0;
|
||
|
||
serverData.models.partner.records[0].date = "2016-12-15";
|
||
serverData.models.partner.records[1].date = "2016-12-17";
|
||
serverData.models.partner.records[2].date = "2016-12-22";
|
||
serverData.models.partner.records[3].date = "2016-12-03";
|
||
|
||
patchDate(2016, 11, 20, 1, 0, 0);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: '<pivot><field name="customer" type="row"/></pivot>',
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="date_filter" date="date" domain="[]" default_period='this_month'/>
|
||
</search>`,
|
||
context: { search_default_date_filter: 1 },
|
||
mockRPC(route, args) {
|
||
if (args.method === "read_group" && mockMock) {
|
||
nbReadGroup++;
|
||
if (nbReadGroup === 4) {
|
||
// this modification is necessary because mockReadGroup does not
|
||
// properly reflect the server response when there is no record
|
||
// and a groupby list of length at least one.
|
||
return Promise.resolve([{}]);
|
||
}
|
||
}
|
||
},
|
||
});
|
||
|
||
mockMock = true;
|
||
|
||
// compare December 2016 to November 2016
|
||
await toggleComparisonMenu(target);
|
||
await toggleMenuItem(target, "Date: Previous period");
|
||
|
||
const values = ["0", "4", "100%", "0", "2", "100%", "0", "2", "100%"];
|
||
assert.strictEqual(getCurrentValues(target), values.join(","));
|
||
assert.containsN(
|
||
target,
|
||
".o_pivot_header_cell_closed",
|
||
3,
|
||
"there should be exactly three closed header ('Total','First', 'Second')"
|
||
);
|
||
});
|
||
|
||
QUnit.test(
|
||
"can sort a pivot view with comparison by clicking on header",
|
||
async function (assert) {
|
||
assert.expect(6);
|
||
|
||
serverData.models.partner.records[0].date = "2016-12-15";
|
||
serverData.models.partner.records[1].date = "2016-12-17";
|
||
serverData.models.partner.records[2].date = "2016-11-22";
|
||
serverData.models.partner.records[3].date = "2016-11-03";
|
||
|
||
patchDate(2016, 11, 20, 1, 0, 0);
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="date" interval="day" type="row"/>
|
||
<field name="company_type" type="col"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="date_filter" date="date" domain="[]" default_period='this_month'/>
|
||
</search>`,
|
||
context: { search_default_date_filter: 1 },
|
||
});
|
||
|
||
// compare December 2016 to November 2016
|
||
await toggleComparisonMenu(target);
|
||
await toggleMenuItem(target, "Date: Previous period");
|
||
|
||
// initial sanity check
|
||
let values = [
|
||
"17",
|
||
"12",
|
||
"-29.41%",
|
||
"2",
|
||
"1",
|
||
"-50%",
|
||
"19",
|
||
"13",
|
||
"-31.58%",
|
||
"2",
|
||
"0",
|
||
"-100%",
|
||
"2",
|
||
"0",
|
||
"-100%",
|
||
"17",
|
||
"0",
|
||
"-100%",
|
||
"17",
|
||
"0",
|
||
"-100%",
|
||
"0",
|
||
"12",
|
||
"100%",
|
||
"0",
|
||
"12",
|
||
"100%",
|
||
"0",
|
||
"1",
|
||
"100%",
|
||
"0",
|
||
"1",
|
||
"100%",
|
||
];
|
||
assert.strictEqual(getCurrentValues(target), values.join());
|
||
|
||
// click on 'Foo' in column Total/Company (should sort by the period of interest, ASC)
|
||
await click(target.querySelector(".o_pivot_measure_row"));
|
||
values = [
|
||
"17",
|
||
"12",
|
||
"-29.41%",
|
||
"2",
|
||
"1",
|
||
"-50%",
|
||
"19",
|
||
"13",
|
||
"-31.58%",
|
||
"2",
|
||
"0",
|
||
"-100%",
|
||
"2",
|
||
"0",
|
||
"-100%",
|
||
"0",
|
||
"12",
|
||
"100%",
|
||
"0",
|
||
"12",
|
||
"100%",
|
||
"0",
|
||
"1",
|
||
"100%",
|
||
"0",
|
||
"1",
|
||
"100%",
|
||
"17",
|
||
"0",
|
||
"-100%",
|
||
"17",
|
||
"0",
|
||
"-100%",
|
||
];
|
||
assert.strictEqual(getCurrentValues(target), values.join());
|
||
|
||
// click again on 'Foo' in column Total/Company (should sort by the period of interest, DESC)
|
||
await click(target.querySelector(".o_pivot_measure_row"));
|
||
values = [
|
||
"17",
|
||
"12",
|
||
"-29.41%",
|
||
"2",
|
||
"1",
|
||
"-50%",
|
||
"19",
|
||
"13",
|
||
"-31.58%",
|
||
"17",
|
||
"0",
|
||
"-100%",
|
||
"17",
|
||
"0",
|
||
"-100%",
|
||
"2",
|
||
"0",
|
||
"-100%",
|
||
"2",
|
||
"0",
|
||
"-100%",
|
||
"0",
|
||
"12",
|
||
"100%",
|
||
"0",
|
||
"12",
|
||
"100%",
|
||
"0",
|
||
"1",
|
||
"100%",
|
||
"0",
|
||
"1",
|
||
"100%",
|
||
];
|
||
assert.strictEqual(getCurrentValues(target), values.join());
|
||
|
||
// click on 'This Month' in column Total/Individual/Foo
|
||
await click(target.querySelectorAll(".o_pivot_origin_row")[3]);
|
||
values = [
|
||
"17",
|
||
"12",
|
||
"-29.41%",
|
||
"2",
|
||
"1",
|
||
"-50%",
|
||
"19",
|
||
"13",
|
||
"-31.58%",
|
||
"17",
|
||
"0",
|
||
"-100%",
|
||
"17",
|
||
"0",
|
||
"-100%",
|
||
"0",
|
||
"12",
|
||
"100%",
|
||
"0",
|
||
"12",
|
||
"100%",
|
||
"0",
|
||
"1",
|
||
"100%",
|
||
"0",
|
||
"1",
|
||
"100%",
|
||
"2",
|
||
"0",
|
||
"-100%",
|
||
"2",
|
||
"0",
|
||
"-100%",
|
||
];
|
||
assert.strictEqual(getCurrentValues(target), values.join());
|
||
|
||
// click on 'Previous Period' in column Total/Individual/Foo
|
||
await click(target.querySelectorAll(".o_pivot_origin_row")[4]);
|
||
values = [
|
||
"17",
|
||
"12",
|
||
"-29.41%",
|
||
"2",
|
||
"1",
|
||
"-50%",
|
||
"19",
|
||
"13",
|
||
"-31.58%",
|
||
"2",
|
||
"0",
|
||
"-100%",
|
||
"2",
|
||
"0",
|
||
"-100%",
|
||
"17",
|
||
"0",
|
||
"-100%",
|
||
"17",
|
||
"0",
|
||
"-100%",
|
||
"0",
|
||
"12",
|
||
"100%",
|
||
"0",
|
||
"12",
|
||
"100%",
|
||
"0",
|
||
"1",
|
||
"100%",
|
||
"0",
|
||
"1",
|
||
"100%",
|
||
];
|
||
assert.strictEqual(getCurrentValues(target), values.join());
|
||
|
||
// click on 'Variation' in column Total/Foo
|
||
await click(target.querySelectorAll(".o_pivot_origin_row")[8]);
|
||
values = [
|
||
"17",
|
||
"12",
|
||
"-29.41%",
|
||
"2",
|
||
"1",
|
||
"-50%",
|
||
"19",
|
||
"13",
|
||
"-31.58%",
|
||
"2",
|
||
"0",
|
||
"-100%",
|
||
"2",
|
||
"0",
|
||
"-100%",
|
||
"17",
|
||
"0",
|
||
"-100%",
|
||
"17",
|
||
"0",
|
||
"-100%",
|
||
"0",
|
||
"12",
|
||
"100%",
|
||
"0",
|
||
"12",
|
||
"100%",
|
||
"0",
|
||
"1",
|
||
"100%",
|
||
"0",
|
||
"1",
|
||
"100%",
|
||
];
|
||
assert.strictEqual(getCurrentValues(target), values.join());
|
||
}
|
||
);
|
||
|
||
QUnit.test("Click on the measure list but not on a menu item", async function (assert) {
|
||
assert.expect(4);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
// have at least a measure to have a separator in the Measures dropdown:
|
||
//
|
||
// Foo
|
||
// -----
|
||
// Count
|
||
arch: `<pivot><field name="foo" type="measure"/></pivot>`,
|
||
});
|
||
|
||
assert.containsNone(target, ".o_cp_bottom_left .dropdown-menu");
|
||
|
||
// open the "Measures" menu
|
||
await click(target.querySelector(".o_cp_bottom_left .dropdown-toggle"));
|
||
assert.containsOnce(target, ".o_cp_bottom_left .dropdown-menu");
|
||
|
||
// click on the divider in the "Measures" menu does not crash
|
||
await click(target.querySelector(".o_cp_bottom_left .dropdown-menu .dropdown-divider"));
|
||
// the menu should still be open
|
||
assert.containsOnce(target, ".o_cp_bottom_left .dropdown-menu");
|
||
|
||
// click on the measure list but not on a menu item or the separator
|
||
await click(target.querySelector(".o_cp_bottom_left .dropdown-menu"));
|
||
// the menu should still be open
|
||
assert.containsOnce(target, ".o_cp_bottom_left .dropdown-menu");
|
||
});
|
||
|
||
QUnit.test(
|
||
"Navigation list view for a group and back with breadcrumbs",
|
||
async function (assert) {
|
||
assert.expect(16);
|
||
|
||
serverData.views = {
|
||
"partner,false,pivot": `
|
||
<pivot>
|
||
<field name="customer" type="row"/>
|
||
</pivot>`,
|
||
"partner,false,search": `
|
||
<search>
|
||
<filter name="bayou" string="Bayou" domain="[('foo','=', 12)]"/>
|
||
</search>`,
|
||
"partner,false,list": '<tree><field name="foo"/></tree>',
|
||
"partner,false,form": '<form><field name="foo"/></form>',
|
||
};
|
||
|
||
let readGroupCount = 0;
|
||
const mockRPC = (route, args) => {
|
||
if (args.method === "read_group") {
|
||
assert.step("read_group");
|
||
const domain = args.kwargs.domain;
|
||
if ([0, 1].indexOf(readGroupCount) !== -1) {
|
||
assert.deepEqual(domain, [], "domain empty");
|
||
} else if ([2, 3, 4, 5].indexOf(readGroupCount) !== -1) {
|
||
assert.deepEqual(
|
||
domain,
|
||
[["foo", "=", 12]],
|
||
"domain conserved when back with breadcrumbs"
|
||
);
|
||
}
|
||
readGroupCount++;
|
||
}
|
||
if (args.method === "web_search_read") {
|
||
assert.step("search_read");
|
||
const domain = args.kwargs.domain;
|
||
assert.deepEqual(
|
||
domain,
|
||
["&", ["customer", "=", 1], ["foo", "=", 12]],
|
||
"list domain is correct"
|
||
);
|
||
}
|
||
};
|
||
|
||
const webClient = await createWebClient({ serverData, mockRPC });
|
||
|
||
await doAction(webClient, {
|
||
res_model: "partner",
|
||
type: "ir.actions.act_window",
|
||
views: [[false, "pivot"]],
|
||
});
|
||
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, 0);
|
||
await nextTick();
|
||
|
||
await click(target.querySelectorAll(".o_pivot_cell_value")[1]);
|
||
await legacyExtraNextTick();
|
||
|
||
assert.containsOnce(target, ".o_list_view");
|
||
|
||
await click(target.querySelector(".o_control_panel ol.breadcrumb li.breadcrumb-item"));
|
||
|
||
assert.verifySteps([
|
||
"read_group",
|
||
"read_group",
|
||
"read_group",
|
||
"read_group",
|
||
"search_read",
|
||
"read_group",
|
||
"read_group",
|
||
]);
|
||
}
|
||
);
|
||
|
||
QUnit.test(
|
||
"Cell values are kept when flippin a pivot view in comparison mode",
|
||
async function (assert) {
|
||
assert.expect(2);
|
||
|
||
serverData.models.partner.records[0].date = "2016-12-15";
|
||
serverData.models.partner.records[1].date = "2016-12-17";
|
||
serverData.models.partner.records[2].date = "2016-11-22";
|
||
serverData.models.partner.records[3].date = "2016-11-03";
|
||
|
||
patchDate(2016, 11, 20, 1, 0, 0);
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="date" interval="day" type="row"/>
|
||
<field name="company_type" type="col"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="date_filter" date="date" domain="[]" default_period='this_month'/>
|
||
</search>`,
|
||
context: { search_default_date_filter: 1 },
|
||
});
|
||
|
||
// compare December 2016 to November 2016
|
||
await toggleComparisonMenu(target);
|
||
await toggleMenuItem(target, "Date: Previous period");
|
||
|
||
// initial sanity check
|
||
let values = [
|
||
"17",
|
||
"12",
|
||
"-29.41%",
|
||
"2",
|
||
"1",
|
||
"-50%",
|
||
"19",
|
||
"13",
|
||
"-31.58%",
|
||
"2",
|
||
"0",
|
||
"-100%",
|
||
"2",
|
||
"0",
|
||
"-100%",
|
||
"17",
|
||
"0",
|
||
"-100%",
|
||
"17",
|
||
"0",
|
||
"-100%",
|
||
"0",
|
||
"12",
|
||
"100%",
|
||
"0",
|
||
"12",
|
||
"100%",
|
||
"0",
|
||
"1",
|
||
"100%",
|
||
"0",
|
||
"1",
|
||
"100%",
|
||
];
|
||
assert.strictEqual(getCurrentValues(target), values.join());
|
||
|
||
// flip table
|
||
await click(target.querySelector(".o_pivot_flip_button"));
|
||
|
||
values = [
|
||
"2",
|
||
"0",
|
||
"-100%",
|
||
"17",
|
||
"0",
|
||
"-100%",
|
||
"0",
|
||
"12",
|
||
"100%",
|
||
"0",
|
||
"1",
|
||
"100%",
|
||
"19",
|
||
"13",
|
||
"-31.58%",
|
||
"17",
|
||
"0",
|
||
"-100%",
|
||
"0",
|
||
"12",
|
||
"100%",
|
||
"17",
|
||
"12",
|
||
"-29.41%",
|
||
"2",
|
||
"0",
|
||
"-100%",
|
||
"0",
|
||
"1",
|
||
"100%",
|
||
"2",
|
||
"1",
|
||
"-50%",
|
||
];
|
||
assert.strictEqual(getCurrentValues(target), values.join());
|
||
}
|
||
);
|
||
|
||
QUnit.test("Flip then compare, table col groupbys are kept", async function (assert) {
|
||
assert.expect(6);
|
||
|
||
serverData.models.partner.records[0].date = "2016-12-15";
|
||
serverData.models.partner.records[1].date = "2016-12-17";
|
||
serverData.models.partner.records[2].date = "2016-11-22";
|
||
serverData.models.partner.records[3].date = "2016-11-03";
|
||
|
||
patchDate(2016, 11, 20, 1, 0, 0);
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="date" interval="day" type="row"/>
|
||
<field name="company_type" type="col"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="date_filter" date="date" domain="[]" default_period='this_month'/>
|
||
</search>`,
|
||
});
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("thead th")].map((th) => th.innerText),
|
||
["", "Total", "", "Company", "individual", "Foo", "Foo", "Foo"],
|
||
"The col headers should be as expected"
|
||
);
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("tbody th")].map((th) => th.innerText),
|
||
["Total", "2016-11-03", "2016-11-22", "2016-12-15", "2016-12-17"],
|
||
"The row headers should be as expected"
|
||
);
|
||
|
||
// flip
|
||
await click(target.querySelector(".o_pivot_flip_button"));
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("thead th")].map((th) => th.innerText),
|
||
[
|
||
"",
|
||
"Total",
|
||
"",
|
||
"2016-11-03",
|
||
"2016-11-22",
|
||
"2016-12-15",
|
||
"2016-12-17",
|
||
"Foo",
|
||
"Foo",
|
||
"Foo",
|
||
"Foo",
|
||
"Foo",
|
||
],
|
||
"The col headers should be as expected"
|
||
);
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("tbody th")].map((th) => th.innerText),
|
||
["Total", "Company", "individual"],
|
||
"The row headers should be as expected"
|
||
);
|
||
|
||
// Filter on December 2016
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "Date");
|
||
await toggleMenuItemOption(target, "Date", "December");
|
||
|
||
// compare December 2016 to November 2016
|
||
await toggleComparisonMenu(target);
|
||
await toggleMenuItem(target, "Date: Previous period");
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("thead th")].map((th) => th.innerText),
|
||
[
|
||
"",
|
||
"Total",
|
||
"",
|
||
"2016-11-03",
|
||
"2016-11-22",
|
||
"2016-12-15",
|
||
"2016-12-17",
|
||
"Foo",
|
||
"Foo",
|
||
"Foo",
|
||
"Foo",
|
||
"Foo",
|
||
"November 2016",
|
||
"December 2016",
|
||
"Variation",
|
||
"November 2016",
|
||
"December 2016",
|
||
"Variation",
|
||
"November 2016",
|
||
"December 2016",
|
||
"Variation",
|
||
"November 2016",
|
||
"December 2016",
|
||
"Variation",
|
||
"November 2016",
|
||
"December 2016",
|
||
"Variation",
|
||
],
|
||
"The col headers should be as expected"
|
||
);
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("tbody th")].map((th) => th.innerText),
|
||
["Total", "Company", "individual"],
|
||
"The row headers should be as expected"
|
||
);
|
||
});
|
||
|
||
QUnit.test(
|
||
"correctly compute group domain when a date field has false value",
|
||
async function (assert) {
|
||
assert.expect(1);
|
||
|
||
serverData.models.partner.records.forEach((r) => (r.date = false));
|
||
|
||
patchDate(2016, 11, 20, 1, 0, 0);
|
||
|
||
const fakeActionService = {
|
||
start() {
|
||
return {
|
||
doAction(action) {
|
||
assert.deepEqual(action.domain, [["date", "=", false]]);
|
||
return Promise.resolve(true);
|
||
},
|
||
};
|
||
},
|
||
};
|
||
serviceRegistry.add("action", fakeActionService, { force: true });
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot o_enable_linking="1">
|
||
<field name="date" interval="day" type="row"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
await click(target.querySelectorAll(".o_value")[1]);
|
||
}
|
||
);
|
||
QUnit.test(
|
||
"Does not identify 'false' with false as keys when creating group trees",
|
||
async function (assert) {
|
||
assert.expect(2);
|
||
|
||
serverData.models.partner.fields.favorite_animal = {
|
||
string: "Favorite animal",
|
||
type: "char",
|
||
store: true,
|
||
};
|
||
serverData.models.partner.records[0].favorite_animal = "Dog";
|
||
serverData.models.partner.records[1].favorite_animal = "false";
|
||
serverData.models.partner.records[2].favorite_animal = "None";
|
||
|
||
patchDate(2016, 11, 20, 1, 0, 0);
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot o_enable_linking="1">
|
||
<field name="favorite_animal" type="row"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("thead th")].map((th) => th.innerText),
|
||
["", "Total", "Count"],
|
||
"The col headers should be as expected"
|
||
);
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("tbody th")].map((th) => th.innerText),
|
||
["Total", "Dog", "None", "false", "None"],
|
||
"The row headers should be as expected"
|
||
);
|
||
}
|
||
);
|
||
|
||
QUnit.test(
|
||
"group bys added via control panel and expand Header do not stack",
|
||
async function (assert) {
|
||
assert.expect(8);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("thead th")].map((th) => th.innerText),
|
||
["", "Total", "Foo"],
|
||
"The col headers should be as expected"
|
||
);
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("tbody th")].map((th) => th.innerText),
|
||
["Total"],
|
||
"The row headers should be as expected"
|
||
);
|
||
|
||
// open group by menu and add new groupby
|
||
await toggleGroupByMenu(target);
|
||
await toggleAddCustomGroup(target);
|
||
await applyGroup(target);
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("thead th")].map((th) => th.innerText),
|
||
["", "Total", "Foo"],
|
||
"The col headers should be as expected"
|
||
);
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("tbody th")].map((th) => th.innerText),
|
||
["Total", "Company", "individual"],
|
||
"The row headers should be as expected"
|
||
);
|
||
|
||
// Set a Row groupby
|
||
await click(target, "tbody tr:nth-child(2) .o_pivot_header_cell_closed");
|
||
await click(target, "tbody .dropdown-menu .o_menu_item:nth-child(5)");
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("thead th")].map((th) => th.innerText),
|
||
["", "Total", "Foo"],
|
||
"The col headers should be as expected"
|
||
);
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("tbody th")].map((th) => th.innerText),
|
||
["Total", "Company", "xphone", "xpad", "individual"],
|
||
"The row headers should be as expected"
|
||
);
|
||
|
||
// open groupby menu generator and add a new groupby
|
||
await toggleGroupByMenu(target);
|
||
await toggleAddCustomGroup(target);
|
||
await selectGroup(target, "bar");
|
||
await applyGroup(target);
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("thead th")].map((th) => th.innerText),
|
||
["", "Total", "Foo"],
|
||
"The col headers should be as expected"
|
||
);
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("tbody th")].map((th) => th.innerText),
|
||
["Total", "Company", "Yes", "individual", "No", "Yes"],
|
||
"The row headers should be as expected"
|
||
);
|
||
}
|
||
);
|
||
|
||
QUnit.test("display only one dropdown menu", async function (assert) {
|
||
assert.expect(1);
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
// add a col groupby on Product
|
||
await click(target.querySelector("thead th.o_pivot_header_cell_closed"));
|
||
await click(target.querySelectorAll("thead .dropdown-menu .dropdown-item")[5]);
|
||
|
||
// Click on the two header dropdown togglers
|
||
await click(target.querySelectorAll("thead th.o_pivot_header_cell_closed")[0]);
|
||
await click(target.querySelectorAll("thead th.o_pivot_header_cell_closed")[1]);
|
||
|
||
assert.containsOnce(
|
||
target,
|
||
"thead .dropdown-menu",
|
||
"Only one dropdown should be displayed at a time"
|
||
);
|
||
});
|
||
|
||
QUnit.test("Server order is kept by default", async function (assert) {
|
||
assert.expect(1);
|
||
|
||
let isSecondReadGroup = false;
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="customer" type="row"/>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>`,
|
||
mockRPC(route, args) {
|
||
if (args.method === "read_group") {
|
||
if (isSecondReadGroup) {
|
||
return Promise.resolve([
|
||
{
|
||
customer: [2, "Second"],
|
||
foo: 18,
|
||
__count: 2,
|
||
__domain: [["customer", "=", 2]],
|
||
},
|
||
{
|
||
customer: [1, "First"],
|
||
foo: 14,
|
||
__count: 2,
|
||
__domain: [["customer", "=", 1]],
|
||
},
|
||
]);
|
||
}
|
||
isSecondReadGroup = true;
|
||
}
|
||
},
|
||
});
|
||
|
||
const values = [
|
||
"32", // Total Value
|
||
"18", // Second
|
||
"14", // First
|
||
];
|
||
assert.strictEqual(getCurrentValues(target), values.join());
|
||
});
|
||
|
||
QUnit.test("pivot rendering with boolean field", async function (assert) {
|
||
assert.expect(4);
|
||
|
||
serverData.models.partner.fields.bar = {
|
||
string: "bar",
|
||
type: "boolean",
|
||
store: true,
|
||
searchable: true,
|
||
group_operator: "bool_or",
|
||
};
|
||
serverData.models.partner.records = [
|
||
{ id: 1, bar: true, date: "2019-12-14" },
|
||
{ id: 2, bar: false, date: "2019-05-14" },
|
||
];
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="date" type="row" interval="day"/>
|
||
<field name="bar" type="col"/>
|
||
<field name="bar" string="SLA status Failed" type="measure"/>
|
||
</pivot>`,
|
||
});
|
||
|
||
assert.strictEqual(
|
||
$(target).find('tbody tr:contains("2019-12-14")').length,
|
||
1,
|
||
"There should be a first column"
|
||
);
|
||
assert.ok(
|
||
$(target).find('tbody tr:contains("2019-12-14") [type="checkbox"]').is(":checked"),
|
||
"first column contains checkbox and value should be ticked"
|
||
);
|
||
assert.strictEqual(
|
||
$(target).find('tbody tr:contains("2019-05-14")').length,
|
||
1,
|
||
"There should be a second column"
|
||
);
|
||
assert.notOk(
|
||
$(target).find('tbody tr:contains("2019-05-14") [type="checkbox"]').is(":checked"),
|
||
"second column should have checkbox that is not checked by default"
|
||
);
|
||
});
|
||
|
||
QUnit.test("empty pivot view with action helper", async function (assert) {
|
||
serverData.views = {
|
||
"partner,false,pivot": `
|
||
<pivot>
|
||
<field name="product_id" type="measure"/>
|
||
<field name="date" interval="month" type="col"/>
|
||
</pivot>`,
|
||
"partner,false,search": `
|
||
<search>
|
||
<filter name="small_than_0" string="Small Than 0" domain="[('id', '<', 0)]"/>
|
||
</search>`,
|
||
};
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
context: { search_default_small_than_0: true },
|
||
noContentHelp: markup('<p class="abc">click to add a foo</p>'),
|
||
config: {
|
||
views: [[false, "search"]],
|
||
},
|
||
});
|
||
|
||
assert.containsOnce(target, ".o_view_nocontent .abc");
|
||
assert.containsNone(target, "table");
|
||
|
||
await removeFacet(target);
|
||
|
||
assert.containsNone(target, ".o_view_nocontent .abc");
|
||
assert.containsOnce(target, "table");
|
||
});
|
||
|
||
QUnit.test("empty pivot view with sample data", async function (assert) {
|
||
serverData.views = {
|
||
"partner,false,pivot": `
|
||
<pivot sample="1">
|
||
<field name="product_id" type="measure"/>
|
||
<field name="date" interval="month" type="col"/>
|
||
</pivot>`,
|
||
"partner,false,search": `
|
||
<search>
|
||
<filter name="small_than_0" string="Small Than 0" domain="[('id', '<', 0)]"/>
|
||
</search>`,
|
||
};
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
context: { search_default_small_than_0: true },
|
||
noContentHelp: markup('<p class="abc">click to add a foo</p>'),
|
||
config: {
|
||
views: [[false, "search"]],
|
||
},
|
||
});
|
||
|
||
assert.hasClass(target.querySelector(".o_pivot_view .o_content"), "o_view_sample_data");
|
||
assert.containsOnce(target, ".o_view_nocontent .abc");
|
||
|
||
await removeFacet(target);
|
||
|
||
assert.doesNotHaveClass(
|
||
target.querySelector(".o_pivot_view .o_content"),
|
||
"o_view_sample_data"
|
||
);
|
||
assert.containsNone(target, ".o_view_nocontent .abc");
|
||
assert.containsOnce(target, "table");
|
||
});
|
||
|
||
QUnit.test("non empty pivot view with sample data", async function (assert) {
|
||
serverData.views = {
|
||
"partner,false,pivot": `
|
||
<pivot sample="1">
|
||
<field name="product_id" type="measure"/>
|
||
<field name="date" interval="month" type="col"/>
|
||
</pivot>`,
|
||
"partner,false,search": `
|
||
<search>
|
||
<filter name="small_than_0" string="Small Than 0" domain="[('id', '<', 0)]"/>
|
||
</search>`,
|
||
};
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
noContentHelp: markup('<p class="abc">click to add a foo</p>'),
|
||
config: {
|
||
views: [[false, "search"]],
|
||
},
|
||
});
|
||
|
||
assert.doesNotHaveClass(target, "o_view_sample_data");
|
||
assert.containsNone(target, ".o_view_nocontent .abc");
|
||
assert.containsOnce(target, "table");
|
||
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "Small Than 0");
|
||
|
||
assert.doesNotHaveClass(target, "o_view_sample_data");
|
||
assert.containsOnce(target, ".o_view_nocontent .abc");
|
||
assert.containsNone(target, "table");
|
||
});
|
||
|
||
QUnit.test("pivot is reloaded when leaving and coming back", async function (assert) {
|
||
serverData.views = {
|
||
"partner,false,pivot": `
|
||
<pivot>
|
||
<field name="customer" type="row"/>
|
||
</pivot>`,
|
||
"partner,false,search": `<search/>`,
|
||
"partner,false,list": '<tree><field name="foo"/></tree>',
|
||
};
|
||
|
||
const mockRPC = (route, args) => {
|
||
assert.step(args.method || route);
|
||
};
|
||
const webClient = await createWebClient({ serverData, mockRPC });
|
||
|
||
await doAction(webClient, {
|
||
res_model: "partner",
|
||
type: "ir.actions.act_window",
|
||
views: [
|
||
[false, "pivot"],
|
||
[false, "list"],
|
||
],
|
||
});
|
||
|
||
assert.containsOnce(target, ".o_pivot_view");
|
||
assert.strictEqual(getCurrentValues(target), ["4", "2", "2"].join(","));
|
||
|
||
assert.verifySteps(["/web/webclient/load_menus", "get_views", "read_group", "read_group"]);
|
||
|
||
// switch to list view
|
||
await click(target.querySelector(".o_control_panel .o_switch_view.o_list"));
|
||
await legacyExtraNextTick();
|
||
|
||
assert.containsOnce(target, ".o_list_view");
|
||
assert.verifySteps(["web_search_read"]);
|
||
|
||
// switch back to pivot
|
||
await click(target.querySelector(".o_control_panel .o_switch_view.o_pivot"));
|
||
|
||
assert.containsOnce(target, ".o_pivot_view");
|
||
assert.strictEqual(getCurrentValues(target), ["4", "2", "2"].join(","));
|
||
|
||
assert.verifySteps(["read_group", "read_group"]);
|
||
});
|
||
|
||
QUnit.test("expanded groups are kept when leaving and coming back", async function (assert) {
|
||
serverData.views = {
|
||
"partner,false,pivot": `
|
||
<pivot>
|
||
<field name="customer" type="row"/>
|
||
</pivot>`,
|
||
"partner,false,search": `<search/>`,
|
||
"partner,false,list": '<tree><field name="foo"/></tree>',
|
||
};
|
||
|
||
const webClient = await createWebClient({ serverData });
|
||
|
||
await doAction(webClient, {
|
||
res_model: "partner",
|
||
type: "ir.actions.act_window",
|
||
views: [
|
||
[false, "pivot"],
|
||
[false, "list"],
|
||
],
|
||
});
|
||
|
||
assert.containsOnce(target, ".o_pivot_view");
|
||
assert.strictEqual(getCurrentValues(target), ["4", "2", "2"].join(","));
|
||
|
||
// drill down first row group (group by company_type)
|
||
await click(target.querySelector("tbody .o_pivot_header_cell_closed"));
|
||
await click(target.querySelector(".o_pivot .dropdown-menu .dropdown-item"));
|
||
|
||
assert.strictEqual(getCurrentValues(target), ["4", "2", "1", "1", "2"].join(","));
|
||
|
||
// switch to list view
|
||
await click(target.querySelector(".o_control_panel .o_switch_view.o_list"));
|
||
await legacyExtraNextTick();
|
||
|
||
assert.containsOnce(target, ".o_list_view");
|
||
|
||
// switch back to pivot
|
||
await click(target.querySelector(".o_control_panel .o_switch_view.o_pivot"));
|
||
|
||
assert.containsOnce(target, ".o_pivot_view");
|
||
assert.strictEqual(getCurrentValues(target), ["4", "2", "1", "1", "2"].join(","));
|
||
});
|
||
|
||
QUnit.test("sorted rows are kept when leaving and coming back", async function (assert) {
|
||
serverData.views = {
|
||
"partner,false,pivot": `
|
||
<pivot>
|
||
<field name="foo" type="measure"/>
|
||
<field name="product_id" type="row"/>
|
||
</pivot>`,
|
||
"partner,false,search": `<search/>`,
|
||
"partner,false,list": '<tree><field name="foo"/></tree>',
|
||
};
|
||
|
||
const webClient = await createWebClient({ serverData });
|
||
|
||
await doAction(webClient, {
|
||
res_model: "partner",
|
||
type: "ir.actions.act_window",
|
||
views: [
|
||
[false, "pivot"],
|
||
[false, "list"],
|
||
],
|
||
});
|
||
|
||
assert.containsOnce(target, ".o_pivot_view");
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "20"].join(","));
|
||
|
||
// sort the first group
|
||
await click(target.querySelector("th.o_pivot_measure_row"));
|
||
await click(target.querySelector("th.o_pivot_measure_row"));
|
||
|
||
assert.strictEqual(getCurrentValues(target), ["32", "20", "12"].join(","));
|
||
|
||
// switch to list view
|
||
await click(target.querySelector(".o_control_panel .o_switch_view.o_list"));
|
||
await legacyExtraNextTick();
|
||
|
||
assert.containsOnce(target, ".o_list_view");
|
||
|
||
// switch back to pivot
|
||
await click(target.querySelector(".o_control_panel .o_switch_view.o_pivot"));
|
||
|
||
assert.containsOnce(target, ".o_pivot_view");
|
||
assert.strictEqual(getCurrentValues(target), ["32", "20", "12"].join(","));
|
||
});
|
||
|
||
QUnit.test("correctly handle concurrent reloads", async function (assert) {
|
||
serverData.views = {
|
||
"partner,false,pivot": `
|
||
<pivot>
|
||
<field name="foo" type="measure"/>
|
||
<field name="product_id" type="row"/>
|
||
</pivot>`,
|
||
"partner,false,search": `<search/>`,
|
||
"partner,false,list": '<tree><field name="foo"/></tree>',
|
||
};
|
||
|
||
let def;
|
||
let readGroupCount = 0;
|
||
const mockRPC = (route, args) => {
|
||
if (args.method === "read_group" && def) {
|
||
readGroupCount++;
|
||
if (readGroupCount === 2) {
|
||
// slow down last read_group of first reload
|
||
return Promise.resolve(def);
|
||
}
|
||
}
|
||
};
|
||
const webClient = await createWebClient({ serverData, mockRPC });
|
||
|
||
await doAction(webClient, {
|
||
res_model: "partner",
|
||
type: "ir.actions.act_window",
|
||
views: [
|
||
[false, "pivot"],
|
||
[false, "list"], // s.t. there is a pivot view switcher
|
||
],
|
||
});
|
||
|
||
assert.containsOnce(target, ".o_pivot_view");
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "20"].join(","));
|
||
|
||
// drill down first row group (group by company_type)
|
||
await click(target.querySelector("tbody .o_pivot_header_cell_closed"));
|
||
await click(target.querySelector(".o_pivot .dropdown-menu .dropdown-item"));
|
||
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "12", "20"].join(","));
|
||
|
||
// reload twice by clicking on pivot view switcher
|
||
def = makeDeferred();
|
||
await click(target.querySelector(".o_control_panel .o_switch_view.o_pivot"));
|
||
await click(target.querySelector(".o_control_panel .o_switch_view.o_pivot"));
|
||
|
||
def.resolve();
|
||
await nextTick();
|
||
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "12", "20"].join(","));
|
||
});
|
||
|
||
QUnit.test("consecutively toggle several measures", async function (assert) {
|
||
let def;
|
||
serverData.models.partner.fields.foo2 = {
|
||
...serverData.models.partner.fields.foo,
|
||
string: "Foo2",
|
||
store: true,
|
||
};
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="foo" type="measure"/>
|
||
<field name="product_id" type="row"/>
|
||
</pivot>`,
|
||
mockRPC(route, args) {
|
||
if (args.method === "read_group") {
|
||
return Promise.resolve(def);
|
||
}
|
||
},
|
||
});
|
||
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "20"].join(","));
|
||
|
||
// Toggle several measures (the reload is blocked, so all measures should be toggled in once)
|
||
def = makeDeferred();
|
||
await toggleMenu(target, "Measures");
|
||
await toggleMenuItem(target, "Foo2"); // add foo2
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "20"].join(","));
|
||
await toggleMenuItem(target, "Foo"); // remove foo
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "20"].join(","));
|
||
await toggleMenuItem(target, "Count"); // add count
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "20"].join(","));
|
||
|
||
def.resolve();
|
||
await nextTick();
|
||
|
||
assert.strictEqual(getCurrentValues(target), ["0", "4", "0", "1", "0", "3"].join(","));
|
||
});
|
||
|
||
QUnit.test("flip axis while loading a filter", async function (assert) {
|
||
let def;
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="foo" type="measure"/>
|
||
<field name="date" type="col"/>
|
||
<field name="product_id" type="row"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="my_filter" string="My Filter" domain="[('product_id', '=', 41)]"/>
|
||
</search>`,
|
||
mockRPC(route, args) {
|
||
if (args.method === "read_group") {
|
||
return Promise.resolve(def);
|
||
}
|
||
},
|
||
});
|
||
|
||
const values = ["2", "1", "29", "32", "12", "12", "2", "1", "17", "20"];
|
||
assert.strictEqual(getCurrentValues(target), values.join(","));
|
||
|
||
// Set a domain (this reload is delayed)
|
||
def = makeDeferred();
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "My Filter");
|
||
assert.strictEqual(getCurrentValues(target), values.join(","));
|
||
|
||
// Flip axis
|
||
await click(target.querySelector(".o_pivot_flip_button"));
|
||
assert.strictEqual(getCurrentValues(target), values.join(","));
|
||
|
||
def.resolve();
|
||
await nextTick();
|
||
|
||
assert.strictEqual(getCurrentValues(target), ["20", "2", "1", "17"].join(","));
|
||
});
|
||
|
||
QUnit.test("sort rows while loading a filter", async function (assert) {
|
||
let def;
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="foo" type="measure"/>
|
||
<field name="product_id" type="row"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="my_filter" string="My Filter" domain="[('product_id', '=', 41)]"/>
|
||
</search>`,
|
||
mockRPC(route, args) {
|
||
if (args.method === "read_group") {
|
||
return Promise.resolve(def);
|
||
}
|
||
},
|
||
});
|
||
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "20"].join(","));
|
||
|
||
// Set a domain (this reload is delayed)
|
||
def = makeDeferred();
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "My Filter");
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "20"].join(","));
|
||
|
||
// Sort rows (this operation should be ignored as it concerns the old
|
||
// table, which will be replaced soon)
|
||
await click(target.querySelector("th.o_pivot_measure_row"));
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "20"].join(","));
|
||
|
||
def.resolve();
|
||
await nextTick();
|
||
|
||
assert.strictEqual(getCurrentValues(target), ["20", "20"].join(","));
|
||
});
|
||
|
||
QUnit.test("close a group while loading a filter", async function (assert) {
|
||
let def;
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="foo" type="measure"/>
|
||
<field name="product_id" type="row"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="my_filter" string="My Filter" domain="[('product_id', '=', 41)]"/>
|
||
</search>`,
|
||
mockRPC(route, args) {
|
||
if (args.method === "read_group") {
|
||
return Promise.resolve(def);
|
||
}
|
||
},
|
||
});
|
||
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "20"].join(","));
|
||
|
||
// Set a domain (this reload is delayed)
|
||
def = makeDeferred();
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "My Filter");
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "20"].join(","));
|
||
|
||
// Close a group (this operation should be ignored as it concerns the old
|
||
// table, which will be replaced soon)
|
||
await click(target.querySelector("tbody .o_pivot_header_cell_opened"));
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "20"].join(","));
|
||
|
||
def.resolve();
|
||
await nextTick();
|
||
|
||
assert.strictEqual(getCurrentValues(target), ["20", "20"].join(","));
|
||
});
|
||
|
||
QUnit.test("add a groupby while loading a filter", async function (assert) {
|
||
let def;
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="foo" type="measure"/>
|
||
<field name="product_id" type="row"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="my_filter" string="My Filter" domain="[('product_id', '=', 41)]"/>
|
||
</search>`,
|
||
mockRPC(route, args) {
|
||
if (args.method === "read_group") {
|
||
return Promise.resolve(def);
|
||
}
|
||
},
|
||
});
|
||
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "20"].join(","));
|
||
|
||
// Set a domain (this reload is delayed)
|
||
def = makeDeferred();
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "My Filter");
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "20"].join(","));
|
||
|
||
// Add a groupby (this operation should be ignored as it concerns the old
|
||
// table, which will be replaced soon)
|
||
await click(target.querySelector("thead .o_pivot_header_cell_closed"));
|
||
await click(target.querySelector("thead .dropdown-menu .dropdown-item"));
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "20"].join(","));
|
||
|
||
def.resolve();
|
||
await nextTick();
|
||
|
||
assert.strictEqual(getCurrentValues(target), ["20", "20"].join(","));
|
||
});
|
||
|
||
QUnit.test("expand a group while loading a filter", async function (assert) {
|
||
let def;
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="foo" type="measure"/>
|
||
<field name="product_id" type="row"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="my_filter" string="My Filter" domain="[('product_id', '=', 41)]"/>
|
||
</search>`,
|
||
mockRPC(route, args) {
|
||
if (args.method === "read_group") {
|
||
return Promise.resolve(def);
|
||
}
|
||
},
|
||
});
|
||
|
||
// Add a groupby, to have a group to expand afterwards
|
||
await click(target.querySelector("tbody .o_pivot_header_cell_closed"));
|
||
await click(target.querySelector("tbody .dropdown-menu .dropdown-item"));
|
||
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "12", "20"].join(","));
|
||
|
||
// Set a domain (this reload is delayed)
|
||
def = makeDeferred();
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "My Filter");
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "12", "20"].join(","));
|
||
|
||
// Expand a group (this operation should be ignored as it concerns the old
|
||
// table, which will be replaced soon)
|
||
await click(target.querySelectorAll("tbody .o_pivot_header_cell_closed")[1]);
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "12", "20"].join(","));
|
||
|
||
def.resolve();
|
||
await nextTick();
|
||
|
||
assert.strictEqual(getCurrentValues(target), ["20", "20"].join(","));
|
||
});
|
||
|
||
QUnit.test(
|
||
"concurrent reloads: add a filter, and directly toggle a measure",
|
||
async function (assert) {
|
||
let def;
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="foo" type="measure"/>
|
||
<field name="product_id" type="row"/>
|
||
</pivot>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="my_filter" string="My Filter" domain="[('product_id', '=', 37)]"/>
|
||
</search>`,
|
||
mockRPC(route, args) {
|
||
if (args.method === "read_group") {
|
||
return Promise.resolve(def);
|
||
}
|
||
},
|
||
});
|
||
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "20"].join(","));
|
||
|
||
// Set a domain (this reload is delayed)
|
||
def = makeDeferred();
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "My Filter");
|
||
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "20"].join(","));
|
||
|
||
// Toggle a measure
|
||
await toggleMenu(target, "Measures");
|
||
await toggleMenuItem(target, "Count");
|
||
|
||
assert.strictEqual(getCurrentValues(target), ["32", "12", "20"].join(","));
|
||
|
||
def.resolve();
|
||
await nextTick();
|
||
|
||
assert.strictEqual(getCurrentValues(target), ["12", "1", "12", "1"].join(","));
|
||
}
|
||
);
|
||
|
||
QUnit.test(
|
||
"if no measure is set in arch, 'Count' is used as measure initially",
|
||
async function (assert) {
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `<pivot/>`,
|
||
});
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("thead th")].map((e) => e.innerText),
|
||
["", "Total", "Count"]
|
||
);
|
||
}
|
||
);
|
||
|
||
QUnit.test(
|
||
"if (at least) one measure is set in arch and display_quantity is false or unset, 'Count' is not used as measure initially",
|
||
async function (assert) {
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="foo" type="measure"/>
|
||
</pivot>
|
||
`,
|
||
});
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("thead th")].map((e) => e.innerText),
|
||
["", "Total", "Foo"]
|
||
);
|
||
}
|
||
);
|
||
|
||
QUnit.test(
|
||
"if (at least) one measure is set in arch and display_quantity is true, 'Count' is used as measure initially",
|
||
async function (assert) {
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot display_quantity="1">
|
||
<field name="foo" type="measure"/>
|
||
</pivot>
|
||
`,
|
||
});
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("thead th")].map((e) => e.innerText),
|
||
["", "Total", "Count", "Foo"]
|
||
);
|
||
}
|
||
);
|
||
|
||
QUnit.test("'Measures' menu when there is no measurable fields", async function (assert) {
|
||
serverData.models.partner = {
|
||
fields: {},
|
||
records: [{ id: 1, display_name: "The one" }],
|
||
};
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `<pivot/>`,
|
||
});
|
||
|
||
await toggleMenu(target, "Measures");
|
||
|
||
// "Count" is the only measure available
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll(".o_cp_bottom_left .dropdown-menu .o_menu_item")].map(
|
||
(e) => e.innerText
|
||
),
|
||
["Count"]
|
||
);
|
||
// No separator should be displayed in the menu "Measures"
|
||
assert.containsNone(target, ".o_cp_bottom_left .dropdown-menu div.dropdown-divider");
|
||
});
|
||
|
||
QUnit.test(
|
||
"comparison with two groupbys: rows from reference period should be displayed",
|
||
async function (assert) {
|
||
assert.expect(3);
|
||
patchDate(2023, 2, 22, 1, 0, 0);
|
||
|
||
serverData.models.partner.records = [
|
||
{ id: 1, date: "2021-10-10", product_id: 1, customer: 1 },
|
||
{ id: 2, date: "2020-10-10", product_id: 2, customer: 1 },
|
||
];
|
||
serverData.models.product.records = [
|
||
{ id: 1, display_name: "A" },
|
||
{ id: 2, display_name: "B" },
|
||
];
|
||
serverData.models.customer.records = [{ id: 1, display_name: "P" }];
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="customer" type="row"/>
|
||
<field name="product_id" type="row"/>
|
||
</pivot>
|
||
`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name='date' date='date'/>
|
||
</search>
|
||
`,
|
||
});
|
||
|
||
// compare 2021 to 2020
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "Date");
|
||
await toggleMenuItemOption(target, "Date", "2021");
|
||
await toggleComparisonMenu(target);
|
||
await toggleMenuItem(target, 0);
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("th")].slice(0, 6).map((el) => el.innerText),
|
||
["", "Total", "Count", "2020", "2021", "Variation"],
|
||
"The col headers should be as expected"
|
||
);
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("th")].slice(6).map((el) => el.innerText),
|
||
["Total", "P", "B", "A"],
|
||
"The row headers should be as expected"
|
||
);
|
||
|
||
const values = ["1", "1", "0%", "1", "1", "0%", "1", "0", "-100%", "0", "1", "100%"];
|
||
assert.strictEqual(getCurrentValues(target), values.join());
|
||
}
|
||
);
|
||
|
||
QUnit.test("pivot_row_groupby should be also used after first load", async function (assert) {
|
||
const ids = [1, 2];
|
||
const expectedContexts = [
|
||
{
|
||
group_by: ["bar"],
|
||
pivot_column_groupby: [],
|
||
pivot_measures: ["__count"],
|
||
pivot_row_groupby: ["product_id"],
|
||
},
|
||
{
|
||
group_by: ["bar", "customer"],
|
||
pivot_column_groupby: [],
|
||
pivot_measures: ["__count"],
|
||
pivot_row_groupby: ["customer"],
|
||
},
|
||
];
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `<pivot/>`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name='product_id' string="Product" context="{'group_by':'product_id'}"/>
|
||
<filter name='customer' string="Customer" context="{'group_by':'customer'}"/>
|
||
</search>
|
||
`,
|
||
groupBy: ["bar"],
|
||
mockRPC(route, args) {
|
||
if (args.method === "create_or_replace") {
|
||
assert.deepEqual(args.args[0].context, expectedContexts.shift());
|
||
return ids.shift();
|
||
}
|
||
},
|
||
});
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("th")].slice(3).map((el) => el.innerText),
|
||
["Total", "No", "Yes"],
|
||
"The row headers should be as expected"
|
||
);
|
||
|
||
await click(target.querySelector("tbody th")); // click on row header "Total"
|
||
await click(target.querySelector("tbody th")); // click on row header "Total"
|
||
await click(target.querySelector("tbody .o_group_by_menu .o_menu_item")); // select "Product"
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("th")].slice(3).map((el) => el.innerText),
|
||
["Total", "xphone", "xpad"],
|
||
"The row headers should be as expected"
|
||
);
|
||
|
||
await toggleFavoriteMenu(target);
|
||
await toggleSaveFavorite(target);
|
||
await editFavoriteName(target, "Favorite");
|
||
await saveFavorite(target);
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("th")].slice(3).map((el) => el.innerText),
|
||
["Total", "xphone", "xpad"],
|
||
"The row headers should be as expected"
|
||
);
|
||
|
||
await removeFacet(target);
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("th")].slice(3).map((el) => el.innerText),
|
||
["Total", "No", "Yes"],
|
||
"The row headers should be as expected"
|
||
);
|
||
|
||
await toggleFavoriteMenu(target);
|
||
await toggleMenuItem(target, "Favorite");
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("th")].slice(3).map((el) => el.innerText),
|
||
["Total", "xphone", "xpad"],
|
||
"The row headers should be as expected"
|
||
);
|
||
|
||
await toggleGroupByMenu(target);
|
||
await toggleMenuItem(target, "Customer");
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("th")].slice(3).map((el) => el.innerText),
|
||
["Total", "xphone", "First", "xpad", "First", "Second"],
|
||
"The row headers should be as expected"
|
||
);
|
||
|
||
await click(target.querySelector("tbody th")); // click on row header "Total"
|
||
await click(target.querySelector("tbody th")); // click on row header "Total"
|
||
await click(target.querySelectorAll("tbody .o_group_by_menu .o_menu_item")[1]); // select "Customer"
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("th")].slice(3).map((el) => el.innerText),
|
||
["Total", "First", "Second"],
|
||
"The row headers should be as expected"
|
||
);
|
||
|
||
await toggleFavoriteMenu(target);
|
||
await toggleSaveFavorite(target);
|
||
await editFavoriteName(target, "Favorite 2");
|
||
await saveFavorite(target);
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("th")].slice(3).map((el) => el.innerText),
|
||
["Total", "First", "Second"],
|
||
"The row headers should be as expected"
|
||
);
|
||
});
|
||
|
||
QUnit.test(
|
||
"pivot_row_groupby should be also used after first load (2)",
|
||
async function (assert) {
|
||
await makeView({
|
||
serverData,
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
groupBy: ["product_id"],
|
||
arch: `<pivot/>`,
|
||
irFilters: [
|
||
{
|
||
user_id: [2, "Mitchell Admin"],
|
||
name: "Favorite",
|
||
id: 1,
|
||
context: `
|
||
{
|
||
"group_by": [],
|
||
"pivot_row_groupby": ["customer"],
|
||
"pivot_col_groupby": [],
|
||
"pivot_measures": ["foo"],
|
||
}
|
||
`,
|
||
sort: "[]",
|
||
domain: "",
|
||
is_default: false,
|
||
model_id: "foo",
|
||
action_id: false,
|
||
},
|
||
],
|
||
});
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("th")].slice(3).map((el) => el.innerText),
|
||
["Total", "xphone", "xpad"],
|
||
"The row headers should be as expected"
|
||
);
|
||
|
||
await toggleFavoriteMenu(target);
|
||
await toggleMenuItem(target, "Favorite");
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("th")].slice(3).map((el) => el.innerText),
|
||
["Total", "First", "Second"],
|
||
"The row headers should be as expected"
|
||
);
|
||
}
|
||
);
|
||
|
||
QUnit.test(
|
||
"specific pivot keys in action context must have less importance than in favorite context",
|
||
async function (assert) {
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `<pivot/>`,
|
||
context: {
|
||
pivot_column_groupby: [],
|
||
pivot_measures: ["__count"],
|
||
pivot_row_groupby: [],
|
||
},
|
||
irFilters: [
|
||
{
|
||
user_id: [2, "Mitchell Admin"],
|
||
name: "My favorite",
|
||
id: 1,
|
||
context: `{
|
||
"pivot_column_groupby": ["bar"],
|
||
"pivot_measures": ["computed_field"],
|
||
"pivot_row_groupby": [],
|
||
}`,
|
||
sort: "[]",
|
||
domain: "",
|
||
is_default: true,
|
||
model_id: "partner",
|
||
action_id: false,
|
||
},
|
||
{
|
||
user_id: [2, "Mitchell Admin"],
|
||
name: "My favorite 2",
|
||
id: 2,
|
||
context: `{
|
||
"pivot_column_groupby": ["product_id"],
|
||
"pivot_measures": ["computed_field", "__count"],
|
||
"pivot_row_groupby": [],
|
||
}`,
|
||
sort: "[]",
|
||
domain: "",
|
||
is_default: false,
|
||
model_id: "partner",
|
||
action_id: false,
|
||
},
|
||
],
|
||
});
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("th")].slice(1, 6).map((el) => el.innerText),
|
||
["Total", "", "No", "Yes", "Computed and not stored"]
|
||
);
|
||
|
||
await toggleFavoriteMenu(target);
|
||
await toggleMenuItem(target, "My favorite 2");
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("th")].slice(1, 11).map((el) => el.innerText),
|
||
[
|
||
"Total",
|
||
"",
|
||
"xphone",
|
||
"xpad",
|
||
"Computed and not stored",
|
||
"Count",
|
||
"Computed and not stored",
|
||
"Count",
|
||
"Computed and not stored",
|
||
"Count",
|
||
]
|
||
);
|
||
}
|
||
);
|
||
|
||
QUnit.test(
|
||
"favorite pivot_measures should be used even if found also in global context",
|
||
async function (assert) {
|
||
serverData.models.partner.fields.computed_field.store = true; // --> Computed and not stored displayed in "Measures" menu
|
||
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `<pivot/>`,
|
||
context: {
|
||
pivot_measures: ["__count"],
|
||
},
|
||
mockRPC(route, args) {
|
||
if (args.method === "create_or_replace") {
|
||
assert.deepEqual(args.args[0].context, {
|
||
group_by: [],
|
||
pivot_column_groupby: [],
|
||
pivot_measures: ["computed_field"],
|
||
pivot_row_groupby: [],
|
||
});
|
||
return 1;
|
||
}
|
||
},
|
||
});
|
||
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("th")].slice(1, 3).map((el) => el.innerText),
|
||
["Total", "Count"]
|
||
);
|
||
|
||
await toggleMenu(target, "Measures");
|
||
await toggleMenuItem(target, "Count");
|
||
await toggleMenuItem(target, "Computed and not stored");
|
||
|
||
assert.deepEqual(getFacetTexts(target), []);
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("th")].slice(1, 3).map((el) => el.innerText),
|
||
["Total", "Computed and not stored"]
|
||
);
|
||
|
||
await toggleFavoriteMenu(target);
|
||
await toggleSaveFavorite(target);
|
||
await editFavoriteName(target, "Favorite");
|
||
await saveFavorite(target);
|
||
|
||
assert.deepEqual(getFacetTexts(target), ["Favorite"]);
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("th")].slice(1, 3).map((el) => el.innerText),
|
||
["Total", "Computed and not stored"]
|
||
);
|
||
}
|
||
);
|
||
|
||
QUnit.test("filter -> sort -> unfilter should not crash", async function (assert) {
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot>
|
||
<field name="product_id" type="row"/>
|
||
<field name="bar" type="row"/>
|
||
</pivot>
|
||
`,
|
||
searchViewArch: `
|
||
<search>
|
||
<filter name="xphone" domain="[('product_id', '=', 37)]" />
|
||
</search>
|
||
`,
|
||
context: {
|
||
search_default_xphone: true,
|
||
},
|
||
});
|
||
|
||
assert.deepEqual(getFacetTexts(target), ["xphone"]);
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("tbody th")].map((el) => el.innerText),
|
||
["Total", "xphone", "Yes"]
|
||
);
|
||
assert.strictEqual(getCurrentValues(target), ["1", "1", "1"].join());
|
||
|
||
await click(target, ".o_pivot_measure_row");
|
||
await toggleFilterMenu(target);
|
||
await toggleMenuItem(target, "xphone");
|
||
|
||
assert.deepEqual(getFacetTexts(target), []);
|
||
assert.deepEqual(
|
||
[...target.querySelectorAll("tbody th")].map((el) => el.innerText),
|
||
["Total", "xphone", "Yes", "xpad"]
|
||
);
|
||
assert.strictEqual(getCurrentValues(target), ["4", "1", "1", "3"].join());
|
||
});
|
||
|
||
QUnit.test(
|
||
"no class 'o_view_sample_data' when real data are presented",
|
||
async function (assert) {
|
||
serverData.models.partner.fields.foo.store = true;
|
||
serverData.models.partner.records = [];
|
||
await makeView({
|
||
type: "pivot",
|
||
resModel: "partner",
|
||
serverData,
|
||
arch: `
|
||
<pivot sample="1">
|
||
<field name="product_id" type="row"/>
|
||
</pivot>
|
||
`,
|
||
});
|
||
|
||
assert.containsOnce(target, ".o_pivot_view .o_view_sample_data");
|
||
assert.containsOnce(target, ".o_pivot_view table");
|
||
await toggleMenu(target, "Measures");
|
||
await toggleMenuItem(target, "Foo");
|
||
assert.containsNone(target, ".o_pivot_view .o_view_sample_data");
|
||
assert.containsNone(target, ".o_pivot_view table");
|
||
}
|
||
);
|
||
});
|