mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-21 06:12:04 +02:00
vanilla 18.0
This commit is contained in:
parent
5454004ff9
commit
d7f6d2725e
979 changed files with 428093 additions and 0 deletions
|
|
@ -0,0 +1,271 @@
|
|||
import {
|
||||
contains,
|
||||
defineModels,
|
||||
fields,
|
||||
models,
|
||||
mountView,
|
||||
onRpc,
|
||||
stepAllNetworkCalls,
|
||||
} from "../web_test_helpers";
|
||||
import { beforeEach, describe, expect, test } from "@odoo/hoot";
|
||||
import { queryAllTexts } from "@odoo/hoot-dom";
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
/** Foo is dummy model to test `action.report` with domain of its field `value`. **/
|
||||
class Foo extends models.Model {
|
||||
_name = "foo";
|
||||
|
||||
value = fields.Boolean();
|
||||
|
||||
_records = [
|
||||
{
|
||||
id: 1,
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
value: false,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
class IrActionsReport extends models.Model {
|
||||
_name = "ir.actions.report";
|
||||
|
||||
get_valid_action_reports(self, model, recordIds) {
|
||||
const validActionIds = [1];
|
||||
if (recordIds.includes(1)) {
|
||||
validActionIds.push(2);
|
||||
}
|
||||
if (recordIds.includes(2)) {
|
||||
validActionIds.push(3);
|
||||
}
|
||||
if (!recordIds.includes(1) && !recordIds.includes(2)) {
|
||||
// new record are initialized with value=False so domain of action 3 is satisfied
|
||||
validActionIds.push(3);
|
||||
}
|
||||
return validActionIds;
|
||||
}
|
||||
}
|
||||
|
||||
defineModels([Foo, IrActionsReport]);
|
||||
|
||||
describe.current.tags("desktop");
|
||||
|
||||
beforeEach(() => {
|
||||
onRpc("has_group", () => true);
|
||||
});
|
||||
|
||||
const printItems = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Some Report always visible",
|
||||
type: "ir.actions.action_report",
|
||||
domain: "",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Some Report with domain 1",
|
||||
type: "ir.actions.action_report",
|
||||
domain: [["value", "=", "True"]],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Some Report with domain 2",
|
||||
type: "ir.actions.action_report",
|
||||
domain: [["value", "=", "False"]],
|
||||
},
|
||||
];
|
||||
|
||||
test("render ActionMenus in list view", async () => {
|
||||
stepAllNetworkCalls();
|
||||
await mountView({
|
||||
type: "list",
|
||||
resModel: "foo",
|
||||
actionMenus: {
|
||||
action: [],
|
||||
print: printItems,
|
||||
},
|
||||
loadActionMenus: true,
|
||||
arch: /* xml */ `
|
||||
<list>
|
||||
<field name="value"/>
|
||||
</list>
|
||||
`,
|
||||
});
|
||||
expect.verifySteps([
|
||||
"/web/webclient/translations",
|
||||
"/web/webclient/load_menus",
|
||||
"get_views",
|
||||
"web_search_read",
|
||||
"has_group",
|
||||
]);
|
||||
|
||||
// select all records
|
||||
await contains(`thead .o_list_record_selector input`).click();
|
||||
expect(`div.o_control_panel .o_cp_action_menus`).toHaveCount(1);
|
||||
expect(queryAllTexts(`div.o_control_panel .o_cp_action_menus .dropdown-toggle`)).toEqual([
|
||||
"Print",
|
||||
"Actions",
|
||||
]);
|
||||
|
||||
// select Print dropdown
|
||||
await contains(`.o_cp_action_menus .dropdown-toggle:eq(0)`).click();
|
||||
expect(`.o-dropdown--menu .o-dropdown-item`).toHaveCount(3);
|
||||
expect(queryAllTexts(`.o-dropdown--menu .o-dropdown-item`)).toEqual([
|
||||
"Some Report always visible",
|
||||
"Some Report with domain 1",
|
||||
"Some Report with domain 2",
|
||||
]);
|
||||
|
||||
// the last RPC call to retrieve print items only happens when the dropdown is clicked
|
||||
expect.verifySteps(["get_valid_action_reports"]);
|
||||
|
||||
// select only the record that satisfies domain 1
|
||||
await contains(`.o_data_row:eq(1) input`).click();
|
||||
await contains(`.o_cp_action_menus .dropdown-toggle:eq(0)`).click();
|
||||
expect(`.o-dropdown--menu .o-dropdown-item`).toHaveCount(2);
|
||||
expect(queryAllTexts(`.o-dropdown--menu .o-dropdown-item`)).toEqual([
|
||||
"Some Report always visible",
|
||||
"Some Report with domain 1",
|
||||
]);
|
||||
|
||||
expect.verifySteps(["get_valid_action_reports"]);
|
||||
});
|
||||
|
||||
test("render ActionMenus in form view", async () => {
|
||||
stepAllNetworkCalls();
|
||||
await mountView({
|
||||
type: "form",
|
||||
resModel: "foo",
|
||||
resId: 1,
|
||||
actionMenus: {
|
||||
action: [],
|
||||
print: printItems,
|
||||
},
|
||||
loadActionMenus: true,
|
||||
arch: /* xml */ `
|
||||
<form>
|
||||
<field name="value"/>
|
||||
</form>
|
||||
`,
|
||||
});
|
||||
expect.verifySteps([
|
||||
"/web/webclient/translations",
|
||||
"/web/webclient/load_menus",
|
||||
"get_views",
|
||||
"web_read",
|
||||
]);
|
||||
|
||||
// select CogMenu
|
||||
await contains(`div.o_control_panel_breadcrumbs_actions i.fa-cog`).click();
|
||||
|
||||
// select Print dropdown
|
||||
await contains(`button.o-dropdown:contains(Print)`).click();
|
||||
expect(queryAllTexts(`.o-dropdown--menu-submenu span.o-dropdown-item`)).toEqual([
|
||||
"Some Report always visible",
|
||||
"Some Report with domain 1",
|
||||
]);
|
||||
|
||||
// the RPC call to retrieve print items only happens when the dropdown is clicked
|
||||
expect.verifySteps(["get_valid_action_reports"]);
|
||||
|
||||
// create a new record
|
||||
await contains(`button.o_form_button_create`).click();
|
||||
await contains(`button.o_form_button_save`).click();
|
||||
expect(`.o_pager_counter`).toHaveText("2 / 2");
|
||||
expect.verifySteps(["onchange", "web_save"]);
|
||||
await contains(`div.o_control_panel_breadcrumbs_actions i.fa-cog`).click();
|
||||
await contains(`button.o-dropdown:contains(Print)`).click();
|
||||
expect(queryAllTexts(`.o-dropdown--menu-submenu span.o-dropdown-item`)).toEqual([
|
||||
"Some Report always visible",
|
||||
"Some Report with domain 2",
|
||||
]);
|
||||
expect.verifySteps(["get_valid_action_reports"]);
|
||||
|
||||
// switch back to first record
|
||||
await contains(`.o_pager_previous`).click();
|
||||
expect(`.o_pager_counter`).toHaveText("1 / 2");
|
||||
await contains(`div.o_control_panel_breadcrumbs_actions i.fa-cog`).click();
|
||||
await contains(`button.o-dropdown:contains(Print)`).click();
|
||||
expect(queryAllTexts(`.o-dropdown--menu-submenu span.o-dropdown-item`)).toEqual([
|
||||
"Some Report always visible",
|
||||
"Some Report with domain 1",
|
||||
]);
|
||||
expect.verifySteps(["web_read", "get_valid_action_reports"]);
|
||||
});
|
||||
|
||||
test("render ActionMenus in list view with extraPrintItems", async () => {
|
||||
stepAllNetworkCalls();
|
||||
const listView = registry.category("views").get("list");
|
||||
class ExtraPrintController extends listView.Controller {
|
||||
get actionMenuProps() {
|
||||
return {
|
||||
...super.actionMenuProps,
|
||||
loadExtraPrintItems: () => {
|
||||
return [
|
||||
{
|
||||
key: "extra_print_key",
|
||||
description: "Extra Print Item",
|
||||
class: "o_menu_item",
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
registry.category("views").add("extra_print", {
|
||||
...listView,
|
||||
Controller: ExtraPrintController,
|
||||
});
|
||||
await mountView({
|
||||
resModel: "foo",
|
||||
type: "list",
|
||||
arch: `<list js_class="extra_print"><field name="value"/></list>`,
|
||||
actionMenus: {
|
||||
action: [],
|
||||
print: printItems,
|
||||
},
|
||||
});
|
||||
|
||||
expect.verifySteps([
|
||||
"/web/webclient/translations",
|
||||
"/web/webclient/load_menus",
|
||||
"get_views",
|
||||
"web_search_read",
|
||||
"has_group",
|
||||
]);
|
||||
|
||||
// select all records
|
||||
await contains(`thead .o_list_record_selector input`).click();
|
||||
expect(`div.o_control_panel .o_cp_action_menus`).toHaveCount(1);
|
||||
expect(queryAllTexts(`div.o_control_panel .o_cp_action_menus .dropdown-toggle`)).toEqual([
|
||||
"Print",
|
||||
"Actions",
|
||||
]);
|
||||
|
||||
// select Print dropdown
|
||||
await contains(`.o_cp_action_menus .dropdown-toggle:eq(0)`).click();
|
||||
expect(`.o-dropdown--menu .o-dropdown-item`).toHaveCount(4);
|
||||
expect(queryAllTexts(`.o-dropdown--menu .o-dropdown-item`)).toEqual([
|
||||
"Extra Print Item",
|
||||
"Some Report always visible",
|
||||
"Some Report with domain 1",
|
||||
"Some Report with domain 2",
|
||||
]);
|
||||
|
||||
// the last RPC call to retrieve print items only happens when the dropdown is clicked
|
||||
expect.verifySteps(["get_valid_action_reports"]);
|
||||
|
||||
// select only the record that satisfies domain 1
|
||||
await contains(`.o_data_row:eq(1) input`).click();
|
||||
await contains(`.o_cp_action_menus .dropdown-toggle:eq(0)`).click();
|
||||
expect(`.o-dropdown--menu .o-dropdown-item`).toHaveCount(3);
|
||||
expect(queryAllTexts(`.o-dropdown--menu .o-dropdown-item`)).toEqual([
|
||||
"Extra Print Item",
|
||||
"Some Report always visible",
|
||||
"Some Report with domain 1",
|
||||
]);
|
||||
|
||||
expect.verifySteps(["get_valid_action_reports"]);
|
||||
});
|
||||
|
|
@ -0,0 +1,255 @@
|
|||
import { expect, test, getFixture } from "@odoo/hoot";
|
||||
import { click, press, keyDown, keyUp, queryAll, queryFirst } from "@odoo/hoot-dom";
|
||||
import { animationFrame } from "@odoo/hoot-mock";
|
||||
import { reactive } from "@odoo/owl";
|
||||
import {
|
||||
contains,
|
||||
defineModels,
|
||||
fields,
|
||||
getService,
|
||||
models,
|
||||
mountWithCleanup,
|
||||
mountWithSearch,
|
||||
onRpc,
|
||||
} from "@web/../tests/web_test_helpers";
|
||||
|
||||
import { ControlPanel } from "@web/search/control_panel/control_panel";
|
||||
import { WebClient } from "@web/webclient/webclient";
|
||||
|
||||
class Foo extends models.Model {
|
||||
_views = {
|
||||
kanban: `<kanban><t t-name="card"></t></kanban>`,
|
||||
};
|
||||
}
|
||||
defineModels([Foo]);
|
||||
|
||||
test("simple rendering", async () => {
|
||||
await mountWithSearch(ControlPanel, { resModel: "foo" });
|
||||
|
||||
expect(`.o_control_panel_breadcrumbs`).toHaveCount(1);
|
||||
expect(`.o_control_panel_actions`).toHaveCount(1);
|
||||
expect(`.o_control_panel_actions > *`).toHaveCount(0);
|
||||
expect(`.o_control_panel_navigation`).toHaveCount(1);
|
||||
expect(`.o_control_panel_navigation > *`).toHaveCount(0);
|
||||
expect(`.o_cp_switch_buttons`).toHaveCount(0);
|
||||
expect(`.o_breadcrumb`).toHaveCount(1);
|
||||
});
|
||||
|
||||
test.tags("desktop");
|
||||
test("breadcrumbs", async () => {
|
||||
await mountWithSearch(
|
||||
ControlPanel,
|
||||
{ resModel: "foo" },
|
||||
{
|
||||
breadcrumbs: [
|
||||
{
|
||||
jsId: "controller_7",
|
||||
name: "Previous",
|
||||
onSelected: () => expect.step("controller_7"),
|
||||
},
|
||||
{
|
||||
jsId: "controller_9",
|
||||
name: "Current",
|
||||
onSelected: () => expect.step("controller_9"),
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
const breadcrumbItems = queryAll(`.o_breadcrumb li.breadcrumb-item, .o_breadcrumb .active`);
|
||||
expect(breadcrumbItems).toHaveCount(2);
|
||||
expect(breadcrumbItems[0]).toHaveText("Previous");
|
||||
expect(breadcrumbItems[1]).toHaveText("Current");
|
||||
expect(breadcrumbItems[1]).toHaveClass("active");
|
||||
|
||||
await click(breadcrumbItems[0]);
|
||||
expect.verifySteps(["controller_7"]);
|
||||
});
|
||||
|
||||
test.tags("desktop");
|
||||
test("view switcher", async () => {
|
||||
await mountWithSearch(
|
||||
ControlPanel,
|
||||
{ resModel: "foo" },
|
||||
{
|
||||
viewSwitcherEntries: [
|
||||
{ type: "list", active: true, icon: "oi-view-list", name: "List" },
|
||||
{ type: "kanban", icon: "oi-view-kanban", name: "Kanban" },
|
||||
],
|
||||
}
|
||||
);
|
||||
expect(`.o_control_panel_navigation .o_cp_switch_buttons`).toHaveCount(1);
|
||||
expect(`.o_switch_view`).toHaveCount(2);
|
||||
|
||||
const views = queryAll`.o_switch_view`;
|
||||
expect(views[0]).toHaveAttribute("data-tooltip", "List");
|
||||
expect(views[0]).toHaveClass("active");
|
||||
expect(`.o_switch_view:eq(0) .oi-view-list`).toHaveCount(1);
|
||||
expect(views[1]).toHaveAttribute("data-tooltip", "Kanban");
|
||||
expect(views[1]).not.toHaveClass("active");
|
||||
expect(`.o_switch_view:eq(1) .oi-view-kanban`).toHaveCount(1);
|
||||
|
||||
getService("action").switchView = (viewType) => expect.step(viewType);
|
||||
await click(views[1]);
|
||||
expect.verifySteps(["kanban"]);
|
||||
});
|
||||
|
||||
test.tags("mobile");
|
||||
test("view switcher on mobile", async () => {
|
||||
await mountWithSearch(
|
||||
ControlPanel,
|
||||
{ resModel: "foo" },
|
||||
{
|
||||
viewSwitcherEntries: [
|
||||
{ type: "list", active: true, icon: "oi-view-list", name: "List" },
|
||||
{ type: "kanban", icon: "oi-view-kanban", name: "Kanban" },
|
||||
],
|
||||
}
|
||||
);
|
||||
expect(`.o_control_panel_navigation .o_cp_switch_buttons`).toHaveCount(1);
|
||||
|
||||
await click(".o_control_panel_navigation .o_cp_switch_buttons .dropdown-toggle");
|
||||
await animationFrame();
|
||||
|
||||
expect(`.dropdown-item`).toHaveCount(2);
|
||||
|
||||
const views = queryAll`.dropdown-item`;
|
||||
expect(views[0]).toHaveText("List");
|
||||
expect(views[0]).toHaveClass("selected");
|
||||
expect(queryAll(`.oi-view-list`, { root: views[0] })).toHaveCount(1);
|
||||
expect(views[1]).toHaveText("Kanban");
|
||||
expect(views[1]).not.toHaveClass("selected");
|
||||
expect(queryAll(`.oi-view-kanban`, { root: views[1] })).toHaveCount(1);
|
||||
|
||||
getService("action").switchView = (viewType) => expect.step(viewType);
|
||||
await click(views[1]);
|
||||
expect.verifySteps(["kanban"]);
|
||||
});
|
||||
|
||||
test("pager", async () => {
|
||||
const pagerProps = reactive({
|
||||
offset: 0,
|
||||
limit: 10,
|
||||
total: 50,
|
||||
onUpdate: () => {},
|
||||
});
|
||||
|
||||
await mountWithSearch(ControlPanel, { resModel: "foo" }, { pagerProps });
|
||||
expect(`.o_pager`).toHaveCount(1);
|
||||
|
||||
pagerProps.total = 0;
|
||||
await animationFrame();
|
||||
expect(`.o_pager`).toHaveCount(0);
|
||||
});
|
||||
|
||||
test("view switcher hotkey cycles through views", async () => {
|
||||
onRpc("has_group", () => true);
|
||||
|
||||
await mountWithCleanup(WebClient);
|
||||
await getService("action").doAction({
|
||||
res_model: "foo",
|
||||
type: "ir.actions.act_window",
|
||||
views: [
|
||||
[false, "list"],
|
||||
[false, "kanban"],
|
||||
],
|
||||
});
|
||||
expect(`.o_list_view`).toHaveCount(1);
|
||||
|
||||
await press(["alt", "shift", "v"]);
|
||||
await animationFrame();
|
||||
expect(`.o_kanban_view`).toHaveCount(1);
|
||||
|
||||
await press(["alt", "shift", "v"]);
|
||||
await animationFrame();
|
||||
expect(`.o_list_view`).toHaveCount(1);
|
||||
});
|
||||
|
||||
test.tags("desktop");
|
||||
test("hotkey overlay not overlapped by active view button", async () => {
|
||||
onRpc("has_group", () => true);
|
||||
|
||||
await mountWithCleanup(WebClient);
|
||||
await getService("action").doAction({
|
||||
res_model: "foo",
|
||||
type: "ir.actions.act_window",
|
||||
views: [
|
||||
[false, "list"],
|
||||
[false, "kanban"],
|
||||
],
|
||||
});
|
||||
|
||||
await keyDown("alt");
|
||||
expect(`.o_cp_switch_buttons .o_web_hotkey_overlay`).toHaveCount(1);
|
||||
expect(`.o_switch_view.active`).toHaveCount(1);
|
||||
|
||||
const hotkeyZIndex = Number(
|
||||
getComputedStyle(queryFirst(`.o_cp_switch_buttons .o_web_hotkey_overlay`)).zIndex
|
||||
);
|
||||
const buttonZIndex = Number(getComputedStyle(queryFirst(`.o_switch_view.active`)).zIndex);
|
||||
|
||||
expect(hotkeyZIndex).toBeGreaterThan(buttonZIndex);
|
||||
|
||||
await keyUp("alt");
|
||||
expect(`.o_cp_switch_buttons .o_web_hotkey_overlay`).toHaveCount(0);
|
||||
});
|
||||
|
||||
test.tags("desktop");
|
||||
test("control panel layout buttons in dialog", async () => {
|
||||
onRpc("has_group", () => true);
|
||||
Foo._fields.char = fields.Char();
|
||||
Foo._records = [
|
||||
{
|
||||
char: "a",
|
||||
},
|
||||
{
|
||||
char: "b",
|
||||
},
|
||||
];
|
||||
Foo._views["list"] = `<list editable="top"><field name="char"/></list>`;
|
||||
|
||||
await mountWithCleanup(WebClient);
|
||||
await getService("action").doAction({
|
||||
res_model: "foo",
|
||||
type: "ir.actions.act_window",
|
||||
target: "new",
|
||||
views: [[false, "list"]],
|
||||
});
|
||||
expect(`.o_list_view`).toHaveCount(1);
|
||||
await contains(".o_data_cell").click();
|
||||
expect(".modal-footer .o_list_buttons button").toHaveCount(2);
|
||||
expect(".o_control_panel .o_list_buttons button").toHaveCount(0, {
|
||||
message: "layout buttons are not replicated in the control panel when inside a dialog",
|
||||
});
|
||||
});
|
||||
|
||||
test.tags("mobile");
|
||||
test("Control panel is shown/hide on top when scrolling", async () => {
|
||||
await mountWithSearch(
|
||||
ControlPanel,
|
||||
{ resModel: "foo" },
|
||||
{
|
||||
viewSwitcherEntries: [
|
||||
{ type: "list", active: true, icon: "oi-view-list", name: "List" },
|
||||
{ type: "kanban", icon: "oi-view-kanban", name: "Kanban" },
|
||||
],
|
||||
}
|
||||
);
|
||||
const contentHeight = 200;
|
||||
const sampleContent = document.createElement("div");
|
||||
sampleContent.style.minHeight = `${2 * contentHeight}px`;
|
||||
const target = getFixture();
|
||||
target.appendChild(sampleContent);
|
||||
target.style.maxHeight = `${contentHeight}px`;
|
||||
target.style.overflow = "auto";
|
||||
target.scrollTo({ top: 50 });
|
||||
await animationFrame();
|
||||
expect(".o_control_panel").toHaveClass("o_mobile_sticky", {
|
||||
message: "control panel becomes sticky when the target is not on top",
|
||||
});
|
||||
target.scrollTo({ top: -50 });
|
||||
await animationFrame();
|
||||
expect(".o_control_panel").not.toHaveClass("o_mobile_sticky", {
|
||||
message: "control panel is not sticky anymore",
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,299 @@
|
|||
import { after, expect, test } from "@odoo/hoot";
|
||||
import { press, queryAllTexts } from "@odoo/hoot-dom";
|
||||
import { Component, xml } from "@odoo/owl";
|
||||
import {
|
||||
contains,
|
||||
defineModels,
|
||||
editFavoriteName,
|
||||
editSearch,
|
||||
fields,
|
||||
getFacetTexts,
|
||||
mockService,
|
||||
models,
|
||||
mountWithSearch,
|
||||
onRpc,
|
||||
saveFavorite,
|
||||
toggleSaveFavorite,
|
||||
toggleSearchBarMenu,
|
||||
validateSearch,
|
||||
} from "@web/../tests/web_test_helpers";
|
||||
|
||||
import { SearchBar } from "@web/search/search_bar/search_bar";
|
||||
import { SearchBarMenu } from "@web/search/search_bar_menu/search_bar_menu";
|
||||
import { useSetupAction } from "@web/search/action_hook";
|
||||
|
||||
class Foo extends models.Model {
|
||||
bar = fields.Many2one({ relation: "partner" });
|
||||
birthday = fields.Date();
|
||||
date_field = fields.Date();
|
||||
float_field = fields.Float();
|
||||
foo = fields.Char();
|
||||
}
|
||||
|
||||
class Partner extends models.Model {}
|
||||
|
||||
defineModels([Foo, Partner]);
|
||||
|
||||
test("simple rendering", async () => {
|
||||
await mountWithSearch(
|
||||
SearchBar,
|
||||
{
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["favorite"],
|
||||
searchViewId: false,
|
||||
},
|
||||
{
|
||||
getDisplayName: () => "Action Name",
|
||||
}
|
||||
);
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
await toggleSaveFavorite();
|
||||
expect(`.o_add_favorite + .o_accordion_values input[type="text"]`).toHaveValue("Action Name");
|
||||
expect(`.o_add_favorite + .o_accordion_values input[type="checkbox"]`).toHaveCount(2);
|
||||
expect(queryAllTexts(`.o_add_favorite + .o_accordion_values .form-check label`)).toEqual([
|
||||
"Default filter",
|
||||
"Shared",
|
||||
]);
|
||||
});
|
||||
|
||||
test("favorites use by default and share are exclusive", async () => {
|
||||
await mountWithSearch(SearchBar, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["favorite"],
|
||||
searchViewId: false,
|
||||
});
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
await toggleSaveFavorite();
|
||||
expect(`input[type="checkbox"]`).toHaveCount(2);
|
||||
expect(`input[type="checkbox"]:checked`).toHaveCount(0);
|
||||
|
||||
await contains(`input[type="checkbox"]:eq(0)`).check();
|
||||
expect(`input[type="checkbox"]:eq(0)`).toBeChecked();
|
||||
expect(`input[type="checkbox"]:eq(1)`).not.toBeChecked();
|
||||
|
||||
await contains(`input[type="checkbox"]:eq(1)`).check();
|
||||
expect(`input[type="checkbox"]:eq(0)`).not.toBeChecked();
|
||||
expect(`input[type="checkbox"]:eq(1)`).toBeChecked();
|
||||
|
||||
await contains(`input[type="checkbox"]:eq(0)`).check();
|
||||
expect(`input[type="checkbox"]:eq(0)`).toBeChecked();
|
||||
expect(`input[type="checkbox"]:eq(1)`).not.toBeChecked();
|
||||
|
||||
await contains(`input[type="checkbox"]:eq(0)`).uncheck();
|
||||
expect(`input[type="checkbox"]:eq(0)`).not.toBeChecked();
|
||||
expect(`input[type="checkbox"]:eq(1)`).not.toBeChecked();
|
||||
});
|
||||
|
||||
test("save filter", async () => {
|
||||
class TestComponent extends Component {
|
||||
static components = { SearchBarMenu };
|
||||
static template = xml`<div><SearchBarMenu/></div>`;
|
||||
static props = ["*"];
|
||||
setup() {
|
||||
useSetupAction({
|
||||
getContext: () => {
|
||||
return { someKey: "foo" };
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
onRpc("create_or_replace", ({ args, route }) => {
|
||||
expect.step(route);
|
||||
const irFilter = args[0];
|
||||
expect(irFilter.context).toEqual({ group_by: [], someKey: "foo" });
|
||||
return 7; // fake serverSideId
|
||||
});
|
||||
|
||||
const component = await mountWithSearch(TestComponent, {
|
||||
resModel: "foo",
|
||||
context: { someOtherKey: "bar" }, // should not end up in filter's context
|
||||
searchViewId: false,
|
||||
});
|
||||
const clearCacheListener = () => expect.step("CLEAR-CACHES");
|
||||
component.env.bus.addEventListener("CLEAR-CACHES", clearCacheListener);
|
||||
after(() => component.env.bus.removeEventListener("CLEAR-CACHES", clearCacheListener));
|
||||
expect.verifySteps([]);
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
await toggleSaveFavorite();
|
||||
await editFavoriteName("aaa");
|
||||
await saveFavorite();
|
||||
expect.verifySteps(["/web/dataset/call_kw/ir.filters/create_or_replace", "CLEAR-CACHES"]);
|
||||
});
|
||||
|
||||
test("dynamic filters are saved dynamic", async () => {
|
||||
onRpc("create_or_replace", ({ args, route }) => {
|
||||
expect.step(route);
|
||||
const irFilter = args[0];
|
||||
expect(irFilter.domain).toBe(
|
||||
`[("date_field", ">=", (context_today() + relativedelta()).strftime("%Y-%m-%d"))]`
|
||||
);
|
||||
return 7; // fake serverSideId
|
||||
});
|
||||
|
||||
await mountWithSearch(SearchBar, {
|
||||
resModel: "foo",
|
||||
context: { search_default_filter: 1 },
|
||||
searchMenuTypes: ["filter", "favorite"],
|
||||
searchViewId: false,
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Filter" name="filter" domain="[('date_field', '>=', (context_today() + relativedelta()).strftime('%Y-%m-%d'))]"/>
|
||||
</search>
|
||||
`,
|
||||
});
|
||||
expect(getFacetTexts()).toEqual(["Filter"]);
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
await toggleSaveFavorite();
|
||||
await editFavoriteName("My favorite");
|
||||
await saveFavorite();
|
||||
expect(getFacetTexts()).toEqual(["My favorite"]);
|
||||
expect.verifySteps(["/web/dataset/call_kw/ir.filters/create_or_replace"]);
|
||||
});
|
||||
|
||||
test("save filters created via autocompletion works", async () => {
|
||||
onRpc("create_or_replace", ({ args, route }) => {
|
||||
expect.step(route);
|
||||
const irFilter = args[0];
|
||||
expect(irFilter.domain).toBe(`[("foo", "ilike", "a")]`);
|
||||
return 7; // fake serverSideId
|
||||
});
|
||||
|
||||
await mountWithSearch(SearchBar, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["favorite"],
|
||||
searchViewId: false,
|
||||
searchViewArch: `<search><field name="foo"/></search>`,
|
||||
});
|
||||
expect(getFacetTexts()).toEqual([]);
|
||||
|
||||
await editSearch("a");
|
||||
await validateSearch();
|
||||
expect(getFacetTexts()).toEqual(["Foo\na"]);
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
await toggleSaveFavorite();
|
||||
await editFavoriteName("My favorite");
|
||||
await saveFavorite();
|
||||
expect(getFacetTexts()).toEqual(["My favorite"]);
|
||||
expect.verifySteps(["/web/dataset/call_kw/ir.filters/create_or_replace"]);
|
||||
});
|
||||
|
||||
test("favorites have unique descriptions (the submenus of the favorite menu are correctly updated)", async () => {
|
||||
mockService("notification", {
|
||||
add(message, options) {
|
||||
expect.step("notification");
|
||||
expect(message).toBe("A filter with same name already exists.");
|
||||
expect(options).toEqual({ type: "danger" });
|
||||
},
|
||||
});
|
||||
|
||||
onRpc("create_or_replace", ({ args, route }) => {
|
||||
expect.step(route);
|
||||
expect(args[0]).toEqual({
|
||||
action_id: false,
|
||||
context: { group_by: [] },
|
||||
domain: `[]`,
|
||||
is_default: false,
|
||||
model_id: "foo",
|
||||
name: "My favorite 2",
|
||||
sort: `[]`,
|
||||
embedded_action_id: false,
|
||||
embedded_parent_res_id: false,
|
||||
user_id: 7,
|
||||
});
|
||||
return 2; // fake serverSideId
|
||||
});
|
||||
|
||||
await mountWithSearch(SearchBar, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["favorite"],
|
||||
searchViewId: false,
|
||||
irFilters: [
|
||||
{
|
||||
context: "{}",
|
||||
domain: "[]",
|
||||
id: 1,
|
||||
is_default: false,
|
||||
name: "My favorite",
|
||||
sort: "[]",
|
||||
user_id: [2, "Mitchell Admin"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
await toggleSaveFavorite();
|
||||
|
||||
// first try: should fail
|
||||
await editFavoriteName("My favorite");
|
||||
await saveFavorite();
|
||||
expect.verifySteps(["notification"]);
|
||||
|
||||
// second try: should succeed
|
||||
await editFavoriteName("My favorite 2");
|
||||
await saveFavorite();
|
||||
expect.verifySteps(["/web/dataset/call_kw/ir.filters/create_or_replace"]);
|
||||
|
||||
// third try: should fail
|
||||
await editFavoriteName("My favorite 2");
|
||||
await saveFavorite();
|
||||
expect.verifySteps(["notification"]);
|
||||
});
|
||||
|
||||
test("undefined name for filter shows notification and not error", async () => {
|
||||
mockService("notification", {
|
||||
add(message, options) {
|
||||
expect.step("notification");
|
||||
expect(message).toBe("A name for your favorite filter is required.");
|
||||
expect(options).toEqual({ type: "danger" });
|
||||
},
|
||||
});
|
||||
|
||||
onRpc("create_or_replace", () => 7); // fake serverSideId
|
||||
|
||||
await mountWithSearch(SearchBarMenu, {
|
||||
resModel: "foo",
|
||||
searchViewId: false,
|
||||
});
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
await toggleSaveFavorite();
|
||||
await saveFavorite();
|
||||
expect.verifySteps(["notification"]);
|
||||
});
|
||||
|
||||
test("add favorite with enter which already exists", async () => {
|
||||
mockService("notification", {
|
||||
add(message, options) {
|
||||
expect.step("notification");
|
||||
expect(message).toBe("A name for your favorite filter is required.");
|
||||
expect(options).toEqual({ type: "danger" });
|
||||
},
|
||||
});
|
||||
await mountWithSearch(SearchBarMenu, {
|
||||
resModel: "foo",
|
||||
searchViewId: false,
|
||||
irFilters: [
|
||||
{
|
||||
context: "{}",
|
||||
domain: "[]",
|
||||
id: 1,
|
||||
is_default: false,
|
||||
name: "My favorite",
|
||||
sort: "[]",
|
||||
user_id: [2, "Mitchell Admin"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
await toggleSaveFavorite();
|
||||
await editFavoriteName("My favorite");
|
||||
await press("Enter");
|
||||
|
||||
expect.verifySteps(["notification"]);
|
||||
});
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
import { expect, test } from "@odoo/hoot";
|
||||
import { queryAllTexts } from "@odoo/hoot-dom";
|
||||
import {
|
||||
defineModels,
|
||||
fields,
|
||||
getFacetTexts,
|
||||
isItemSelected,
|
||||
isOptionSelected,
|
||||
models,
|
||||
mountWithSearch,
|
||||
selectGroup,
|
||||
toggleMenuItem,
|
||||
toggleSearchBarMenu,
|
||||
} from "@web/../tests/web_test_helpers";
|
||||
|
||||
import { SearchBar } from "@web/search/search_bar/search_bar";
|
||||
|
||||
class Foo extends models.Model {
|
||||
bar = fields.Many2one({ relation: "partner", groupable: false });
|
||||
birthday = fields.Date();
|
||||
date = fields.Date();
|
||||
float = fields.Float({ groupable: false });
|
||||
foo = fields.Char();
|
||||
}
|
||||
|
||||
class Partner extends models.Model {}
|
||||
|
||||
defineModels([Foo, Partner]);
|
||||
|
||||
test(`simple rendering`, async () => {
|
||||
await mountWithSearch(SearchBar, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
});
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
expect(`.o_group_by_menu option[disabled]`).toHaveText(`Add Custom Group`);
|
||||
expect(queryAllTexts`.o_add_custom_group_menu option:not([disabled])`).toEqual([
|
||||
"Birthday",
|
||||
"Created on",
|
||||
"Date",
|
||||
"Display name",
|
||||
"Foo",
|
||||
"Last Modified on",
|
||||
]);
|
||||
});
|
||||
|
||||
test(`the ID field should not be proposed in "Add Custom Group" menu`, async () => {
|
||||
await mountWithSearch(SearchBar, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewFields: {
|
||||
foo: { string: "Foo", type: "char", store: true, sortable: true, groupable: true },
|
||||
id: { string: "ID", type: "integer", sortable: true, groupable: true },
|
||||
},
|
||||
});
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
expect(queryAllTexts`.o_add_custom_group_menu option:not([disabled])`).toEqual(["Foo"]);
|
||||
});
|
||||
|
||||
test(`stored many2many should be proposed in "Add Custom Group" menu`, async () => {
|
||||
await mountWithSearch(SearchBar, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewFields: {
|
||||
char_a: {
|
||||
string: "Char A",
|
||||
type: "char",
|
||||
store: true,
|
||||
sortable: true,
|
||||
groupable: true,
|
||||
},
|
||||
m2m_no_stored: { string: "M2M Not Stored", type: "many2many" },
|
||||
m2m_stored: {
|
||||
string: "M2M Stored",
|
||||
type: "many2many",
|
||||
store: true,
|
||||
groupable: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
expect(queryAllTexts`.o_add_custom_group_menu option:not([disabled])`).toEqual([
|
||||
"Char A",
|
||||
"M2M Stored",
|
||||
]);
|
||||
});
|
||||
|
||||
test(`add a date field in "Add Custom Group" activate a groupby with global default option "month"`, async () => {
|
||||
const component = await mountWithSearch(SearchBar, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewFields: {
|
||||
date: {
|
||||
string: "Date",
|
||||
type: "date",
|
||||
store: true,
|
||||
sortable: true,
|
||||
groupable: true,
|
||||
},
|
||||
id: { sortable: true, string: "ID", type: "integer", groupable: true },
|
||||
},
|
||||
});
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
expect(component.env.searchModel.groupBy).toEqual([]);
|
||||
expect(`.o_add_custom_group_menu`).toHaveCount(1); // Add Custom Group
|
||||
|
||||
await selectGroup("date");
|
||||
expect(component.env.searchModel.groupBy).toEqual(["date:month"]);
|
||||
expect(getFacetTexts()).toEqual(["Date: Month"]);
|
||||
expect(isItemSelected("Date")).toBe(true);
|
||||
|
||||
await toggleMenuItem("Date");
|
||||
expect(isOptionSelected("Date", "Month")).toBe(true);
|
||||
});
|
||||
|
||||
test(`click on add custom group toggle group selector`, async () => {
|
||||
await mountWithSearch(SearchBar, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewFields: {
|
||||
date: {
|
||||
sortable: true,
|
||||
name: "date",
|
||||
string: "Super Date",
|
||||
type: "date",
|
||||
groupable: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
expect(`.o_add_custom_group_menu option[disabled]`).toHaveText("Add Custom Group");
|
||||
|
||||
// Single select node with a single option
|
||||
expect(`.o_add_custom_group_menu option:not([disabled])`).toHaveCount(1);
|
||||
expect(`.o_add_custom_group_menu option:not([disabled])`).toHaveText("Super Date");
|
||||
});
|
||||
|
||||
test(`select a field name in Add Custom Group menu properly trigger the corresponding field`, async () => {
|
||||
await mountWithSearch(SearchBar, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewFields: {
|
||||
candle_light: {
|
||||
sortable: true,
|
||||
groupable: true,
|
||||
string: "Candlelight",
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
await selectGroup("candle_light");
|
||||
expect(`.o_group_by_menu .o_menu_item`).toHaveCount(2);
|
||||
expect(`.o_add_custom_group_menu`).toHaveCount(1);
|
||||
expect(getFacetTexts()).toEqual(["Candlelight"]);
|
||||
});
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
import { describe, expect, test } from "@odoo/hoot";
|
||||
|
||||
import { DEFAULT_INTERVAL } from "@web/search/utils/dates";
|
||||
import { getGroupBy } from "@web/search/utils/group_by";
|
||||
|
||||
const fields = {
|
||||
display_name: { string: "Displayed name", type: "char" },
|
||||
foo: {
|
||||
string: "Foo",
|
||||
type: "char",
|
||||
default: "My little Foo Value",
|
||||
store: true,
|
||||
sortable: true,
|
||||
},
|
||||
date_field: { string: "Date", type: "date", store: true, sortable: true },
|
||||
float_field: { string: "Float", type: "float" },
|
||||
bar: { string: "Bar", type: "many2one", relation: "partner" },
|
||||
};
|
||||
|
||||
describe("Without field validation", () => {
|
||||
test("simple valid group by", async () => {
|
||||
let groupBy = getGroupBy("display_name");
|
||||
expect(groupBy.fieldName).toBe("display_name");
|
||||
expect(groupBy.interval).toBe(null);
|
||||
expect(groupBy.spec).toBe("display_name");
|
||||
|
||||
groupBy = getGroupBy("display_name:quarter");
|
||||
expect(groupBy.fieldName).toBe("display_name");
|
||||
expect(groupBy.interval).toBe("quarter");
|
||||
expect(groupBy.spec).toBe("display_name:quarter");
|
||||
});
|
||||
|
||||
test("simple invalid group by", async () => {
|
||||
expect(() => getGroupBy(":day")).toThrow();
|
||||
expect(() => getGroupBy("diay_name:yar")).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("With field validation", () => {
|
||||
test("simple valid group by", async () => {
|
||||
const groupBy = getGroupBy("display_name", fields);
|
||||
expect(groupBy.fieldName).toBe("display_name");
|
||||
expect(groupBy.interval).toBe(null);
|
||||
expect(groupBy.spec).toBe("display_name");
|
||||
});
|
||||
|
||||
test("simple invalid group by", async () => {
|
||||
expect(() => getGroupBy("", fields)).toThrow();
|
||||
expect(() => getGroupBy("display_name:day", fields)).toThrow();
|
||||
expect(() => getGroupBy("diay_name:year", fields)).toThrow();
|
||||
expect(() => getGroupBy("diay_name:yar", fields)).toThrow();
|
||||
});
|
||||
|
||||
test("simple valid date group by", async () => {
|
||||
let groupBy = getGroupBy("date_field:year", fields);
|
||||
expect(groupBy.fieldName).toBe("date_field");
|
||||
expect(groupBy.interval).toBe("year");
|
||||
expect(groupBy.spec).toBe("date_field:year");
|
||||
|
||||
groupBy = getGroupBy("date_field", fields);
|
||||
expect(groupBy.fieldName).toBe("date_field");
|
||||
expect(groupBy.interval).toBe(DEFAULT_INTERVAL);
|
||||
expect(groupBy.spec).toBe(`date_field:${DEFAULT_INTERVAL}`);
|
||||
});
|
||||
|
||||
test("simple invalid date group by", async () => {
|
||||
expect(() => getGroupBy("date_field:yar", fields)).toThrow();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
import { expect, test } from "@odoo/hoot";
|
||||
import { Component, useState, xml } from "@odoo/owl";
|
||||
import {
|
||||
defineModels,
|
||||
getPagerLimit,
|
||||
getPagerValue,
|
||||
models,
|
||||
mountWithSearch,
|
||||
pagerNext,
|
||||
} from "@web/../tests/web_test_helpers";
|
||||
|
||||
import { ControlPanel } from "@web/search/control_panel/control_panel";
|
||||
import { usePager } from "@web/search/pager_hook";
|
||||
import { animationFrame } from "@odoo/hoot-mock";
|
||||
|
||||
class Foo extends models.Model {}
|
||||
|
||||
defineModels([Foo]);
|
||||
|
||||
test("pager is correctly displayed", async () => {
|
||||
class TestComponent extends Component {
|
||||
static components = { ControlPanel };
|
||||
static template = xml`<ControlPanel />`;
|
||||
static props = ["*"];
|
||||
setup() {
|
||||
usePager(() => ({
|
||||
offset: 0,
|
||||
limit: 10,
|
||||
total: 50,
|
||||
onUpdate: () => {},
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
await mountWithSearch(TestComponent, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: [],
|
||||
});
|
||||
expect(`.o_pager`).toHaveCount(1);
|
||||
expect(".o_pager button.o_pager_next").toHaveCount(1);
|
||||
expect(".o_pager button.o_pager_previous").toHaveCount(1);
|
||||
});
|
||||
|
||||
test.tags("desktop");
|
||||
test("pager is correctly displayed on desktop", async () => {
|
||||
class TestComponent extends Component {
|
||||
static components = { ControlPanel };
|
||||
static template = xml`<ControlPanel />`;
|
||||
static props = ["*"];
|
||||
setup() {
|
||||
usePager(() => ({
|
||||
offset: 0,
|
||||
limit: 10,
|
||||
total: 50,
|
||||
onUpdate: () => {},
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
await mountWithSearch(TestComponent, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: [],
|
||||
});
|
||||
expect(`.o_pager`).toHaveCount(1);
|
||||
expect(getPagerValue()).toEqual([1, 10]);
|
||||
expect(getPagerLimit()).toBe(50);
|
||||
});
|
||||
|
||||
test("pager is correctly updated", async () => {
|
||||
class TestComponent extends Component {
|
||||
static components = { ControlPanel };
|
||||
static template = xml`<ControlPanel />`;
|
||||
static props = ["*"];
|
||||
setup() {
|
||||
this.state = useState({ offset: 0, limit: 10 });
|
||||
usePager(() => ({
|
||||
offset: this.state.offset,
|
||||
limit: this.state.limit,
|
||||
total: 50,
|
||||
onUpdate: (newState) => {
|
||||
Object.assign(this.state, newState);
|
||||
},
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
const component = await mountWithSearch(TestComponent, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: [],
|
||||
});
|
||||
expect(`.o_pager`).toHaveCount(1);
|
||||
expect(component.state).toEqual({ offset: 0, limit: 10 });
|
||||
|
||||
await pagerNext();
|
||||
expect(`.o_pager`).toHaveCount(1);
|
||||
expect(component.state).toEqual({ offset: 10, limit: 10 });
|
||||
|
||||
component.state.offset = 20;
|
||||
await animationFrame();
|
||||
expect(`.o_pager`).toHaveCount(1);
|
||||
expect(component.state).toEqual({ offset: 20, limit: 10 });
|
||||
});
|
||||
|
||||
test.tags("desktop");
|
||||
test("pager is correctly updated on desktop", async () => {
|
||||
class TestComponent extends Component {
|
||||
static components = { ControlPanel };
|
||||
static template = xml`<ControlPanel />`;
|
||||
static props = ["*"];
|
||||
setup() {
|
||||
this.state = useState({ offset: 0, limit: 10 });
|
||||
usePager(() => ({
|
||||
offset: this.state.offset,
|
||||
limit: this.state.limit,
|
||||
total: 50,
|
||||
onUpdate: (newState) => {
|
||||
Object.assign(this.state, newState);
|
||||
},
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
const component = await mountWithSearch(TestComponent, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: [],
|
||||
});
|
||||
expect(`.o_pager`).toHaveCount(1);
|
||||
expect(getPagerValue()).toEqual([1, 10]);
|
||||
expect(getPagerLimit()).toBe(50);
|
||||
|
||||
await pagerNext();
|
||||
expect(`.o_pager`).toHaveCount(1);
|
||||
expect(getPagerValue()).toEqual([11, 20]);
|
||||
expect(getPagerLimit()).toBe(50);
|
||||
|
||||
component.state.offset = 20;
|
||||
await animationFrame();
|
||||
expect(`.o_pager`).toHaveCount(1);
|
||||
expect(getPagerValue()).toEqual([21, 30]);
|
||||
expect(getPagerLimit()).toBe(50);
|
||||
});
|
||||
1878
odoo-bringout-oca-ocb-web/web/static/tests/search/search_bar.test.js
Normal file
1878
odoo-bringout-oca-ocb-web/web/static/tests/search/search_bar.test.js
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,87 @@
|
|||
import { expect, test } from "@odoo/hoot";
|
||||
import { mockDate } from "@odoo/hoot-mock";
|
||||
import {
|
||||
getFacetTexts,
|
||||
mountWithSearch,
|
||||
removeFacet,
|
||||
toggleMenuItem,
|
||||
toggleMenuItemOption,
|
||||
toggleSearchBarMenu,
|
||||
} from "@web/../tests/web_test_helpers";
|
||||
import { defineSearchBarModels } from "./models";
|
||||
|
||||
import { SearchBarMenu } from "@web/search/search_bar_menu/search_bar_menu";
|
||||
import { queryAll, queryAllTexts } from "@odoo/hoot-dom";
|
||||
import { SearchBar } from "@web/search/search_bar/search_bar";
|
||||
|
||||
defineSearchBarModels();
|
||||
|
||||
test("simple rendering", async () => {
|
||||
mockDate("1997-01-09T12:00:00");
|
||||
|
||||
await mountWithSearch(SearchBarMenu, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["filter", "comparison"],
|
||||
searchViewId: false,
|
||||
});
|
||||
expect(`.o_searchview_dropdown_toggler`).toHaveCount(1);
|
||||
expect(`.dropdown.o_comparison_menu`).toHaveCount(0);
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
await toggleMenuItem("Birthday");
|
||||
await toggleMenuItemOption("Birthday", "January");
|
||||
expect(`.o_comparison_menu .fa.fa-adjust`).toHaveCount(1);
|
||||
expect(`.o_comparison_menu .o_dropdown_title`).toHaveText(/^comparison$/i);
|
||||
expect(`.o_comparison_menu .dropdown-item`).toHaveCount(2);
|
||||
expect(`.o_comparison_menu .dropdown-item[role=menuitemcheckbox]`).toHaveCount(2);
|
||||
expect(queryAllTexts`.o_comparison_menu .dropdown-item`).toEqual([
|
||||
"Birthday: Previous Period",
|
||||
"Birthday: Previous Year",
|
||||
]);
|
||||
expect(queryAll`.o_comparison_menu .dropdown-item`.map((e) => e.ariaChecked)).toEqual([
|
||||
"false",
|
||||
"false",
|
||||
]);
|
||||
});
|
||||
|
||||
test("activate a comparison works", async () => {
|
||||
mockDate("1997-01-09T12:00:00");
|
||||
|
||||
await mountWithSearch(SearchBar, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["filter", "comparison"],
|
||||
searchViewId: false,
|
||||
});
|
||||
await toggleSearchBarMenu();
|
||||
await toggleMenuItem("Birthday");
|
||||
await toggleMenuItemOption("Birthday", "January");
|
||||
await toggleMenuItem("Birthday: Previous Period");
|
||||
expect(getFacetTexts()).toEqual(["Birthday: January 1997", "Birthday: Previous Period"]);
|
||||
|
||||
await toggleMenuItem("Date");
|
||||
await toggleMenuItemOption("Date", "December");
|
||||
await toggleMenuItem("Date: Previous Year");
|
||||
expect(getFacetTexts()).toEqual([
|
||||
["Birthday: January 1997", "Date: December 1996"].join("\nor\n"),
|
||||
"Date: Previous Year",
|
||||
]);
|
||||
|
||||
await toggleMenuItemOption("Date", "1996");
|
||||
expect(getFacetTexts()).toEqual(["Birthday: January 1997"]);
|
||||
|
||||
await toggleMenuItem("Birthday: Previous Year");
|
||||
expect(`.o_comparison_menu .dropdown-item`).toHaveCount(2);
|
||||
expect(`.o_comparison_menu .dropdown-item[role=menuitemcheckbox]`).toHaveCount(2);
|
||||
expect(queryAllTexts`.o_comparison_menu .dropdown-item`).toEqual([
|
||||
"Birthday: Previous Period",
|
||||
"Birthday: Previous Year",
|
||||
]);
|
||||
expect(queryAll`.o_comparison_menu .dropdown-item`.map((e) => e.ariaChecked)).toEqual([
|
||||
"false",
|
||||
"true",
|
||||
]);
|
||||
expect(getFacetTexts()).toEqual(["Birthday: January 1997", "Birthday: Previous Year"]);
|
||||
|
||||
await removeFacet("Birthday: January 1997");
|
||||
expect(getFacetTexts()).toEqual([]);
|
||||
});
|
||||
|
|
@ -0,0 +1,338 @@
|
|||
import { after, expect, test } from "@odoo/hoot";
|
||||
import { queryFirst } from "@odoo/hoot-dom";
|
||||
import { mockDate } from "@odoo/hoot-mock";
|
||||
import { Component, onWillUpdateProps, xml } from "@odoo/owl";
|
||||
import { editValue } from "@web/../tests/core/tree_editor/condition_tree_editor_test_helpers";
|
||||
import {
|
||||
contains,
|
||||
deleteFavorite,
|
||||
editFavoriteName,
|
||||
getFacetTexts,
|
||||
getService,
|
||||
isItemSelected,
|
||||
mountWithCleanup,
|
||||
mountWithSearch,
|
||||
onRpc,
|
||||
patchWithCleanup,
|
||||
saveFavorite,
|
||||
serverState,
|
||||
toggleMenuItem,
|
||||
toggleSaveFavorite,
|
||||
toggleSearchBarMenu,
|
||||
} from "@web/../tests/web_test_helpers";
|
||||
import { Foo, defineSearchBarModels } from "./models";
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { SearchBar } from "@web/search/search_bar/search_bar";
|
||||
import { SearchBarMenu } from "@web/search/search_bar_menu/search_bar_menu";
|
||||
import { WebClient } from "@web/webclient/webclient";
|
||||
|
||||
const favoriteMenuRegistry = registry.category("favoriteMenu");
|
||||
const viewsRegistry = registry.category("views");
|
||||
|
||||
defineSearchBarModels();
|
||||
|
||||
test("simple rendering with no favorite (without ability to save)", async () => {
|
||||
const registryItem = favoriteMenuRegistry.content["custom-favorite-item"];
|
||||
favoriteMenuRegistry.remove("custom-favorite-item");
|
||||
after(() => {
|
||||
favoriteMenuRegistry.add("custom-favorite-item", registryItem[1], {
|
||||
sequence: registryItem[1],
|
||||
});
|
||||
});
|
||||
|
||||
await mountWithSearch(
|
||||
SearchBarMenu,
|
||||
{
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["favorite"],
|
||||
searchViewId: false,
|
||||
},
|
||||
{ getDisplayName: () => "Action Name" }
|
||||
);
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
expect(`.o_favorite_menu .fa.fa-star`).toHaveCount(1);
|
||||
expect(`.o_favorite_menu .o_dropdown_title`).toHaveText(/^favorites$/i);
|
||||
expect(`.o_favorite_menu`).toHaveCount(1);
|
||||
expect(`.o_favorite_menu .o_menu_item`).toHaveCount(0);
|
||||
});
|
||||
|
||||
test("simple rendering with no favorite", async () => {
|
||||
await mountWithSearch(
|
||||
SearchBarMenu,
|
||||
{
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["favorite"],
|
||||
searchViewId: false,
|
||||
},
|
||||
{
|
||||
getDisplayName: () => "Action Name",
|
||||
}
|
||||
);
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
expect(`.o_favorite_menu .fa.fa-star`).toHaveCount(1);
|
||||
expect(`.o_favorite_menu .o_dropdown_title`).toHaveText(/^favorites$/i);
|
||||
expect(`.o_favorite_menu`).toHaveCount(1);
|
||||
expect(`.o_favorite_menu .dropdown-divider`).toHaveCount(0);
|
||||
expect(`.o_favorite_menu .o_add_favorite`).toHaveCount(1);
|
||||
});
|
||||
|
||||
test("delete an active favorite", async () => {
|
||||
class ToyController extends Component {
|
||||
static components = { SearchBar };
|
||||
static template = xml`<div><SearchBar/></div>`;
|
||||
static props = ["*"];
|
||||
|
||||
setup() {
|
||||
expect(this.props.domain).toEqual([["foo", "=", "qsdf"]]);
|
||||
onWillUpdateProps((nextProps) => {
|
||||
expect.step("props updated");
|
||||
expect(nextProps.domain).toEqual([]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
patchWithCleanup(serverState.view_info, {
|
||||
toy: { multi_record: true, display_name: "Toy", icon: "fab fa-android" },
|
||||
});
|
||||
viewsRegistry.add("toy", {
|
||||
type: "toy",
|
||||
Controller: ToyController,
|
||||
});
|
||||
after(() => viewsRegistry.remove("toy"));
|
||||
Foo._filters = [
|
||||
{
|
||||
context: "{}",
|
||||
domain: "[['foo', '=', 'qsdf']]",
|
||||
id: 7,
|
||||
is_default: true,
|
||||
name: "My favorite",
|
||||
sort: "[]",
|
||||
user_id: [2, "Mitchell Admin"],
|
||||
},
|
||||
];
|
||||
|
||||
onRpc("unlink", () => {
|
||||
expect.step("deleteFavorite");
|
||||
return true;
|
||||
});
|
||||
|
||||
const webClient = await mountWithCleanup(WebClient);
|
||||
|
||||
const clearCacheListener = () => expect.step("CLEAR-CACHES");
|
||||
webClient.env.bus.addEventListener("CLEAR-CACHES", clearCacheListener);
|
||||
after(() => webClient.env.bus.removeEventListener("CLEAR-CACHES", clearCacheListener));
|
||||
|
||||
await getService("action").doAction({
|
||||
name: "Action",
|
||||
res_model: "foo",
|
||||
type: "ir.actions.act_window",
|
||||
views: [[false, "toy"]],
|
||||
});
|
||||
await toggleSearchBarMenu();
|
||||
const favorite = queryFirst`.o_favorite_menu .dropdown-item`;
|
||||
expect(favorite).toHaveText("My favorite");
|
||||
expect(favorite).toHaveAttribute("role", "menuitemcheckbox");
|
||||
expect(favorite).toHaveProperty("ariaChecked", "true");
|
||||
expect(getFacetTexts()).toEqual(["My favorite"]);
|
||||
expect(queryFirst`.o_favorite_menu .o_menu_item`).toHaveClass("selected");
|
||||
|
||||
await deleteFavorite("My favorite");
|
||||
expect.verifySteps([]);
|
||||
|
||||
await contains(`div.o_dialog footer button`).click();
|
||||
expect(getFacetTexts()).toEqual([]);
|
||||
expect(".o_favorite_menu .o_menu_item").toHaveCount(1);
|
||||
expect(".o_favorite_menu .o_add_favorite").toHaveCount(1);
|
||||
expect.verifySteps(["deleteFavorite", "CLEAR-CACHES", "props updated"]);
|
||||
});
|
||||
|
||||
test("default favorite is not activated if activateFavorite is set to false", async () => {
|
||||
const searchBarMenu = await mountWithSearch(SearchBarMenu, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["favorite"],
|
||||
searchViewId: false,
|
||||
irFilters: [
|
||||
{
|
||||
context: "{}",
|
||||
domain: "[('foo', '=', 'a')]",
|
||||
id: 7,
|
||||
is_default: true,
|
||||
name: "My favorite",
|
||||
sort: "[]",
|
||||
user_id: [2, "Mitchell Admin"],
|
||||
},
|
||||
],
|
||||
activateFavorite: false,
|
||||
});
|
||||
await toggleSearchBarMenu();
|
||||
expect(isItemSelected("My favorite")).toBe(false);
|
||||
expect(searchBarMenu.env.searchModel.domain).toEqual([]);
|
||||
expect(getFacetTexts()).toEqual([]);
|
||||
});
|
||||
|
||||
test(`toggle favorite correctly clears filter, groupbys, comparison and field "options"`, async () => {
|
||||
mockDate("2019-07-31T13:43:00");
|
||||
|
||||
const searchBar = await mountWithSearch(SearchBar, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["filter", "groupBy", "comparison", "favorite"],
|
||||
searchViewId: false,
|
||||
irFilters: [
|
||||
{
|
||||
context: `
|
||||
{
|
||||
"group_by": ["foo"],
|
||||
"comparison": {
|
||||
"favorite comparison content": "bla bla..."
|
||||
},
|
||||
}
|
||||
`,
|
||||
domain: "['!', ['foo', '=', 'qsdf']]",
|
||||
id: 7,
|
||||
is_default: false,
|
||||
name: "My favorite",
|
||||
sort: "[]",
|
||||
user_id: [2, "Mitchell Admin"],
|
||||
},
|
||||
],
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<field string="Foo" name="foo"/>
|
||||
<filter string="Date Field Filter" name="positive" date="date_field" default_period="year"/>
|
||||
<filter string="Date Field Groupby" name="coolName" context="{'group_by': 'date_field'}"/>
|
||||
</search>
|
||||
`,
|
||||
context: {
|
||||
search_default_positive: true,
|
||||
search_default_coolName: true,
|
||||
search_default_foo: "a",
|
||||
},
|
||||
});
|
||||
expect(searchBar.env.searchModel.domain).toEqual([
|
||||
"&",
|
||||
["foo", "ilike", "a"],
|
||||
"&",
|
||||
["date_field", ">=", "2019-01-01"],
|
||||
["date_field", "<=", "2019-12-31"],
|
||||
]);
|
||||
expect(searchBar.env.searchModel.groupBy).toEqual(["date_field:month"]);
|
||||
expect(searchBar.env.searchModel.getFullComparison()).toBe(null);
|
||||
expect(getFacetTexts()).toEqual([
|
||||
"Foo\na",
|
||||
"Date Field Filter: 2019",
|
||||
"Date Field Groupby: Month",
|
||||
]);
|
||||
|
||||
// activate a comparison
|
||||
await toggleSearchBarMenu();
|
||||
await toggleMenuItem("Date Field Filter: Previous Period");
|
||||
expect(searchBar.env.searchModel.domain).toEqual([["foo", "ilike", "a"]]);
|
||||
expect(searchBar.env.searchModel.groupBy).toEqual(["date_field:month"]);
|
||||
expect(searchBar.env.searchModel.getFullComparison()).toEqual({
|
||||
comparisonId: "previous_period",
|
||||
comparisonRange: [
|
||||
"&",
|
||||
["date_field", ">=", "2018-01-01"],
|
||||
["date_field", "<=", "2018-12-31"],
|
||||
],
|
||||
comparisonRangeDescription: "2018",
|
||||
fieldDescription: "Date Field Filter",
|
||||
fieldName: "date_field",
|
||||
range: ["&", ["date_field", ">=", "2019-01-01"], ["date_field", "<=", "2019-12-31"]],
|
||||
rangeDescription: "2019",
|
||||
});
|
||||
|
||||
// activate the unique existing favorite
|
||||
const favorite = queryFirst`.o_favorite_menu .dropdown-item`;
|
||||
expect(favorite).toHaveText("My favorite");
|
||||
expect(favorite).toHaveAttribute("role", "menuitemcheckbox");
|
||||
expect(favorite).toHaveProperty("ariaChecked", "false");
|
||||
|
||||
await toggleMenuItem("My favorite");
|
||||
expect(favorite).toHaveProperty("ariaChecked", "true");
|
||||
expect(searchBar.env.searchModel.domain).toEqual(["!", ["foo", "=", "qsdf"]]);
|
||||
expect(searchBar.env.searchModel.groupBy).toEqual(["foo"]);
|
||||
expect(searchBar.env.searchModel.getFullComparison()).toEqual({
|
||||
"favorite comparison content": "bla bla...",
|
||||
});
|
||||
expect(getFacetTexts()).toEqual(["My favorite"]);
|
||||
});
|
||||
|
||||
test("edit a favorite with a groupby", async () => {
|
||||
const irFilters = [
|
||||
{
|
||||
context: "{ 'some_key': 'some_value', 'group_by': ['bar'] }",
|
||||
domain: "[('foo', 'ilike', 'abc')]",
|
||||
id: 1,
|
||||
is_default: true,
|
||||
name: "My favorite",
|
||||
sort: "[]",
|
||||
user_id: [2, "Mitchell Admin"],
|
||||
},
|
||||
];
|
||||
|
||||
onRpc("/web/domain/validate", () => true);
|
||||
await mountWithSearch(SearchBar, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["groupBy"], // we need it to have facet (see facets getter in search_model)
|
||||
searchViewId: false,
|
||||
searchViewArch: `<search/>`,
|
||||
irFilters,
|
||||
});
|
||||
expect(getFacetTexts()).toEqual(["My favorite"]);
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
expect(`.o_group_by_menu .o_menu_item:not(.o_add_custom_group_menu)`).toHaveCount(0);
|
||||
|
||||
await contains(`.o_searchview_facet_label`).click();
|
||||
expect(`.modal`).toHaveCount(1);
|
||||
|
||||
await editValue("abcde");
|
||||
await contains(`.modal footer button`).click();
|
||||
expect(`.modal`).toHaveCount(0);
|
||||
expect(getFacetTexts()).toEqual(["Bar", "Foo contains abcde"]);
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
expect(`.o_group_by_menu .o_menu_item:not(.o_add_custom_group_menu)`).toHaveCount(0);
|
||||
});
|
||||
|
||||
test("shared favorites are grouped under a dropdown if there are more than 10", async () => {
|
||||
onRpc("create_or_replace", ({ args, route }) => {
|
||||
expect.step(route);
|
||||
const irFilter = args[0];
|
||||
expect(irFilter.domain).toBe(`[]`);
|
||||
return 10; // fake serverSideId
|
||||
});
|
||||
const irFilters = [];
|
||||
for (let i = 1; i < 11; i++) {
|
||||
irFilters.push({
|
||||
context: "{}",
|
||||
domain: "[('foo', '=', 'a')]",
|
||||
id: i,
|
||||
is_default: false,
|
||||
name: "My favorite" + i,
|
||||
sort: "[]",
|
||||
});
|
||||
}
|
||||
await mountWithSearch(SearchBarMenu, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["favorite"],
|
||||
searchViewId: false,
|
||||
irFilters,
|
||||
activateFavorite: false,
|
||||
});
|
||||
await toggleSearchBarMenu();
|
||||
expect(".o_favorite_menu .o-dropdown-item").toHaveCount(10);
|
||||
await toggleSaveFavorite();
|
||||
await editFavoriteName("My favorite11");
|
||||
await contains(".o-checkbox:eq(1)").click();
|
||||
await saveFavorite();
|
||||
expect.verifySteps(["/web/dataset/call_kw/ir.filters/create_or_replace"]);
|
||||
expect(".o_favorite_menu .o-dropdown-item").toHaveCount(0);
|
||||
expect(".o_favorite_menu .o_menu_item:contains(Shared filters)").toHaveCount(1);
|
||||
await contains(".o_favorite_menu .o_menu_item:contains(Shared filters)").click();
|
||||
expect(".o_favorite_menu .o-dropdown-item").toHaveCount(11);
|
||||
});
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,382 @@
|
|||
import { expect, test } from "@odoo/hoot";
|
||||
import { queryAllTexts, queryFirst } from "@odoo/hoot-dom";
|
||||
import {
|
||||
contains,
|
||||
getFacetTexts,
|
||||
isItemSelected,
|
||||
isOptionSelected,
|
||||
mountWithSearch,
|
||||
removeFacet,
|
||||
toggleMenuItem,
|
||||
toggleMenuItemOption,
|
||||
toggleSearchBarMenu,
|
||||
} from "@web/../tests/web_test_helpers";
|
||||
import { defineSearchBarModels } from "./models";
|
||||
|
||||
import { animationFrame } from "@odoo/hoot-mock";
|
||||
import { SearchBar } from "@web/search/search_bar/search_bar";
|
||||
import { SearchBarMenu } from "@web/search/search_bar_menu/search_bar_menu";
|
||||
|
||||
defineSearchBarModels();
|
||||
|
||||
test("simple rendering with neither groupbys nor groupable fields", async () => {
|
||||
await mountWithSearch(SearchBarMenu, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewArch: `<search />`,
|
||||
searchViewId: false,
|
||||
searchViewFields: {},
|
||||
});
|
||||
await toggleSearchBarMenu();
|
||||
expect(`.o_menu_item`).toHaveCount(0);
|
||||
expect(`.dropdown-divider`).toHaveCount(0);
|
||||
expect(`.o_add_custom_group_menu`).toHaveCount(0);
|
||||
});
|
||||
|
||||
test("simple rendering with no groupby", async () => {
|
||||
await mountWithSearch(SearchBarMenu, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
});
|
||||
await toggleSearchBarMenu();
|
||||
expect(`.o_menu_item`).toHaveCount(1);
|
||||
expect(`.dropdown-divider`).toHaveCount(0);
|
||||
expect(`.o_add_custom_group_menu`).toHaveCount(1);
|
||||
});
|
||||
|
||||
test("simple rendering with a single groupby", async () => {
|
||||
await mountWithSearch(SearchBarMenu, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Foo" name="group_by_foo" context="{'group_by': 'foo'}"/>
|
||||
</search>
|
||||
`,
|
||||
});
|
||||
await toggleSearchBarMenu();
|
||||
|
||||
expect(`.o_menu_item`).toHaveCount(2);
|
||||
const menuItem = queryFirst`.o_menu_item`;
|
||||
expect(menuItem).toHaveText("Foo");
|
||||
expect(menuItem).toHaveAttribute("role", "menuitemcheckbox");
|
||||
expect(menuItem).toHaveProperty("ariaChecked", "false");
|
||||
expect(".dropdown-divider").toHaveCount(1);
|
||||
expect(".o_add_custom_group_menu").toHaveCount(1);
|
||||
});
|
||||
|
||||
test(`toggle a "simple" groupby in groupby menu works`, async () => {
|
||||
const searchBar = await mountWithSearch(SearchBar, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Foo" name="group_by_foo" context="{'group_by': 'foo'}"/>
|
||||
</search>
|
||||
`,
|
||||
});
|
||||
await toggleSearchBarMenu();
|
||||
|
||||
expect(searchBar.env.searchModel.groupBy).toEqual([]);
|
||||
expect(getFacetTexts()).toEqual([]);
|
||||
expect(isItemSelected("Foo")).toBe(false);
|
||||
|
||||
const menuItem = queryFirst`.o_menu_item`;
|
||||
expect(menuItem).toHaveText("Foo");
|
||||
expect(menuItem).toHaveAttribute("role", "menuitemcheckbox");
|
||||
expect(menuItem).toHaveProperty("ariaChecked", "false");
|
||||
|
||||
await toggleMenuItem("Foo");
|
||||
expect(menuItem).toHaveProperty("ariaChecked", "true");
|
||||
expect(searchBar.env.searchModel.groupBy).toEqual(["foo"]);
|
||||
expect(getFacetTexts()).toEqual(["Foo"]);
|
||||
expect(`.o_searchview .o_searchview_facet .o_searchview_facet_label`).toHaveCount(1);
|
||||
expect(isItemSelected("Foo")).toBe(true);
|
||||
|
||||
await toggleMenuItem("Foo");
|
||||
expect(searchBar.env.searchModel.groupBy).toEqual([]);
|
||||
expect(getFacetTexts()).toEqual([]);
|
||||
expect(isItemSelected("Foo")).toBe(false);
|
||||
});
|
||||
|
||||
test(`toggle a "simple" groupby quickly does not crash`, async () => {
|
||||
await mountWithSearch(SearchBarMenu, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Foo" name="group_by_foo" context="{'group_by': 'foo'}"/>
|
||||
</search>
|
||||
`,
|
||||
});
|
||||
await toggleSearchBarMenu();
|
||||
|
||||
await toggleMenuItem("Foo");
|
||||
await toggleMenuItem("Foo");
|
||||
await animationFrame();
|
||||
|
||||
expect(isItemSelected("Foo")).toBe(false);
|
||||
});
|
||||
|
||||
test(`remove a "Group By" facet properly unchecks groupbys in groupby menu`, async () => {
|
||||
const searchBar = await mountWithSearch(SearchBar, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Foo" name="group_by_foo" context="{'group_by': 'foo'}"/>
|
||||
</search>
|
||||
`,
|
||||
context: { search_default_group_by_foo: 1 },
|
||||
});
|
||||
await toggleSearchBarMenu();
|
||||
expect(getFacetTexts()).toEqual(["Foo"]);
|
||||
expect(searchBar.env.searchModel.groupBy).toEqual(["foo"]);
|
||||
expect(isItemSelected("Foo")).toBe(true);
|
||||
|
||||
await removeFacet("Foo");
|
||||
expect(getFacetTexts()).toEqual([]);
|
||||
expect(searchBar.env.searchModel.groupBy).toEqual([]);
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
expect(isItemSelected("Foo")).toBe(false);
|
||||
});
|
||||
|
||||
test("group by a date field using interval works", async () => {
|
||||
const searchBar = await mountWithSearch(SearchBar, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Date" name="date" context="{'group_by': 'date_field:week'}"/>
|
||||
</search>
|
||||
`,
|
||||
context: { search_default_date: 1 },
|
||||
});
|
||||
await toggleSearchBarMenu();
|
||||
expect(searchBar.env.searchModel.groupBy).toEqual(["date_field:week"]);
|
||||
|
||||
await toggleMenuItem("Date");
|
||||
expect(isOptionSelected("Date", "Week")).toBe(true);
|
||||
expect(queryAllTexts`.o_item_option`).toEqual(["Year", "Quarter", "Month", "Week", "Day"]);
|
||||
|
||||
await toggleMenuItemOption("Date", "Year");
|
||||
expect(searchBar.env.searchModel.groupBy).toEqual(["date_field:year", "date_field:week"]);
|
||||
expect(getFacetTexts()).toEqual(["Date: Year\n>\nDate: Week"]);
|
||||
expect(isOptionSelected("Date", "Year")).toBe(true);
|
||||
expect(isOptionSelected("Date", "Week")).toBe(true);
|
||||
|
||||
await toggleMenuItemOption("Date", "Month");
|
||||
expect(searchBar.env.searchModel.groupBy).toEqual([
|
||||
"date_field:year",
|
||||
"date_field:month",
|
||||
"date_field:week",
|
||||
]);
|
||||
expect(getFacetTexts()).toEqual(["Date: Year\n>\nDate: Month\n>\nDate: Week"]);
|
||||
expect(isOptionSelected("Date", "Year")).toBe(true);
|
||||
expect(isOptionSelected("Date", "Month")).toBe(true);
|
||||
expect(isOptionSelected("Date", "Week")).toBe(true);
|
||||
|
||||
await toggleMenuItemOption("Date", "Week");
|
||||
expect(searchBar.env.searchModel.groupBy).toEqual(["date_field:year", "date_field:month"]);
|
||||
expect(getFacetTexts()).toEqual(["Date: Year\n>\nDate: Month"]);
|
||||
expect(isOptionSelected("Date", "Year")).toBe(true);
|
||||
expect(isOptionSelected("Date", "Month")).toBe(true);
|
||||
|
||||
await toggleMenuItemOption("Date", "Month");
|
||||
expect(searchBar.env.searchModel.groupBy).toEqual(["date_field:year"]);
|
||||
expect(getFacetTexts()).toEqual(["Date: Year"]);
|
||||
expect(isOptionSelected("Date", "Year")).toBe(true);
|
||||
|
||||
await toggleMenuItemOption("Date", "Year");
|
||||
expect(searchBar.env.searchModel.groupBy).toEqual([]);
|
||||
expect(getFacetTexts()).toEqual([]);
|
||||
});
|
||||
|
||||
test("interval options are correctly grouped and ordered", async () => {
|
||||
await mountWithSearch(SearchBar, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Bar" name="bar" context="{'group_by': 'bar'}"/>
|
||||
<filter string="Date" name="date" context="{'group_by': 'date_field'}"/>
|
||||
<filter string="Foo" name="foo" context="{'group_by': 'foo'}"/>
|
||||
</search>
|
||||
`,
|
||||
context: { search_default_bar: 1 },
|
||||
});
|
||||
expect(getFacetTexts()).toEqual(["Bar"]);
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
await toggleMenuItem("Date");
|
||||
await toggleMenuItemOption("Date", "Week");
|
||||
expect(getFacetTexts()).toEqual(["Bar\n>\nDate: Week"]);
|
||||
|
||||
await toggleMenuItemOption("Date", "Day");
|
||||
expect(getFacetTexts()).toEqual(["Bar\n>\nDate: Week\n>\nDate: Day"]);
|
||||
|
||||
await toggleMenuItemOption("Date", "Year");
|
||||
expect(getFacetTexts()).toEqual(["Bar\n>\nDate: Year\n>\nDate: Week\n>\nDate: Day"]);
|
||||
|
||||
await toggleMenuItem("Foo");
|
||||
expect(getFacetTexts()).toEqual(["Bar\n>\nDate: Year\n>\nDate: Week\n>\nDate: Day\n>\nFoo"]);
|
||||
|
||||
await toggleMenuItemOption("Date", "Quarter");
|
||||
expect(getFacetTexts()).toEqual([
|
||||
"Bar\n>\nDate: Year\n>\nDate: Quarter\n>\nDate: Week\n>\nDate: Day\n>\nFoo",
|
||||
]);
|
||||
|
||||
await toggleMenuItem("Bar");
|
||||
expect(getFacetTexts()).toEqual([
|
||||
"Date: Year\n>\nDate: Quarter\n>\nDate: Week\n>\nDate: Day\n>\nFoo",
|
||||
]);
|
||||
|
||||
await toggleMenuItemOption("Date", "Week");
|
||||
expect(getFacetTexts()).toEqual(["Date: Year\n>\nDate: Quarter\n>\nDate: Day\n>\nFoo"]);
|
||||
});
|
||||
|
||||
test("default groupbys can be ordered", async () => {
|
||||
const searchBar = await mountWithSearch(SearchBar, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Birthday" name="birthday" context="{'group_by': 'birthday'}"/>
|
||||
<filter string="Date" name="date" context="{'group_by': 'date_field:week'}"/>
|
||||
</search>
|
||||
`,
|
||||
context: { search_default_birthday: 2, search_default_date: 1 },
|
||||
});
|
||||
|
||||
// the default groupbys should be activated in the right order
|
||||
expect(searchBar.env.searchModel.groupBy).toEqual(["date_field:week", "birthday:month"]);
|
||||
expect(getFacetTexts()).toEqual(["Date: Week\n>\nBirthday: Month"]);
|
||||
});
|
||||
|
||||
test("a separator in groupbys does not cause problems", async () => {
|
||||
await mountWithSearch(SearchBar, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Date" name="coolName" context="{'group_by': 'date_field'}"/>
|
||||
<separator/>
|
||||
<filter string="Bar" name="superName" context="{'group_by': 'bar'}"/>
|
||||
</search>
|
||||
`,
|
||||
});
|
||||
await toggleSearchBarMenu();
|
||||
await toggleMenuItem("Date");
|
||||
await toggleMenuItemOption("Date", "Day");
|
||||
expect(isItemSelected("Date")).toBe(true);
|
||||
expect(isItemSelected("Bar")).toBe(false);
|
||||
expect(isOptionSelected("Date", "Day")).toBe(true);
|
||||
expect(getFacetTexts()).toEqual(["Date: Day"]);
|
||||
|
||||
await toggleMenuItem("Bar");
|
||||
expect(isItemSelected("Date")).toBe(true);
|
||||
expect(isItemSelected("Bar")).toBe(true);
|
||||
expect(isOptionSelected("Date", "Day")).toBe(true);
|
||||
expect(getFacetTexts()).toEqual(["Date: Day\n>\nBar"]);
|
||||
|
||||
await toggleMenuItemOption("Date", "Quarter");
|
||||
expect(isItemSelected("Date")).toBe(true);
|
||||
expect(isItemSelected("Bar")).toBe(true);
|
||||
expect(isOptionSelected("Date", "Quarter")).toBe(true);
|
||||
expect(isOptionSelected("Date", "Day")).toBe(true);
|
||||
expect(getFacetTexts()).toEqual(["Date: Quarter\n>\nDate: Day\n>\nBar"]);
|
||||
|
||||
await toggleMenuItem("Bar");
|
||||
expect(isItemSelected("Date")).toBe(true);
|
||||
expect(isItemSelected("Bar")).toBe(false);
|
||||
expect(isOptionSelected("Date", "Quarter")).toBe(true);
|
||||
expect(isOptionSelected("Date", "Day")).toBe(true);
|
||||
expect(getFacetTexts()).toEqual(["Date: Quarter\n>\nDate: Day"]);
|
||||
|
||||
await contains(`.o_facet_remove`).click();
|
||||
expect(getFacetTexts()).toEqual([]);
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
await toggleMenuItem("Date");
|
||||
expect(isItemSelected("Date")).toBe(false);
|
||||
expect(isItemSelected("Bar")).toBe(false);
|
||||
expect(isOptionSelected("Date", "Quarter")).toBe(false);
|
||||
expect(isOptionSelected("Date", "Day")).toBe(false);
|
||||
});
|
||||
|
||||
test("falsy search default groupbys are not activated", async () => {
|
||||
const searchBar = await mountWithSearch(SearchBar, {
|
||||
resModel: "foo",
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Birthday" name="birthday" context="{'group_by': 'birthday'}"/>
|
||||
<filter string="Date" name="date" context="{'group_by': 'foo'}"/>
|
||||
</search>
|
||||
`,
|
||||
context: { search_default_birthday: false, search_default_foo: 0 },
|
||||
});
|
||||
expect(searchBar.env.searchModel.groupBy).toEqual([]);
|
||||
expect(getFacetTexts()).toEqual([]);
|
||||
});
|
||||
|
||||
test("Custom group by menu is displayed when hideCustomGroupBy is not set", async () => {
|
||||
await mountWithSearch(SearchBarMenu, {
|
||||
resModel: "foo",
|
||||
searchViewId: false,
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Birthday" name="birthday" context="{'group_by': 'birthday'}"/>
|
||||
<filter string="Date" name="date" context="{'group_by': 'foo'}"/>
|
||||
</search>
|
||||
`,
|
||||
searchMenuTypes: ["groupBy"],
|
||||
});
|
||||
await toggleSearchBarMenu();
|
||||
expect(`.o_add_custom_group_menu`).toHaveCount(1);
|
||||
});
|
||||
|
||||
test("Custom group by menu is displayed when hideCustomGroupBy is false", async () => {
|
||||
await mountWithSearch(SearchBarMenu, {
|
||||
resModel: "foo",
|
||||
searchViewId: false,
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Birthday" name="birthday" context="{'group_by': 'birthday'}"/>
|
||||
<filter string="Date" name="date" context="{'group_by': 'foo'}"/>
|
||||
</search>
|
||||
`,
|
||||
hideCustomGroupBy: false,
|
||||
searchMenuTypes: ["groupBy"],
|
||||
});
|
||||
await toggleSearchBarMenu();
|
||||
expect(`.o_add_custom_group_menu`).toHaveCount(1);
|
||||
});
|
||||
|
||||
test("Custom group by menu is displayed when hideCustomGroupBy is true", async () => {
|
||||
await mountWithSearch(SearchBarMenu, {
|
||||
resModel: "foo",
|
||||
searchViewId: false,
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Birthday" name="birthday" context="{'group_by': 'birthday'}"/>
|
||||
<filter string="Date" name="date" context="{'group_by': 'foo'}"/>
|
||||
</search>
|
||||
`,
|
||||
hideCustomGroupBy: true,
|
||||
searchMenuTypes: ["groupBy"],
|
||||
});
|
||||
await toggleSearchBarMenu();
|
||||
expect(`.o_add_custom_group_menu`).toHaveCount(0);
|
||||
});
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import { defineModels, fields, models } from "@web/../tests/web_test_helpers";
|
||||
|
||||
export class Foo extends models.Model {
|
||||
bar = fields.Many2one({ relation: "partner" });
|
||||
foo = fields.Char();
|
||||
birthday = fields.Date();
|
||||
date_field = fields.Date({ string: "Date" });
|
||||
parent_id = fields.Many2one({ string: "Parent", relation: "parent.model" });
|
||||
properties = fields.Properties({
|
||||
definition_record: "parent_id",
|
||||
definition_record_field: "properties_definition",
|
||||
});
|
||||
|
||||
_views = {
|
||||
search: `
|
||||
<search>
|
||||
<filter name="birthday" date="birthday"/>
|
||||
<filter name="date_field" date="date_field"/>
|
||||
</search>
|
||||
`,
|
||||
};
|
||||
}
|
||||
|
||||
export class Partner extends models.Model {
|
||||
name = fields.Char();
|
||||
}
|
||||
|
||||
export class ParentModel extends models.Model {
|
||||
_name = "parent.model";
|
||||
|
||||
name = fields.Char();
|
||||
properties_definition = fields.PropertiesDefinition();
|
||||
}
|
||||
|
||||
export function defineSearchBarModels() {
|
||||
defineModels([Foo, Partner, ParentModel]);
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,136 @@
|
|||
import { describe, expect, test } from "@odoo/hoot";
|
||||
import { queryAllTexts } from "@odoo/hoot-dom";
|
||||
import { Component, xml } from "@odoo/owl";
|
||||
import {
|
||||
contains,
|
||||
defineModels,
|
||||
fields,
|
||||
models,
|
||||
mountWithSearch,
|
||||
} from "@web/../tests/web_test_helpers";
|
||||
|
||||
import { SearchPanel } from "@web/search/search_panel/search_panel";
|
||||
|
||||
class Partner extends models.Model {
|
||||
name = fields.Char();
|
||||
foo = fields.Char();
|
||||
bar = fields.Boolean();
|
||||
int_field = fields.Integer({ string: "Int Field", aggregator: "sum" });
|
||||
category_id = fields.Many2one({ string: "category", relation: "category" });
|
||||
state = fields.Selection({
|
||||
selection: [
|
||||
["abc", "ABC"],
|
||||
["def", "DEF"],
|
||||
["ghi", "GHI"],
|
||||
],
|
||||
});
|
||||
|
||||
_records = [
|
||||
{
|
||||
id: 1,
|
||||
bar: true,
|
||||
foo: "yop",
|
||||
int_field: 1,
|
||||
state: "abc",
|
||||
category_id: 6,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
bar: true,
|
||||
foo: "blip",
|
||||
int_field: 2,
|
||||
state: "def",
|
||||
category_id: 7,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
bar: true,
|
||||
foo: "gnap",
|
||||
int_field: 4,
|
||||
state: "ghi",
|
||||
category_id: 7,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
bar: false,
|
||||
foo: "blip",
|
||||
int_field: 8,
|
||||
state: "ghi",
|
||||
category_id: 7,
|
||||
},
|
||||
];
|
||||
_views = {
|
||||
search: /* xml */ `
|
||||
<search>
|
||||
<filter name="false_domain" string="False Domain" domain="[(0, '=', 1)]"/>
|
||||
<filter name="filter" string="Filter" domain="[('bar', '=', true)]"/>
|
||||
<filter name="true_domain" string="True Domain" domain="[(1, '=', 1)]"/>
|
||||
<filter name="group_by_bar" string="Bar" context="{ 'group_by': 'bar' }"/>
|
||||
<searchpanel view_types="kanban,list,toy">
|
||||
<field name="category_id" expand="1"/>
|
||||
</searchpanel>
|
||||
</search>
|
||||
`,
|
||||
};
|
||||
}
|
||||
|
||||
class Category extends models.Model {
|
||||
name = fields.Char({ string: "Category Name" });
|
||||
|
||||
_records = [
|
||||
{ id: 6, name: "gold" },
|
||||
{ id: 7, name: "silver" },
|
||||
];
|
||||
}
|
||||
|
||||
defineModels([Partner, Category]);
|
||||
|
||||
describe.current.tags("mobile");
|
||||
|
||||
test("basic search panel rendering", async () => {
|
||||
class Parent extends Component {
|
||||
static components = { SearchPanel };
|
||||
static template = xml`<SearchPanel/>`;
|
||||
static props = ["*"];
|
||||
}
|
||||
|
||||
await mountWithSearch(Parent, {
|
||||
resModel: "partner",
|
||||
searchViewId: false,
|
||||
});
|
||||
|
||||
expect(".o_search_panel .o-dropdown").toHaveCount(1);
|
||||
expect(".o_search_panel .o-dropdown").toHaveText("category");
|
||||
|
||||
await contains(".o_search_panel .o-dropdown").click();
|
||||
expect(".o_search_panel_section.o_search_panel_category").toHaveCount(1);
|
||||
expect(".o_search_panel_category_value").toHaveCount(3);
|
||||
expect(queryAllTexts(".o_search_panel_field li")).toEqual(["All", "gold", "silver"]);
|
||||
|
||||
await contains(".o_search_panel_category_value:nth-of-type(2) header").click();
|
||||
expect(".o_search_panel .o-dropdown").toHaveText("gold");
|
||||
expect(".o_search_panel a").toHaveCount(1);
|
||||
|
||||
await contains(".o_search_panel a").click();
|
||||
expect(".o_search_panel .o-dropdown").toHaveText("category");
|
||||
});
|
||||
|
||||
test("Dropdown closes on category selection", async () => {
|
||||
class Parent extends Component {
|
||||
static components = { SearchPanel };
|
||||
static template = xml`<SearchPanel/>`;
|
||||
static props = ["*"];
|
||||
}
|
||||
|
||||
await mountWithSearch(Parent, {
|
||||
resModel: "partner",
|
||||
searchViewId: false,
|
||||
});
|
||||
|
||||
expect(".o-dropdown--menu").toHaveCount(0);
|
||||
await contains(".o_search_panel .o-dropdown").click();
|
||||
expect(".o-dropdown--menu").toHaveCount(1);
|
||||
|
||||
await contains(".o_search_panel_category_value:nth-of-type(2) header").click();
|
||||
expect(".o-dropdown--menu").toHaveCount(0);
|
||||
});
|
||||
|
|
@ -0,0 +1,459 @@
|
|||
import { beforeEach, describe, expect, test } from "@odoo/hoot";
|
||||
import { mockDate, mockTimeZone } from "@odoo/hoot-mock";
|
||||
import { patchTranslations, patchWithCleanup } from "@web/../tests/web_test_helpers";
|
||||
|
||||
import { Domain } from "@web/core/domain";
|
||||
import { localization } from "@web/core/l10n/localization";
|
||||
import { constructDateDomain } from "@web/search/utils/dates";
|
||||
|
||||
describe.current.tags("headless");
|
||||
|
||||
const dateSearchItem = {
|
||||
fieldName: "date_field",
|
||||
fieldType: "date",
|
||||
optionsParams: {
|
||||
customOptions: [],
|
||||
endMonth: 0,
|
||||
endYear: 0,
|
||||
startMonth: -2,
|
||||
startYear: -2,
|
||||
},
|
||||
type: "dateFilter",
|
||||
};
|
||||
const dateTimeSearchItem = {
|
||||
...dateSearchItem,
|
||||
fieldType: "datetime",
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockTimeZone(0);
|
||||
patchWithCleanup(localization, { direction: "ltr" });
|
||||
patchTranslations();
|
||||
});
|
||||
|
||||
test("construct simple domain based on date field (no comparisonOptionId)", () => {
|
||||
mockDate("2020-06-01T13:00:00");
|
||||
const referenceMoment = luxon.DateTime.local();
|
||||
|
||||
let domain = constructDateDomain(referenceMoment, dateSearchItem, []);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(`[]`),
|
||||
description: "",
|
||||
});
|
||||
|
||||
domain = constructDateDomain(referenceMoment, dateSearchItem, ["month", "year"]);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
`["&", ("date_field", ">=", "2020-06-01"), ("date_field", "<=", "2020-06-30")]`
|
||||
),
|
||||
description: "June 2020",
|
||||
});
|
||||
|
||||
domain = constructDateDomain(referenceMoment, dateSearchItem, ["second_quarter", "year"]);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
`["&", ("date_field", ">=", "2020-04-01"), ("date_field", "<=", "2020-06-30")]`
|
||||
),
|
||||
description: "Q2 2020",
|
||||
});
|
||||
|
||||
domain = constructDateDomain(referenceMoment, dateSearchItem, ["year"]);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
`["&", ("date_field", ">=", "2020-01-01"), ("date_field", "<=", "2020-12-31")]`
|
||||
),
|
||||
description: "2020",
|
||||
});
|
||||
});
|
||||
|
||||
test("construct simple domain based on date field (no comparisonOptionId) - UTC+2", () => {
|
||||
mockTimeZone(2);
|
||||
mockDate("2020-06-01T00:00:00");
|
||||
const referenceMoment = luxon.DateTime.local();
|
||||
|
||||
let domain = constructDateDomain(referenceMoment, dateSearchItem, []);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(`[]`),
|
||||
description: "",
|
||||
});
|
||||
|
||||
domain = constructDateDomain(referenceMoment, dateSearchItem, ["month", "year"]);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
`["&", ("date_field", ">=", "2020-06-01"), ("date_field", "<=", "2020-06-30")]`
|
||||
),
|
||||
description: "June 2020",
|
||||
});
|
||||
|
||||
domain = constructDateDomain(referenceMoment, dateSearchItem, ["second_quarter", "year"]);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
`["&", ("date_field", ">=", "2020-04-01"), ("date_field", "<=", "2020-06-30")]`
|
||||
),
|
||||
description: "Q2 2020",
|
||||
});
|
||||
|
||||
domain = constructDateDomain(referenceMoment, dateSearchItem, ["year"]);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
`["&", ("date_field", ">=", "2020-01-01"), ("date_field", "<=", "2020-12-31")]`
|
||||
),
|
||||
description: "2020",
|
||||
});
|
||||
});
|
||||
|
||||
test("construct simple domain based on datetime field (no comparisonOptionId)", () => {
|
||||
mockDate("2020-06-01T13:00:00");
|
||||
const referenceMoment = luxon.DateTime.local();
|
||||
|
||||
let domain = constructDateDomain(referenceMoment, dateTimeSearchItem, ["month", "year"]);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
`["&", ("date_field", ">=", "2020-06-01 00:00:00"), ("date_field", "<=", "2020-06-30 23:59:59")]`
|
||||
),
|
||||
description: "June 2020",
|
||||
});
|
||||
|
||||
domain = constructDateDomain(referenceMoment, dateTimeSearchItem, ["second_quarter", "year"]);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
`["&", ("date_field", ">=", "2020-04-01 00:00:00"), ("date_field", "<=", "2020-06-30 23:59:59")]`
|
||||
),
|
||||
description: "Q2 2020",
|
||||
});
|
||||
|
||||
domain = constructDateDomain(referenceMoment, dateTimeSearchItem, ["year"]);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
`["&", ("date_field", ">=", "2020-01-01 00:00:00"), ("date_field", "<=", "2020-12-31 23:59:59")]`
|
||||
),
|
||||
description: "2020",
|
||||
});
|
||||
});
|
||||
|
||||
test("construct simple domain based on datetime field (no comparisonOptionId) - UTC+2", () => {
|
||||
mockTimeZone(2);
|
||||
mockDate("2020-06-01T00:00:00");
|
||||
const referenceMoment = luxon.DateTime.local();
|
||||
|
||||
let domain = constructDateDomain(referenceMoment, dateTimeSearchItem, ["month", "year"]);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
`["&", ("date_field", ">=", "2020-05-31 22:00:00"), ("date_field", "<=", "2020-06-30 21:59:59")]`
|
||||
),
|
||||
description: "June 2020",
|
||||
});
|
||||
|
||||
domain = constructDateDomain(referenceMoment, dateTimeSearchItem, ["second_quarter", "year"]);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
`["&", ("date_field", ">=", "2020-03-31 22:00:00"), ("date_field", "<=", "2020-06-30 21:59:59")]`
|
||||
),
|
||||
description: "Q2 2020",
|
||||
});
|
||||
|
||||
domain = constructDateDomain(referenceMoment, dateTimeSearchItem, ["year"]);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
`["&", ("date_field", ">=", "2019-12-31 22:00:00"), ("date_field", "<=", "2020-12-31 21:59:59")]`
|
||||
),
|
||||
description: "2020",
|
||||
});
|
||||
});
|
||||
|
||||
test("construct domain based on date field (no comparisonOptionId)", () => {
|
||||
mockDate("2020-01-01T12:00:00");
|
||||
const referenceMoment = luxon.DateTime.local();
|
||||
|
||||
let domain = constructDateDomain(referenceMoment, dateSearchItem, [
|
||||
"month",
|
||||
"first_quarter",
|
||||
"year",
|
||||
]);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
"[" +
|
||||
`"|", ` +
|
||||
`"&", ("date_field", ">=", "2020-01-01"), ("date_field", "<=", "2020-01-31"), ` +
|
||||
`"&", ("date_field", ">=", "2020-01-01"), ("date_field", "<=", "2020-03-31")` +
|
||||
"]"
|
||||
),
|
||||
description: "January 2020/Q1 2020",
|
||||
});
|
||||
|
||||
domain = constructDateDomain(referenceMoment, dateSearchItem, [
|
||||
"second_quarter",
|
||||
"year",
|
||||
"year-1",
|
||||
]);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
"[" +
|
||||
`"|", ` +
|
||||
`"&", ("date_field", ">=", "2019-04-01"), ("date_field", "<=", "2019-06-30"), ` +
|
||||
`"&", ("date_field", ">=", "2020-04-01"), ("date_field", "<=", "2020-06-30")` +
|
||||
"]"
|
||||
),
|
||||
description: "Q2 2019/Q2 2020",
|
||||
});
|
||||
|
||||
domain = constructDateDomain(referenceMoment, dateSearchItem, ["year", "month", "month-2"]);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
"[" +
|
||||
`"|", ` +
|
||||
`"&", ("date_field", ">=", "2020-01-01"), ("date_field", "<=", "2020-01-31"), ` +
|
||||
`"&", ("date_field", ">=", "2020-11-01"), ("date_field", "<=", "2020-11-30")` +
|
||||
"]"
|
||||
),
|
||||
description: "January 2020/November 2020",
|
||||
});
|
||||
});
|
||||
|
||||
test("construct domain based on datetime field (no comparisonOptionId)", () => {
|
||||
mockDate("2020-01-01T12:00:00");
|
||||
const referenceMoment = luxon.DateTime.local();
|
||||
|
||||
let domain = constructDateDomain(referenceMoment, dateTimeSearchItem, [
|
||||
"month",
|
||||
"first_quarter",
|
||||
"year",
|
||||
]);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
"[" +
|
||||
`"|", ` +
|
||||
`"&", ("date_field", ">=", "2020-01-01 00:00:00"), ("date_field", "<=", "2020-01-31 23:59:59"), ` +
|
||||
`"&", ("date_field", ">=", "2020-01-01 00:00:00"), ("date_field", "<=", "2020-03-31 23:59:59")` +
|
||||
"]"
|
||||
),
|
||||
description: "January 2020/Q1 2020",
|
||||
});
|
||||
|
||||
domain = constructDateDomain(referenceMoment, dateTimeSearchItem, [
|
||||
"second_quarter",
|
||||
"year",
|
||||
"year-1",
|
||||
]);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
"[" +
|
||||
`"|", ` +
|
||||
`"&", ("date_field", ">=", "2019-04-01 00:00:00"), ("date_field", "<=", "2019-06-30 23:59:59"), ` +
|
||||
`"&", ("date_field", ">=", "2020-04-01 00:00:00"), ("date_field", "<=", "2020-06-30 23:59:59")` +
|
||||
"]"
|
||||
),
|
||||
description: "Q2 2019/Q2 2020",
|
||||
});
|
||||
|
||||
domain = constructDateDomain(referenceMoment, dateTimeSearchItem, ["year", "month", "month-2"]);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
"[" +
|
||||
`"|", ` +
|
||||
`"&", ("date_field", ">=", "2020-01-01 00:00:00"), ("date_field", "<=", "2020-01-31 23:59:59"), ` +
|
||||
`"&", ("date_field", ">=", "2020-11-01 00:00:00"), ("date_field", "<=", "2020-11-30 23:59:59")` +
|
||||
"]"
|
||||
),
|
||||
description: "January 2020/November 2020",
|
||||
});
|
||||
});
|
||||
|
||||
test(`construct comparison domain based on date field and option "previous_period"`, () => {
|
||||
mockDate("2020-01-01T12:00:00");
|
||||
const referenceMoment = luxon.DateTime.local();
|
||||
|
||||
let domain = constructDateDomain(
|
||||
referenceMoment,
|
||||
dateSearchItem,
|
||||
["month", "first_quarter", "year"],
|
||||
"previous_period"
|
||||
);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
"[" +
|
||||
`"|", ` +
|
||||
`"&", ("date_field", ">=", "2019-10-01"), ("date_field", "<=", "2019-10-31"), ` +
|
||||
`"|", ` +
|
||||
`"&", ("date_field", ">=", "2019-11-01"), ("date_field", "<=", "2019-11-30"), ` +
|
||||
`"&", ("date_field", ">=", "2019-12-01"), ("date_field", "<=", "2019-12-31")` +
|
||||
"]"
|
||||
),
|
||||
description: "October 2019/November 2019/December 2019",
|
||||
});
|
||||
|
||||
domain = constructDateDomain(
|
||||
referenceMoment,
|
||||
dateSearchItem,
|
||||
["second_quarter", "year", "year-1"],
|
||||
"previous_period"
|
||||
);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
"[" +
|
||||
`"|", ` +
|
||||
`"&", ("date_field", ">=", "2018-01-01"), ("date_field", "<=", "2018-03-31"), ` +
|
||||
`"&", ("date_field", ">=", "2019-01-01"), ("date_field", "<=", "2019-03-31")` +
|
||||
"]"
|
||||
),
|
||||
description: "Q1 2018/Q1 2019",
|
||||
});
|
||||
|
||||
domain = constructDateDomain(
|
||||
referenceMoment,
|
||||
dateSearchItem,
|
||||
["year", "year-2", "month", "month-2"],
|
||||
"previous_period"
|
||||
);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
"[" +
|
||||
`"|", ` +
|
||||
`"&", ("date_field", ">=", "2015-02-01"), ("date_field", "<=", "2015-02-28"), ` +
|
||||
`"|", ` +
|
||||
`"&", ("date_field", ">=", "2015-12-01"), ("date_field", "<=", "2015-12-31"), ` +
|
||||
`"|", ` +
|
||||
`"&", ("date_field", ">=", "2017-02-01"), ("date_field", "<=", "2017-02-28"), ` +
|
||||
`"&", ("date_field", ">=", "2017-12-01"), ("date_field", "<=", "2017-12-31")` +
|
||||
"]"
|
||||
),
|
||||
description: "February 2015/December 2015/February 2017/December 2017",
|
||||
});
|
||||
|
||||
domain = constructDateDomain(
|
||||
referenceMoment,
|
||||
dateSearchItem,
|
||||
["year", "year-1"],
|
||||
"previous_period"
|
||||
);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
"[" +
|
||||
`"|", ` +
|
||||
`"&", ("date_field", ">=", "2017-01-01"), ("date_field", "<=", "2017-12-31"), ` +
|
||||
`"&", ("date_field", ">=", "2018-01-01"), ("date_field", "<=", "2018-12-31")` +
|
||||
"]"
|
||||
),
|
||||
description: "2017/2018",
|
||||
});
|
||||
|
||||
domain = constructDateDomain(
|
||||
referenceMoment,
|
||||
dateSearchItem,
|
||||
["second_quarter", "third_quarter", "year-1"],
|
||||
"previous_period"
|
||||
);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
"[" +
|
||||
`"|", ` +
|
||||
`"&", ("date_field", ">=", "2018-10-01"), ("date_field", "<=", "2018-12-31"), ` +
|
||||
`"&", ("date_field", ">=", "2019-01-01"), ("date_field", "<=", "2019-03-31")` +
|
||||
"]"
|
||||
),
|
||||
description: "Q4 2018/Q1 2019",
|
||||
});
|
||||
});
|
||||
|
||||
test(`construct comparison domain based on datetime field and option "previous_year"`, () => {
|
||||
mockDate("2020-06-01T13:00:00");
|
||||
const referenceMoment = luxon.DateTime.local();
|
||||
|
||||
let domain = constructDateDomain(
|
||||
referenceMoment,
|
||||
dateTimeSearchItem,
|
||||
["month", "first_quarter", "year"],
|
||||
"previous_year"
|
||||
);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
"[" +
|
||||
`"|", ` +
|
||||
`"&", ("date_field", ">=", "2019-06-01 00:00:00"), ("date_field", "<=", "2019-06-30 23:59:59"), ` +
|
||||
`"&", ("date_field", ">=", "2019-01-01 00:00:00"), ("date_field", "<=", "2019-03-31 23:59:59")` +
|
||||
"]"
|
||||
),
|
||||
description: "June 2019/Q1 2019",
|
||||
});
|
||||
|
||||
domain = constructDateDomain(
|
||||
referenceMoment,
|
||||
dateTimeSearchItem,
|
||||
["second_quarter", "year", "year-1"],
|
||||
"previous_year"
|
||||
);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
"[" +
|
||||
`"|", ` +
|
||||
`"&", ("date_field", ">=", "2018-04-01 00:00:00"), ("date_field", "<=", "2018-06-30 23:59:59"), ` +
|
||||
`"&", ("date_field", ">=", "2019-04-01 00:00:00"), ("date_field", "<=", "2019-06-30 23:59:59")` +
|
||||
"]"
|
||||
),
|
||||
description: "Q2 2018/Q2 2019",
|
||||
});
|
||||
|
||||
domain = constructDateDomain(
|
||||
referenceMoment,
|
||||
dateTimeSearchItem,
|
||||
["year", "year-2", "month", "month-2"],
|
||||
"previous_year"
|
||||
);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
"[" +
|
||||
`"|", ` +
|
||||
`"&", ("date_field", ">=", "2017-04-01 00:00:00"), ("date_field", "<=", "2017-04-30 23:59:59"), ` +
|
||||
`"|", ` +
|
||||
`"&", ("date_field", ">=", "2017-06-01 00:00:00"), ("date_field", "<=", "2017-06-30 23:59:59"), ` +
|
||||
`"|", ` +
|
||||
`"&", ("date_field", ">=", "2019-04-01 00:00:00"), ("date_field", "<=", "2019-04-30 23:59:59"), ` +
|
||||
`"&", ("date_field", ">=", "2019-06-01 00:00:00"), ("date_field", "<=", "2019-06-30 23:59:59")` +
|
||||
"]"
|
||||
),
|
||||
description: "April 2017/June 2017/April 2019/June 2019",
|
||||
});
|
||||
});
|
||||
|
||||
test("Quarter option: custom translation", async () => {
|
||||
mockDate("2020-06-01T13:00:00");
|
||||
const referenceMoment = luxon.DateTime.local().setLocale("en");
|
||||
patchTranslations({ Q2: "Deuxième trimestre de l'an de grâce" });
|
||||
|
||||
const domain = constructDateDomain(referenceMoment, dateSearchItem, ["second_quarter", "year"]);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
`["&", ("date_field", ">=", "2020-04-01"), ("date_field", "<=", "2020-06-30")]`
|
||||
),
|
||||
description: "Deuxième trimestre de l'an de grâce 2020",
|
||||
});
|
||||
});
|
||||
|
||||
test("Quarter option: right to left", async () => {
|
||||
mockDate("2020-06-01T13:00:00");
|
||||
const referenceMoment = luxon.DateTime.local().setLocale("en");
|
||||
patchWithCleanup(localization, { direction: "rtl" });
|
||||
|
||||
const domain = constructDateDomain(referenceMoment, dateSearchItem, ["second_quarter", "year"]);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
`["&", ("date_field", ">=", "2020-04-01"), ("date_field", "<=", "2020-06-30")]`
|
||||
),
|
||||
description: "2020 Q2",
|
||||
});
|
||||
});
|
||||
|
||||
test("Quarter option: custom translation and right to left", async () => {
|
||||
mockDate("2020-06-01T13:00:00");
|
||||
const referenceMoment = luxon.DateTime.local().setLocale("en");
|
||||
patchWithCleanup(localization, { direction: "rtl" });
|
||||
patchTranslations({ Q2: "2e Trimestre" });
|
||||
|
||||
const domain = constructDateDomain(referenceMoment, dateSearchItem, ["second_quarter", "year"]);
|
||||
expect(domain).toEqual({
|
||||
domain: new Domain(
|
||||
`["&", ("date_field", ">=", "2020-04-01"), ("date_field", "<=", "2020-06-30")]`
|
||||
),
|
||||
description: "2020 2e Trimestre",
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,347 @@
|
|||
import { expect, test } from "@odoo/hoot";
|
||||
import { animationFrame } from "@odoo/hoot-mock";
|
||||
import { Component, onWillStart, onWillUpdateProps, useState, useSubEnv, xml } from "@odoo/owl";
|
||||
import {
|
||||
defineModels,
|
||||
fields,
|
||||
getMenuItemTexts,
|
||||
models,
|
||||
mountWithCleanup,
|
||||
mountWithSearch,
|
||||
onRpc,
|
||||
toggleMenuItem,
|
||||
toggleSearchBarMenu,
|
||||
} from "@web/../tests/web_test_helpers";
|
||||
|
||||
import { SearchBarMenu } from "@web/search/search_bar_menu/search_bar_menu";
|
||||
import { WithSearch } from "@web/search/with_search/with_search";
|
||||
|
||||
class Animal extends models.Model {
|
||||
name = fields.Char();
|
||||
birthday = fields.Date({ groupable: false });
|
||||
type = fields.Selection({
|
||||
groupable: false,
|
||||
selection: [
|
||||
["omnivorous", "Omnivorous"],
|
||||
["herbivorous", "Herbivorous"],
|
||||
["carnivorous", "Carnivorous"],
|
||||
],
|
||||
});
|
||||
|
||||
_views = {
|
||||
[["search", 1]]: `
|
||||
<search>
|
||||
<filter name="filter" string="True domain" domain="[(1, '=', 1)]"/>
|
||||
<filter name="group_by" context="{ 'group_by': 'name' }"/>
|
||||
</search>
|
||||
`,
|
||||
};
|
||||
}
|
||||
|
||||
defineModels([Animal]);
|
||||
|
||||
test("simple rendering", async () => {
|
||||
class TestComponent extends Component {
|
||||
static props = ["*"];
|
||||
static template = xml`<div class="o_test_component">Test component content</div>`;
|
||||
}
|
||||
|
||||
await mountWithSearch(TestComponent, {
|
||||
resModel: "animal",
|
||||
});
|
||||
expect(".o_test_component").toHaveCount(1);
|
||||
expect(".o_test_component").toHaveText("Test component content");
|
||||
});
|
||||
|
||||
test("search model in sub env", async () => {
|
||||
class TestComponent extends Component {
|
||||
static props = ["*"];
|
||||
static template = xml`<div class="o_test_component">Test component content</div>`;
|
||||
}
|
||||
|
||||
const component = await mountWithSearch(TestComponent, {
|
||||
resModel: "animal",
|
||||
});
|
||||
expect(component.env.searchModel).not.toBeEmpty();
|
||||
});
|
||||
|
||||
test("search query props are passed as props to concrete component", async () => {
|
||||
class TestComponent extends Component {
|
||||
static props = ["*"];
|
||||
static template = xml`<div class="o_test_component">Test component content</div>`;
|
||||
|
||||
setup() {
|
||||
expect.step("setup");
|
||||
const { context, domain, groupBy, orderBy } = this.props;
|
||||
expect(context).toEqual({
|
||||
allowed_company_ids: [1],
|
||||
lang: "en",
|
||||
tz: "taht",
|
||||
uid: 7,
|
||||
key: "val",
|
||||
});
|
||||
expect(domain).toEqual([[0, "=", 1]]);
|
||||
expect(groupBy).toEqual(["birthday"]);
|
||||
expect(orderBy).toEqual([{ name: "bar", asc: true }]);
|
||||
}
|
||||
}
|
||||
|
||||
await mountWithSearch(TestComponent, {
|
||||
resModel: "animal",
|
||||
domain: [[0, "=", 1]],
|
||||
groupBy: ["birthday"],
|
||||
context: { key: "val" },
|
||||
orderBy: [{ name: "bar", asc: true }],
|
||||
});
|
||||
expect.verifySteps(["setup"]);
|
||||
});
|
||||
|
||||
test("do not load search view description by default", async () => {
|
||||
class TestComponent extends Component {
|
||||
static props = ["*"];
|
||||
static template = xml`<div class="o_test_component">Test component content</div>`;
|
||||
}
|
||||
|
||||
onRpc("get_views", ({ method }) => {
|
||||
expect.step(method);
|
||||
throw new Error("No get_views should be done");
|
||||
});
|
||||
await mountWithSearch(TestComponent, {
|
||||
resModel: "animal",
|
||||
});
|
||||
expect.verifySteps([]);
|
||||
});
|
||||
|
||||
test("load search view description if not provided and loadSearchView=true", async () => {
|
||||
class TestComponent extends Component {
|
||||
static props = ["*"];
|
||||
static template = xml`<div class="o_test_component">Test component content</div>`;
|
||||
}
|
||||
|
||||
onRpc("get_views", ({ method, kwargs }) => {
|
||||
expect.step(method);
|
||||
delete kwargs.options.mobile;
|
||||
expect(kwargs).toMatchObject({
|
||||
options: {
|
||||
action_id: false,
|
||||
load_filters: false,
|
||||
toolbar: false,
|
||||
embedded_action_id: false,
|
||||
embedded_parent_res_id: false,
|
||||
},
|
||||
views: [[false, "search"]],
|
||||
});
|
||||
});
|
||||
await mountWithSearch(TestComponent, {
|
||||
resModel: "animal",
|
||||
searchViewId: false,
|
||||
});
|
||||
expect.verifySteps(["get_views"]);
|
||||
});
|
||||
|
||||
test("do not load the search view description if provided even if loadSearchView=true", async () => {
|
||||
class TestComponent extends Component {
|
||||
static props = ["*"];
|
||||
static template = xml`<div class="o_test_component">Test component content</div>`;
|
||||
}
|
||||
|
||||
onRpc("get_views", ({ method }) => {
|
||||
expect.step(method);
|
||||
throw new Error("No get_views should be done");
|
||||
});
|
||||
await mountWithSearch(TestComponent, {
|
||||
resModel: "animal",
|
||||
searchViewArch: "<search/>",
|
||||
searchViewFields: {},
|
||||
searchViewId: false,
|
||||
});
|
||||
expect.verifySteps([]);
|
||||
});
|
||||
|
||||
test("load view description if it is not complete and loadSearchView=true", async () => {
|
||||
class TestComponent extends Component {
|
||||
static props = ["*"];
|
||||
static template = xml`<div class="o_test_component">Test component content</div>`;
|
||||
}
|
||||
|
||||
onRpc("get_views", ({ method, kwargs }) => {
|
||||
expect.step(method);
|
||||
delete kwargs.options.mobile;
|
||||
expect(kwargs.options).toEqual({
|
||||
action_id: false,
|
||||
load_filters: true,
|
||||
toolbar: false,
|
||||
embedded_action_id: false,
|
||||
embedded_parent_res_id: false,
|
||||
});
|
||||
});
|
||||
await mountWithSearch(TestComponent, {
|
||||
resModel: "animal",
|
||||
searchViewArch: "<search/>",
|
||||
searchViewFields: {},
|
||||
searchViewId: true,
|
||||
loadIrFilters: true,
|
||||
});
|
||||
expect.verifySteps(["get_views"]);
|
||||
});
|
||||
|
||||
test("load view description with given id if it is not provided and loadSearchView=true", async () => {
|
||||
class TestComponent extends Component {
|
||||
static props = ["*"];
|
||||
static components = { SearchBarMenu };
|
||||
static template = xml`<div class="o_test_component"><SearchBarMenu/></div>`;
|
||||
}
|
||||
|
||||
onRpc("get_views", ({ method, kwargs }) => {
|
||||
expect.step(method);
|
||||
expect(kwargs.views).toEqual([[1, "search"]]);
|
||||
});
|
||||
await mountWithSearch(TestComponent, {
|
||||
resModel: "animal",
|
||||
searchViewId: 1,
|
||||
});
|
||||
expect.verifySteps(["get_views"]);
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
expect(getMenuItemTexts()).toEqual([
|
||||
"True domain",
|
||||
"Add Custom Filter",
|
||||
"Name",
|
||||
"Add Custom Group\nCreated on\nDisplay name\nLast Modified on\nName",
|
||||
"Save current search",
|
||||
]);
|
||||
});
|
||||
|
||||
test("toggle a filter render the underlying component with an updated domain", async () => {
|
||||
class TestComponent extends Component {
|
||||
static props = ["*"];
|
||||
static components = { SearchBarMenu };
|
||||
static template = xml`<div class="o_test_component"><SearchBarMenu/></div>`;
|
||||
|
||||
setup() {
|
||||
onWillStart(() => {
|
||||
expect.step("willStart");
|
||||
expect(this.props.domain).toEqual([]);
|
||||
});
|
||||
onWillUpdateProps((nextProps) => {
|
||||
expect.step("willUpdateProps");
|
||||
expect(nextProps.domain).toEqual([[1, "=", 1]]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await mountWithSearch(TestComponent, {
|
||||
resModel: "animal",
|
||||
searchViewId: 1,
|
||||
});
|
||||
expect.verifySteps(["willStart"]);
|
||||
|
||||
await toggleSearchBarMenu();
|
||||
await toggleMenuItem("True domain");
|
||||
expect.verifySteps(["willUpdateProps"]);
|
||||
});
|
||||
|
||||
test("react to prop 'domain' changes", async () => {
|
||||
class TestComponent extends Component {
|
||||
static props = ["*"];
|
||||
static template = xml`<div class="o_test_component">Test component content</div>`;
|
||||
|
||||
setup() {
|
||||
onWillStart(() => {
|
||||
expect.step("willStart");
|
||||
expect(this.props.domain).toEqual([["type", "=", "carnivorous"]]);
|
||||
});
|
||||
onWillUpdateProps((nextProps) => {
|
||||
expect.step("willUpdateProps");
|
||||
expect(nextProps.domain).toEqual([["type", "=", "herbivorous"]]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class Parent extends Component {
|
||||
static props = ["*"];
|
||||
static template = xml`
|
||||
<WithSearch t-props="searchState" t-slot-scope="search">
|
||||
<TestComponent domain="search.domain"/>
|
||||
</WithSearch>
|
||||
`;
|
||||
static components = { WithSearch, TestComponent };
|
||||
setup() {
|
||||
useSubEnv({ config: {} });
|
||||
this.searchState = useState({
|
||||
resModel: "animal",
|
||||
domain: [["type", "=", "carnivorous"]],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const parent = await mountWithCleanup(Parent);
|
||||
expect.verifySteps(["willStart"]);
|
||||
|
||||
parent.searchState.domain = [["type", "=", "herbivorous"]];
|
||||
await animationFrame();
|
||||
expect.verifySteps(["willUpdateProps"]);
|
||||
});
|
||||
|
||||
test("search defaults are removed from context at reload", async function () {
|
||||
const context = {
|
||||
search_default_x: true,
|
||||
searchpanel_default_y: true,
|
||||
};
|
||||
|
||||
class TestComponent extends Component {
|
||||
static template = xml`<div class="o_test_component">Test component content</div>`;
|
||||
static props = { context: Object };
|
||||
setup() {
|
||||
onWillStart(() => {
|
||||
expect.step("willStart");
|
||||
expect(this.props.context).toEqual({
|
||||
lang: "en",
|
||||
tz: "taht",
|
||||
uid: 7,
|
||||
allowed_company_ids: [1],
|
||||
});
|
||||
});
|
||||
onWillUpdateProps((nextProps) => {
|
||||
expect.step("willUpdateProps");
|
||||
expect(nextProps.context).toEqual({
|
||||
lang: "en",
|
||||
tz: "taht",
|
||||
uid: 7,
|
||||
allowed_company_ids: [1],
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class Parent extends Component {
|
||||
static props = ["*"];
|
||||
static template = xml`
|
||||
<WithSearch t-props="searchState" t-slot-scope="search">
|
||||
<TestComponent
|
||||
context="search.context"
|
||||
/>
|
||||
</WithSearch>
|
||||
`;
|
||||
static components = { WithSearch, TestComponent };
|
||||
setup() {
|
||||
useSubEnv({ config: {} });
|
||||
this.searchState = useState({
|
||||
resModel: "animal",
|
||||
domain: [["type", "=", "carnivorous"]],
|
||||
context,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const parent = await mountWithCleanup(Parent);
|
||||
expect.verifySteps(["willStart"]);
|
||||
|
||||
expect(parent.searchState.context).toEqual(context);
|
||||
|
||||
parent.searchState.domain = [["type", "=", "herbivorous"]];
|
||||
|
||||
await animationFrame();
|
||||
expect.verifySteps(["willUpdateProps"]);
|
||||
expect(parent.searchState.context).toEqual(context);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue